MediaWiki  REL1_19
LogFormatter.php
Go to the documentation of this file.
00001 <?php
00018 class LogFormatter {
00019         // Audience options for viewing usernames, comments, and actions
00020         const FOR_PUBLIC = 1;
00021         const FOR_THIS_USER = 2;
00022 
00023         // Static->
00024 
00030         public static function newFromEntry( LogEntry $entry ) {
00031                 global $wgLogActionsHandlers;
00032                 $fulltype = $entry->getFullType();
00033                 $wildcard = $entry->getType() . '/*';
00034                 $handler = '';
00035 
00036                 if ( isset( $wgLogActionsHandlers[$fulltype] ) ) {
00037                         $handler = $wgLogActionsHandlers[$fulltype];
00038                 } elseif ( isset( $wgLogActionsHandlers[$wildcard] ) ) {
00039                         $handler = $wgLogActionsHandlers[$wildcard];
00040                 }
00041 
00042                 if ( $handler !== '' && is_string( $handler ) && class_exists( $handler ) ) {
00043                         return new $handler( $entry );
00044                 }
00045 
00046                 return new LegacyLogFormatter( $entry );
00047         }
00048 
00056         public static function newFromRow( $row ) {
00057                 return self::newFromEntry( DatabaseLogEntry::newFromRow( $row ) );
00058         }
00059 
00060         // Nonstatic->
00061 
00063         protected $entry;
00064 
00066         protected $audience = self::FOR_PUBLIC;
00067 
00069         protected $linkFlood = false;
00070 
00078         protected $plaintext = false;
00079 
00080         protected $irctext = false;
00081 
00082         protected function __construct( LogEntry $entry ) {
00083                 $this->entry = $entry;
00084                 $this->context = RequestContext::getMain();
00085         }
00086 
00091         public function setContext( IContextSource $context ) {
00092                 $this->context = $context;
00093         }
00094 
00101         public function setAudience( $audience ) {
00102                 $this->audience = ( $audience == self::FOR_THIS_USER )
00103                         ? self::FOR_THIS_USER 
00104                         : self::FOR_PUBLIC;
00105         }
00106 
00112         protected function canView( $field ) {
00113                 if ( $this->audience == self::FOR_THIS_USER ) {
00114                         return LogEventsList::userCanBitfield( 
00115                                 $this->entry->getDeleted(), $field, $this->context->getUser() );
00116                 } else {
00117                         return !$this->entry->isDeleted( $field );
00118                 }
00119         }
00120 
00127         public function setShowUserToolLinks( $value ) {
00128                 $this->linkFlood = $value;
00129         }
00130 
00138         public function getPlainActionText() {
00139                 $this->plaintext = true;
00140                 $text = $this->getActionText();
00141                 $this->plaintext = false;
00142                 return $text;
00143         }
00144 
00151         public function getIRCActionText() {
00152                 $this->plaintext = true;
00153                 $text = $this->getActionText();
00154 
00155                 $entry = $this->entry;
00156                 $parameters = $entry->getParameters();
00157                 // @see LogPage::actionText()
00158                 $msgOpts = array( 'parsemag', 'escape', 'replaceafter', 'content' );
00159                 // Text of title the action is aimed at.
00160                 $target = $entry->getTarget()->getPrefixedText() ;
00161                 $text = null;
00162                 switch( $entry->getType() ) {
00163                         case 'move':
00164                                 switch( $entry->getSubtype() ) {
00165                                         case 'move':
00166                                                 $movesource =  $parameters['4::target'];
00167                                                 $text = wfMsgExt( '1movedto2', $msgOpts, $target, $movesource );
00168                                                 break;
00169                                         case 'move_redir':
00170                                                 $movesource =  $parameters['4::target'];
00171                                                 $text = wfMsgExt( '1movedto2_redir', $msgOpts, $target, $movesource );
00172                                                 break;
00173                                         case 'move-noredirect':
00174                                                 break;
00175                                         case 'move_redir-noredirect':
00176                                                 break;
00177                                 }
00178                                 break;
00179 
00180                         case 'delete':
00181                                 switch( $entry->getSubtype() ) {
00182                                         case 'delete':
00183                                                 $text = wfMsgExt( 'deletedarticle', $msgOpts, $target );
00184                                                 break;
00185                                         case 'restore':
00186                                                 $text = wfMsgExt( 'undeletedarticle', $msgOpts, $target );
00187                                                 break;
00188                                         //case 'revision': // Revision deletion
00189                                         //case 'event': // Log deletion
00190                                                 // see https://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/LogPage.php?&pathrev=97044&r1=97043&r2=97044
00191                                         //default:
00192                                 }
00193                                 break;
00194 
00195                         case 'patrol':
00196                                 // https://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/PatrolLog.php?&pathrev=97495&r1=97494&r2=97495
00197                                 // Create a diff link to the patrolled revision
00198                                 if ( $entry->getSubtype() === 'patrol' ) {
00199                                         $diffLink = htmlspecialchars(
00200                                                 wfMsgForContent( 'patrol-log-diff', $parameters['4::curid'] ) );
00201                                         $text = wfMsgForContent( 'patrol-log-line', $diffLink, "[[$target]]", "" );
00202                                 } else {
00203                                         // broken??
00204                                 }
00205                                 break;
00206 
00207                         case 'newusers':
00208                                 switch( $entry->getSubtype() ) {
00209                                         case 'newusers':
00210                                         case 'create':
00211                                                 $text = wfMsgExt( 'newuserlog-create-entry', $msgOpts /* no params */ );
00212                                                 break;
00213                                         case 'create2':
00214                                                 $text = wfMsgExt( 'newuserlog-create2-entry', $msgOpts, $target );
00215                                                 break;
00216                                         case 'autocreate':
00217                                                 $text = wfMsgExt( 'newuserlog-autocreate-entry', $msgOpts /* no params */ );
00218                                                 break;
00219                                 }
00220                                 break;
00221 
00222                         case 'upload':
00223                                 switch( $entry->getSubtype() ) {
00224                                         case 'upload':
00225                                                 $text = wfMsgExt( 'uploadedimage', $msgOpts, $target );
00226                                                 break;
00227                                         case 'overwrite':
00228                                                 $text = wfMsgExt( 'overwroteimage', $msgOpts, $target );
00229                                                 break;
00230                                 }
00231                                 break;
00232 
00233                         // case 'suppress' --private log -- aaron  (sign your messages so we know who to blame in a few years :-D)
00234                         // default:
00235                 }
00236                 if( is_null( $text ) ) {
00237                         $text = $this->getPlainActionText();
00238                 }
00239 
00240                 $this->plaintext = false;
00241                 return $text;
00242         }
00243 
00248         public function getActionText() {
00249                 if ( $this->canView( LogPage::DELETED_ACTION ) ) {
00250                         $element = $this->getActionMessage();
00251                         if ( $element instanceof Message ) {
00252                                 $element = $this->plaintext ? $element->text() : $element->escaped();
00253                         }
00254                         if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) ) {
00255                                 $element = $this->styleRestricedElement( $element );
00256                         }
00257                 } else {
00258                         $performer = $this->getPerformerElement() . $this->msg( 'word-separator' )->text();
00259                         $element = $performer . $this->getRestrictedElement( 'rev-deleted-event' );
00260                 }
00261 
00262                 return $element;
00263         }
00264 
00271         protected function getActionMessage() {
00272                 $message = $this->msg( $this->getMessageKey() );
00273                 $message->params( $this->getMessageParameters() );
00274                 return $message;
00275         }
00276 
00284         protected function getMessageKey() {
00285                 $type = $this->entry->getType();
00286                 $subtype = $this->entry->getSubtype();
00287 
00288                 return "logentry-$type-$subtype";
00289         }
00290 
00296         protected function extractParameters() {
00297                 $entry = $this->entry;
00298                 $params = array();
00299 
00300                 if ( $entry->isLegacy() ) {
00301                         foreach ( $entry->getParameters() as $index => $value ) {
00302                                 $params[$index + 3] = $value;
00303                         }
00304                 }
00305 
00306                 // Filter out parameters which are not in format #:foo
00307                 foreach ( $entry->getParameters() as $key => $value ) {
00308                         if ( strpos( $key, ':' ) === false ) continue;
00309                         list( $index, $type, $name ) = explode( ':', $key, 3 );
00310                         $params[$index - 1] = $value;
00311                 }
00312 
00313                 /* Message class doesn't like non consecutive numbering.
00314                  * Fill in missing indexes with empty strings to avoid
00315                  * incorrect renumbering.
00316                  */
00317                 if ( count( $params ) ) {
00318                         $max = max( array_keys( $params ) );
00319                         for ( $i = 4; $i < $max; $i++ ) {
00320                                 if ( !isset( $params[$i] ) ) {
00321                                         $params[$i] = '';
00322                                 }
00323                         }
00324                 }
00325                 return $params;
00326         }
00327 
00337         protected function getMessageParameters() {
00338                 if ( isset( $this->parsedParameters ) ) {
00339                         return $this->parsedParameters;
00340                 }
00341 
00342                 $entry = $this->entry;
00343                 $params = $this->extractParameters();
00344                 $params[0] = Message::rawParam( $this->getPerformerElement() );
00345                 $params[1] = $entry->getPerformer()->getName();
00346                 $params[2] = Message::rawParam( $this->makePageLink( $entry->getTarget() ) );
00347 
00348                 // Bad things happens if the numbers are not in correct order
00349                 ksort( $params );
00350                 return $this->parsedParameters = $params;
00351         }
00352 
00360         protected function makePageLink( Title $title = null, $parameters = array() ) {
00361                 if ( !$this->plaintext ) {
00362                         $link = Linker::link( $title, null, array(), $parameters );
00363                 } else {
00364                         if ( !$title instanceof Title ) {
00365                                 throw new MWException( "Expected title, got null" );
00366                         }
00367                         $link = '[[' . $title->getPrefixedText() . ']]';
00368                 }
00369                 return $link;
00370         }
00371 
00377         public function getPerformerElement() {
00378                 if ( $this->canView( LogPage::DELETED_USER ) ) {
00379                         $performer = $this->entry->getPerformer();
00380                         $element = $this->makeUserLink( $performer );
00381                         if ( $this->entry->isDeleted( LogPage::DELETED_USER ) ) {
00382                                 $element = $this->styleRestricedElement( $element );
00383                         }
00384                 } else {
00385                         $element = $this->getRestrictedElement( 'rev-deleted-user' );
00386                 }
00387 
00388                 return $element;
00389         }
00390 
00395         public function getComment() {
00396                 if ( $this->canView( LogPage::DELETED_COMMENT ) ) {
00397                         $comment = Linker::commentBlock( $this->entry->getComment() );
00398                         // No hard coded spaces thanx
00399                         $element = ltrim( $comment );
00400                         if ( $this->entry->isDeleted( LogPage::DELETED_COMMENT ) ) {
00401                                 $element = $this->styleRestricedElement( $element );
00402                         }
00403                 } else {
00404                         $element = $this->getRestrictedElement( 'rev-deleted-comment' );
00405                 }
00406 
00407                 return $element;
00408         }
00409 
00415         protected function getRestrictedElement( $message ) {
00416                 if ( $this->plaintext ) {
00417                         return $this->msg( $message )->text();
00418                 }
00419 
00420                 $content =  $this->msg( $message )->escaped();
00421                 $attribs = array( 'class' => 'history-deleted' );
00422                 return Html::rawElement( 'span', $attribs, $content );
00423         }
00424 
00430         protected function styleRestricedElement( $content ) {
00431                 if ( $this->plaintext ) {
00432                         return $content;
00433                 }
00434                 $attribs = array( 'class' => 'history-deleted' );
00435                 return Html::rawElement( 'span', $attribs, $content );
00436         }
00437 
00444         protected function msg( $key ) {
00445                 return wfMessage( $key )
00446                         ->inLanguage( $this->context->getLanguage() )
00447                         ->title( $this->context->getTitle() );
00448         }
00449 
00450         protected function makeUserLink( User $user ) {
00451                 if ( $this->plaintext ) {
00452                         $element = $user->getName();
00453                 } else {
00454                         $element = Linker::userLink(
00455                                 $user->getId(),
00456                                 $user->getName()
00457                         );
00458 
00459                         if ( $this->linkFlood ) {
00460                                 $element .= Linker::userToolLinks(
00461                                         $user->getId(),
00462                                         $user->getName(),
00463                                         true, // Red if no edits
00464                                         0, // Flags
00465                                         $user->getEditCount()
00466                                 );
00467                         }
00468                 }
00469                 return $element;
00470         }
00471 
00475         public function getPreloadTitles() {
00476                 return array();
00477         }
00478 
00479 }
00480 
00490 class LegacyLogFormatter extends LogFormatter {
00491         protected function getActionMessage() {
00492                 $entry = $this->entry;
00493                 $action = LogPage::actionText(
00494                         $entry->getType(),
00495                         $entry->getSubtype(),
00496                         $entry->getTarget(),
00497                         $this->plaintext ? null : $this->context->getSkin(),
00498                         (array)$entry->getParameters(),
00499                         !$this->plaintext // whether to filter [[]] links
00500                 );
00501 
00502                 $performer = $this->getPerformerElement();
00503                 return $performer .  $this->msg( 'word-separator' )->text() . $action;
00504         }
00505 
00506 }
00507 
00512 class MoveLogFormatter extends LogFormatter {
00513         public function getPreloadTitles() {
00514                 $params = $this->extractParameters();
00515                 return array( Title::newFromText( $params[3] ) );
00516         }
00517 
00518         protected function getMessageKey() {
00519                 $key = parent::getMessageKey();
00520                 $params = $this->getMessageParameters();
00521                 if ( isset( $params[4] ) && $params[4] === '1' ) {
00522                         $key .= '-noredirect';
00523                 }
00524                 return $key;
00525         }
00526 
00527         protected function getMessageParameters() {
00528                 $params = parent::getMessageParameters();
00529                 $oldname = $this->makePageLink( $this->entry->getTarget(), array( 'redirect' => 'no' ) );
00530                 $newname = $this->makePageLink( Title::newFromText( $params[3] ) );
00531                 $params[2] = Message::rawParam( $oldname );
00532                 $params[3] = Message::rawParam( $newname );
00533                 return $params;
00534         }
00535 }
00536 
00541 class DeleteLogFormatter extends LogFormatter {
00542         protected function getMessageKey() {
00543                 $key = parent::getMessageKey();
00544                 if ( in_array( $this->entry->getSubtype(), array( 'event', 'revision' ) ) ) {
00545                         if ( count( $this->getMessageParameters() ) < 5 ) {
00546                                 return "$key-legacy";
00547                         }
00548                 }
00549                 return $key;
00550         }
00551 
00552         protected function getMessageParameters() {
00553                 if ( isset( $this->parsedParametersDeleteLog ) ) {
00554                         return $this->parsedParametersDeleteLog;
00555                 }
00556 
00557                 $params = parent::getMessageParameters();
00558                 $subtype = $this->entry->getSubtype();
00559                 if ( in_array( $subtype, array( 'event', 'revision' ) ) ) {
00560                         if (
00561                                 ($subtype === 'event' && count( $params ) === 6 ) ||
00562                                 ($subtype === 'revision' && isset( $params[3] ) && $params[3] === 'revision' )
00563                         ) {
00564                                 $paramStart = $subtype === 'revision' ? 4 : 3;
00565 
00566                                 $old = $this->parseBitField( $params[$paramStart+1] );
00567                                 $new = $this->parseBitField( $params[$paramStart+2] );
00568                                 list( $hid, $unhid, $extra ) = RevisionDeleter::getChanges( $new, $old );
00569                                 $changes = array();
00570                                 foreach ( $hid as $v ) {
00571                                         $changes[] = $this->msg( "$v-hid" )->plain();
00572                                 }
00573                                 foreach ( $unhid as $v ) {
00574                                         $changes[] = $this->msg( "$v-unhid" )->plain();
00575                                 }
00576                                 foreach ( $extra as $v ) {
00577                                         $changes[] = $this->msg( $v )->plain();
00578                                 }
00579                                 $changeText =  $this->context->getLanguage()->listToText( $changes );
00580 
00581 
00582                                 $newParams = array_slice( $params, 0, 3 );
00583                                 $newParams[3] = $changeText;
00584                                 $count = count( explode( ',', $params[$paramStart] ) );
00585                                 $newParams[4] = $this->context->getLanguage()->formatNum( $count );
00586                                 return $this->parsedParametersDeleteLog = $newParams;
00587                         } else {
00588                                 return $this->parsedParametersDeleteLog = array_slice( $params, 0, 3 );
00589                         }
00590                 }
00591 
00592                 return $this->parsedParametersDeleteLog = $params;
00593         }
00594 
00595         protected function parseBitField( $string ) {
00596                 // Input is like ofield=2134 or just the number
00597                 if ( strpos( $string, 'field=' ) === 1 ) {
00598                         list( , $field ) = explode( '=', $string );
00599                         return (int) $field;
00600                 } else {
00601                         return (int) $string;
00602                 }
00603         }
00604 }
00605 
00610 class PatrolLogFormatter extends LogFormatter {
00611         protected function getMessageKey() {
00612                 $key = parent::getMessageKey();
00613                 $params = $this->getMessageParameters();
00614                 if ( isset( $params[5] ) && $params[5] ) {
00615                         $key .= '-auto';
00616                 }
00617                 return $key;
00618         }
00619 
00620         protected function getMessageParameters() {
00621                 $params = parent::getMessageParameters();
00622                 $newParams = array_slice( $params, 0, 3 );
00623 
00624                 $target = $this->entry->getTarget();
00625                 $oldid = $params[3];
00626                 $revision = $this->context->getLanguage()->formatNum( $oldid, true );
00627 
00628                 if ( $this->plaintext ) {
00629                         $revlink = $revision;
00630                 } elseif ( $target->exists() ) {
00631                         $query = array(
00632                                 'oldid' => $oldid,
00633                                 'diff' => 'prev'
00634                         );
00635                         $revlink = Linker::link( $target, htmlspecialchars( $revision ), array(), $query );
00636                 } else {
00637                         $revlink = htmlspecialchars( $revision );
00638                 }
00639 
00640                 $newParams[3] = Message::rawParam( $revlink );
00641                 return $newParams;
00642         }
00643 }
00644 
00649 class NewUsersLogFormatter extends LogFormatter {
00650         protected function getMessageParameters() {
00651                 $params = parent::getMessageParameters();
00652                 if ( $this->entry->getSubtype() === 'create2' ) {
00653                         if ( isset( $params[3] ) ) {
00654                                 $target = User::newFromId( $params[3] );
00655                         } else {
00656                                 $target = User::newFromName( $this->entry->getTarget()->getText(), false );
00657                         }
00658                         $params[2] = Message::rawParam( $this->makeUserLink( $target ) );
00659                         $params[3] = $target->getName();
00660                 }
00661                 return $params;
00662         }
00663 
00664         public function getComment() {
00665                 $timestamp = wfTimestamp( TS_MW, $this->entry->getTimestamp() );
00666                 if ( $timestamp < '20080129000000' ) {
00667                         # Suppress $comment from old entries (before 2008-01-29),
00668                         # not needed and can contain incorrect links
00669                         return '';
00670                 }
00671                 return parent::getComment();
00672         }
00673 }