MediaWiki  REL1_22
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