MediaWiki  REL1_21
LogFormatter.php
Go to the documentation of this file.
00001 <?php
00033 class LogFormatter {
00034         // Audience options for viewing usernames, comments, and actions
00035         const FOR_PUBLIC = 1;
00036         const FOR_THIS_USER = 2;
00037 
00038         // Static->
00039 
00045         public static function newFromEntry( LogEntry $entry ) {
00046                 global $wgLogActionsHandlers;
00047                 $fulltype = $entry->getFullType();
00048                 $wildcard = $entry->getType() . '/*';
00049                 $handler = '';
00050 
00051                 if ( isset( $wgLogActionsHandlers[$fulltype] ) ) {
00052                         $handler = $wgLogActionsHandlers[$fulltype];
00053                 } elseif ( isset( $wgLogActionsHandlers[$wildcard] ) ) {
00054                         $handler = $wgLogActionsHandlers[$wildcard];
00055                 }
00056 
00057                 if ( $handler !== '' && is_string( $handler ) && class_exists( $handler ) ) {
00058                         return new $handler( $entry );
00059                 }
00060 
00061                 return new LegacyLogFormatter( $entry );
00062         }
00063 
00071         public static function newFromRow( $row ) {
00072                 return self::newFromEntry( DatabaseLogEntry::newFromRow( $row ) );
00073         }
00074 
00075         // Nonstatic->
00076 
00078         protected $entry;
00079 
00081         protected $audience = self::FOR_PUBLIC;
00082 
00084         protected $linkFlood = false;
00085 
00093         protected $plaintext = false;
00094 
00095         protected $irctext = false;
00096 
00097         protected function __construct( LogEntry $entry ) {
00098                 $this->entry = $entry;
00099                 $this->context = RequestContext::getMain();
00100         }
00101 
00106         public function setContext( IContextSource $context ) {
00107                 $this->context = $context;
00108         }
00109 
00116         public function setAudience( $audience ) {
00117                 $this->audience = ( $audience == self::FOR_THIS_USER )
00118                         ? self::FOR_THIS_USER
00119                         : self::FOR_PUBLIC;
00120         }
00121 
00127         protected function canView( $field ) {
00128                 if ( $this->audience == self::FOR_THIS_USER ) {
00129                         return LogEventsList::userCanBitfield(
00130                                 $this->entry->getDeleted(), $field, $this->context->getUser() );
00131                 } else {
00132                         return !$this->entry->isDeleted( $field );
00133                 }
00134         }
00135 
00142         public function setShowUserToolLinks( $value ) {
00143                 $this->linkFlood = $value;
00144         }
00145 
00153         public function getPlainActionText() {
00154                 $this->plaintext = true;
00155                 $text = $this->getActionText();
00156                 $this->plaintext = false;
00157                 return $text;
00158         }
00159 
00166         public function getIRCActionComment() {
00167                 $actionComment = $this->getIRCActionText();
00168                 $comment = $this->entry->getComment();
00169 
00170                 if ( $comment != '' ) {
00171                         if ( $actionComment == '' ) {
00172                                 $actionComment = $comment;
00173                         } else {
00174                                 $actionComment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $comment;
00175                         }
00176                 }
00177 
00178                 return $actionComment;
00179         }
00180 
00187         public function getIRCActionText() {
00188                 $this->plaintext = true;
00189                 $this->irctext = true;
00190 
00191                 $entry = $this->entry;
00192                 $parameters = $entry->getParameters();
00193                 // @see LogPage::actionText()
00194                 // Text of title the action is aimed at.
00195                 $target = $entry->getTarget()->getPrefixedText();
00196                 $text = null;
00197                 switch( $entry->getType() ) {
00198                         case 'move':
00199                                 switch( $entry->getSubtype() ) {
00200                                         case 'move':
00201                                                 $movesource = $parameters['4::target'];
00202                                                 $text = wfMessage( '1movedto2' )
00203                                                         ->rawParams( $target, $movesource )->inContentLanguage()->escaped();
00204                                                 break;
00205                                         case 'move_redir':
00206                                                 $movesource = $parameters['4::target'];
00207                                                 $text = wfMessage( '1movedto2_redir' )
00208                                                         ->rawParams( $target, $movesource )->inContentLanguage()->escaped();
00209                                                 break;
00210                                         case 'move-noredirect':
00211                                                 break;
00212                                         case 'move_redir-noredirect':
00213                                                 break;
00214                                 }
00215                                 break;
00216 
00217                         case 'delete':
00218                                 switch( $entry->getSubtype() ) {
00219                                         case 'delete':
00220                                                 $text = wfMessage( 'deletedarticle' )
00221                                                         ->rawParams( $target )->inContentLanguage()->escaped();
00222                                                 break;
00223                                         case 'restore':
00224                                                 $text = wfMessage( 'undeletedarticle' )
00225                                                         ->rawParams( $target )->inContentLanguage()->escaped();
00226                                                 break;
00227                                         //case 'revision': // Revision deletion
00228                                         //case 'event': // Log deletion
00229                                                 // see https://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/LogPage.php?&pathrev=97044&r1=97043&r2=97044
00230                                         //default:
00231                                 }
00232                                 break;
00233 
00234                         case 'patrol':
00235                                 // https://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/PatrolLog.php?&pathrev=97495&r1=97494&r2=97495
00236                                 // Create a diff link to the patrolled revision
00237                                 if ( $entry->getSubtype() === 'patrol' ) {
00238                                         $diffLink = htmlspecialchars(
00239                                                 wfMessage( 'patrol-log-diff', $parameters['4::curid'] )
00240                                                         ->inContentLanguage()->text() );
00241                                         $text = wfMessage( 'patrol-log-line', $diffLink, "[[$target]]", "" )
00242                                                 ->inContentLanguage()->text();
00243                                 } else {
00244                                         // broken??
00245                                 }
00246                                 break;
00247 
00248                         case 'protect':
00249                                 switch( $entry->getSubtype() ) {
00250                                 case 'protect':
00251                                         $text = wfMessage( 'protectedarticle' )
00252                                                 ->rawParams( $target . ' ' . $parameters[0] )->inContentLanguage()->escaped();
00253                                         break;
00254                                 case 'unprotect':
00255                                         $text = wfMessage( 'unprotectedarticle' )
00256                                                 ->rawParams( $target )->inContentLanguage()->escaped();
00257                                         break;
00258                                 case 'modify':
00259                                         $text = wfMessage( 'modifiedarticleprotection' )
00260                                                 ->rawParams( $target . ' ' . $parameters[0] )->inContentLanguage()->escaped();
00261                                         break;
00262                                 }
00263                                 break;
00264 
00265                         case 'newusers':
00266                                 switch( $entry->getSubtype() ) {
00267                                         case 'newusers':
00268                                         case 'create':
00269                                                 $text = wfMessage( 'newuserlog-create-entry' )
00270                                                         ->inContentLanguage()->escaped();
00271                                                 break;
00272                                         case 'create2':
00273                                         case 'byemail':
00274                                                 $text = wfMessage( 'newuserlog-create2-entry' )
00275                                                         ->rawParams( $target )->inContentLanguage()->escaped();
00276                                                 break;
00277                                         case 'autocreate':
00278                                                 $text = wfMessage( 'newuserlog-autocreate-entry' )
00279                                                         ->inContentLanguage()->escaped();
00280                                                 break;
00281                                 }
00282                                 break;
00283 
00284                         case 'upload':
00285                                 switch( $entry->getSubtype() ) {
00286                                         case 'upload':
00287                                                 $text = wfMessage( 'uploadedimage' )
00288                                                         ->rawParams( $target )->inContentLanguage()->escaped();
00289                                                 break;
00290                                         case 'overwrite':
00291                                                 $text = wfMessage( 'overwroteimage' )
00292                                                         ->rawParams( $target )->inContentLanguage()->escaped();
00293                                                 break;
00294                                 }
00295                                 break;
00296 
00297                         case 'rights':
00298                                 if ( count( $parameters['4::oldgroups'] ) ) {
00299                                         $oldgroups = implode( ', ', $parameters['4::oldgroups'] );
00300                                 } else {
00301                                         $oldgroups = wfMessage( 'rightsnone' )->inContentLanguage()->escaped();
00302                                 }
00303                                 if ( count( $parameters['5::newgroups'] ) ) {
00304                                         $newgroups = implode( ', ', $parameters['5::newgroups'] );
00305                                 } else {
00306                                         $newgroups = wfMessage( 'rightsnone' )->inContentLanguage()->escaped();
00307                                 }
00308                                 switch( $entry->getSubtype() ) {
00309                                         case 'rights':
00310                                                 $text = wfMessage( 'rightslogentry' )
00311                                                         ->rawParams( $target, $oldgroups, $newgroups )->inContentLanguage()->escaped();
00312                                                 break;
00313                                         case 'autopromote':
00314                                                 $text = wfMessage( 'rightslogentry-autopromote' )
00315                                                         ->rawParams( $target, $oldgroups, $newgroups )->inContentLanguage()->escaped();
00316                                                 break;
00317                                 }
00318                                 break;
00319 
00320                         // case 'suppress' --private log -- aaron  (sign your messages so we know who to blame in a few years :-D)
00321                         // default:
00322                 }
00323                 if( is_null( $text ) ) {
00324                         $text = $this->getPlainActionText();
00325                 }
00326 
00327                 $this->plaintext = false;
00328                 $this->irctext = false;
00329                 return $text;
00330         }
00331 
00336         public function getActionText() {
00337                 if ( $this->canView( LogPage::DELETED_ACTION ) ) {
00338                         $element = $this->getActionMessage();
00339                         if ( $element instanceof Message ) {
00340                                 $element = $this->plaintext ? $element->text() : $element->escaped();
00341                         }
00342                         if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) {
00343                                 $element = $this->styleRestricedElement( $element );
00344                         }
00345                 } else {
00346                         $performer = $this->getPerformerElement() . $this->msg( 'word-separator' )->text();
00347                         $element = $performer . $this->getRestrictedElement( 'rev-deleted-event' );
00348                 }
00349 
00350                 return $element;
00351         }
00352 
00359         protected function getActionMessage() {
00360                 $message = $this->msg( $this->getMessageKey() );
00361                 $message->params( $this->getMessageParameters() );
00362                 return $message;
00363         }
00364 
00372         protected function getMessageKey() {
00373                 $type = $this->entry->getType();
00374                 $subtype = $this->entry->getSubtype();
00375 
00376                 return "logentry-$type-$subtype";
00377         }
00378 
00384         public function getActionLinks() {
00385                 return '';
00386         }
00387 
00393         protected function extractParameters() {
00394                 $entry = $this->entry;
00395                 $params = array();
00396 
00397                 if ( $entry->isLegacy() ) {
00398                         foreach ( $entry->getParameters() as $index => $value ) {
00399                                 $params[$index + 3] = $value;
00400                         }
00401                 }
00402 
00403                 // Filter out parameters which are not in format #:foo
00404                 foreach ( $entry->getParameters() as $key => $value ) {
00405                         if ( strpos( $key, ':' ) === false ) {
00406                                 continue;
00407                         }
00408                         list( $index, $type, ) = explode( ':', $key, 3 );
00409                         $params[$index - 1] = $this->formatParameterValue( $type, $value );
00410                 }
00411 
00412                 /* Message class doesn't like non consecutive numbering.
00413                  * Fill in missing indexes with empty strings to avoid
00414                  * incorrect renumbering.
00415                  */
00416                 if ( count( $params ) ) {
00417                         $max = max( array_keys( $params ) );
00418                         for ( $i = 4; $i < $max; $i++ ) {
00419                                 if ( !isset( $params[$i] ) ) {
00420                                         $params[$i] = '';
00421                                 }
00422                         }
00423                 }
00424                 return $params;
00425         }
00426 
00436         protected function getMessageParameters() {
00437                 if ( isset( $this->parsedParameters ) ) {
00438                         return $this->parsedParameters;
00439                 }
00440 
00441                 $entry = $this->entry;
00442                 $params = $this->extractParameters();
00443                 $params[0] = Message::rawParam( $this->getPerformerElement() );
00444                 $params[1] = $this->canView( LogPage::DELETED_USER ) ? $entry->getPerformer()->getName() : '';
00445                 $params[2] = Message::rawParam( $this->makePageLink( $entry->getTarget() ) );
00446 
00447                 // Bad things happens if the numbers are not in correct order
00448                 ksort( $params );
00449                 return $this->parsedParameters = $params;
00450         }
00451 
00479         protected function formatParameterValue( $type, $value ) {
00480                 $saveLinkFlood = $this->linkFlood;
00481 
00482                 switch( strtolower( trim( $type ) ) ) {
00483                         case 'raw':
00484                                 $value = Message::rawParam( $value );
00485                                 break;
00486                         case 'msg':
00487                                 $value = $this->msg( $value )->text();
00488                                 break;
00489                         case 'msg-content':
00490                                 $value = $this->msg( $value )->inContentLanguage()->text();
00491                                 break;
00492                         case 'number':
00493                                 $value = Message::numParam( $value );
00494                                 break;
00495                         case 'user':
00496                                 $user = User::newFromName( $value );
00497                                 $value = $user->getName();
00498                                 break;
00499                         case 'user-link':
00500                                 $this->setShowUserToolLinks( false );
00501 
00502                                 $user = User::newFromName( $value );
00503                                 $value = Message::rawParam( $this->makeUserLink( $user ) );
00504 
00505                                 $this->setShowUserToolLinks( $saveLinkFlood );
00506                                 break;
00507                         case 'title':
00508                                 $title = Title::newFromText( $value );
00509                                 $value = $title->getPrefixedText();
00510                                 break;
00511                         case 'title-link':
00512                                 $title = Title::newFromText( $value );
00513                                 $value = Message::rawParam( $this->makePageLink( $title ) );
00514                                 break;
00515                         case 'plain':
00516                                 // Plain text, nothing to do
00517                         default:
00518                                 // Catch other types and use the old behavior (return as-is)
00519                 }
00520 
00521                 return $value;
00522         }
00523 
00532         protected function makePageLink( Title $title = null, $parameters = array() ) {
00533                 if ( !$this->plaintext ) {
00534                         $link = Linker::link( $title, null, array(), $parameters );
00535                 } else {
00536                         if ( !$title instanceof Title ) {
00537                                 throw new MWException( "Expected title, got null" );
00538                         }
00539                         $link = '[[' . $title->getPrefixedText() . ']]';
00540                 }
00541                 return $link;
00542         }
00543 
00550         public function getPerformerElement() {
00551                 if ( $this->canView( LogPage::DELETED_USER ) ) {
00552                         $performer = $this->entry->getPerformer();
00553                         $element = $this->makeUserLink( $performer );
00554                         if ( $this->entry->isDeleted( LogPage::DELETED_USER ) ) {
00555                                 $element = $this->styleRestricedElement( $element );
00556                         }
00557                 } else {
00558                         $element = $this->getRestrictedElement( 'rev-deleted-user' );
00559                 }
00560 
00561                 return $element;
00562         }
00563 
00568         public function getComment() {
00569                 if ( $this->canView( LogPage::DELETED_COMMENT ) ) {
00570                         $comment = Linker::commentBlock( $this->entry->getComment() );
00571                         // No hard coded spaces thanx
00572                         $element = ltrim( $comment );
00573                         if ( $this->entry->isDeleted( LogPage::DELETED_COMMENT ) ) {
00574                                 $element = $this->styleRestricedElement( $element );
00575                         }
00576                 } else {
00577                         $element = $this->getRestrictedElement( 'rev-deleted-comment' );
00578                 }
00579 
00580                 return $element;
00581         }
00582 
00588         protected function getRestrictedElement( $message ) {
00589                 if ( $this->plaintext ) {
00590                         return $this->msg( $message )->text();
00591                 }
00592 
00593                 $content = $this->msg( $message )->escaped();
00594                 $attribs = array( 'class' => 'history-deleted' );
00595                 return Html::rawElement( 'span', $attribs, $content );
00596         }
00597 
00603         protected function styleRestricedElement( $content ) {
00604                 if ( $this->plaintext ) {
00605                         return $content;
00606                 }
00607                 $attribs = array( 'class' => 'history-deleted' );
00608                 return Html::rawElement( 'span', $attribs, $content );
00609         }
00610 
00617         protected function msg( $key ) {
00618                 return $this->context->msg( $key );
00619         }
00620 
00621         protected function makeUserLink( User $user ) {
00622                 if ( $this->plaintext ) {
00623                         $element = $user->getName();
00624                 } else {
00625                         $element = Linker::userLink(
00626                                 $user->getId(),
00627                                 $user->getName()
00628                         );
00629 
00630                         if ( $this->linkFlood ) {
00631                                 $element .= Linker::userToolLinksRedContribs(
00632                                         $user->getId(),
00633                                         $user->getName(),
00634                                         $user->getEditCount()
00635                                 );
00636                         }
00637                 }
00638                 return $element;
00639         }
00640 
00644         public function getPreloadTitles() {
00645                 return array();
00646         }
00647 
00651         public function getMessageParametersForTesting() {
00652                 // This function was added because getMessageParameters() is
00653                 // protected and a change from protected to public caused
00654                 // problems with extensions
00655                 return $this->getMessageParameters();
00656         }
00657 
00658 }
00659 
00669 class LegacyLogFormatter extends LogFormatter {
00670 
00680         private $comment = null;
00681 
00689         private $revert = null;
00690 
00691         public function getComment() {
00692                 if ( $this->comment === null ) {
00693                         $this->comment = parent::getComment();
00694                 }
00695 
00696                 // Make sure we execute the LogLine hook so that we immediately return
00697                 // the correct value.
00698                 if ( $this->revert === null ) {
00699                         $this->getActionLinks();
00700                 }
00701 
00702                 return $this->comment;
00703         }
00704 
00705         protected function getActionMessage() {
00706                 $entry = $this->entry;
00707                 $action = LogPage::actionText(
00708                         $entry->getType(),
00709                         $entry->getSubtype(),
00710                         $entry->getTarget(),
00711                         $this->plaintext ? null : $this->context->getSkin(),
00712                         (array)$entry->getParameters(),
00713                         !$this->plaintext // whether to filter [[]] links
00714                 );
00715 
00716                 $performer = $this->getPerformerElement();
00717                 if ( !$this->irctext ) {
00718                         $action = $performer . $this->msg( 'word-separator' )->text() . $action;
00719                 }
00720 
00721                 return $action;
00722         }
00723 
00724         public function getActionLinks() {
00725                 if ( $this->revert !== null ) {
00726                         return $this->revert;
00727                 }
00728 
00729                 if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) {
00730                         return $this->revert = '';
00731                 }
00732 
00733                 $title = $this->entry->getTarget();
00734                 $type = $this->entry->getType();
00735                 $subtype = $this->entry->getSubtype();
00736 
00737                 // Show unblock/change block link
00738                 if ( ( $type == 'block' || $type == 'suppress' ) && ( $subtype == 'block' || $subtype == 'reblock' ) ) {
00739                         if ( !$this->context->getUser()->isAllowed( 'block' ) ) {
00740                                 return '';
00741                         }
00742 
00743                         $links = array(
00744                                 Linker::linkKnown(
00745                                         SpecialPage::getTitleFor( 'Unblock', $title->getDBkey() ),
00746                                         $this->msg( 'unblocklink' )->escaped()
00747                                 ),
00748                                 Linker::linkKnown(
00749                                         SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
00750                                         $this->msg( 'change-blocklink' )->escaped()
00751                                 )
00752                         );
00753                         return $this->msg( 'parentheses' )->rawParams(
00754                                 $this->context->getLanguage()->pipeList( $links ) )->escaped();
00755                 // Show change protection link
00756                 } elseif ( $type == 'protect' && ( $subtype == 'protect' || $subtype == 'modify' || $subtype == 'unprotect' ) ) {
00757                         $links = array(
00758                                 Linker::link( $title,
00759                                         $this->msg( 'hist' )->escaped(),
00760                                         array(),
00761                                         array(
00762                                                 'action' => 'history',
00763                                                 'offset' => $this->entry->getTimestamp()
00764                                         )
00765                                 )
00766                         );
00767                         if ( $this->context->getUser()->isAllowed( 'protect' ) ) {
00768                                 $links[] = Linker::linkKnown(
00769                                         $title,
00770                                         $this->msg( 'protect_change' )->escaped(),
00771                                         array(),
00772                                         array( 'action' => 'protect' )
00773                                 );
00774                         }
00775                         return $this->msg( 'parentheses' )->rawParams(
00776                                 $this->context->getLanguage()->pipeList( $links ) )->escaped();
00777                 // Show unmerge link
00778                 } elseif( $type == 'merge' && $subtype == 'merge' ) {
00779                         if ( !$this->context->getUser()->isAllowed( 'mergehistory' ) ) {
00780                                 return '';
00781                         }
00782 
00783                         $params = $this->extractParameters();
00784                         $revert = Linker::linkKnown(
00785                                 SpecialPage::getTitleFor( 'MergeHistory' ),
00786                                 $this->msg( 'revertmerge' )->escaped(),
00787                                 array(),
00788                                 array(
00789                                         'target' => $params[3],
00790                                         'dest' => $title->getPrefixedDBkey(),
00791                                         'mergepoint' => $params[4]
00792                                 )
00793                         );
00794                         return $this->msg( 'parentheses' )->rawParams( $revert )->escaped();
00795                 }
00796 
00797                 // Do nothing. The implementation is handled by the hook modifiying the
00798                 // passed-by-ref parameters. This also changes the default value so that
00799                 // getComment() and getActionLinks() do not call them indefinitely.
00800                 $this->revert = '';
00801 
00802                 // This is to populate the $comment member of this instance so that it
00803                 // can be modified when calling the hook just below.
00804                 if ( $this->comment === null ) {
00805                         $this->getComment();
00806                 }
00807 
00808                 $params = $this->entry->getParameters();
00809 
00810                 wfRunHooks( 'LogLine', array( $type, $subtype, $title, $params,
00811                         &$this->comment, &$this->revert, $this->entry->getTimestamp() ) );
00812 
00813                 return $this->revert;
00814         }
00815 }
00816 
00821 class MoveLogFormatter extends LogFormatter {
00822         public function getPreloadTitles() {
00823                 $params = $this->extractParameters();
00824                 return array( Title::newFromText( $params[3] ) );
00825         }
00826 
00827         protected function getMessageKey() {
00828                 $key = parent::getMessageKey();
00829                 $params = $this->getMessageParameters();
00830                 if ( isset( $params[4] ) && $params[4] === '1' ) {
00831                         $key .= '-noredirect';
00832                 }
00833                 return $key;
00834         }
00835 
00836         protected function getMessageParameters() {
00837                 $params = parent::getMessageParameters();
00838                 $oldname = $this->makePageLink( $this->entry->getTarget(), array( 'redirect' => 'no' ) );
00839                 $newname = $this->makePageLink( Title::newFromText( $params[3] ) );
00840                 $params[2] = Message::rawParam( $oldname );
00841                 $params[3] = Message::rawParam( $newname );
00842                 return $params;
00843         }
00844 
00845         public function getActionLinks() {
00846                 if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) // Action is hidden
00847                         || $this->entry->getSubtype() !== 'move'
00848                         || !$this->context->getUser()->isAllowed( 'move' ) )
00849                 {
00850                         return '';
00851                 }
00852 
00853                 $params = $this->extractParameters();
00854                 $destTitle = Title::newFromText( $params[3] );
00855                 if ( !$destTitle ) {
00856                         return '';
00857                 }
00858 
00859                 $revert = Linker::linkKnown(
00860                         SpecialPage::getTitleFor( 'Movepage' ),
00861                         $this->msg( 'revertmove' )->escaped(),
00862                         array(),
00863                         array(
00864                                 'wpOldTitle' => $destTitle->getPrefixedDBkey(),
00865                                 'wpNewTitle' => $this->entry->getTarget()->getPrefixedDBkey(),
00866                                 'wpReason'   => $this->msg( 'revertmove' )->inContentLanguage()->text(),
00867                                 'wpMovetalk' => 0
00868                         )
00869                 );
00870                 return $this->msg( 'parentheses' )->rawParams( $revert )->escaped();
00871         }
00872 }
00873 
00878 class DeleteLogFormatter extends LogFormatter {
00879         protected function getMessageKey() {
00880                 $key = parent::getMessageKey();
00881                 if ( in_array( $this->entry->getSubtype(), array( 'event', 'revision' ) ) ) {
00882                         if ( count( $this->getMessageParameters() ) < 5 ) {
00883                                 return "$key-legacy";
00884                         }
00885                 }
00886                 return $key;
00887         }
00888 
00889         protected function getMessageParameters() {
00890                 if ( isset( $this->parsedParametersDeleteLog ) ) {
00891                         return $this->parsedParametersDeleteLog;
00892                 }
00893 
00894                 $params = parent::getMessageParameters();
00895                 $subtype = $this->entry->getSubtype();
00896                 if ( in_array( $subtype, array( 'event', 'revision' ) ) ) {
00897                         // $params[3] here is 'revision' for page revisions, 'oldimage' for file versions, or a comma-separated list of log_ids for log entries.
00898                         // $subtype here is 'revision' for page revisions and file versions, or 'event' for log entries.
00899                         if (
00900                                 ( $subtype === 'event' && count( $params ) === 6 ) ||
00901                                 ( $subtype === 'revision' && isset( $params[3] ) && ( $params[3] === 'revision' || $params[3] === 'oldimage' ) )
00902                         ) {
00903                                 $paramStart = $subtype === 'revision' ? 4 : 3;
00904 
00905                                 $old = $this->parseBitField( $params[$paramStart+1] );
00906                                 $new = $this->parseBitField( $params[$paramStart+2] );
00907                                 list( $hid, $unhid, $extra ) = RevisionDeleter::getChanges( $new, $old );
00908                                 $changes = array();
00909                                 foreach ( $hid as $v ) {
00910                                         $changes[] = $this->msg( "$v-hid" )->plain();
00911                                 }
00912                                 foreach ( $unhid as $v ) {
00913                                         $changes[] = $this->msg( "$v-unhid" )->plain();
00914                                 }
00915                                 foreach ( $extra as $v ) {
00916                                         $changes[] = $this->msg( $v )->plain();
00917                                 }
00918                                 $changeText = $this->context->getLanguage()->listToText( $changes );
00919 
00920                                 $newParams = array_slice( $params, 0, 3 );
00921                                 $newParams[3] = $changeText;
00922                                 $count = count( explode( ',', $params[$paramStart] ) );
00923                                 $newParams[4] = $this->context->getLanguage()->formatNum( $count );
00924                                 return $this->parsedParametersDeleteLog = $newParams;
00925                         } else {
00926                                 return $this->parsedParametersDeleteLog = array_slice( $params, 0, 3 );
00927                         }
00928                 }
00929 
00930                 return $this->parsedParametersDeleteLog = $params;
00931         }
00932 
00933         protected function parseBitField( $string ) {
00934                 // Input is like ofield=2134 or just the number
00935                 if ( strpos( $string, 'field=' ) === 1 ) {
00936                         list( , $field ) = explode( '=', $string );
00937                         return (int) $field;
00938                 } else {
00939                         return (int) $string;
00940                 }
00941         }
00942 
00943         public function getActionLinks() {
00944                 $user = $this->context->getUser();
00945                 if ( !$user->isAllowed( 'deletedhistory' ) || $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) {
00946                         return '';
00947                 }
00948 
00949                 switch ( $this->entry->getSubtype() ) {
00950                 case 'delete': // Show undelete link
00951                         if( $user->isAllowed( 'undelete' ) ) {
00952                                 $message = 'undeletelink';
00953                         } else {
00954                                 $message = 'undeleteviewlink';
00955                         }
00956                         $revert = Linker::linkKnown(
00957                                 SpecialPage::getTitleFor( 'Undelete' ),
00958                                 $this->msg( $message )->escaped(),
00959                                 array(),
00960                                 array( 'target' => $this->entry->getTarget()->getPrefixedDBkey() )
00961                         );
00962                         return $this->msg( 'parentheses' )->rawParams( $revert )->escaped();
00963 
00964                 case 'revision': // If an edit was hidden from a page give a review link to the history
00965                         $params = $this->extractParameters();
00966                         if ( !isset( $params[3] ) || !isset( $params[4] ) ) {
00967                                 return '';
00968                         }
00969 
00970                         // Different revision types use different URL params...
00971                         $key = $params[3];
00972                         // This is a CSV of the IDs
00973                         $ids = explode( ',', $params[4] );
00974 
00975                         $links = array();
00976 
00977                         // If there's only one item, we can show a diff link
00978                         if ( count( $ids ) == 1 ) {
00979                                 // Live revision diffs...
00980                                 if ( $key == 'oldid' || $key == 'revision' ) {
00981                                         $links[] = Linker::linkKnown(
00982                                                 $this->entry->getTarget(),
00983                                                 $this->msg( 'diff' )->escaped(),
00984                                                 array(),
00985                                                 array(
00986                                                         'diff' => intval( $ids[0] ),
00987                                                         'unhide' => 1
00988                                                 )
00989                                         );
00990                                 // Deleted revision diffs...
00991                                 } elseif ( $key == 'artimestamp' || $key == 'archive' ) {
00992                                         $links[] = Linker::linkKnown(
00993                                                 SpecialPage::getTitleFor( 'Undelete' ),
00994                                                 $this->msg( 'diff' )->escaped(),
00995                                                 array(),
00996                                                 array(
00997                                                         'target'    => $this->entry->getTarget()->getPrefixedDBkey(),
00998                                                         'diff'      => 'prev',
00999                                                         'timestamp' => $ids[0]
01000                                                 )
01001                                         );
01002                                 }
01003                         }
01004 
01005                         // View/modify link...
01006                         $links[] = Linker::linkKnown(
01007                                 SpecialPage::getTitleFor( 'Revisiondelete' ),
01008                                 $this->msg( 'revdel-restore' )->escaped(),
01009                                 array(),
01010                                 array(
01011                                         'target' => $this->entry->getTarget()->getPrefixedText(),
01012                                         'type' => $key,
01013                                         'ids' => implode( ',', $ids ),
01014                                 )
01015                         );
01016 
01017                         return $this->msg( 'parentheses' )->rawParams(
01018                                 $this->context->getLanguage()->pipeList( $links ) )->escaped();
01019 
01020                 case 'event': // Hidden log items, give review link
01021                         $params = $this->extractParameters();
01022                         if ( !isset( $params[3] ) ) {
01023                                 return '';
01024                         }
01025                         // This is a CSV of the IDs
01026                         $query = $params[3];
01027                         // Link to each hidden object ID, $params[1] is the url param
01028                         $revert = Linker::linkKnown(
01029                                 SpecialPage::getTitleFor( 'Revisiondelete' ),
01030                                 $this->msg( 'revdel-restore' )->escaped(),
01031                                 array(),
01032                                 array(
01033                                         'target' => $this->entry->getTarget()->getPrefixedText(),
01034                                         'type' => 'logging',
01035                                         'ids' => $query
01036                                 )
01037                         );
01038                         return $this->msg( 'parentheses' )->rawParams( $revert )->escaped();
01039                 default:
01040                         return '';
01041                 }
01042         }
01043 }
01044 
01049 class PatrolLogFormatter extends LogFormatter {
01050         protected function getMessageKey() {
01051                 $key = parent::getMessageKey();
01052                 $params = $this->getMessageParameters();
01053                 if ( isset( $params[5] ) && $params[5] ) {
01054                         $key .= '-auto';
01055                 }
01056                 return $key;
01057         }
01058 
01059         protected function getMessageParameters() {
01060                 $params = parent::getMessageParameters();
01061 
01062                 $target = $this->entry->getTarget();
01063                 $oldid = $params[3];
01064                 $revision = $this->context->getLanguage()->formatNum( $oldid, true );
01065 
01066                 if ( $this->plaintext ) {
01067                         $revlink = $revision;
01068                 } elseif ( $target->exists() ) {
01069                         $query = array(
01070                                 'oldid' => $oldid,
01071                                 'diff' => 'prev'
01072                         );
01073                         $revlink = Linker::link( $target, htmlspecialchars( $revision ), array(), $query );
01074                 } else {
01075                         $revlink = htmlspecialchars( $revision );
01076                 }
01077 
01078                 $params[3] = Message::rawParam( $revlink );
01079                 return $params;
01080         }
01081 }
01082 
01087 class NewUsersLogFormatter extends LogFormatter {
01088         protected function getMessageParameters() {
01089                 $params = parent::getMessageParameters();
01090                 $subtype = $this->entry->getSubtype();
01091                 if ( $subtype === 'create2' || $subtype === 'byemail' ) {
01092                         if ( isset( $params[3] ) ) {
01093                                 $target = User::newFromId( $params[3] );
01094                         } else {
01095                                 $target = User::newFromName( $this->entry->getTarget()->getText(), false );
01096                         }
01097                         $params[2] = Message::rawParam( $this->makeUserLink( $target ) );
01098                         $params[3] = $target->getName();
01099                 }
01100                 return $params;
01101         }
01102 
01103         public function getComment() {
01104                 $timestamp = wfTimestamp( TS_MW, $this->entry->getTimestamp() );
01105                 if ( $timestamp < '20080129000000' ) {
01106                         # Suppress $comment from old entries (before 2008-01-29),
01107                         # not needed and can contain incorrect links
01108                         return '';
01109                 }
01110                 return parent::getComment();
01111         }
01112 
01113         public function getPreloadTitles() {
01114                 $subtype = $this->entry->getSubtype();
01115                 if ( $subtype === 'create2' || $subtype === 'byemail' ) {
01116                         //add the user talk to LinkBatch for the userLink
01117                         return array( Title::makeTitle( NS_USER_TALK, $this->entry->getTarget()->getText() ) );
01118                 }
01119                 return array();
01120         }
01121 }
01122 
01127 class RightsLogFormatter extends LogFormatter {
01128         protected function makePageLink( Title $title = null, $parameters = array() ) {
01129                 global $wgContLang, $wgUserrightsInterwikiDelimiter;
01130 
01131                 if ( !$this->plaintext ) {
01132                         $text = $wgContLang->ucfirst( $title->getText() );
01133                         $parts = explode( $wgUserrightsInterwikiDelimiter, $text, 2 );
01134 
01135                         if ( count( $parts ) === 2 ) {
01136                                 $titleLink = WikiMap::foreignUserLink( $parts[1], $parts[0],
01137                                         htmlspecialchars( $title->getPrefixedText() ) );
01138 
01139                                 if ( $titleLink !== false ) {
01140                                         return $titleLink;
01141                                 }
01142                         }
01143                 }
01144 
01145                 return parent::makePageLink( $title, $parameters );
01146         }
01147 
01148         protected function getMessageKey() {
01149                 $key = parent::getMessageKey();
01150                 $params = $this->getMessageParameters();
01151                 if ( !isset( $params[3] ) && !isset( $params[4] ) ) {
01152                         $key .= '-legacy';
01153                 }
01154                 return $key;
01155         }
01156 
01157         protected function getMessageParameters() {
01158                 $params = parent::getMessageParameters();
01159 
01160                 // Really old entries
01161                 if ( !isset( $params[3] ) && !isset( $params[4] ) ) {
01162                         return $params;
01163                 }
01164 
01165                 $oldGroups = $params[3];
01166                 $newGroups = $params[4];
01167 
01168                 // Less old entries
01169                 if ( $oldGroups === '' ) {
01170                         $oldGroups = array();
01171                 } elseif ( is_string( $oldGroups ) ) {
01172                         $oldGroups = array_map( 'trim', explode( ',', $oldGroups ) );
01173                 }
01174                 if ( $newGroups === '' ) {
01175                         $newGroups = array();
01176                 } elseif ( is_string( $newGroups ) ) {
01177                         $newGroups = array_map( 'trim', explode( ',', $newGroups ) );
01178                 }
01179 
01180                 $userName = $this->entry->getTarget()->getText();
01181                 if ( !$this->plaintext && count( $oldGroups ) ) {
01182                         foreach ( $oldGroups as &$group ) {
01183                                 $group = User::getGroupMember( $group, $userName );
01184                         }
01185                 }
01186                 if ( !$this->plaintext && count( $newGroups ) ) {
01187                         foreach ( $newGroups as &$group ) {
01188                                 $group = User::getGroupMember( $group, $userName );
01189                         }
01190                 }
01191 
01192                 $lang = $this->context->getLanguage();
01193                 if ( count( $oldGroups ) ) {
01194                         $params[3] = $lang->listToText( $oldGroups );
01195                 } else {
01196                         $params[3] = $this->msg( 'rightsnone' )->text();
01197                 }
01198                 if ( count( $newGroups ) ) {
01199                         // Array_values is used here because of bug 42211
01200                         // see use of array_unique in UserrightsPage::doSaveUserGroups on $newGroups.
01201                         $params[4] = $lang->listToText( array_values( $newGroups ) );
01202                 } else {
01203                         $params[4] = $this->msg( 'rightsnone' )->text();
01204                 }
01205 
01206                 return $params;
01207         }
01208 }