From e93fa029e2d61bc6d3ebc4e8cfeaaa246569d90f Mon Sep 17 00:00:00 2001 From: Alexandre Emsenhuber Date: Sat, 30 Jun 2012 20:18:03 +0200 Subject: [PATCH] Moved "action links" from LogEventsList to LogFormatter. * This allow extensions to set both action text and links directly from a subclass LogFormatter instead of having to use the LogLine hooks for this purpose. * Moved stuff for logs that already use a subclass of LogFormatter there and the remaining in LegacyLogFormatter. I had to an hack there so that the pass-by-ref of the comment parameter in the LogLine hook still works. * Removed RevisionDeleter::getLogLinks(), included it directly in DeleteLogFormatter::getActionLinks(); it have a so ugly signature that is very hard to use it somewhere else (and maintain backward compatibility too). * Removed the message caching system in LogEventsList since it's not used anymore. * Some misc cleanup to LogEventsList::logLine() Change-Id: I96d696eed2ae35f5f5640e422ceb0a5b0c33babf --- includes/logging/LogEventsList.php | 182 ++----------- includes/logging/LogFormatter.php | 274 +++++++++++++++++++- includes/revisiondelete/RevisionDeleter.php | 65 ----- 3 files changed, 288 insertions(+), 233 deletions(-) diff --git a/includes/logging/LogEventsList.php b/includes/logging/LogEventsList.php index 61cba5763f..6016641c43 100644 --- a/includes/logging/LogEventsList.php +++ b/includes/logging/LogEventsList.php @@ -30,11 +30,6 @@ class LogEventsList extends ContextSource { public $flags; - /** - * @var Array - */ - protected $message; - /** * @var Array */ @@ -59,7 +54,6 @@ class LogEventsList extends ContextSource { } $this->flags = $flags; - $this->preCacheMessages(); } /** @@ -72,22 +66,6 @@ class LogEventsList extends ContextSource { return $this->getTitle(); } - /** - * As we use the same small set of messages in various methods and that - * they are called often, we call them once and save them in $this->message - */ - private function preCacheMessages() { - // Precache various messages - if( !isset( $this->message ) ) { - $messages = array( 'revertmerge', 'protect_change', 'unblocklink', 'change-blocklink', - 'revertmove', 'undeletelink', 'undeleteviewlink', 'revdel-restore', 'hist', 'diff', - 'pipe-separator', 'revdel-restore-deleted', 'revdel-restore-visible' ); - foreach( $messages as $msg ) { - $this->message[$msg] = $this->msg( $msg )->escaped(); - } - } - } - /** * Set page title and show header for this log type * @param $type Array @@ -329,157 +307,35 @@ class LogEventsList extends ContextSource { $formatter->setContext( $this->getContext() ); $formatter->setShowUserToolLinks( !( $this->flags & self::NO_EXTRA_USER_LINKS ) ); + $title = $entry->getTarget(); + $time = htmlspecialchars( $this->getLanguage()->userTimeAndDate( + $entry->getTimestamp(), $this->getUser() ) ); + $action = $formatter->getActionText(); - $comment = $formatter->getComment(); - $classes = array( 'mw-logline-' . $entry->getType() ); - $title = $entry->getTarget(); - $time = $this->logTimestamp( $entry ); + if ( $this->flags & self::NO_ACTION_LINK ) { + $revert = ''; + } else { + $revert = $formatter->getActionLinks(); + if ( $revert != '' ) { + $revert = '' . $revert . ''; + } + } - // Extract extra parameters - $paramArray = LogPage::extractParams( $row->log_params ); - // Add review/revert links and such... - $revert = $this->logActionLinks( $row, $title, $paramArray, $comment ); + $comment = $formatter->getComment(); // Some user can hide log items and have review links $del = $this->getShowHideLinks( $row ); - if( $del != '' ) $del .= ' '; // Any tags... list( $tagDisplay, $newClasses ) = ChangeTags::formatSummaryRow( $row->ts_tags, 'logevent' ); - $classes = array_merge( $classes, $newClasses ); - - return Xml::tags( 'li', array( "class" => implode( ' ', $classes ) ), - $del . "$time $action $comment $revert $tagDisplay" ) . "\n"; - } - - private function logTimestamp( LogEntry $entry ) { - return htmlspecialchars( $this->getLanguage()->userTimeAndDate( - $entry->getTimestamp(), $this->getUser() ) ); - } + $classes = array_merge( + array( 'mw-logline-' . $entry->getType() ), + $newClasses + ); - /** - * @todo split up! - * - * @param $row - * @param Title $title - * @param Array $paramArray - * @param String $comment Passed by reference - * @return String - */ - private function logActionLinks( $row, $title, $paramArray, &$comment ) { - if( ( $this->flags & self::NO_ACTION_LINK ) // we don't want to see the action - || self::isDeleted( $row, LogPage::DELETED_ACTION ) ) // action is hidden - { - return ''; - } - $revert = ''; - if( self::typeAction( $row, 'move', 'move', 'move' ) && !empty( $paramArray[0] ) ) { - $destTitle = Title::newFromText( $paramArray[0] ); - if( $destTitle ) { - $revert = Linker::linkKnown( - SpecialPage::getTitleFor( 'Movepage' ), - $this->message['revertmove'], - array(), - array( - 'wpOldTitle' => $destTitle->getPrefixedDBkey(), - 'wpNewTitle' => $title->getPrefixedDBkey(), - 'wpReason' => $this->msg( 'revertmove' )->inContentLanguage()->text(), - 'wpMovetalk' => 0 - ) - ); - $revert = $this->msg( 'parentheses' )->rawParams( $revert )->escaped(); - } - // Show undelete link - } elseif( self::typeAction( $row, array( 'delete', 'suppress' ), 'delete', 'deletedhistory' ) ) { - if( !$this->getUser()->isAllowed( 'undelete' ) ) { - $viewdeleted = $this->message['undeleteviewlink']; - } else { - $viewdeleted = $this->message['undeletelink']; - } - $revert = Linker::linkKnown( - SpecialPage::getTitleFor( 'Undelete' ), - $viewdeleted, - array(), - array( 'target' => $title->getPrefixedDBkey() ) - ); - $revert = $this->msg( 'parentheses' )->rawParams( $revert )->escaped(); - // Show unblock/change block link - } elseif( self::typeAction( $row, array( 'block', 'suppress' ), array( 'block', 'reblock' ), 'block' ) ) { - $revert = Linker::linkKnown( - SpecialPage::getTitleFor( 'Unblock', $row->log_title ), - $this->message['unblocklink'] - ) . - $this->message['pipe-separator'] . - Linker::linkKnown( - SpecialPage::getTitleFor( 'Block', $row->log_title ), - $this->message['change-blocklink'] - ); - $revert = $this->msg( 'parentheses' )->rawParams( $revert )->escaped(); - // Show change protection link - } elseif( self::typeAction( $row, 'protect', array( 'modify', 'protect', 'unprotect' ) ) ) { - $revert .= Linker::link( $title, - $this->message['hist'], - array(), - array( - 'action' => 'history', - 'offset' => $row->log_timestamp - ) - ); - if( $this->getUser()->isAllowed( 'protect' ) ) { - $revert .= $this->message['pipe-separator'] . - Linker::link( $title, - $this->message['protect_change'], - array(), - array( 'action' => 'protect' ), - 'known' ); - } - $revert = ' ' . $this->msg( 'parentheses' )->rawParams( $revert )->escaped(); - // Show unmerge link - } elseif( self::typeAction( $row, 'merge', 'merge', 'mergehistory' ) ) { - $revert = Linker::linkKnown( - SpecialPage::getTitleFor( 'MergeHistory' ), - $this->message['revertmerge'], - array(), - array( - 'target' => $paramArray[0], - 'dest' => $title->getPrefixedDBkey(), - 'mergepoint' => $paramArray[1] - ) - ); - $revert = $this->msg( 'parentheses' )->rawParams( $revert )->escaped(); - // If an edit was hidden from a page give a review link to the history - } elseif( self::typeAction( $row, array( 'delete', 'suppress' ), 'revision', 'deletedhistory' ) ) { - $revert = RevisionDeleter::getLogLinks( $title, $paramArray, - $this->message ); - // Hidden log items, give review link - } elseif( self::typeAction( $row, array( 'delete', 'suppress' ), 'event', 'deletedhistory' ) ) { - if( count($paramArray) >= 1 ) { - $revdel = SpecialPage::getTitleFor( 'Revisiondelete' ); - // $paramArray[1] is a CSV of the IDs - $query = $paramArray[0]; - // Link to each hidden object ID, $paramArray[1] is the url param - $revert = Linker::linkKnown( - $revdel, - $this->message['revdel-restore'], - array(), - array( - 'target' => $title->getPrefixedText(), - 'type' => 'logging', - 'ids' => $query - ) - ); - $revert = $this->msg( 'parentheses' )->rawParams( $revert )->escaped(); - } - // Do nothing. The implementation is handled by the hook modifiying the passed-by-ref parameters. - } else { - wfRunHooks( 'LogLine', array( $row->log_type, $row->log_action, $title, $paramArray, - &$comment, &$revert, $row->log_timestamp ) ); - } - if( $revert != '' ) { - $revert = '' . $revert . ''; - } - return $revert; + return Html::rawElement( 'li', array( 'class' => $classes ), + "$del $time $action $comment $revert $tagDisplay" ) . "\n"; } /** diff --git a/includes/logging/LogFormatter.php b/includes/logging/LogFormatter.php index 4f3292f34f..8d2219a026 100644 --- a/includes/logging/LogFormatter.php +++ b/includes/logging/LogFormatter.php @@ -109,24 +109,24 @@ class LogFormatter { /** * Set the visibility restrictions for displaying content. - * If set to public, and an item is deleted, then it will be replaced + * If set to public, and an item is deleted, then it will be replaced * with a placeholder even if the context user is allowed to view it. * @param $audience integer self::FOR_THIS_USER or self::FOR_PUBLIC */ public function setAudience( $audience ) { $this->audience = ( $audience == self::FOR_THIS_USER ) - ? self::FOR_THIS_USER + ? self::FOR_THIS_USER : self::FOR_PUBLIC; } /** * Check if a log item can be displayed * @param $field integer LogPage::DELETED_* constant - * @return bool + * @return bool */ protected function canView( $field ) { if ( $this->audience == self::FOR_THIS_USER ) { - return LogEventsList::userCanBitfield( + return LogEventsList::userCanBitfield( $this->entry->getDeleted(), $field, $this->context->getUser() ); } else { return !$this->entry->isDeleted( $field ); @@ -279,7 +279,7 @@ class LogFormatter { break; } break; - + // case 'suppress' --private log -- aaron (sign your messages so we know who to blame in a few years :-D) // default: @@ -340,6 +340,15 @@ class LogFormatter { return "logentry-$type-$subtype"; } + /** + * Returns extra links that comes after the action text, like "revert", etc. + * + * @return string + */ + public function getActionLinks() { + return ''; + } + /** * Extracts the optional extra parameters for use in action messages. * The array indexes start from number 3. @@ -537,6 +546,41 @@ class LogFormatter { * @since 1.19 */ class LegacyLogFormatter extends LogFormatter { + + /** + * Backward compatibility for extension changing the comment from + * the LogLine hook. This will be set by the first call on getComment(), + * then it might be modified by the hook when calling getActionLinks(), + * so that the modified value will be returned when calling getComment() + * a second time. + * + * @var string|null + */ + private $comment = null; + + /** + * Cache for the result of getActionLinks() so that it does not need to + * run multiple times depending on the order that getComment() and + * getActionLinks() are called. + * + * @var string|null + */ + private $revert = null; + + public function getComment() { + if ( $this->comment === null ) { + $this->comment = parent::getComment(); + } + + // Make sure we execute the LogLine hook so that we immediately return + // the correct value. + if ( $this->revert === null ) { + $this->getActionLinks(); + } + + return $this->comment; + } + protected function getActionMessage() { $entry = $this->entry; $action = LogPage::actionText( @@ -556,6 +600,97 @@ class LegacyLogFormatter extends LogFormatter { return $action; } + public function getActionLinks() { + if ( $this->revert !== null ) { + return $this->revert; + } + + if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) { + return $this->revert = ''; + } + + $title = $this->entry->getTarget(); + $type = $this->entry->getType(); + $subtype = $this->entry->getSubtype(); + + // Show unblock/change block link + if ( ( $type == 'block' || $type == 'suppress' ) && ( $subtype == 'block' || $subtype == 'reblock' ) ) { + if ( !$this->context->getUser()->isAllowed( 'block' ) ) { + return ''; + } + + $links = array( + Linker::linkKnown( + SpecialPage::getTitleFor( 'Unblock', $title->getDBkey() ), + $this->msg( 'unblocklink' )->escaped() + ), + Linker::linkKnown( + SpecialPage::getTitleFor( 'Block', $title->getDBkey() ), + $this->msg( 'change-blocklink' )->escaped() + ) + ); + return $this->msg( 'parentheses' )->rawParams( + $this->context->getLanguage()->pipeList( $links ) )->escaped(); + // Show change protection link + } elseif ( $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(); + // Show unmerge link + } elseif( $type == 'merge' && $subtype == 'merge' ) { + if ( !$this->context->getUser()->isAllowed( 'mergehistory' ) ) { + return ''; + } + + $params = $this->extractParameters(); + $revert = Linker::linkKnown( + SpecialPage::getTitleFor( 'MergeHistory' ), + $this->msg( 'revertmerge' )->escaped(), + array(), + array( + 'target' => $params[3], + 'dest' => $title->getPrefixedDBkey(), + 'mergepoint' => $params[4] + ) + ); + return $this->msg( 'parentheses' )->rawParams( $revert )->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. + $this->revert = ''; + + // This is to populate the $comment member of this instance so that it + // can be modified when calling the hook just below. + if ( $this->comment === null ) { + $this->getComment(); + } + + $params = $this->entry->getParameters(); + + wfRunHooks( 'LogLine', array( $type, $subtype, $title, $params, + &$this->comment, &$this->revert, $this->entry->getTimestamp() ) ); + + return $this->revert; + } } /** @@ -585,6 +720,34 @@ class MoveLogFormatter extends LogFormatter { $params[3] = Message::rawParam( $newname ); return $params; } + + public function getActionLinks() { + if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) // Action is hidden + || $this->entry->getSubtype() !== 'move' + || !$this->context->getUser()->isAllowed( 'move' ) ) + { + return ''; + } + + $params = $this->extractParameters(); + $destTitle = Title::newFromText( $params[3] ); + if ( !$destTitle ) { + return ''; + } + + $revert = Linker::linkKnown( + SpecialPage::getTitleFor( 'Movepage' ), + $this->msg( 'revertmove' )->escaped(), + array(), + array( + 'wpOldTitle' => $destTitle->getPrefixedDBkey(), + 'wpNewTitle' => $this->entry->getTarget()->getPrefixedDBkey(), + 'wpReason' => $this->msg( 'revertmove' )->inContentLanguage()->text(), + 'wpMovetalk' => 0 + ) + ); + return $this->msg( 'parentheses' )->rawParams( $revert )->escaped(); + } } /** @@ -654,6 +817,107 @@ class DeleteLogFormatter extends LogFormatter { return (int) $string; } } + + public function getActionLinks() { + $user = $this->context->getUser(); + if ( !$user->isAllowed( 'deletedhistory' ) || $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) { + return ''; + } + + switch ( $this->entry->getSubtype() ) { + case 'delete': // Show undelete link + if( $user->isAllowed( 'undelete' ) ) { + $message = 'undeletelink'; + } else { + $message = 'undeleteviewlink'; + } + $revert = Linker::linkKnown( + SpecialPage::getTitleFor( 'Undelete' ), + $this->msg( $message )->escaped(), + array(), + array( 'target' => $this->entry->getTarget()->getPrefixedDBkey() ) + ); + return $this->msg( 'parentheses' )->rawParams( $revert )->escaped(); + + case 'revision': // If an edit was hidden from a page give a review link to the history + $params = $this->extractParameters(); + if ( !isset( $params[3] ) || !isset( $params[4] ) ) { + return ''; + } + + // Different revision types use different URL params... + $key = $params[3]; + // This is a CSV of the IDs + $ids = explode( ',', $params[4] ); + + $links = array(); + + // If there's only one item, we can show a diff link + if ( count( $ids ) == 1 ) { + // Live revision diffs... + if ( $key == 'oldid' || $key == 'revision' ) { + $links[] = Linker::linkKnown( + $this->entry->getTarget(), + $this->msg( 'diff' )->escaped(), + array(), + array( + 'diff' => intval( $ids[0] ), + 'unhide' => 1 + ) + ); + // Deleted revision diffs... + } elseif ( $key == 'artimestamp' || $key == 'archive' ) { + $links[] = Linker::linkKnown( + SpecialPage::getTitleFor( 'Undelete' ), + $this->msg( 'diff' )->escaped(), + array(), + array( + 'target' => $this->entry->getTarget()->getPrefixedDBKey(), + 'diff' => 'prev', + 'timestamp' => $ids[0] + ) + ); + } + } + + // View/modify link... + $links[] = Linker::linkKnown( + SpecialPage::getTitleFor( 'Revisiondelete' ), + $this->msg( 'revdel-restore' )->escaped(), + array(), + array( + 'target' => $this->entry->getTarget()->getPrefixedText(), + 'type' => $key, + 'ids' => implode( ',', $ids ), + ) + ); + + return $this->msg( 'parentheses' )->rawParams( + $this->context->getLanguage()->pipeList( $links ) )->escaped(); + + case 'event': // Hidden log items, give review link + $params = $this->extractParameters(); + if ( !isset( $params[3] ) ) { + return ''; + } + // This is a CSV of the IDs + $query = $params[3]; + // Link to each hidden object ID, $params[1] is the url param + $revert = Linker::linkKnown( + SpecialPage::getTitleFor( 'Revisiondelete' ), + $this->msg( 'revdel-restore' )->escaped(), + array(), + array( + 'target' => $this->entry->getTarget()->getPrefixedText(), + 'type' => 'logging', + 'ids' => $query + ) + ); + return $this->msg( 'parentheses' )->rawParams( $revert )->escaped(); + default: + return ''; + } + } } /** diff --git a/includes/revisiondelete/RevisionDeleter.php b/includes/revisiondelete/RevisionDeleter.php index 95d372c540..c59edc2a34 100644 --- a/includes/revisiondelete/RevisionDeleter.php +++ b/includes/revisiondelete/RevisionDeleter.php @@ -124,69 +124,4 @@ class RevisionDeleter { return $timestamp; } - - /** - * Creates utility links for log entries. - * - * @param $title Title - * @param $paramArray Array - * @param $messages - * @return String - */ - public static function getLogLinks( $title, $paramArray, $messages ) { - global $wgLang; - - if ( count( $paramArray ) >= 2 ) { - // Different revision types use different URL params... - $key = $paramArray[0]; - // $paramArray[1] is a CSV of the IDs - $Ids = explode( ',', $paramArray[1] ); - - $revert = array(); - - // Diff link for single rev deletions - if ( count( $Ids ) == 1 ) { - // Live revision diffs... - if ( in_array( $key, array( 'oldid', 'revision' ) ) ) { - $revert[] = Linker::linkKnown( - $title, - $messages['diff'], - array(), - array( - 'diff' => intval( $Ids[0] ), - 'unhide' => 1 - ) - ); - // Deleted revision diffs... - } elseif ( in_array( $key, array( 'artimestamp','archive' ) ) ) { - $revert[] = Linker::linkKnown( - SpecialPage::getTitleFor( 'Undelete' ), - $messages['diff'], - array(), - array( - 'target' => $title->getPrefixedDBKey(), - 'diff' => 'prev', - 'timestamp' => $Ids[0] - ) - ); - } - } - - // View/modify link... - $revert[] = Linker::linkKnown( - SpecialPage::getTitleFor( 'Revisiondelete' ), - $messages['revdel-restore'], - array(), - array( - 'target' => $title->getPrefixedText(), - 'type' => $key, - 'ids' => implode(',', $Ids), - ) - ); - - // Pipe links - return wfMsg( 'parentheses', $wgLang->pipeList( $revert ) ); - } - return ''; - } } -- 2.20.1