MediaWiki
REL1_19
|
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 00104 ); 00105 } elseif( $this->sendToUDP ) { 00106 # Don't send private logs to UDP 00107 if( isset( $wgLogRestrictions[$this->type] ) && $wgLogRestrictions[$this->type] != '*' ) { 00108 return true; 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 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 .= wfMsgForContent( 'colon-separator' ) . $this->comment; 00137 } 00138 } 00139 00140 return $rcComment; 00141 } 00142 00146 public function getComment() { 00147 return $this->comment; 00148 } 00149 00155 public static function validTypes() { 00156 global $wgLogTypes; 00157 return $wgLogTypes; 00158 } 00159 00166 public static function isLogType( $type ) { 00167 return in_array( $type, LogPage::validTypes() ); 00168 } 00169 00177 public static function logName( $type ) { 00178 wfDeprecated( __METHOD__, '1.19' ); 00179 global $wgLogNames; 00180 00181 if( isset( $wgLogNames[$type] ) ) { 00182 return str_replace( '_', ' ', wfMsg( $wgLogNames[$type] ) ); 00183 } else { 00184 // Bogus log types? Perhaps an extension was removed. 00185 return $type; 00186 } 00187 } 00188 00197 public static function logHeader( $type ) { 00198 wfDeprecated( __METHOD__, '1.19' ); 00199 global $wgLogHeaders; 00200 return wfMsgExt( $wgLogHeaders[$type], array( 'parseinline' ) ); 00201 } 00202 00216 public static function actionText( $type, $action, $title = null, $skin = null, 00217 $params = array(), $filterWikilinks = false ) 00218 { 00219 global $wgLang, $wgContLang, $wgLogActions; 00220 00221 if ( is_null( $skin ) ) { 00222 $langObj = $wgContLang; 00223 $langObjOrNull = null; 00224 } else { 00225 $langObj = $wgLang; 00226 $langObjOrNull = $wgLang; 00227 } 00228 00229 $key = "$type/$action"; 00230 00231 if( isset( $wgLogActions[$key] ) ) { 00232 if( is_null( $title ) ) { 00233 $rv = wfMsgExt( $wgLogActions[$key], array( 'parsemag', 'escape', 'language' => $langObj ) ); 00234 } else { 00235 $titleLink = self::getTitleLink( $type, $langObjOrNull, $title, $params ); 00236 00237 if( preg_match( '/^rights\/(rights|autopromote)/', $key ) ) { 00238 $rightsnone = wfMsgExt( 'rightsnone', array( 'parsemag', 'language' => $langObj ) ); 00239 00240 if( $skin ) { 00241 foreach ( $params as &$param ) { 00242 $groupArray = array_map( 'trim', explode( ',', $param ) ); 00243 $groupArray = array_map( array( 'User', 'getGroupMember' ), $groupArray ); 00244 $param = $wgLang->listToText( $groupArray ); 00245 } 00246 } 00247 00248 if( !isset( $params[0] ) || trim( $params[0] ) == '' ) { 00249 $params[0] = $rightsnone; 00250 } 00251 00252 if( !isset( $params[1] ) || trim( $params[1] ) == '' ) { 00253 $params[1] = $rightsnone; 00254 } 00255 } 00256 00257 if( count( $params ) == 0 ) { 00258 $rv = wfMsgExt( $wgLogActions[$key], array( 'parsemag', 'escape', 'replaceafter', 'language' => $langObj ), $titleLink ); 00259 } else { 00260 $details = ''; 00261 array_unshift( $params, $titleLink ); 00262 00263 // User suppression 00264 if ( preg_match( '/^(block|suppress)\/(block|reblock)$/', $key ) ) { 00265 if ( $skin ) { 00266 $params[1] = '<span class="blockExpiry" dir="ltr" title="' . htmlspecialchars( $params[1] ). '">' . 00267 $wgLang->translateBlockExpiry( $params[1] ) . '</span>'; 00268 } else { 00269 $params[1] = $wgContLang->translateBlockExpiry( $params[1] ); 00270 } 00271 00272 $params[2] = isset( $params[2] ) ? 00273 self::formatBlockFlags( $params[2], $langObj ) : ''; 00274 // Page protections 00275 } elseif ( $type == 'protect' && count($params) == 3 ) { 00276 // Restrictions and expiries 00277 if( $skin ) { 00278 $details .= $wgLang->getDirMark() . htmlspecialchars( " {$params[1]}" ); 00279 } else { 00280 $details .= " {$params[1]}"; 00281 } 00282 00283 // Cascading flag... 00284 if( $params[2] ) { 00285 $details .= ' [' . wfMsgExt( 'protect-summary-cascade', array( 'parsemag', 'language' => $langObj ) ) . ']'; 00286 } 00287 } 00288 00289 $rv = wfMsgExt( $wgLogActions[$key], array( 'parsemag', 'escape', 'replaceafter', 'language' => $langObj ), $params ) . $details; 00290 } 00291 } 00292 } else { 00293 global $wgLogActionsHandlers; 00294 00295 if( isset( $wgLogActionsHandlers[$key] ) ) { 00296 $args = func_get_args(); 00297 $rv = call_user_func_array( $wgLogActionsHandlers[$key], $args ); 00298 } else { 00299 wfDebug( "LogPage::actionText - unknown action $key\n" ); 00300 $rv = "$action"; 00301 } 00302 } 00303 00304 // For the perplexed, this feature was added in r7855 by Erik. 00305 // The feature was added because we liked adding [[$1]] in our log entries 00306 // but the log entries are parsed as Wikitext on RecentChanges but as HTML 00307 // on Special:Log. The hack is essentially that [[$1]] represented a link 00308 // to the title in question. The first parameter to the HTML version (Special:Log) 00309 // is that link in HTML form, and so this just gets rid of the ugly [[]]. 00310 // However, this is a horrible hack and it doesn't work like you expect if, say, 00311 // you want to link to something OTHER than the title of the log entry. 00312 // The real problem, which Erik was trying to fix (and it sort-of works now) is 00313 // that the same messages are being treated as both wikitext *and* HTML. 00314 if( $filterWikilinks ) { 00315 $rv = str_replace( '[[', '', $rv ); 00316 $rv = str_replace( ']]', '', $rv ); 00317 } 00318 00319 return $rv; 00320 } 00321 00330 protected static function getTitleLink( $type, $lang, $title, &$params ) { 00331 global $wgContLang, $wgUserrightsInterwikiDelimiter; 00332 00333 if( !$lang ) { 00334 return $title->getPrefixedText(); 00335 } 00336 00337 switch( $type ) { 00338 case 'move': 00339 $titleLink = Linker::link( 00340 $title, 00341 htmlspecialchars( $title->getPrefixedText() ), 00342 array(), 00343 array( 'redirect' => 'no' ) 00344 ); 00345 00346 $targetTitle = Title::newFromText( $params[0] ); 00347 00348 if ( !$targetTitle ) { 00349 # Workaround for broken database 00350 $params[0] = htmlspecialchars( $params[0] ); 00351 } else { 00352 $params[0] = Linker::link( 00353 $targetTitle, 00354 htmlspecialchars( $params[0] ) 00355 ); 00356 } 00357 break; 00358 case 'block': 00359 if( substr( $title->getText(), 0, 1 ) == '#' ) { 00360 $titleLink = $title->getText(); 00361 } else { 00362 // @todo Store the user identifier in the parameters 00363 // to make this faster for future log entries 00364 $id = User::idFromName( $title->getText() ); 00365 $titleLink = Linker::userLink( $id, $title->getText() ) 00366 . Linker::userToolLinks( $id, $title->getText(), false, Linker::TOOL_LINKS_NOBLOCK ); 00367 } 00368 break; 00369 case 'rights': 00370 $text = $wgContLang->ucfirst( $title->getText() ); 00371 $parts = explode( $wgUserrightsInterwikiDelimiter, $text, 2 ); 00372 00373 if ( count( $parts ) == 2 ) { 00374 $titleLink = WikiMap::foreignUserLink( $parts[1], $parts[0], 00375 htmlspecialchars( $title->getPrefixedText() ) ); 00376 00377 if ( $titleLink !== false ) { 00378 break; 00379 } 00380 } 00381 $titleLink = Linker::link( Title::makeTitle( NS_USER, $text ) ); 00382 break; 00383 case 'merge': 00384 $titleLink = Linker::link( 00385 $title, 00386 $title->getPrefixedText(), 00387 array(), 00388 array( 'redirect' => 'no' ) 00389 ); 00390 $params[0] = Linker::link( 00391 Title::newFromText( $params[0] ), 00392 htmlspecialchars( $params[0] ) 00393 ); 00394 $params[1] = $lang->timeanddate( $params[1] ); 00395 break; 00396 default: 00397 if( $title->isSpecialPage() ) { 00398 list( $name, $par ) = SpecialPageFactory::resolveAlias( $title->getDBkey() ); 00399 00400 # Use the language name for log titles, rather than Log/X 00401 if( $name == 'Log' ) { 00402 $titleLink = '(' . Linker::link( $title, LogPage::logName( $par ) ) . ')'; 00403 } else { 00404 $titleLink = Linker::link( $title ); 00405 } 00406 } else { 00407 $titleLink = Linker::link( $title ); 00408 } 00409 } 00410 00411 return $titleLink; 00412 } 00413 00426 public function addEntry( $action, $target, $comment, $params = array(), $doer = null ) { 00427 global $wgContLang; 00428 00429 if ( !is_array( $params ) ) { 00430 $params = array( $params ); 00431 } 00432 00433 if ( $comment === null ) { 00434 $comment = ''; 00435 } 00436 00437 # Truncate for whole multibyte characters. 00438 $comment = $wgContLang->truncate( $comment, 255 ); 00439 00440 $this->action = $action; 00441 $this->target = $target; 00442 $this->comment = $comment; 00443 $this->params = LogPage::makeParamBlob( $params ); 00444 00445 if ( $doer === null ) { 00446 global $wgUser; 00447 $doer = $wgUser; 00448 } elseif ( !is_object( $doer ) ) { 00449 $doer = User::newFromId( $doer ); 00450 } 00451 00452 $this->doer = $doer; 00453 00454 $logEntry = new ManualLogEntry( $this->type, $action ); 00455 $logEntry->setTarget( $target ); 00456 $logEntry->setPerformer( $doer ); 00457 $logEntry->setParameters( $params ); 00458 00459 $formatter = LogFormatter::newFromEntry( $logEntry ); 00460 $context = RequestContext::newExtraneousContext( $target ); 00461 $formatter->setContext( $context ); 00462 00463 $this->actionText = $formatter->getPlainActionText(); 00464 00465 return $this->saveContent(); 00466 } 00467 00476 public function addRelations( $field, $values, $logid ) { 00477 if( !strlen( $field ) || empty( $values ) ) { 00478 return false; // nothing 00479 } 00480 00481 $data = array(); 00482 00483 foreach( $values as $value ) { 00484 $data[] = array( 00485 'ls_field' => $field, 00486 'ls_value' => $value, 00487 'ls_log_id' => $logid 00488 ); 00489 } 00490 00491 $dbw = wfGetDB( DB_MASTER ); 00492 $dbw->insert( 'log_search', $data, __METHOD__, 'IGNORE' ); 00493 00494 return true; 00495 } 00496 00503 public static function makeParamBlob( $params ) { 00504 return implode( "\n", $params ); 00505 } 00506 00513 public static function extractParams( $blob ) { 00514 if ( $blob === '' ) { 00515 return array(); 00516 } else { 00517 return explode( "\n", $blob ); 00518 } 00519 } 00520 00529 public static function formatBlockFlags( $flags, $lang ) { 00530 $flags = explode( ',', trim( $flags ) ); 00531 00532 if( count( $flags ) > 0 ) { 00533 for( $i = 0; $i < count( $flags ); $i++ ) { 00534 $flags[$i] = self::formatBlockFlag( $flags[$i], $lang ); 00535 } 00536 return '(' . $lang->commaList( $flags ) . ')'; 00537 } else { 00538 return ''; 00539 } 00540 } 00541 00549 public static function formatBlockFlag( $flag, $lang ) { 00550 static $messages = array(); 00551 00552 if( !isset( $messages[$flag] ) ) { 00553 $messages[$flag] = htmlspecialchars( $flag ); // Fallback 00554 00555 // For grepping. The following core messages can be used here: 00556 // * block-log-flags-angry-autoblock 00557 // * block-log-flags-anononly 00558 // * block-log-flags-hiddenname 00559 // * block-log-flags-noautoblock 00560 // * block-log-flags-nocreate 00561 // * block-log-flags-noemail 00562 // * block-log-flags-nousertalk 00563 $msg = wfMessage( 'block-log-flags-' . $flag )->inLanguage( $lang ); 00564 00565 if ( $msg->exists() ) { 00566 $messages[$flag] = $msg->escaped(); 00567 } 00568 } 00569 00570 return $messages[$flag]; 00571 } 00572 00573 00579 public function getName() { 00580 global $wgLogNames; 00581 00582 // BC 00583 if ( isset( $wgLogNames[$this->type] ) ) { 00584 $key = $wgLogNames[$this->type]; 00585 } else { 00586 $key = 'log-name-' . $this->type; 00587 } 00588 00589 return wfMessage( $key ); 00590 } 00591 00597 public function getDescription() { 00598 global $wgLogHeaders; 00599 // BC 00600 if ( isset( $wgLogHeaders[$this->type] ) ) { 00601 $key = $wgLogHeaders[$this->type]; 00602 } else { 00603 $key = 'log-description-' . $this->type; 00604 } 00605 return wfMessage( $key ); 00606 } 00607 00613 public function getRestriction() { 00614 global $wgLogRestrictions; 00615 if ( isset( $wgLogRestrictions[$this->type] ) ) { 00616 $restriction = $wgLogRestrictions[$this->type]; 00617 } else { 00618 // '' always returns true with $user->isAllowed() 00619 $restriction = ''; 00620 } 00621 return $restriction; 00622 } 00623 00629 public function isRestricted() { 00630 $restriction = $this->getRestriction(); 00631 return $restriction !== '' && $restriction !== '*'; 00632 } 00633 00634 }