MediaWiki  REL1_24
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 
00488     protected function formatParameterValue( $type, $value ) {
00489         $saveLinkFlood = $this->linkFlood;
00490 
00491         switch ( strtolower( trim( $type ) ) ) {
00492             case 'raw':
00493                 $value = Message::rawParam( $value );
00494                 break;
00495             case 'msg':
00496                 $value = $this->msg( $value )->text();
00497                 break;
00498             case 'msg-content':
00499                 $value = $this->msg( $value )->inContentLanguage()->text();
00500                 break;
00501             case 'number':
00502                 $value = Message::numParam( $value );
00503                 break;
00504             case 'user':
00505                 $user = User::newFromName( $value );
00506                 $value = $user->getName();
00507                 break;
00508             case 'user-link':
00509                 $this->setShowUserToolLinks( false );
00510 
00511                 $user = User::newFromName( $value );
00512                 $value = Message::rawParam( $this->makeUserLink( $user ) );
00513 
00514                 $this->setShowUserToolLinks( $saveLinkFlood );
00515                 break;
00516             case 'title':
00517                 $title = Title::newFromText( $value );
00518                 $value = $title->getPrefixedText();
00519                 break;
00520             case 'title-link':
00521                 $title = Title::newFromText( $value );
00522                 $value = Message::rawParam( $this->makePageLink( $title ) );
00523                 break;
00524             case 'plain':
00525                 // Plain text, nothing to do
00526             default:
00527                 // Catch other types and use the old behavior (return as-is)
00528         }
00529 
00530         return $value;
00531     }
00532 
00541     protected function makePageLink( Title $title = null, $parameters = array() ) {
00542         if ( !$this->plaintext ) {
00543             $link = Linker::link( $title, null, array(), $parameters );
00544         } else {
00545             if ( !$title instanceof Title ) {
00546                 throw new MWException( "Expected title, got null" );
00547             }
00548             $link = '[[' . $title->getPrefixedText() . ']]';
00549         }
00550 
00551         return $link;
00552     }
00553 
00560     public function getPerformerElement() {
00561         if ( $this->canView( LogPage::DELETED_USER ) ) {
00562             $performer = $this->entry->getPerformer();
00563             $element = $this->makeUserLink( $performer );
00564             if ( $this->entry->isDeleted( LogPage::DELETED_USER ) ) {
00565                 $element = $this->styleRestricedElement( $element );
00566             }
00567         } else {
00568             $element = $this->getRestrictedElement( 'rev-deleted-user' );
00569         }
00570 
00571         return $element;
00572     }
00573 
00578     public function getComment() {
00579         if ( $this->canView( LogPage::DELETED_COMMENT ) ) {
00580             $comment = Linker::commentBlock( $this->entry->getComment() );
00581             // No hard coded spaces thanx
00582             $element = ltrim( $comment );
00583             if ( $this->entry->isDeleted( LogPage::DELETED_COMMENT ) ) {
00584                 $element = $this->styleRestricedElement( $element );
00585             }
00586         } else {
00587             $element = $this->getRestrictedElement( 'rev-deleted-comment' );
00588         }
00589 
00590         return $element;
00591     }
00592 
00598     protected function getRestrictedElement( $message ) {
00599         if ( $this->plaintext ) {
00600             return $this->msg( $message )->text();
00601         }
00602 
00603         $content = $this->msg( $message )->escaped();
00604         $attribs = array( 'class' => 'history-deleted' );
00605 
00606         return Html::rawElement( 'span', $attribs, $content );
00607     }
00608 
00614     protected function styleRestricedElement( $content ) {
00615         if ( $this->plaintext ) {
00616             return $content;
00617         }
00618         $attribs = array( 'class' => 'history-deleted' );
00619 
00620         return Html::rawElement( 'span', $attribs, $content );
00621     }
00622 
00628     protected function msg( $key ) {
00629         return $this->context->msg( $key );
00630     }
00631 
00632     protected function makeUserLink( User $user ) {
00633         if ( $this->plaintext ) {
00634             $element = $user->getName();
00635         } else {
00636             $element = Linker::userLink(
00637                 $user->getId(),
00638                 $user->getName()
00639             );
00640 
00641             if ( $this->linkFlood ) {
00642                 $element .= Linker::userToolLinksRedContribs(
00643                     $user->getId(),
00644                     $user->getName(),
00645                     $user->getEditCount()
00646                 );
00647             }
00648         }
00649 
00650         return $element;
00651     }
00652 
00656     public function getPreloadTitles() {
00657         return array();
00658     }
00659 
00663     public function getMessageParametersForTesting() {
00664         // This function was added because getMessageParameters() is
00665         // protected and a change from protected to public caused
00666         // problems with extensions
00667         return $this->getMessageParameters();
00668     }
00669 }
00670 
00680 class LegacyLogFormatter extends LogFormatter {
00690     private $comment = null;
00691 
00699     private $revert = null;
00700 
00701     public function getComment() {
00702         if ( $this->comment === null ) {
00703             $this->comment = parent::getComment();
00704         }
00705 
00706         // Make sure we execute the LogLine hook so that we immediately return
00707         // the correct value.
00708         if ( $this->revert === null ) {
00709             $this->getActionLinks();
00710         }
00711 
00712         return $this->comment;
00713     }
00714 
00715     protected function getActionMessage() {
00716         $entry = $this->entry;
00717         $action = LogPage::actionText(
00718             $entry->getType(),
00719             $entry->getSubtype(),
00720             $entry->getTarget(),
00721             $this->plaintext ? null : $this->context->getSkin(),
00722             (array)$entry->getParameters(),
00723             !$this->plaintext // whether to filter [[]] links
00724         );
00725 
00726         $performer = $this->getPerformerElement();
00727         if ( !$this->irctext ) {
00728             $action = $performer . $this->msg( 'word-separator' )->text() . $action;
00729         }
00730 
00731         return $action;
00732     }
00733 
00734     public function getActionLinks() {
00735         if ( $this->revert !== null ) {
00736             return $this->revert;
00737         }
00738 
00739         if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) {
00740             $this->revert = '';
00741             return $this->revert;
00742         }
00743 
00744         $title = $this->entry->getTarget();
00745         $type = $this->entry->getType();
00746         $subtype = $this->entry->getSubtype();
00747 
00748         // Show unblock/change block link
00749         if ( ( $type == 'block' || $type == 'suppress' )
00750             && ( $subtype == 'block' || $subtype == 'reblock' )
00751         ) {
00752             if ( !$this->context->getUser()->isAllowed( 'block' ) ) {
00753                 return '';
00754             }
00755 
00756             $links = array(
00757                 Linker::linkKnown(
00758                     SpecialPage::getTitleFor( 'Unblock', $title->getDBkey() ),
00759                     $this->msg( 'unblocklink' )->escaped()
00760                 ),
00761                 Linker::linkKnown(
00762                     SpecialPage::getTitleFor( 'Block', $title->getDBkey() ),
00763                     $this->msg( 'change-blocklink' )->escaped()
00764                 )
00765             );
00766 
00767             return $this->msg( 'parentheses' )->rawParams(
00768                 $this->context->getLanguage()->pipeList( $links ) )->escaped();
00769         // Show change protection link
00770         } elseif ( $type == 'protect'
00771             && ( $subtype == 'protect' || $subtype == 'modify' || $subtype == 'unprotect' )
00772         ) {
00773             $links = array(
00774                 Linker::link( $title,
00775                     $this->msg( 'hist' )->escaped(),
00776                     array(),
00777                     array(
00778                         'action' => 'history',
00779                         'offset' => $this->entry->getTimestamp()
00780                     )
00781                 )
00782             );
00783             if ( $this->context->getUser()->isAllowed( 'protect' ) ) {
00784                 $links[] = Linker::linkKnown(
00785                     $title,
00786                     $this->msg( 'protect_change' )->escaped(),
00787                     array(),
00788                     array( 'action' => 'protect' )
00789                 );
00790             }
00791 
00792             return $this->msg( 'parentheses' )->rawParams(
00793                 $this->context->getLanguage()->pipeList( $links ) )->escaped();
00794         // Show unmerge link
00795         } elseif ( $type == 'merge' && $subtype == 'merge' ) {
00796             if ( !$this->context->getUser()->isAllowed( 'mergehistory' ) ) {
00797                 return '';
00798             }
00799 
00800             $params = $this->extractParameters();
00801             $revert = Linker::linkKnown(
00802                 SpecialPage::getTitleFor( 'MergeHistory' ),
00803                 $this->msg( 'revertmerge' )->escaped(),
00804                 array(),
00805                 array(
00806                     'target' => $params[3],
00807                     'dest' => $title->getPrefixedDBkey(),
00808                     'mergepoint' => $params[4],
00809                     'submitted' => 1 // show the revisions immediately
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 }