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