<?php
-# Copyright (C) 2004 Brion Vibber <brion@pobox.com>, 2008 Aaron Schulz
-# http://www.mediawiki.org/
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-# http://www.gnu.org/copyleft/gpl.html
+/**
+ * Contain classes to list log entries
+ *
+ * Copyright © 2004 Brion Vibber <brion@pobox.com>, 2008 Aaron Schulz
+ * http://www.mediawiki.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
class LogEventsList {
const NO_ACTION_LINK = 1;
const NO_EXTRA_USER_LINKS = 2;
+ /**
+ * @var Skin
+ */
private $skin;
+
+ /**
+ * @var OutputPage
+ */
private $out;
public $flags;
+ /**
+ * @var Array
+ */
+ protected $message;
+
+ /**
+ * @var Array
+ */
+ protected $mDefaultQuery;
+
public function __construct( $skin, $out, $flags = 0 ) {
$this->skin = $skin;
$this->out = $out;
/**
* Show options for the log list
+ *
* @param $types string or Array
* @param $user String
* @param $page String
* @param $filter: array
* @param $tagFilter: array?
*/
- public function showOptions( $types=array(), $user='', $page='', $pattern='', $year='',
- $month = '', $filter = null, $tagFilter='' )
- {
+ public function showOptions( $types=array(), $user='', $page='', $pattern='', $year='',
+ $month = '', $filter = null, $tagFilter='' ) {
global $wgScript, $wgMiserMode;
$action = $wgScript;
$tagSelector = ChangeTags::buildTagFilterSelector( $tagFilter );
- $html = '';
- $html .= Xml::hidden( 'title', $special );
+ $html = Html::hidden( 'title', $special );
// Basic selectors
$html .= $this->getTypeMenu( $types ) . "\n";
* @return String: Formatted HTML
*/
private function getFilterLinks( $filter ) {
- global $wgTitle, $wgLang;
+ global $wgLang;
// show/hide links
$messages = array( wfMsgHtml( 'show' ), wfMsgHtml( 'hide' ) );
// Option value -> message mapping
$query[$queryKey] = $hideVal;
$link = $this->skin->link(
- $wgTitle,
+ $this->out->getTitle(),
$messages[$hideVal],
array(),
$query,
);
$links[$type] = wfMsgHtml( "log-show-hide-{$type}", $link );
- $hiddens .= Xml::hidden( "hide_{$type}_log", $val ) . "\n";
+ $hiddens .= Html::hidden( "hide_{$type}_log", $val ) . "\n";
}
// Build links
return '<small>'.$wgLang->pipeList( $links ) . '</small>' . $hiddens;
}
private function getDefaultQuery() {
+ global $wgRequest;
+
if ( !isset( $this->mDefaultQuery ) ) {
- $this->mDefaultQuery = $_GET;
+ $this->mDefaultQuery = $wgRequest->getQueryValues();
unset( $this->mDefaultQuery['title'] );
unset( $this->mDefaultQuery['dir'] );
unset( $this->mDefaultQuery['offset'] );
return Xml::tags( 'li', array( "class" => implode( ' ', $classes ) ),
$del . "$time $userLink $action $comment $revert $tagDisplay" ) . "\n";
}
-
+
private function logTimestamp( $row ) {
global $wgLang;
$time = $wgLang->timeanddate( wfTimestamp( TS_MW, $row->log_timestamp ), true );
}
private function logUserLinks( $row ) {
- $userLinks = '';
if( self::isDeleted( $row, LogPage::DELETED_USER ) ) {
$userLinks = '<span class="history-deleted">' .
wfMsgHtml( 'rev-deleted-user' ) . '</span>';
}
private function logAction( $row, $title, $paramArray ) {
- $action = '';
if( self::isDeleted( $row, LogPage::DELETED_ACTION ) ) {
$action = '<span class="history-deleted">' .
wfMsgHtml( 'rev-deleted-event' ) . '</span>';
}
return $action;
}
-
+
private function logComment( $row ) {
global $wgContLang;
- $comment = '';
if( self::isDeleted( $row, LogPage::DELETED_COMMENT ) ) {
$comment = '<span class="history-deleted">' .
wfMsgHtml( 'rev-deleted-comment' ) . '</span>';
return $comment;
}
- // @TODO: split up!
+ /**
+ * @TODO: split up!
+ *
+ * @param $row
+ * @param Title $title
+ * @param Array $paramArray
+ * @param $comment
+ * @return String
+ */
private function logActionLinks( $row, $title, $paramArray, &$comment ) {
- global $wgUser, $wgLang;
+ global $wgUser;
if( ( $this->flags & self::NO_ACTION_LINK ) // we don't want to see the action
|| self::isDeleted( $row, LogPage::DELETED_ACTION ) ) // action is hidden
{
) . ')';
}
// Show undelete link
- } else if( self::typeAction( $row, array( 'delete', 'suppress' ), 'delete', 'deletedhistory' ) ) {
+ } elseif( self::typeAction( $row, array( 'delete', 'suppress' ), 'delete', 'deletedhistory' ) ) {
if( !$wgUser->isAllowed( 'undelete' ) ) {
$viewdeleted = $this->message['undeleteviewlink'];
} else {
array( 'known', 'noclasses' )
) . ')';
// Show unblock/change block link
- } else if( self::typeAction( $row, array( 'block', 'suppress' ), array( 'block', 'reblock' ), 'block' ) ) {
+ } elseif( self::typeAction( $row, array( 'block', 'suppress' ), array( 'block', 'reblock' ), 'block' ) ) {
$revert = '(' .
$this->skin->link(
- SpecialPage::getTitleFor( 'Ipblocklist' ),
+ SpecialPage::getTitleFor( 'Unblock', $row->log_title ),
$this->message['unblocklink'],
array(),
- array(
- 'action' => 'unblock',
- 'ip' => $row->log_title
- ),
+ array(),
'known'
) .
$this->message['pipe-separator'] .
$this->skin->link(
- SpecialPage::getTitleFor( 'Blockip', $row->log_title ),
+ SpecialPage::getTitleFor( 'Block', $row->log_title ),
$this->message['change-blocklink'],
array(),
array(),
) .
')';
// Show change protection link
- } else if( self::typeAction( $row, 'protect', array( 'modify', 'protect', 'unprotect' ) ) ) {
+ } elseif( self::typeAction( $row, 'protect', array( 'modify', 'protect', 'unprotect' ) ) ) {
$revert .= ' (' .
$this->skin->link( $title,
$this->message['hist'],
}
$revert .= ')';
// Show unmerge link
- } else if( self::typeAction( $row, 'merge', 'merge', 'mergehistory' ) ) {
+ } elseif( self::typeAction( $row, 'merge', 'merge', 'mergehistory' ) ) {
$revert = '(' . $this->skin->link(
SpecialPage::getTitleFor( 'MergeHistory' ),
$this->message['revertmerge'],
array( 'known', 'noclasses' )
) . ')';
// If an edit was hidden from a page give a review link to the history
- } else if( self::typeAction( $row, array( 'delete', 'suppress' ), 'revision', 'deletedhistory' ) ) {
+ } elseif( self::typeAction( $row, array( 'delete', 'suppress' ), 'revision', 'deletedhistory' ) ) {
$revert = RevisionDeleter::getLogLinks( $title, $paramArray,
$this->skin, $this->message );
// Hidden log items, give review link
- } else if( self::typeAction( $row, array( 'delete', 'suppress' ), 'event', 'deletedhistory' ) ) {
+ } elseif( self::typeAction( $row, array( 'delete', 'suppress' ), 'event', 'deletedhistory' ) ) {
if( count($paramArray) >= 1 ) {
$revdel = SpecialPage::getTitleFor( 'Revisiondelete' );
// $paramArray[1] is a CSV of the IDs
- $Ids = explode( ',', $paramArray[0] );
$query = $paramArray[0];
// Link to each hidden object ID, $paramArray[1] is the url param
$revert = '(' . $this->skin->link(
$revdel,
- $this->message['revdel-restore'],
+ $this->message['revdel-restore'],
array(),
array(
'target' => $title->getPrefixedText(),
) . ')';
}
// Self-created users
- } else if( self::typeAction( $row, 'newusers', 'create2' ) ) {
+ } elseif( self::typeAction( $row, 'newusers', 'create2' ) ) {
if( isset( $paramArray[0] ) ) {
$revert = $this->skin->userToolLinks( $paramArray[0], $title->getDBkey(), true );
} else {
# Fall back to a blue contributions link
$revert = $this->skin->userToolLinks( 1, $title->getDBkey() );
}
- $ts = wfTimestamp( TS_UNIX, $row->log_timestamp );
- if( $ts < '20080129000000' ) {
+ if( wfTimestamp( TS_MW, $row->log_timestamp ) < '20080129000000' ) {
# Suppress $comment from old entries (before 2008-01-29),
# not needed and can contain incorrect links
$comment = '';
private function getShowHideLinks( $row ) {
global $wgUser;
if( ( $this->flags & self::NO_ACTION_LINK ) // we don't want to see the links
- || $row->log_type == 'suppress' ) // no one can hide items from the suppress log
- {
+ || $row->log_type == 'suppress' ) { // no one can hide items from the suppress log
return '';
}
$del = '';
* @param $type Mixed: string/array
* @param $action Mixed: string/array
* @param $right string
- * @return bool
+ * @return Boolean
*/
public static function typeAction( $row, $type, $action, $right='' ) {
$match = is_array($type) ?
/**
* Determine if the current user is allowed to view a particular
* field of this log row, if it's marked as deleted.
+ *
* @param $row Row
* @param $field Integer
* @return Boolean
public static function userCan( $row, $field ) {
return self::userCanBitfield( $row->log_deleted, $field );
}
-
+
/**
* Determine if the current user is allowed to view a particular
* field of this log row, if it's marked as deleted.
+ *
* @param $bitfield Integer (current field)
* @param $field Integer
* @return Boolean
public static function userCanBitfield( $bitfield, $field ) {
if( $bitfield & $field ) {
global $wgUser;
- $permission = '';
+
if ( $bitfield & LogPage::DELETED_RESTRICTED ) {
$permission = 'suppressrevision';
} else {
/**
* Show log extract. Either with text and a box (set $msgKey) or without (don't set $msgKey)
- * @param $out OutputPage or String-by-reference
+ *
+ * @param $out OutputPage|String-by-reference
* @param $types String or Array
* @param $page String The page title to show log entries for
* @param $user String The user who made the log entries
/**
* SQL clause to skip forbidden log types for this user
+ *
* @param $db Database
* @param $audience string, public/user
- * @return mixed (string or false)
+ * @return Mixed: string or false
*/
public static function getExcludeClause( $db, $audience = 'public' ) {
global $wgLogRestrictions, $wgUser;
public $mLogEventsList;
/**
- * constructor
+ * Constructor
+ *
* @param $list LogEventsList
- * @param $types String or Array log types to show
- * @param $user String The user who made the log entries
- * @param $title String The page title the log entries are for
- * @param $pattern String Do a prefix search rather than an exact title match
- * @param $conds Array Extra conditions for the query
- * @param $year Integer The year to start from
- * @param $month Integer The month to start from
+ * @param $types String or Array: log types to show
+ * @param $user String: the user who made the log entries
+ * @param $title String: the page title the log entries are for
+ * @param $pattern String: do a prefix search rather than an exact title match
+ * @param $conds Array: extra conditions for the query
+ * @param $year Integer: the year to start from
+ * @param $month Integer: the month to start from
+ * @param $tagFilter String: tag
*/
public function __construct( $list, $types = array(), $user = '', $title = '', $pattern = '',
- $conds = array(), $year = false, $month = false, $tagFilter = '' )
- {
+ $conds = array(), $year = false, $month = false, $tagFilter = '' ) {
parent::__construct();
$this->mConds = $conds;
/**
* Set the log reader to return only entries of the given type.
* Type restrictions enforced here
+ *
* @param $types String or array: Log types ('upload', 'delete', etc);
* empty string means no restriction
*/
/**
* Set the log reader to return only entries by the given user.
+ *
* @param $name String: (In)valid user name
*/
private function limitUser( $name ) {
// Paranoia: avoid brute force searches (bug 17342)
if( !$wgUser->isAllowed( 'deletedhistory' ) ) {
$this->mConds[] = $this->mDb->bitAnd('log_deleted', LogPage::DELETED_USER) . ' = 0';
- } else if( !$wgUser->isAllowed( 'suppressrevision' ) ) {
+ } elseif( !$wgUser->isAllowed( 'suppressrevision' ) ) {
$this->mConds[] = $this->mDb->bitAnd('log_deleted', LogPage::SUPPRESSED_USER) .
' != ' . LogPage::SUPPRESSED_USER;
}
/**
* Set the log reader to return only entries affecting the given page.
* (For the block and rights logs, this is a user page.)
+ *
* @param $page String: Title name as text
* @param $pattern String
*/
global $wgMiserMode, $wgUser;
$title = Title::newFromText( $page );
- if( strlen( $page ) == 0 || !$title instanceof Title )
+ if( strlen( $page ) == 0 || !$title instanceof Title ) {
return false;
+ }
$this->title = $title->getPrefixedText();
$ns = $title->getNamespace();
// Paranoia: avoid brute force searches (bug 17342)
if( !$wgUser->isAllowed( 'deletedhistory' ) ) {
$this->mConds[] = $db->bitAnd('log_deleted', LogPage::DELETED_ACTION) . ' = 0';
- } else if( !$wgUser->isAllowed( 'suppressrevision' ) ) {
+ } elseif( !$wgUser->isAllowed( 'suppressrevision' ) ) {
$this->mConds[] = $db->bitAnd('log_deleted', LogPage::SUPPRESSED_ACTION) .
' != ' . LogPage::SUPPRESSED_ACTION;
}
}
public function getQueryInfo() {
- global $wgOut;
$tables = array( 'logging', 'user' );
$this->mConds[] = 'user_id = log_user';
$index = array();
$options = array();
- # Add log_search table if there are conditions on it
- if( array_key_exists('ls_field',$this->mConds) ) {
+ # Add log_search table if there are conditions on it.
+ # This filters the results to only include log rows that have
+ # log_search records with the specified ls_field and ls_value values.
+ if( array_key_exists( 'ls_field', $this->mConds ) ) {
$tables[] = 'log_search';
$index['log_search'] = 'ls_field_val';
$index['logging'] = 'PRIMARY';
- $options[] = 'DISTINCT';
+ if ( !$this->hasEqualsClause( 'ls_field' )
+ || !$this->hasEqualsClause( 'ls_value' ) )
+ {
+ # Since (ls_field,ls_value,ls_logid) is unique, if the condition is
+ # to match a specific (ls_field,ls_value) tuple, then there will be
+ # no duplicate log rows. Otherwise, we need to remove the duplicates.
+ $options[] = 'DISTINCT';
+ }
# Avoid usage of the wrong index by limiting
# the choices of available indexes. This mainly
# avoids site-breaking filesorts.
- } else if( $this->title || $this->pattern || $this->user ) {
+ } elseif( $this->title || $this->pattern || $this->user ) {
$index['logging'] = array( 'page_time', 'user_time' );
if( count($this->types) == 1 ) {
$index['logging'][] = 'log_user_type_time';
}
- } else if( count($this->types) == 1 ) {
+ } elseif( count($this->types) == 1 ) {
$index['logging'] = 'type_time';
} else {
$index['logging'] = 'times';
'conds' => $this->mConds,
'options' => $options,
'join_conds' => array(
- 'user' => array( 'INNER JOIN', 'user_id=log_user' ),
+ 'user' => array( 'INNER JOIN', 'user_id=log_user' ),
'log_search' => array( 'INNER JOIN', 'ls_log_id=log_id' )
)
);
return $info;
}
+ // Checks if $this->mConds has $field matched to a *single* value
+ protected function hasEqualsClause( $field ) {
+ return (
+ array_key_exists( $field, $this->mConds ) &&
+ ( !is_array( $this->mConds[$field] ) || count( $this->mConds[$field] ) == 1 )
+ );
+ }
+
function getIndexField() {
return 'log_timestamp';
}
# Do a link batch query
if( $this->getNumRows() > 0 ) {
$lb = new LinkBatch;
- while( $row = $this->mResult->fetchObject() ) {
+ foreach ( $this->mResult as $row ) {
$lb->add( $row->log_namespace, $row->log_title );
$lb->addObj( Title::makeTitleSafe( NS_USER, $row->user_name ) );
$lb->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->user_name ) );
}
}
-/**
- * @deprecated
- * @ingroup SpecialPage
- */
-class LogReader {
- var $pager;
- /**
- * @param $request WebRequest: for internal use use a FauxRequest object to pass arbitrary parameters.
- */
- function __construct( $request ) {
- global $wgUser, $wgOut;
- wfDeprecated(__METHOD__);
- # Get parameters
- $type = $request->getVal( 'type' );
- $user = $request->getText( 'user' );
- $title = $request->getText( 'page' );
- $pattern = $request->getBool( 'pattern' );
- $year = $request->getIntOrNull( 'year' );
- $month = $request->getIntOrNull( 'month' );
- $tagFilter = $request->getVal( 'tagfilter' );
- # Don't let the user get stuck with a certain date
- $skip = $request->getText( 'offset' ) || $request->getText( 'dir' ) == 'prev';
- if( $skip ) {
- $year = '';
- $month = '';
- }
- # Use new list class to output results
- $loglist = new LogEventsList( $wgUser->getSkin(), $wgOut, 0 );
- $this->pager = new LogPager( $loglist, $type, $user, $title, $pattern, $year, $month, $tagFilter );
- }
-
- /**
- * Is there at least one row?
- * @return bool
- */
- public function hasRows() {
- return isset($this->pager) ? ($this->pager->getNumRows() > 0) : false;
- }
-}
-
-/**
- * @deprecated
- * @ingroup SpecialPage
- */
-class LogViewer {
- const NO_ACTION_LINK = 1;
-
- /**
- * LogReader object
- */
- var $reader;
-
- /**
- * @param &$reader LogReader: where to get our data from
- * @param $flags Integer: Bitwise combination of flags:
- * LogEventsList::NO_ACTION_LINK Don't show restore/unblock/block links
- */
- function __construct( &$reader, $flags = 0 ) {
- wfDeprecated(__METHOD__);
- $this->reader =& $reader;
- $this->reader->pager->mLogEventsList->flags = $flags;
- # Aliases for shorter code...
- $this->pager =& $this->reader->pager;
- $this->list =& $this->reader->pager->mLogEventsList;
- }
-
- /**
- * Take over the whole output page in $wgOut with the log display.
- */
- public function show() {
- # Set title and add header
- $this->list->showHeader( $pager->getType() );
- # Show form options
- $this->list->showOptions( $this->pager->getType(), $this->pager->getUser(), $this->pager->getPage(),
- $this->pager->getPattern(), $this->pager->getYear(), $this->pager->getMonth() );
- # Insert list
- $logBody = $this->pager->getBody();
- if( $logBody ) {
- $wgOut->addHTML(
- $this->pager->getNavigationBar() .
- $this->list->beginLogEventsList() .
- $logBody .
- $this->list->endLogEventsList() .
- $this->pager->getNavigationBar()
- );
- } else {
- $wgOut->addWikiMsg( 'logempty' );
- }
- }
-
- /**
- * Output just the list of entries given by the linked LogReader,
- * with extraneous UI elements. Use for displaying log fragments in
- * another page (eg at Special:Undelete)
- * @param $out OutputPage: where to send output
- */
- public function showList( &$out ) {
- $logBody = $this->pager->getBody();
- if( $logBody ) {
- $out->addHTML(
- $this->list->beginLogEventsList() .
- $logBody .
- $this->list->endLogEventsList()
- );
- } else {
- $out->addWikiMsg( 'logempty' );
- }
- }
-}