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
*
* 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,
'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',
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' )
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' )
$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.
$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 {
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 );
}
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];
}
}
+ // 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();
+ }
+
}
$logRelationsValues = array();
$logRelationsField = null;
+ $logParamsDetails = array();
if ( $id ) { // Protection of existing page
if ( !Hooks::run( 'ArticleProtect', array( &$this, &$user, $limit, $reason ) ) ) {
__METHOD__
);
if ( $restrictions != '' ) {
+ $cascadeValue = ( $cascade && $action == 'edit' ) ? 1 : 0;
$dbw->insert(
'page_restrictions',
array(
'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,
+ );
}
}
'pt_reason' => $reason,
), __METHOD__
);
+ $logParamsDetails[] = array(
+ 'type' => 'create',
+ 'level' => $limit['create'],
+ 'expiry' => $expiry['create'],
+ );
} else {
$dbw->delete( 'protected_titles',
array(
$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();
}
"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",
"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:\n<code>00: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.)</code>\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",
"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]]}}",
*/
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,
# 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,
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 ) );
}
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.