MediaWiki  REL1_23
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 
00096     protected $irctext = false;
00097 
00098     protected function __construct( LogEntry $entry ) {
00099         $this->entry = $entry;
00100         $this->context = RequestContext::getMain();
00101     }
00102 
00107     public function setContext( IContextSource $context ) {
00108         $this->context = $context;
00109     }
00110 
00117     public function setAudience( $audience ) {
00118         $this->audience = ( $audience == self::FOR_THIS_USER )
00119             ? self::FOR_THIS_USER
00120             : self::FOR_PUBLIC;
00121     }
00122 
00128     protected function canView( $field ) {
00129         if ( $this->audience == self::FOR_THIS_USER ) {
00130             return LogEventsList::userCanBitfield(
00131                 $this->entry->getDeleted(), $field, $this->context->getUser() );
00132         } else {
00133             return !$this->entry->isDeleted( $field );
00134         }
00135     }
00136 
00143     public function setShowUserToolLinks( $value ) {
00144         $this->linkFlood = $value;
00145     }
00146 
00154     public function getPlainActionText() {
00155         $this->plaintext = true;
00156         $text = $this->getActionText();
00157         $this->plaintext = false;
00158 
00159         return $text;
00160     }
00161 
00168     public function getIRCActionComment() {
00169         $actionComment = $this->getIRCActionText();
00170         $comment = $this->entry->getComment();
00171 
00172         if ( $comment != '' ) {
00173             if ( $actionComment == '' ) {
00174                 $actionComment = $comment;
00175             } else {
00176                 $actionComment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $comment;
00177             }
00178         }
00179 
00180         return $actionComment;
00181     }
00182 
00189     public function getIRCActionText() {
00190         $this->plaintext = true;
00191         $this->irctext = true;
00192 
00193         $entry = $this->entry;
00194         $parameters = $entry->getParameters();
00195         // @see LogPage::actionText()
00196         // Text of title the action is aimed at.
00197         $target = $entry->getTarget()->getPrefixedText();
00198         $text = null;
00199         switch ( $entry->getType() ) {
00200             case 'move':
00201                 switch ( $entry->getSubtype() ) {
00202                     case 'move':
00203                         $movesource = $parameters['4::target'];
00204                         $text = wfMessage( '1movedto2' )
00205                             ->rawParams( $target, $movesource )->inContentLanguage()->escaped();
00206                         break;
00207                     case 'move_redir':
00208                         $movesource = $parameters['4::target'];
00209                         $text = wfMessage( '1movedto2_redir' )
00210                             ->rawParams( $target, $movesource )->inContentLanguage()->escaped();
00211                         break;
00212                     case 'move-noredirect':
00213                         break;
00214                     case 'move_redir-noredirect':
00215                         break;
00216                 }
00217                 break;
00218 
00219             case 'delete':
00220                 switch ( $entry->getSubtype() ) {
00221                     case 'delete':
00222                         $text = wfMessage( 'deletedarticle' )
00223                             ->rawParams( $target )->inContentLanguage()->escaped();
00224                         break;
00225                     case 'restore':
00226                         $text = wfMessage( 'undeletedarticle' )
00227                             ->rawParams( $target )->inContentLanguage()->escaped();
00228                         break;
00229                     // @codingStandardsIgnoreStart Long line
00230                     //case 'revision': // Revision deletion
00231                     //case 'event': // Log deletion
00232                     // see https://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/LogPage.php?&pathrev=97044&r1=97043&r2=97044
00233                     //default:
00234                     // @codingStandardsIgnoreEnd
00235                 }
00236                 break;
00237 
00238             case 'patrol':
00239                 // @codingStandardsIgnoreStart Long line
00240                 // https://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/PatrolLog.php?&pathrev=97495&r1=97494&r2=97495
00241                 // @codingStandardsIgnoreEnd
00242                 // Create a diff link to the patrolled revision
00243                 if ( $entry->getSubtype() === 'patrol' ) {
00244                     $diffLink = htmlspecialchars(
00245                         wfMessage( 'patrol-log-diff', $parameters['4::curid'] )
00246                             ->inContentLanguage()->text() );
00247                     $text = wfMessage( 'patrol-log-line', $diffLink, "[[$target]]", "" )
00248                         ->inContentLanguage()->text();
00249                 } else {
00250                     // broken??
00251                 }
00252                 break;
00253 
00254             case 'protect':
00255                 switch ( $entry->getSubtype() ) {
00256                     case 'protect':
00257                         $text = wfMessage( 'protectedarticle' )
00258                             ->rawParams( $target . ' ' . $parameters[0] )->inContentLanguage()->escaped();
00259                         break;
00260                     case 'unprotect':
00261                         $text = wfMessage( 'unprotectedarticle' )
00262                             ->rawParams( $target )->inContentLanguage()->escaped();
00263                         break;
00264                     case 'modify':
00265                         $text = wfMessage( 'modifiedarticleprotection' )
00266                             ->rawParams( $target . ' ' . $parameters[0] )->inContentLanguage()->escaped();
00267                         break;
00268                 }
00269                 break;
00270 
00271             case 'newusers':
00272                 switch ( $entry->getSubtype() ) {
00273                     case 'newusers':
00274                     case 'create':
00275                         $text = wfMessage( 'newuserlog-create-entry' )
00276                             ->inContentLanguage()->escaped();
00277                         break;
00278                     case 'create2':
00279                     case 'byemail':
00280                         $text = wfMessage( 'newuserlog-create2-entry' )
00281                             ->rawParams( $target )->inContentLanguage()->escaped();
00282                         break;
00283                     case 'autocreate':
00284                         $text = wfMessage( 'newuserlog-autocreate-entry' )
00285                             ->inContentLanguage()->escaped();
00286                         break;
00287                 }
00288                 break;
00289 
00290             case 'upload':
00291                 switch ( $entry->getSubtype() ) {
00292                     case 'upload':
00293                         $text = wfMessage( 'uploadedimage' )
00294                             ->rawParams( $target )->inContentLanguage()->escaped();
00295                         break;
00296                     case 'overwrite':
00297                         $text = wfMessage( 'overwroteimage' )
00298                             ->rawParams( $target )->inContentLanguage()->escaped();
00299                         break;
00300                 }
00301                 break;
00302 
00303             case 'rights':
00304                 if ( count( $parameters['4::oldgroups'] ) ) {
00305                     $oldgroups = implode( ', ', $parameters['4::oldgroups'] );
00306                 } else {
00307                     $oldgroups = wfMessage( 'rightsnone' )->inContentLanguage()->escaped();
00308                 }
00309                 if ( count( $parameters['5::newgroups'] ) ) {
00310                     $newgroups = implode( ', ', $parameters['5::newgroups'] );
00311                 } else {
00312                     $newgroups = wfMessage( 'rightsnone' )->inContentLanguage()->escaped();
00313                 }
00314                 switch ( $entry->getSubtype() ) {
00315                     case 'rights':
00316                         $text = wfMessage( 'rightslogentry' )
00317                             ->rawParams( $target, $oldgroups, $newgroups )->inContentLanguage()->escaped();
00318                         break;
00319                     case 'autopromote':
00320                         $text = wfMessage( 'rightslogentry-autopromote' )
00321                             ->rawParams( $target, $oldgroups, $newgroups )->inContentLanguage()->escaped();
00322                         break;
00323                 }
00324                 break;
00325             // case 'suppress' --private log -- aaron  (so we know who to blame in a few years :-D)
00326             // default:
00327         }
00328         if ( is_null( $text ) ) {
00329             $text = $this->getPlainActionText();
00330         }
00331 
00332         $this->plaintext = false;
00333         $this->irctext = false;
00334 
00335         return $text;
00336     }
00337 
00342     public function getActionText() {
00343         if ( $this->canView( LogPage::DELETED_ACTION ) ) {
00344             $element = $this->getActionMessage();
00345             if ( $element instanceof Message ) {
00346                 $element = $this->plaintext ? $element->text() : $element->escaped();
00347             }
00348             if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) {
00349                 $element = $this->styleRestricedElement( $element );
00350             }
00351         } else {
00352             $performer = $this->getPerformerElement() . $this->msg( 'word-separator' )->text();
00353             $element = $performer . $this->getRestrictedElement( 'rev-deleted-event' );
00354         }
00355 
00356         return $element;
00357     }
00358 
00365     protected function getActionMessage() {
00366         $message = $this->msg( $this->getMessageKey() );
00367         $message->params( $this->getMessageParameters() );
00368 
00369         return $message;
00370     }
00371 
00379     protected function getMessageKey() {
00380         $type = $this->entry->getType();
00381         $subtype = $this->entry->getSubtype();
00382 
00383         return "logentry-$type-$subtype";
00384     }
00385 
00391     public function getActionLinks() {
00392         return '';
00393     }
00394 
00400     protected function extractParameters() {
00401         $entry = $this->entry;
00402         $params = array();
00403 
00404         if ( $entry->isLegacy() ) {
00405             foreach ( $entry->getParameters() as $index => $value ) {
00406                 $params[$index + 3] = $value;
00407             }
00408         }
00409 
00410         // Filter out parameters which are not in format #:foo
00411         foreach ( $entry->getParameters() as $key => $value ) {
00412             if ( strpos( $key, ':' ) === false ) {
00413                 continue;
00414             }
00415             list( $index, $type, ) = explode( ':', $key, 3 );
00416             $params[$index - 1] = $this->formatParameterValue( $type, $value );
00417         }
00418 
00419         /* Message class doesn't like non consecutive numbering.
00420          * Fill in missing indexes with empty strings to avoid
00421          * incorrect renumbering.
00422          */
00423         if ( count( $params ) ) {
00424             $max = max( array_keys( $params ) );
00425             for ( $i = 4; $i < $max; $i++ ) {
00426                 if ( !isset( $params[$i] ) ) {
00427                     $params[$i] = '';
00428                 }
00429             }
00430         }
00431 
00432         return $params;
00433     }
00434 
00444     protected function getMessageParameters() {
00445         if ( isset( $this->parsedParameters ) ) {
00446             return $this->parsedParameters;
00447         }
00448 
00449         $entry = $this->entry;
00450         $params = $this->extractParameters();
00451         $params[0] = Message::rawParam( $this->getPerformerElement() );
00452         $params[1] = $this->canView( LogPage::DELETED_USER ) ? $entry->getPerformer()->getName() : '';
00453         $params[2] = Message::rawParam( $this->makePageLink( $entry->getTarget() ) );
00454 
00455         // Bad things happens if the numbers are not in correct order
00456         ksort( $params );
00457 
00458         $this->parsedParameters = $params;
00459         return $this->parsedParameters;
00460     }
00461 
00489     protected function formatParameterValue( $type, $value ) {
00490         $saveLinkFlood = $this->linkFlood;
00491 
00492         switch ( strtolower( trim( $type ) ) ) {
00493             case 'raw':
00494                 $value = Message::rawParam( $value );
00495                 break;
00496             case 'msg':
00497                 $value = $this->msg( $value )->text();
00498                 break;
00499             case 'msg-content':
00500                 $value = $this->msg( $value )->inContentLanguage()->text();
00501                 break;
00502             case 'number':
00503                 $value = Message::numParam( $value );
00504                 break;
00505             case 'user':
00506                 $user = User::newFromName( $value );
00507                 $value = $user->getName();
00508                 break;
00509             case 'user-link':
00510                 $this->setShowUserToolLinks( false );
00511 
00512                 $user = User::newFromName( $value );
00513                 $value = Message::rawParam( $this->makeUserLink( $user ) );
00514 
00515                 $this->setShowUserToolLinks( $saveLinkFlood );
00516                 break;
00517             case 'title':
00518                 $title = Title::newFromText( $value );
00519                 $value = $title->getPrefixedText();
00520                 break;
00521             case 'title-link':
00522                 $title = Title::newFromText( $value );
00523                 $value = Message::rawParam( $this->makePageLink( $title ) );
00524                 break;
00525             case 'plain':
00526                 // Plain text, nothing to do
00527             default:
00528                 // Catch other types and use the old behavior (return as-is)
00529         }
00530 
00531         return $value;
00532     }
00533 
00542     protected function makePageLink( Title $title = null, $parameters = array() ) {
00543         if ( !$this->plaintext ) {
00544             $link = Linker::link( $title, null, array(), $parameters );
00545         } else {
00546             if ( !$title instanceof Title ) {
00547                 throw new MWException( "Expected title, got null" );
00548             }
00549             $link = '[[' . $title->getPrefixedText() . ']]';
00550         }
00551 
00552         return $link;
00553     }
00554 
00561     public function getPerformerElement() {
00562         if ( $this->canView( LogPage::DELETED_USER ) ) {
00563             $performer = $this->entry->getPerformer();
00564             $element = $this->makeUserLink( $performer );
00565             if ( $this->entry->isDeleted( LogPage::DELETED_USER ) ) {
00566                 $element = $this->styleRestricedElement( $element );
00567             }
00568         } else {
00569             $element = $this->getRestrictedElement( 'rev-deleted-user' );
00570         }
00571 
00572         return $element;
00573     }
00574 
00579     public function getComment() {
00580         if ( $this->canView( LogPage::DELETED_COMMENT ) ) {
00581             $comment = Linker::commentBlock( $this->entry->getComment() );
00582             // No hard coded spaces thanx
00583             $element = ltrim( $comment );
00584             if ( $this->entry->isDeleted( LogPage::DELETED_COMMENT ) ) {
00585                 $element = $this->styleRestricedElement( $element );
00586             }
00587         } else {
00588             $element = $this->getRestrictedElement( 'rev-deleted-comment' );
00589         }
00590 
00591         return $element;
00592     }
00593 
00599     protected function getRestrictedElement( $message ) {
00600         if ( $this->plaintext ) {
00601             return $this->msg( $message )->text();
00602         }
00603 
00604         $content = $this->msg( $message )->escaped();
00605         $attribs = array( 'class' => 'history-deleted' );
00606 
00607         return Html::rawElement( 'span', $attribs, $content );
00608     }
00609 
00615     protected function styleRestricedElement( $content ) {
00616         if ( $this->plaintext ) {
00617             return $content;
00618         }
00619         $attribs = array( 'class' => 'history-deleted' );
00620 
00621         return Html::rawElement( 'span', $attribs, $content );
00622     }
00623 
00629     protected function msg( $key ) {
00630         return $this->context->msg( $key );
00631     }
00632 
00633     protected function makeUserLink( User $user ) {
00634         if ( $this->plaintext ) {
00635             $element = $user->getName();
00636         } else {
00637             $element = Linker::userLink(
00638                 $user->getId(),
00639                 $user->getName()
00640             );
00641 
00642             if ( $this->linkFlood ) {
00643                 $element .= Linker::userToolLinksRedContribs(
00644                     $user->getId(),
00645                     $user->getName(),
00646                     $user->getEditCount()
00647                 );
00648             }
00649         }
00650 
00651         return $element;
00652     }
00653 
00657     public function getPreloadTitles() {
00658         return array();
00659     }
00660 
00664     public function getMessageParametersForTesting() {
00665         // This function was added because getMessageParameters() is
00666         // protected and a change from protected to public caused
00667         // problems with extensions
00668         return $this->getMessageParameters();
00669     }
00670 }
00671 
00681 class LegacyLogFormatter extends LogFormatter {
00691     private $comment = null;
00692 
00700     private $revert = null;
00701 
00702     public function getComment() {
00703         if ( $this->comment === null ) {
00704             $this->comment = parent::getComment();
00705         }
00706 
00707         // Make sure we execute the LogLine hook so that we immediately return
00708         // the correct value.
00709         if ( $this->revert === null ) {
00710             $this->getActionLinks();
00711         }
00712 
00713         return $this->comment;
00714     }
00715 
00716     protected function getActionMessage() {
00717         $entry = $this->entry;
00718         $action = LogPage::actionText(
00719             $entry->getType(),
00720             $entry->getSubtype(),
00721             $entry->getTarget(),
00722             $this->plaintext ? null : $this->context->getSkin(),
00723             (array)$entry->getParameters(),
00724             !$this->plaintext // whether to filter [[]] links
00725         );
00726 
00727         $performer = $this->getPerformerElement();
00728         if ( !$this->irctext ) {
00729             $action = $performer . $this->msg( 'word-separator' )->text() . $action;
00730         }
00731 
00732         return $action;
00733     }
00734 
00735     public function getActionLinks() {
00736         if ( $this->revert !== null ) {
00737             return $this->revert;
00738         }
00739 
00740         if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) {
00741             $this->revert = '';
00742             return $this->revert;
00743         }
00744 
00745         $title = $this->entry->getTarget();
00746         $type = $this->entry->getType();
00747         $subtype = $this->entry->getSubtype();
00748 
00749         // Show unblock/change block link
00750         if ( ( $type == 'block' || $type == 'suppress' )
00751             && ( $subtype == 'block' || $subtype == 'reblock' )
00752         ) {
00753             if ( !$this->context->getUser()->isAllowed( 'block' ) ) {
00754                 return '';
00755             }
00756 
00757             $links = array(
00758                 Linker::linkKnown(
00759                     SpecialPage::getTitleFor( 'Unblock', $title->getDBkey() ),
00760                     $this->msg( 'unblocklink' )->escaped()
00761                 ),
00762                 Linker::linkKnown(
00763                     SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
00764                     $this->msg( 'change-blocklink' )->escaped()
00765                 )
00766             );
00767 
00768             return $this->msg( 'parentheses' )->rawParams(
00769                 $this->context->getLanguage()->pipeList( $links ) )->escaped();
00770         // Show change protection link
00771         } elseif ( $type == 'protect'
00772             && ( $subtype == 'protect' || $subtype == 'modify' || $subtype == 'unprotect' )
00773         ) {
00774             $links = array(
00775                 Linker::link( $title,
00776                     $this->msg( 'hist' )->escaped(),
00777                     array(),
00778                     array(
00779                         'action' => 'history',
00780                         'offset' => $this->entry->getTimestamp()
00781                     )
00782                 )
00783             );
00784             if ( $this->context->getUser()->isAllowed( 'protect' ) ) {
00785                 $links[] = Linker::linkKnown(
00786                     $title,
00787                     $this->msg( 'protect_change' )->escaped(),
00788                     array(),
00789                     array( 'action' => 'protect' )
00790                 );
00791             }
00792 
00793             return $this->msg( 'parentheses' )->rawParams(
00794                 $this->context->getLanguage()->pipeList( $links ) )->escaped();
00795         // Show unmerge link
00796         } elseif ( $type == 'merge' && $subtype == 'merge' ) {
00797             if ( !$this->context->getUser()->isAllowed( 'mergehistory' ) ) {
00798                 return '';
00799             }
00800 
00801             $params = $this->extractParameters();
00802             $revert = Linker::linkKnown(
00803                 SpecialPage::getTitleFor( 'MergeHistory' ),
00804                 $this->msg( 'revertmerge' )->escaped(),
00805                 array(),
00806                 array(
00807                     'target' => $params[3],
00808                     'dest' => $title->getPrefixedDBkey(),
00809                     'mergepoint' => $params[4]
00810                 )
00811             );
00812 
00813             return $this->msg( 'parentheses' )->rawParams( $revert )->escaped();
00814         }
00815 
00816         // Do nothing. The implementation is handled by the hook modifiying the
00817         // passed-by-ref parameters. This also changes the default value so that
00818         // getComment() and getActionLinks() do not call them indefinitely.
00819         $this->revert = '';
00820 
00821         // This is to populate the $comment member of this instance so that it
00822         // can be modified when calling the hook just below.
00823         if ( $this->comment === null ) {
00824             $this->getComment();
00825         }
00826 
00827         $params = $this->entry->getParameters();
00828 
00829         wfRunHooks( 'LogLine', array( $type, $subtype, $title, $params,
00830             &$this->comment, &$this->revert, $this->entry->getTimestamp() ) );
00831 
00832         return $this->revert;
00833     }
00834 }