00001 <?php
00002 #
00003 # Copyright (C) 2002, 2004 Brion Vibber <brion@pobox.com>
00004 # http://www.mediawiki.org/
00005 #
00006 # This program is free software; you can redistribute it and/or modify
00007 # it under the terms of the GNU General Public License as published by
00008 # the Free Software Foundation; either version 2 of the License, or
00009 # (at your option) any later version.
00010 #
00011 # This program is distributed in the hope that it will be useful,
00012 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014 # GNU General Public License for more details.
00015 #
00016 # You should have received a copy of the GNU General Public License along
00017 # with this program; if not, write to the Free Software Foundation, Inc.,
00018 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019 # http://www.gnu.org/copyleft/gpl.html
00020
00032 class LogPage {
00033 const DELETED_ACTION = 1;
00034 const DELETED_COMMENT = 2;
00035 const DELETED_USER = 4;
00036 const DELETED_RESTRICTED = 8;
00037
00038 const SUPPRESSED_USER = 12;
00039 const SUPPRESSED_ACTION = 9;
00040
00041 var $type, $action, $comment, $params, $target, $doer;
00042
00043 var $updateRecentChanges, $sendToUDP;
00044
00053 public function __construct( $type, $rc = true, $udp = 'skipUDP' ) {
00054 $this->type = $type;
00055 $this->updateRecentChanges = $rc;
00056 $this->sendToUDP = ($udp == 'UDP');
00057 }
00058
00059 protected function saveContent() {
00060 global $wgLogRestrictions;
00061
00062 $dbw = wfGetDB( DB_MASTER );
00063 $log_id = $dbw->nextSequenceValue( 'logging_log_id_seq' );
00064
00065 $this->timestamp = $now = wfTimestampNow();
00066 $data = array(
00067 'log_id' => $log_id,
00068 'log_type' => $this->type,
00069 'log_action' => $this->action,
00070 'log_timestamp' => $dbw->timestamp( $now ),
00071 'log_user' => $this->doer->getId(),
00072 'log_user_text' => $this->doer->getName(),
00073 'log_namespace' => $this->target->getNamespace(),
00074 'log_title' => $this->target->getDBkey(),
00075 'log_page' => $this->target->getArticleId(),
00076 'log_comment' => $this->comment,
00077 'log_params' => $this->params
00078 );
00079 $dbw->insert( 'logging', $data, __METHOD__ );
00080 $newId = !is_null($log_id) ? $log_id : $dbw->insertId();
00081
00082 # And update recentchanges
00083 if( $this->updateRecentChanges ) {
00084 $titleObj = SpecialPage::getTitleFor( 'Log', $this->type );
00085 RecentChange::notifyLog( $now, $titleObj, $this->doer, $this->getRcComment(), '', $this->type,
00086 $this->action, $this->target, $this->comment, $this->params, $newId );
00087 } else if( $this->sendToUDP ) {
00088 # Don't send private logs to UDP
00089 if( isset($wgLogRestrictions[$this->type]) && $wgLogRestrictions[$this->type] !='*' ) {
00090 return true;
00091 }
00092 # Notify external application via UDP.
00093 # We send this to IRC but do not want to add it the RC table.
00094 $titleObj = SpecialPage::getTitleFor( 'Log', $this->type );
00095 $rc = RecentChange::newLogEntry( $now, $titleObj, $this->doer, $this->getRcComment(), '',
00096 $this->type, $this->action, $this->target, $this->comment, $this->params, $newId );
00097 $rc->notifyRC2UDP();
00098 }
00099 return $newId;
00100 }
00101
00105 public function getRcComment() {
00106 $rcComment = $this->actionText;
00107 if( $this->comment != '' ) {
00108 if ($rcComment == '')
00109 $rcComment = $this->comment;
00110 else
00111 $rcComment .= wfMsgForContent( 'colon-separator' ) . $this->comment;
00112 }
00113 return $rcComment;
00114 }
00115
00119 public function getComment() {
00120 return $this->comment;
00121 }
00122
00126 public static function validTypes() {
00127 global $wgLogTypes;
00128 return $wgLogTypes;
00129 }
00130
00134 public static function isLogType( $type ) {
00135 return in_array( $type, LogPage::validTypes() );
00136 }
00137
00142 public static function logName( $type ) {
00143 global $wgLogNames, $wgMessageCache;
00144
00145 if( isset( $wgLogNames[$type] ) ) {
00146 $wgMessageCache->loadAllMessages();
00147 return str_replace( '_', ' ', wfMsg( $wgLogNames[$type] ) );
00148 } else {
00149
00150 return $type;
00151 }
00152 }
00153
00159 public static function logHeader( $type ) {
00160 global $wgLogHeaders, $wgMessageCache;
00161 $wgMessageCache->loadAllMessages();
00162 return wfMsgExt($wgLogHeaders[$type],array('parseinline'));
00163 }
00164
00169 public static function actionText( $type, $action, $title = null, $skin = null,
00170 $params = array(), $filterWikilinks = false )
00171 {
00172 global $wgLang, $wgContLang, $wgLogActions, $wgMessageCache;
00173
00174 $wgMessageCache->loadAllMessages();
00175 $key = "$type/$action";
00176 # Defer patrol log to PatrolLog class
00177 if( $key == 'patrol/patrol' ) {
00178 return PatrolLog::makeActionText( $title, $params, $skin );
00179 }
00180 if( isset( $wgLogActions[$key] ) ) {
00181 if( is_null( $title ) ) {
00182 $rv = wfMsgHtml( $wgLogActions[$key] );
00183 } else {
00184 $titleLink = self::getTitleLink( $type, $skin, $title, $params );
00185 if( $key == 'rights/rights' ) {
00186 if( $skin ) {
00187 $rightsnone = wfMsg( 'rightsnone' );
00188 foreach ( $params as &$param ) {
00189 $groupArray = array_map( 'trim', explode( ',', $param ) );
00190 $groupArray = array_map( array( 'User', 'getGroupName' ), $groupArray );
00191 $param = $wgLang->listToText( $groupArray );
00192 }
00193 } else {
00194 $rightsnone = wfMsgForContent( 'rightsnone' );
00195 }
00196 if( !isset( $params[0] ) || trim( $params[0] ) == '' )
00197 $params[0] = $rightsnone;
00198 if( !isset( $params[1] ) || trim( $params[1] ) == '' )
00199 $params[1] = $rightsnone;
00200 }
00201 if( count( $params ) == 0 ) {
00202 if ( $skin ) {
00203 $rv = wfMsgHtml( $wgLogActions[$key], $titleLink );
00204 } else {
00205 $rv = wfMsgExt( $wgLogActions[$key], array( 'parsemag', 'escape', 'replaceafter', 'content' ), $titleLink );
00206 }
00207 } else {
00208 $details = '';
00209 array_unshift( $params, $titleLink );
00210
00211 if ( preg_match( '/^(block|suppress)\/(block|reblock)$/', $key ) ) {
00212 if ( $skin ) {
00213 $params[1] = '<span title="' . htmlspecialchars( $params[1] ). '">' .
00214 $wgLang->translateBlockExpiry( $params[1] ) . '</span>';
00215 } else {
00216 $params[1] = $wgContLang->translateBlockExpiry( $params[1] );
00217 }
00218 $params[2] = isset( $params[2] ) ?
00219 self::formatBlockFlags( $params[2], is_null( $skin ) ) : '';
00220
00221 } else if ( $type == 'protect' && count($params) == 3 ) {
00222
00223 if( $skin ) {
00224 $details .= htmlspecialchars( " {$params[1]}" );
00225 } else {
00226 $details .= " {$params[1]}";
00227 }
00228
00229 if( $params[2] ) {
00230 if ( $skin ) {
00231 $details .= ' ['.wfMsg('protect-summary-cascade').']';
00232 } else {
00233 $details .= ' ['.wfMsgForContent('protect-summary-cascade').']';
00234 }
00235 }
00236
00237 } else if ( $type == 'move' && count( $params ) == 3 ) {
00238 if( $params[2] ) {
00239 if ( $skin ) {
00240 $details .= ' [' . wfMsg( 'move-redirect-suppressed' ) . ']';
00241 } else {
00242 $details .= ' [' . wfMsgForContent( 'move-redirect-suppressed' ) . ']';
00243 }
00244 }
00245
00246 } else if ( preg_match( '/^(delete|suppress)\/revision$/', $key ) && count( $params ) == 5 ) {
00247 $count = substr_count( $params[2], ',' ) + 1;
00248 $ofield = intval( substr( $params[3], 7 ) );
00249 $nfield = intval( substr( $params[4], 7 ) );
00250 $details .= ': '.RevisionDeleter::getLogMessage( $count, $nfield, $ofield, false );
00251
00252 } else if ( preg_match( '/^(delete|suppress)\/event$/', $key ) && count( $params ) == 4 ) {
00253 $count = substr_count( $params[1], ',' ) + 1;
00254 $ofield = intval( substr( $params[2], 7 ) );
00255 $nfield = intval( substr( $params[3], 7 ) );
00256 $details .= ': '.RevisionDeleter::getLogMessage( $count, $nfield, $ofield, true );
00257 }
00258 if ( $skin ) {
00259 $rv = wfMsgHtml( $wgLogActions[$key], $params ) . $details;
00260 } else {
00261 $rv = wfMsgExt( $wgLogActions[$key], array( 'parsemag', 'escape', 'replaceafter', 'content' ), $params ) . $details;
00262 }
00263 }
00264 }
00265 } else {
00266 global $wgLogActionsHandlers;
00267 if( isset( $wgLogActionsHandlers[$key] ) ) {
00268 $args = func_get_args();
00269 $rv = call_user_func_array( $wgLogActionsHandlers[$key], $args );
00270 } else {
00271 wfDebug( "LogPage::actionText - unknown action $key\n" );
00272 $rv = "$action";
00273 }
00274 }
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286 if( $filterWikilinks ) {
00287 $rv = str_replace( "[[", "", $rv );
00288 $rv = str_replace( "]]", "", $rv );
00289 }
00290 return $rv;
00291 }
00292
00293 protected static function getTitleLink( $type, $skin, $title, &$params ) {
00294 global $wgLang, $wgContLang, $wgUserrightsInterwikiDelimiter;
00295 if( !$skin ) {
00296 return $title->getPrefixedText();
00297 }
00298 switch( $type ) {
00299 case 'move':
00300 $titleLink = $skin->link(
00301 $title,
00302 htmlspecialchars( $title->getPrefixedText() ),
00303 array(),
00304 array( 'redirect' => 'no' )
00305 );
00306 $targetTitle = Title::newFromText( $params[0] );
00307 if ( !$targetTitle ) {
00308 # Workaround for broken database
00309 $params[0] = htmlspecialchars( $params[0] );
00310 } else {
00311 $params[0] = $skin->link(
00312 $targetTitle,
00313 htmlspecialchars( $params[0] )
00314 );
00315 }
00316 break;
00317 case 'block':
00318 if( substr( $title->getText(), 0, 1 ) == '#' ) {
00319 $titleLink = $title->getText();
00320 } else {
00321
00322
00323 $id = User::idFromName( $title->getText() );
00324 $titleLink = $skin->userLink( $id, $title->getText() )
00325 . $skin->userToolLinks( $id, $title->getText(), false, Linker::TOOL_LINKS_NOBLOCK );
00326 }
00327 break;
00328 case 'rights':
00329 $text = $wgContLang->ucfirst( $title->getText() );
00330 $parts = explode( $wgUserrightsInterwikiDelimiter, $text, 2 );
00331 if ( count( $parts ) == 2 ) {
00332 $titleLink = WikiMap::foreignUserLink( $parts[1], $parts[0],
00333 htmlspecialchars( $title->getPrefixedText() ) );
00334 if ( $titleLink !== false )
00335 break;
00336 }
00337 $titleLink = $skin->link( Title::makeTitle( NS_USER, $text ) );
00338 break;
00339 case 'merge':
00340 $titleLink = $skin->link(
00341 $title,
00342 $title->getPrefixedText(),
00343 array(),
00344 array( 'redirect' => 'no' )
00345 );
00346 $params[0] = $skin->link(
00347 Title::newFromText( $params[0] ),
00348 htmlspecialchars( $params[0] )
00349 );
00350 $params[1] = $wgLang->timeanddate( $params[1] );
00351 break;
00352 default:
00353 if( $title->getNamespace() == NS_SPECIAL ) {
00354 list( $name, $par ) = SpecialPage::resolveAliasWithSubpage( $title->getDBkey() );
00355 # Use the language name for log titles, rather than Log/X
00356 if( $name == 'Log' ) {
00357 $titleLink = '('.$skin->link( $title, LogPage::logName( $par ) ).')';
00358 } else {
00359 $titleLink = $skin->link( $title );
00360 }
00361 } else {
00362 $titleLink = $skin->link( $title );
00363 }
00364 }
00365 return $titleLink;
00366 }
00367
00376 public function addEntry( $action, $target, $comment, $params = array(), $doer = null ) {
00377 if ( !is_array( $params ) ) {
00378 $params = array( $params );
00379 }
00380
00381 if ( $comment === null ) $comment = "";
00382
00383 $this->action = $action;
00384 $this->target = $target;
00385 $this->comment = $comment;
00386 $this->params = LogPage::makeParamBlob( $params );
00387
00388 if ($doer === null) {
00389 global $wgUser;
00390 $doer = $wgUser;
00391 } elseif (!is_object( $doer ) ) {
00392 $doer = User::newFromId( $doer );
00393 }
00394
00395 $this->doer = $doer;
00396
00397 $this->actionText = LogPage::actionText( $this->type, $action, $target, null, $params );
00398
00399 return $this->saveContent();
00400 }
00401
00406 public function addRelations( $field, $values, $logid ) {
00407 if( !strlen($field) || empty($values) )
00408 return false;
00409 $data = array();
00410 foreach( $values as $value ) {
00411 $data[] = array('ls_field' => $field,'ls_value' => $value,'ls_log_id' => $logid);
00412 }
00413 $dbw = wfGetDB( DB_MASTER );
00414 $dbw->insert( 'log_search', $data, __METHOD__, 'IGNORE' );
00415 return true;
00416 }
00417
00422 public static function makeParamBlob( $params ) {
00423 return implode( "\n", $params );
00424 }
00425
00430 public static function extractParams( $blob ) {
00431 if ( $blob === '' ) {
00432 return array();
00433 } else {
00434 return explode( "\n", $blob );
00435 }
00436 }
00437
00447 public static function formatBlockFlags( $flags, $forContent = false ) {
00448 global $wgLang;
00449
00450 $flags = explode( ',', trim( $flags ) );
00451 if( count( $flags ) > 0 ) {
00452 for( $i = 0; $i < count( $flags ); $i++ )
00453 $flags[$i] = self::formatBlockFlag( $flags[$i], $forContent );
00454 return '(' . $wgLang->commaList( $flags ) . ')';
00455 } else {
00456 return '';
00457 }
00458 }
00459
00468 public static function formatBlockFlag( $flag, $forContent = false ) {
00469 static $messages = array();
00470 if( !isset( $messages[$flag] ) ) {
00471 $k = 'block-log-flags-' . $flag;
00472 if( $forContent )
00473 $msg = wfMsgForContent( $k );
00474 else
00475 $msg = wfMsg( $k );
00476 $messages[$flag] = htmlspecialchars( wfEmptyMsg( $k, $msg ) ? $flag : $msg );
00477 }
00478 return $messages[$flag];
00479 }
00480 }
00481
00485 define( 'MW_LOG_DELETED_ACTION', LogPage::DELETED_ACTION );
00486 define( 'MW_LOG_DELETED_USER', LogPage::DELETED_USER );
00487 define( 'MW_LOG_DELETED_COMMENT', LogPage::DELETED_COMMENT );
00488 define( 'MW_LOG_DELETED_RESTRICTED', LogPage::DELETED_RESTRICTED );