MediaWiki
REL1_20
|
00001 <?php 00032 class LogPage { 00033 const DELETED_ACTION = 1; 00034 const DELETED_COMMENT = 2; 00035 const DELETED_USER = 4; 00036 const DELETED_RESTRICTED = 8; 00037 // Convenience fields 00038 const SUPPRESSED_USER = 12; 00039 const SUPPRESSED_ACTION = 9; 00040 /* @access private */ 00041 var $type, $action, $comment, $params; 00042 00046 var $doer; 00047 00051 var $target; 00052 00053 /* @acess public */ 00054 var $updateRecentChanges, $sendToUDP; 00055 00064 public function __construct( $type, $rc = true, $udp = 'skipUDP' ) { 00065 $this->type = $type; 00066 $this->updateRecentChanges = $rc; 00067 $this->sendToUDP = ( $udp == 'UDP' ); 00068 } 00069 00073 protected function saveContent() { 00074 global $wgLogRestrictions; 00075 00076 $dbw = wfGetDB( DB_MASTER ); 00077 $log_id = $dbw->nextSequenceValue( 'logging_log_id_seq' ); 00078 00079 $this->timestamp = $now = wfTimestampNow(); 00080 $data = array( 00081 'log_id' => $log_id, 00082 'log_type' => $this->type, 00083 'log_action' => $this->action, 00084 'log_timestamp' => $dbw->timestamp( $now ), 00085 'log_user' => $this->doer->getId(), 00086 'log_user_text' => $this->doer->getName(), 00087 'log_namespace' => $this->target->getNamespace(), 00088 'log_title' => $this->target->getDBkey(), 00089 'log_page' => $this->target->getArticleID(), 00090 'log_comment' => $this->comment, 00091 'log_params' => $this->params 00092 ); 00093 $dbw->insert( 'logging', $data, __METHOD__ ); 00094 $newId = !is_null( $log_id ) ? $log_id : $dbw->insertId(); 00095 00096 # And update recentchanges 00097 if( $this->updateRecentChanges ) { 00098 $titleObj = SpecialPage::getTitleFor( 'Log', $this->type ); 00099 00100 RecentChange::notifyLog( 00101 $now, $titleObj, $this->doer, $this->getRcComment(), '', 00102 $this->type, $this->action, $this->target, $this->comment, 00103 $this->params, $newId, $this->getRcCommentIRC() 00104 ); 00105 } elseif( $this->sendToUDP ) { 00106 # Don't send private logs to UDP 00107 if( isset( $wgLogRestrictions[$this->type] ) && $wgLogRestrictions[$this->type] != '*' ) { 00108 return $newId; 00109 } 00110 00111 # Notify external application via UDP. 00112 # We send this to IRC but do not want to add it the RC table. 00113 $titleObj = SpecialPage::getTitleFor( 'Log', $this->type ); 00114 $rc = RecentChange::newLogEntry( 00115 $now, $titleObj, $this->doer, $this->getRcComment(), '', 00116 $this->type, $this->action, $this->target, $this->comment, 00117 $this->params, $newId, $this->getRcCommentIRC() 00118 ); 00119 $rc->notifyRC2UDP(); 00120 } 00121 return $newId; 00122 } 00123 00129 public function getRcComment() { 00130 $rcComment = $this->actionText; 00131 00132 if( $this->comment != '' ) { 00133 if ( $rcComment == '' ) { 00134 $rcComment = $this->comment; 00135 } else { 00136 $rcComment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . 00137 $this->comment; 00138 } 00139 } 00140 00141 return $rcComment; 00142 } 00143 00149 public function getRcCommentIRC() { 00150 $rcComment = $this->ircActionText; 00151 00152 if( $this->comment != '' ) { 00153 if ( $rcComment == '' ) { 00154 $rcComment = $this->comment; 00155 } else { 00156 $rcComment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . 00157 $this->comment; 00158 } 00159 } 00160 00161 return $rcComment; 00162 } 00163 00167 public function getComment() { 00168 return $this->comment; 00169 } 00170 00176 public static function validTypes() { 00177 global $wgLogTypes; 00178 return $wgLogTypes; 00179 } 00180 00187 public static function isLogType( $type ) { 00188 return in_array( $type, LogPage::validTypes() ); 00189 } 00190 00198 public static function logName( $type ) { 00199 global $wgLogNames; 00200 00201 if( isset( $wgLogNames[$type] ) ) { 00202 return str_replace( '_', ' ', wfMessage( $wgLogNames[$type] )->text() ); 00203 } else { 00204 // Bogus log types? Perhaps an extension was removed. 00205 return $type; 00206 } 00207 } 00208 00217 public static function logHeader( $type ) { 00218 global $wgLogHeaders; 00219 return wfMessage( $wgLogHeaders[$type] )->parse(); 00220 } 00221 00235 public static function actionText( $type, $action, $title = null, $skin = null, 00236 $params = array(), $filterWikilinks = false ) 00237 { 00238 global $wgLang, $wgContLang, $wgLogActions; 00239 00240 if ( is_null( $skin ) ) { 00241 $langObj = $wgContLang; 00242 $langObjOrNull = null; 00243 } else { 00244 $langObj = $wgLang; 00245 $langObjOrNull = $wgLang; 00246 } 00247 00248 $key = "$type/$action"; 00249 00250 if( isset( $wgLogActions[$key] ) ) { 00251 if( is_null( $title ) ) { 00252 $rv = wfMessage( $wgLogActions[$key] )->inLanguage( $langObj )->escaped(); 00253 } else { 00254 $titleLink = self::getTitleLink( $type, $langObjOrNull, $title, $params ); 00255 00256 if( preg_match( '/^rights\/(rights|autopromote)/', $key ) ) { 00257 $rightsnone = wfMessage( 'rightsnone' )->inLanguage( $langObj )->text(); 00258 00259 if( $skin ) { 00260 $username = $title->getText(); 00261 foreach ( $params as &$param ) { 00262 $groupArray = array_map( 'trim', explode( ',', $param ) ); 00263 foreach( $groupArray as &$group ) { 00264 $group = User::getGroupMember( $group, $username ); 00265 } 00266 $param = $wgLang->listToText( $groupArray ); 00267 } 00268 } 00269 00270 if( !isset( $params[0] ) || trim( $params[0] ) == '' ) { 00271 $params[0] = $rightsnone; 00272 } 00273 00274 if( !isset( $params[1] ) || trim( $params[1] ) == '' ) { 00275 $params[1] = $rightsnone; 00276 } 00277 } 00278 00279 if( count( $params ) == 0 ) { 00280 $rv = wfMessage( $wgLogActions[$key] )->rawParams( $titleLink )->inLanguage( $langObj )->escaped(); 00281 } else { 00282 $details = ''; 00283 array_unshift( $params, $titleLink ); 00284 00285 // User suppression 00286 if ( preg_match( '/^(block|suppress)\/(block|reblock)$/', $key ) ) { 00287 if ( $skin ) { 00288 $params[1] = '<span class="blockExpiry" dir="ltr" title="' . htmlspecialchars( $params[1] ). '">' . 00289 $wgLang->translateBlockExpiry( $params[1] ) . '</span>'; 00290 } else { 00291 $params[1] = $wgContLang->translateBlockExpiry( $params[1] ); 00292 } 00293 00294 $params[2] = isset( $params[2] ) ? 00295 self::formatBlockFlags( $params[2], $langObj ) : ''; 00296 // Page protections 00297 } elseif ( $type == 'protect' && count($params) == 3 ) { 00298 // Restrictions and expiries 00299 if( $skin ) { 00300 $details .= $wgLang->getDirMark() . htmlspecialchars( " {$params[1]}" ); 00301 } else { 00302 $details .= " {$params[1]}"; 00303 } 00304 00305 // Cascading flag... 00306 if( $params[2] ) { 00307 $details .= ' [' . wfMessage( 'protect-summary-cascade' )->inLanguage( $langObj )->text() . ']'; 00308 } 00309 } 00310 00311 $rv = wfMessage( $wgLogActions[$key] )->rawParams( $params )->inLanguage( $langObj )->escaped() . $details; 00312 } 00313 } 00314 } else { 00315 global $wgLogActionsHandlers; 00316 00317 if( isset( $wgLogActionsHandlers[$key] ) ) { 00318 $args = func_get_args(); 00319 $rv = call_user_func_array( $wgLogActionsHandlers[$key], $args ); 00320 } else { 00321 wfDebug( "LogPage::actionText - unknown action $key\n" ); 00322 $rv = "$action"; 00323 } 00324 } 00325 00326 // For the perplexed, this feature was added in r7855 by Erik. 00327 // The feature was added because we liked adding [[$1]] in our log entries 00328 // but the log entries are parsed as Wikitext on RecentChanges but as HTML 00329 // on Special:Log. The hack is essentially that [[$1]] represented a link 00330 // to the title in question. The first parameter to the HTML version (Special:Log) 00331 // is that link in HTML form, and so this just gets rid of the ugly [[]]. 00332 // However, this is a horrible hack and it doesn't work like you expect if, say, 00333 // you want to link to something OTHER than the title of the log entry. 00334 // The real problem, which Erik was trying to fix (and it sort-of works now) is 00335 // that the same messages are being treated as both wikitext *and* HTML. 00336 if( $filterWikilinks ) { 00337 $rv = str_replace( '[[', '', $rv ); 00338 $rv = str_replace( ']]', '', $rv ); 00339 } 00340 00341 return $rv; 00342 } 00343 00352 protected static function getTitleLink( $type, $lang, $title, &$params ) { 00353 global $wgContLang, $wgUserrightsInterwikiDelimiter; 00354 00355 if( !$lang ) { 00356 return $title->getPrefixedText(); 00357 } 00358 00359 switch( $type ) { 00360 case 'move': 00361 $titleLink = Linker::link( 00362 $title, 00363 htmlspecialchars( $title->getPrefixedText() ), 00364 array(), 00365 array( 'redirect' => 'no' ) 00366 ); 00367 00368 $targetTitle = Title::newFromText( $params[0] ); 00369 00370 if ( !$targetTitle ) { 00371 # Workaround for broken database 00372 $params[0] = htmlspecialchars( $params[0] ); 00373 } else { 00374 $params[0] = Linker::link( 00375 $targetTitle, 00376 htmlspecialchars( $params[0] ) 00377 ); 00378 } 00379 break; 00380 case 'block': 00381 if( substr( $title->getText(), 0, 1 ) == '#' ) { 00382 $titleLink = $title->getText(); 00383 } else { 00384 // @todo Store the user identifier in the parameters 00385 // to make this faster for future log entries 00386 $id = User::idFromName( $title->getText() ); 00387 $titleLink = Linker::userLink( $id, $title->getText() ) 00388 . Linker::userToolLinks( $id, $title->getText(), false, Linker::TOOL_LINKS_NOBLOCK ); 00389 } 00390 break; 00391 case 'rights': 00392 $text = $wgContLang->ucfirst( $title->getText() ); 00393 $parts = explode( $wgUserrightsInterwikiDelimiter, $text, 2 ); 00394 00395 if ( count( $parts ) == 2 ) { 00396 $titleLink = WikiMap::foreignUserLink( $parts[1], $parts[0], 00397 htmlspecialchars( $title->getPrefixedText() ) ); 00398 00399 if ( $titleLink !== false ) { 00400 break; 00401 } 00402 } 00403 $titleLink = Linker::link( Title::makeTitle( NS_USER, $text ) ); 00404 break; 00405 case 'merge': 00406 $titleLink = Linker::link( 00407 $title, 00408 $title->getPrefixedText(), 00409 array(), 00410 array( 'redirect' => 'no' ) 00411 ); 00412 $params[0] = Linker::link( 00413 Title::newFromText( $params[0] ), 00414 htmlspecialchars( $params[0] ) 00415 ); 00416 $params[1] = $lang->timeanddate( $params[1] ); 00417 break; 00418 default: 00419 if( $title->isSpecialPage() ) { 00420 list( $name, $par ) = SpecialPageFactory::resolveAlias( $title->getDBkey() ); 00421 00422 # Use the language name for log titles, rather than Log/X 00423 if( $name == 'Log' ) { 00424 $logPage = new LogPage( $par ); 00425 $titleLink = Linker::link( $title, $logPage->getName()->escaped() ); 00426 $titleLink = wfMessage( 'parentheses' ) 00427 ->inLanguage( $lang ) 00428 ->rawParams( $titleLink ) 00429 ->escaped(); 00430 } else { 00431 $titleLink = Linker::link( $title ); 00432 } 00433 } else { 00434 $titleLink = Linker::link( $title ); 00435 } 00436 } 00437 00438 return $titleLink; 00439 } 00440 00452 public function addEntry( $action, $target, $comment, $params = array(), $doer = null ) { 00453 global $wgContLang; 00454 00455 if ( !is_array( $params ) ) { 00456 $params = array( $params ); 00457 } 00458 00459 if ( $comment === null ) { 00460 $comment = ''; 00461 } 00462 00463 # Truncate for whole multibyte characters. 00464 $comment = $wgContLang->truncate( $comment, 255 ); 00465 00466 $this->action = $action; 00467 $this->target = $target; 00468 $this->comment = $comment; 00469 $this->params = LogPage::makeParamBlob( $params ); 00470 00471 if ( $doer === null ) { 00472 global $wgUser; 00473 $doer = $wgUser; 00474 } elseif ( !is_object( $doer ) ) { 00475 $doer = User::newFromId( $doer ); 00476 } 00477 00478 $this->doer = $doer; 00479 00480 $logEntry = new ManualLogEntry( $this->type, $action ); 00481 $logEntry->setTarget( $target ); 00482 $logEntry->setPerformer( $doer ); 00483 $logEntry->setParameters( $params ); 00484 00485 $formatter = LogFormatter::newFromEntry( $logEntry ); 00486 $context = RequestContext::newExtraneousContext( $target ); 00487 $formatter->setContext( $context ); 00488 00489 $this->actionText = $formatter->getPlainActionText(); 00490 $this->ircActionText = $formatter->getIRCActionText(); 00491 00492 return $this->saveContent(); 00493 } 00494 00503 public function addRelations( $field, $values, $logid ) { 00504 if( !strlen( $field ) || empty( $values ) ) { 00505 return false; // nothing 00506 } 00507 00508 $data = array(); 00509 00510 foreach( $values as $value ) { 00511 $data[] = array( 00512 'ls_field' => $field, 00513 'ls_value' => $value, 00514 'ls_log_id' => $logid 00515 ); 00516 } 00517 00518 $dbw = wfGetDB( DB_MASTER ); 00519 $dbw->insert( 'log_search', $data, __METHOD__, 'IGNORE' ); 00520 00521 return true; 00522 } 00523 00530 public static function makeParamBlob( $params ) { 00531 return implode( "\n", $params ); 00532 } 00533 00540 public static function extractParams( $blob ) { 00541 if ( $blob === '' ) { 00542 return array(); 00543 } else { 00544 return explode( "\n", $blob ); 00545 } 00546 } 00547 00556 public static function formatBlockFlags( $flags, $lang ) { 00557 $flags = explode( ',', trim( $flags ) ); 00558 00559 if( count( $flags ) > 0 ) { 00560 for( $i = 0; $i < count( $flags ); $i++ ) { 00561 $flags[$i] = self::formatBlockFlag( $flags[$i], $lang ); 00562 } 00563 return wfMessage( 'parentheses' )->inLanguage( $lang ) 00564 ->rawParams( $lang->commaList( $flags ) )->escaped(); 00565 } else { 00566 return ''; 00567 } 00568 } 00569 00577 public static function formatBlockFlag( $flag, $lang ) { 00578 static $messages = array(); 00579 00580 if( !isset( $messages[$flag] ) ) { 00581 $messages[$flag] = htmlspecialchars( $flag ); // Fallback 00582 00583 // For grepping. The following core messages can be used here: 00584 // * block-log-flags-angry-autoblock 00585 // * block-log-flags-anononly 00586 // * block-log-flags-hiddenname 00587 // * block-log-flags-noautoblock 00588 // * block-log-flags-nocreate 00589 // * block-log-flags-noemail 00590 // * block-log-flags-nousertalk 00591 $msg = wfMessage( 'block-log-flags-' . $flag )->inLanguage( $lang ); 00592 00593 if ( $msg->exists() ) { 00594 $messages[$flag] = $msg->escaped(); 00595 } 00596 } 00597 00598 return $messages[$flag]; 00599 } 00600 00601 00607 public function getName() { 00608 global $wgLogNames; 00609 00610 // BC 00611 if ( isset( $wgLogNames[$this->type] ) ) { 00612 $key = $wgLogNames[$this->type]; 00613 } else { 00614 $key = 'log-name-' . $this->type; 00615 } 00616 00617 return wfMessage( $key ); 00618 } 00619 00625 public function getDescription() { 00626 global $wgLogHeaders; 00627 // BC 00628 if ( isset( $wgLogHeaders[$this->type] ) ) { 00629 $key = $wgLogHeaders[$this->type]; 00630 } else { 00631 $key = 'log-description-' . $this->type; 00632 } 00633 return wfMessage( $key ); 00634 } 00635 00641 public function getRestriction() { 00642 global $wgLogRestrictions; 00643 if ( isset( $wgLogRestrictions[$this->type] ) ) { 00644 $restriction = $wgLogRestrictions[$this->type]; 00645 } else { 00646 // '' always returns true with $user->isAllowed() 00647 $restriction = ''; 00648 } 00649 return $restriction; 00650 } 00651 00657 public function isRestricted() { 00658 $restriction = $this->getRestriction(); 00659 return $restriction !== '' && $restriction !== '*'; 00660 } 00661 00662 }