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