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 var $type, $action, $comment, $params, $target, $doer;
00039
00040 var $updateRecentChanges, $sendToUDP;
00041
00050 public function __construct( $type, $rc = true, $udp = 'skipUDP' ) {
00051 $this->type = $type;
00052 $this->updateRecentChanges = $rc;
00053 $this->sendToUDP = ($udp == 'UDP');
00054 }
00055
00056 protected function saveContent() {
00057 global $wgLogRestrictions;
00058
00059 $dbw = wfGetDB( DB_MASTER );
00060 $log_id = $dbw->nextSequenceValue( 'log_log_id_seq' );
00061
00062 $this->timestamp = $now = wfTimestampNow();
00063 $data = array(
00064 'log_id' => $log_id,
00065 'log_type' => $this->type,
00066 'log_action' => $this->action,
00067 'log_timestamp' => $dbw->timestamp( $now ),
00068 'log_user' => $this->doer->getId(),
00069 'log_namespace' => $this->target->getNamespace(),
00070 'log_title' => $this->target->getDBkey(),
00071 'log_comment' => $this->comment,
00072 'log_params' => $this->params
00073 );
00074 $dbw->insert( 'logging', $data, __METHOD__ );
00075 $newId = !is_null($log_id) ? $log_id : $dbw->insertId();
00076
00077 # And update recentchanges
00078 if( $this->updateRecentChanges ) {
00079 $titleObj = SpecialPage::getTitleFor( 'Log', $this->type );
00080 RecentChange::notifyLog( $now, $titleObj, $this->doer, $this->getRcComment(), '', $this->type,
00081 $this->action, $this->target, $this->comment, $this->params, $newId );
00082 } else if( $this->sendToUDP ) {
00083 # Don't send private logs to UDP
00084 if( isset($wgLogRestrictions[$this->type]) && $wgLogRestrictions[$this->type] !='*' ) {
00085 return true;
00086 }
00087 # Notify external application via UDP.
00088 # We send this to IRC but do not want to add it the RC table.
00089 $titleObj = SpecialPage::getTitleFor( 'Log', $this->type );
00090 $rc = RecentChange::newLogEntry( $now, $titleObj, $this->doer, $this->getRcComment(), '',
00091 $this->type, $this->action, $this->target, $this->comment, $this->params, $newId );
00092 $rc->notifyRC2UDP();
00093 }
00094 return true;
00095 }
00096
00100 public function getRcComment() {
00101 $rcComment = $this->actionText;
00102 if( '' != $this->comment ) {
00103 if ($rcComment == '')
00104 $rcComment = $this->comment;
00105 else
00106 $rcComment .= wfMsgForContent( 'colon-separator' ) . $this->comment;
00107 }
00108 return $rcComment;
00109 }
00110
00114 public function getComment() {
00115 return $this->comment;
00116 }
00117
00121 public static function validTypes() {
00122 global $wgLogTypes;
00123 return $wgLogTypes;
00124 }
00125
00129 public static function isLogType( $type ) {
00130 return in_array( $type, LogPage::validTypes() );
00131 }
00132
00136 public static function logName( $type ) {
00137 global $wgLogNames, $wgMessageCache;
00138
00139 if( isset( $wgLogNames[$type] ) ) {
00140 $wgMessageCache->loadAllMessages();
00141 return str_replace( '_', ' ', wfMsg( $wgLogNames[$type] ) );
00142 } else {
00143
00144 return $type;
00145 }
00146 }
00147
00153 public static function logHeader( $type ) {
00154 global $wgLogHeaders, $wgMessageCache;
00155 $wgMessageCache->loadAllMessages();
00156 return wfMsgExt($wgLogHeaders[$type],array('parseinline'));
00157 }
00158
00163 public static function actionText( $type, $action, $title = NULL, $skin = NULL,
00164 $params = array(), $filterWikilinks = false )
00165 {
00166 global $wgLang, $wgContLang, $wgLogActions, $wgMessageCache;
00167
00168 $wgMessageCache->loadAllMessages();
00169 $key = "$type/$action";
00170 # Defer patrol log to PatrolLog class
00171 if( $key == 'patrol/patrol' ) {
00172 return PatrolLog::makeActionText( $title, $params, $skin );
00173 }
00174 if( isset( $wgLogActions[$key] ) ) {
00175 if( is_null( $title ) ) {
00176 $rv = wfMsg( $wgLogActions[$key] );
00177 } else {
00178 $titleLink = self::getTitleLink( $type, $skin, $title, $params );
00179 if( $key == 'rights/rights' ) {
00180 if( $skin ) {
00181 $rightsnone = wfMsg( 'rightsnone' );
00182 foreach ( $params as &$param ) {
00183 $groupArray = array_map( 'trim', explode( ',', $param ) );
00184 $groupArray = array_map( array( 'User', 'getGroupName' ), $groupArray );
00185 $param = $wgLang->listToText( $groupArray );
00186 }
00187 } else {
00188 $rightsnone = wfMsgForContent( 'rightsnone' );
00189 }
00190 if( !isset( $params[0] ) || trim( $params[0] ) == '' )
00191 $params[0] = $rightsnone;
00192 if( !isset( $params[1] ) || trim( $params[1] ) == '' )
00193 $params[1] = $rightsnone;
00194 }
00195 if( count( $params ) == 0 ) {
00196 if ( $skin ) {
00197 $rv = wfMsg( $wgLogActions[$key], $titleLink );
00198 } else {
00199 $rv = wfMsgForContent( $wgLogActions[$key], $titleLink );
00200 }
00201 } else {
00202 $details = '';
00203 array_unshift( $params, $titleLink );
00204 if ( preg_match( '/^(block|suppress)\/(block|reblock)$/', $key ) ) {
00205 if ( $skin ) {
00206 $params[1] = '<span title="' . htmlspecialchars( $params[1] ). '">' .
00207 $wgLang->translateBlockExpiry( $params[1] ) . '</span>';
00208 } else {
00209 $params[1] = $wgContLang->translateBlockExpiry( $params[1] );
00210 }
00211 $params[2] = isset( $params[2] ) ?
00212 self::formatBlockFlags( $params[2], is_null( $skin ) ) : '';
00213 } else if ( $type == 'protect' && count($params) == 3 ) {
00214 $details .= " {$params[1]}";
00215 if( $params[2] ) {
00216 if ( $skin ) {
00217 $details .= ' ['.wfMsg('protect-summary-cascade').']';
00218 } else {
00219 $details .= ' ['.wfMsgForContent('protect-summary-cascade').']';
00220 }
00221 }
00222 } else if ( $type == 'move' && count( $params ) == 3 ) {
00223 if( $params[2] ) {
00224 if ( $skin ) {
00225 $details .= ' [' . wfMsg( 'move-redirect-suppressed' ) . ']';
00226 } else {
00227 $details .= ' [' . wfMsgForContent( 'move-redirect-suppressed' ) . ']';
00228 }
00229 }
00230 }
00231 $rv = wfMsgReal( $wgLogActions[$key], $params, true, !$skin ) . $details;
00232 }
00233 }
00234 } else {
00235 global $wgLogActionsHandlers;
00236 if( isset( $wgLogActionsHandlers[$key] ) ) {
00237 $args = func_get_args();
00238 $rv = call_user_func_array( $wgLogActionsHandlers[$key], $args );
00239 } else {
00240 wfDebug( "LogPage::actionText - unknown action $key\n" );
00241 $rv = "$action";
00242 }
00243 }
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255 if( $filterWikilinks ) {
00256 $rv = str_replace( "[[", "", $rv );
00257 $rv = str_replace( "]]", "", $rv );
00258 }
00259 return $rv;
00260 }
00261
00262 protected static function getTitleLink( $type, $skin, $title, &$params ) {
00263 global $wgLang, $wgContLang;
00264 if( !$skin ) {
00265 return $title->getPrefixedText();
00266 }
00267 switch( $type ) {
00268 case 'move':
00269 $titleLink = $skin->makeLinkObj( $title,
00270 htmlspecialchars( $title->getPrefixedText() ), 'redirect=no' );
00271 $targetTitle = Title::newFromText( $params[0] );
00272 if ( !$targetTitle ) {
00273 # Workaround for broken database
00274 $params[0] = htmlspecialchars( $params[0] );
00275 } else {
00276 $params[0] = $skin->makeLinkObj( $targetTitle, htmlspecialchars( $params[0] ) );
00277 }
00278 break;
00279 case 'block':
00280 if( substr( $title->getText(), 0, 1 ) == '#' ) {
00281 $titleLink = $title->getText();
00282 } else {
00283
00284
00285 $id = User::idFromName( $title->getText() );
00286 $titleLink = $skin->userLink( $id, $title->getText() )
00287 . $skin->userToolLinks( $id, $title->getText(), false, Linker::TOOL_LINKS_NOBLOCK );
00288 }
00289 break;
00290 case 'rights':
00291 $text = $wgContLang->ucfirst( $title->getText() );
00292 $titleLink = $skin->makeLinkObj( Title::makeTitle( NS_USER, $text ) );
00293 break;
00294 case 'merge':
00295 $titleLink = $skin->makeLinkObj( $title, $title->getPrefixedText(), 'redirect=no' );
00296 $params[0] = $skin->makeLinkObj( Title::newFromText( $params[0] ), htmlspecialchars( $params[0] ) );
00297 $params[1] = $wgLang->timeanddate( $params[1] );
00298 break;
00299 default:
00300 if( $title->getNamespace() == NS_SPECIAL ) {
00301 list( $name, $par ) = SpecialPage::resolveAliasWithSubpage( $title->getDBKey() );
00302 # Use the language name for log titles, rather than Log/X
00303 if( $name == 'Log' ) {
00304 $titleLink = '('.$skin->makeLinkObj( $title, LogPage::logName( $par ) ).')';
00305 } else {
00306 $titleLink = $skin->makeLinkObj( $title );
00307 }
00308 } else {
00309 $titleLink = $skin->makeLinkObj( $title );
00310 }
00311 }
00312 return $titleLink;
00313 }
00314
00323 public function addEntry( $action, $target, $comment, $params = array(), $doer = null ) {
00324 if ( !is_array( $params ) ) {
00325 $params = array( $params );
00326 }
00327
00328 $this->action = $action;
00329 $this->target = $target;
00330 $this->comment = $comment;
00331 $this->params = LogPage::makeParamBlob( $params );
00332
00333 if ($doer === null) {
00334 global $wgUser;
00335 $doer = $wgUser;
00336 } elseif (!is_object( $doer ) ) {
00337 $doer = User::newFromId( $doer );
00338 }
00339
00340 $this->doer = $doer;
00341
00342 $this->actionText = LogPage::actionText( $this->type, $action, $target, NULL, $params );
00343
00344 return $this->saveContent();
00345 }
00346
00351 public static function makeParamBlob( $params ) {
00352 return implode( "\n", $params );
00353 }
00354
00359 public static function extractParams( $blob ) {
00360 if ( $blob === '' ) {
00361 return array();
00362 } else {
00363 return explode( "\n", $blob );
00364 }
00365 }
00366
00376 public static function formatBlockFlags( $flags, $forContent = false ) {
00377 global $wgLang;
00378
00379 $flags = explode( ',', trim( $flags ) );
00380 if( count( $flags ) > 0 ) {
00381 for( $i = 0; $i < count( $flags ); $i++ )
00382 $flags[$i] = self::formatBlockFlag( $flags[$i], $forContent );
00383 return '(' . $wgLang->commaList( $flags ) . ')';
00384 } else {
00385 return '';
00386 }
00387 }
00388
00397 public static function formatBlockFlag( $flag, $forContent = false ) {
00398 static $messages = array();
00399 if( !isset( $messages[$flag] ) ) {
00400 $k = 'block-log-flags-' . $flag;
00401 if( $forContent )
00402 $msg = wfMsgForContent( $k );
00403 else
00404 $msg = wfMsg( $k );
00405 $messages[$flag] = htmlspecialchars( wfEmptyMsg( $k, $msg ) ? $flag : $msg );
00406 }
00407 return $messages[$flag];
00408 }
00409 }
00410
00414 define( 'MW_LOG_DELETED_ACTION', LogPage::DELETED_ACTION );
00415 define( 'MW_LOG_DELETED_USER', LogPage::DELETED_USER );
00416 define( 'MW_LOG_DELETED_COMMENT', LogPage::DELETED_COMMENT );
00417 define( 'MW_LOG_DELETED_RESTRICTED', LogPage::DELETED_RESTRICTED );