MediaWiki
REL1_23
|
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 }