2004-02-18 02:15:00 +00:00
|
|
|
<?php
|
2008-06-26 19:12:52 +00:00
|
|
|
|
2004-09-02 23:28:24 +00:00
|
|
|
/**
|
2008-06-26 19:12:52 +00:00
|
|
|
* Implements Special:Recentchanges
|
WARNING: HUGE COMMIT
Doxygen documentation update:
* Changed alls @addtogroup to @ingroup. @addtogroup adds the comment to the group description, but doesn't add the file, class, function, ... to the group like @ingroup does. See for example http://svn.wikimedia.org/doc/group__SpecialPage.html where it's impossible to see related files, classes, ... that should belong to that group.
* Added @file to file description, it seems that it should be explicitely decalred for file descriptions, otherwise doxygen will think that the comment document the first class, variabled, function, ... that is in that file.
* Removed some empty comments
* Removed some ?>
Added following groups:
* ExternalStorage
* JobQueue
* MaintenanceLanguage
One more thing: there are still a lot of warnings when generating the doc.
2008-05-20 17:13:28 +00:00
|
|
|
* @ingroup SpecialPage
|
2004-09-02 23:28:24 +00:00
|
|
|
*/
|
2008-06-17 08:24:00 +00:00
|
|
|
class SpecialRecentChanges extends SpecialPage {
|
|
|
|
|
public function __construct() {
|
2008-07-25 16:46:43 +00:00
|
|
|
parent::__construct( 'Recentchanges' );
|
2008-06-17 08:24:00 +00:00
|
|
|
$this->includable( true );
|
|
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
/**
|
|
|
|
|
* Get a FormOptions object containing the default options
|
|
|
|
|
*
|
|
|
|
|
* @return FormOptions
|
|
|
|
|
*/
|
2008-06-17 08:24:00 +00:00
|
|
|
public function getDefaultOptions() {
|
2008-07-25 16:46:43 +00:00
|
|
|
global $wgUser;
|
2008-06-17 08:24:00 +00:00
|
|
|
$opts = new FormOptions();
|
|
|
|
|
|
2008-07-25 16:46:43 +00:00
|
|
|
$opts->add( 'days', (int)$wgUser->getOption( 'rcdays' ) );
|
|
|
|
|
$opts->add( 'limit', (int)$wgUser->getOption( 'rclimit' ) );
|
2008-06-17 08:24:00 +00:00
|
|
|
$opts->add( 'from', '' );
|
|
|
|
|
|
2008-07-25 16:46:43 +00:00
|
|
|
$opts->add( 'hideminor', (bool)$wgUser->getOption( 'hideminor' ) );
|
2008-06-17 08:24:00 +00:00
|
|
|
$opts->add( 'hidebots', true );
|
|
|
|
|
$opts->add( 'hideanons', false );
|
|
|
|
|
$opts->add( 'hideliu', false );
|
|
|
|
|
$opts->add( 'hidepatrolled', false );
|
|
|
|
|
$opts->add( 'hidemyself', false );
|
|
|
|
|
|
|
|
|
|
$opts->add( 'namespace', '', FormOptions::INTNULL );
|
|
|
|
|
$opts->add( 'invert', false );
|
|
|
|
|
|
|
|
|
|
$opts->add( 'categories', '' );
|
|
|
|
|
$opts->add( 'categories_any', false );
|
|
|
|
|
return $opts;
|
2008-06-26 19:12:52 +00:00
|
|
|
}
|
2008-06-17 08:24:00 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
/**
|
|
|
|
|
* Get a FormOptions object with options as specified by the user
|
|
|
|
|
*
|
|
|
|
|
* @return FormOptions
|
|
|
|
|
*/
|
2008-06-17 08:24:00 +00:00
|
|
|
public function setup( $parameters ) {
|
2008-07-25 16:46:43 +00:00
|
|
|
global $wgRequest;
|
2008-06-17 08:24:00 +00:00
|
|
|
|
|
|
|
|
$opts = $this->getDefaultOptions();
|
|
|
|
|
$opts->fetchValuesFromRequest( $wgRequest );
|
|
|
|
|
|
|
|
|
|
// Give precedence to subpage syntax
|
|
|
|
|
if ( $parameters !== null ) {
|
2008-06-17 10:44:12 +00:00
|
|
|
$this->parseParameters( $parameters, $opts );
|
2004-12-12 04:13:19 +00:00
|
|
|
}
|
2008-06-17 08:24:00 +00:00
|
|
|
|
|
|
|
|
$opts->validateIntBounds( 'limit', 0, 5000 );
|
|
|
|
|
return $opts;
|
2003-07-02 06:22:03 +00:00
|
|
|
}
|
2004-08-09 05:38:11 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
/**
|
|
|
|
|
* Get a FormOptions object sepcific for feed requests
|
|
|
|
|
*
|
|
|
|
|
* @return FormOptions
|
|
|
|
|
*/
|
2008-06-17 08:24:00 +00:00
|
|
|
public function feedSetup() {
|
|
|
|
|
global $wgFeedLimit, $wgRequest;
|
|
|
|
|
$opts = $this->getDefaultOptions();
|
|
|
|
|
$opts->fetchValuesFromRequest( $wgRequest, array( 'days', 'limit', 'hideminor' ) );
|
|
|
|
|
$opts->validateIntBounds( 'limit', 0, $wgFeedLimit );
|
|
|
|
|
return $opts;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
/**
|
|
|
|
|
* Main execution point
|
|
|
|
|
*
|
|
|
|
|
* @param $parameters string
|
|
|
|
|
*/
|
2008-06-17 08:24:00 +00:00
|
|
|
public function execute( $parameters ) {
|
|
|
|
|
global $wgRequest, $wgOut;
|
|
|
|
|
$feedFormat = $wgRequest->getVal( 'feed' );
|
|
|
|
|
|
|
|
|
|
# 10 seconds server-side caching max
|
|
|
|
|
$wgOut->setSquidMaxage( 10 );
|
2005-05-28 11:09:22 +00:00
|
|
|
|
2008-06-17 08:24:00 +00:00
|
|
|
$lastmod = $this->checkLastModified( $feedFormat );
|
2008-06-17 10:44:12 +00:00
|
|
|
if( $lastmod === false ){
|
2008-06-17 08:24:00 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$opts = $feedFormat ? $this->feedSetup() : $this->setup( $parameters );
|
|
|
|
|
$this->setHeaders();
|
2008-06-26 19:12:52 +00:00
|
|
|
$this->outputHeader();
|
2008-06-17 08:24:00 +00:00
|
|
|
|
|
|
|
|
// Fetch results, prepare a batch link existence check query
|
|
|
|
|
$rows = array();
|
|
|
|
|
$batch = new LinkBatch;
|
|
|
|
|
$conds = $this->buildMainQueryConds( $opts );
|
2008-07-27 18:14:59 +00:00
|
|
|
$rows = $this->doMainQuery( $conds, $opts );
|
|
|
|
|
if( $rows === false ){
|
2008-10-08 21:30:21 +00:00
|
|
|
if( !$this->including() ) {
|
|
|
|
|
$this->doHeader( $opts );
|
|
|
|
|
}
|
2008-06-26 19:12:52 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2008-07-26 15:51:49 +00:00
|
|
|
|
2008-07-27 18:14:59 +00:00
|
|
|
foreach( $rows as $row ) {
|
2008-06-17 08:24:00 +00:00
|
|
|
if ( !$feedFormat ) {
|
|
|
|
|
// User page and talk links
|
|
|
|
|
$batch->add( NS_USER, $row->rc_user_text );
|
|
|
|
|
$batch->add( NS_USER_TALK, $row->rc_user_text );
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-08-02 13:35:19 +00:00
|
|
|
|
2008-06-17 08:24:00 +00:00
|
|
|
if ( $feedFormat ) {
|
2008-06-26 19:12:52 +00:00
|
|
|
list( $feed, $feedObj ) = $this->getFeedObject( $feedFormat );
|
2008-06-17 08:24:00 +00:00
|
|
|
$feed->execute( $feedObj, $rows, $opts['limit'], $opts['hideminor'], $lastmod );
|
|
|
|
|
} else {
|
|
|
|
|
$batch->execute();
|
|
|
|
|
$this->webOutput( $rows, $opts );
|
|
|
|
|
}
|
2008-07-27 18:14:59 +00:00
|
|
|
|
|
|
|
|
$rows->free();
|
2005-05-04 21:56:14 +00:00
|
|
|
}
|
2005-08-02 13:35:19 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
/**
|
|
|
|
|
* Return an array with a ChangesFeed object and ChannelFeed object
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function getFeedObject( $feedFormat ){
|
|
|
|
|
$feed = new ChangesFeed( $feedFormat, 'rcfeed' );
|
|
|
|
|
$feedObj = $feed->getFeedObject(
|
|
|
|
|
wfMsgForContent( 'recentchanges' ),
|
|
|
|
|
wfMsgForContent( 'recentchanges-feed-description' )
|
|
|
|
|
);
|
|
|
|
|
return array( $feed, $feedObj );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Process $par and put options found if $opts
|
|
|
|
|
* Mainly used when including the page
|
|
|
|
|
*
|
|
|
|
|
* @param $par String
|
|
|
|
|
* @param $opts FormOptions
|
|
|
|
|
*/
|
2008-06-17 08:24:00 +00:00
|
|
|
public function parseParameters( $par, FormOptions $opts ) {
|
|
|
|
|
$bits = preg_split( '/\s*,\s*/', trim( $par ) );
|
|
|
|
|
foreach ( $bits as $bit ) {
|
|
|
|
|
if ( 'hidebots' === $bit ) $opts['hidebots'] = true;
|
|
|
|
|
if ( 'bots' === $bit ) $opts['hidebots'] = false;
|
|
|
|
|
if ( 'hideminor' === $bit ) $opts['hideminor'] = true;
|
|
|
|
|
if ( 'minor' === $bit ) $opts['hideminor'] = false;
|
|
|
|
|
if ( 'hideliu' === $bit ) $opts['hideliu'] = true;
|
|
|
|
|
if ( 'hidepatrolled' === $bit ) $opts['hidepatrolled'] = true;
|
|
|
|
|
if ( 'hideanons' === $bit ) $opts['hideanons'] = true;
|
|
|
|
|
if ( 'hidemyself' === $bit ) $opts['hidemyself'] = true;
|
|
|
|
|
|
|
|
|
|
if ( is_numeric( $bit ) ) $opts['limit'] = $bit;
|
|
|
|
|
|
|
|
|
|
$m = array();
|
|
|
|
|
if ( preg_match( '/^limit=(\d+)$/', $bit, $m ) ) $opts['limit'] = $m[1];
|
|
|
|
|
if ( preg_match( '/^days=(\d+)$/', $bit, $m ) ) $opts['days'] = $m[1];
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-11-18 11:37:14 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
/**
|
|
|
|
|
* Get last modified date, for client caching
|
|
|
|
|
* Don't use this if we are using the patrol feature, patrol changes don't
|
|
|
|
|
* update the timestamp
|
|
|
|
|
*
|
|
|
|
|
* @param $feedFormat String
|
|
|
|
|
* @return int or false
|
|
|
|
|
*/
|
2008-06-17 08:24:00 +00:00
|
|
|
public function checkLastModified( $feedFormat ) {
|
|
|
|
|
global $wgUseRCPatrol, $wgOut;
|
|
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
|
|
|
|
$lastmod = $dbr->selectField( 'recentchanges', 'MAX(rc_timestamp)', false, __FUNCTION__ );
|
|
|
|
|
if ( $feedFormat || !$wgUseRCPatrol ) {
|
|
|
|
|
if( $lastmod && $wgOut->checkLastModified( $lastmod ) ){
|
|
|
|
|
# Client cache fresh and headers sent, nothing more to do.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2004-11-18 11:37:14 +00:00
|
|
|
}
|
2008-06-17 08:24:00 +00:00
|
|
|
return $lastmod;
|
2003-12-11 20:16:34 +00:00
|
|
|
}
|
2003-04-14 23:10:40 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
/**
|
|
|
|
|
* Return an array of conditions depending of options set in $opts
|
|
|
|
|
*
|
|
|
|
|
* @param $opts FormOptions
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
2008-06-17 08:24:00 +00:00
|
|
|
public function buildMainQueryConds( FormOptions $opts ) {
|
|
|
|
|
global $wgUser;
|
|
|
|
|
|
|
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
|
|
|
|
$conds = array();
|
|
|
|
|
|
|
|
|
|
# It makes no sense to hide both anons and logged-in users
|
|
|
|
|
# Where this occurs, force anons to be shown
|
|
|
|
|
$forcebot = false;
|
|
|
|
|
if( $opts['hideanons'] && $opts['hideliu'] ){
|
|
|
|
|
# Check if the user wants to show bots only
|
|
|
|
|
if( $opts['hidebots'] ){
|
|
|
|
|
$opts['hideanons'] = false;
|
|
|
|
|
} else {
|
|
|
|
|
$forcebot = true;
|
|
|
|
|
$opts['hidebots'] = false;
|
|
|
|
|
}
|
2008-04-02 10:47:50 +00:00
|
|
|
}
|
2006-04-03 01:32:35 +00:00
|
|
|
|
2008-06-17 08:24:00 +00:00
|
|
|
// Calculate cutoff
|
|
|
|
|
$cutoff_unixtime = time() - ( $opts['days'] * 86400 );
|
|
|
|
|
$cutoff_unixtime = $cutoff_unixtime - ($cutoff_unixtime % 86400);
|
|
|
|
|
$cutoff = $dbr->timestamp( $cutoff_unixtime );
|
2008-04-02 10:47:50 +00:00
|
|
|
|
2008-06-17 08:24:00 +00:00
|
|
|
$fromValid = preg_match('/^[0-9]{14}$/', $opts['from']);
|
|
|
|
|
if( $fromValid && $opts['from'] > wfTimestamp(TS_MW,$cutoff) ) {
|
|
|
|
|
$cutoff = $dbr->timestamp($opts['from']);
|
2006-02-11 12:28:50 +00:00
|
|
|
} else {
|
2008-06-17 08:24:00 +00:00
|
|
|
$opts->reset( 'from' );
|
2006-02-11 12:28:50 +00:00
|
|
|
}
|
2008-06-17 08:24:00 +00:00
|
|
|
|
|
|
|
|
$conds[] = 'rc_timestamp >= ' . $dbr->addQuotes( $cutoff );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$hidePatrol = $wgUser->useRCPatrol() && $opts['hidepatrolled'];
|
|
|
|
|
$hideLoggedInUsers = $opts['hideliu'] && !$forcebot;
|
|
|
|
|
$hideAnonymousUsers = $opts['hideanons'] && !$forcebot;
|
|
|
|
|
|
|
|
|
|
if ( $opts['hideminor'] ) $conds['rc_minor'] = 0;
|
|
|
|
|
if ( $opts['hidebots'] ) $conds['rc_bot'] = 0;
|
|
|
|
|
if ( $hidePatrol ) $conds['rc_patrolled'] = 0;
|
|
|
|
|
if ( $forcebot ) $conds['rc_bot'] = 1;
|
|
|
|
|
if ( $hideLoggedInUsers ) $conds[] = 'rc_user = 0';
|
|
|
|
|
if ( $hideAnonymousUsers ) $conds[] = 'rc_user != 0';
|
|
|
|
|
|
|
|
|
|
if( $opts['hidemyself'] ) {
|
|
|
|
|
if( $wgUser->getId() ) {
|
|
|
|
|
$conds[] = 'rc_user != ' . $dbr->addQuotes( $wgUser->getId() );
|
|
|
|
|
} else {
|
|
|
|
|
$conds[] = 'rc_user_text != ' . $dbr->addQuotes( $wgUser->getName() );
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-06-26 19:12:52 +00:00
|
|
|
|
2008-06-17 08:24:00 +00:00
|
|
|
# Namespace filtering
|
|
|
|
|
if ( $opts['namespace'] !== '' ) {
|
|
|
|
|
if ( !$opts['invert'] ) {
|
|
|
|
|
$conds[] = 'rc_namespace = ' . $dbr->addQuotes( $opts['namespace'] );
|
|
|
|
|
} else {
|
|
|
|
|
$conds[] = 'rc_namespace != ' . $dbr->addQuotes( $opts['namespace'] );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $conds;
|
2006-02-11 12:28:50 +00:00
|
|
|
}
|
2006-04-03 01:32:35 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
/**
|
|
|
|
|
* Process the query
|
|
|
|
|
*
|
|
|
|
|
* @param $conds array
|
|
|
|
|
* @param $opts FormOptions
|
|
|
|
|
* @return database result or false (for Recentchangeslinked only)
|
|
|
|
|
*/
|
2008-06-17 08:24:00 +00:00
|
|
|
public function doMainQuery( $conds, $opts ) {
|
|
|
|
|
global $wgUser;
|
|
|
|
|
|
2008-09-16 05:56:43 +00:00
|
|
|
$tables = array( 'recentchanges' );
|
|
|
|
|
$join_conds = array();
|
2008-06-17 08:24:00 +00:00
|
|
|
|
|
|
|
|
$uid = $wgUser->getId();
|
|
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
|
|
|
|
$limit = $opts['limit'];
|
|
|
|
|
$namespace = $opts['namespace'];
|
|
|
|
|
$invert = $opts['invert'];
|
|
|
|
|
|
|
|
|
|
// JOIN on watchlist for users
|
2008-06-26 19:12:52 +00:00
|
|
|
if( $uid ) {
|
2008-06-17 08:24:00 +00:00
|
|
|
$tables[] = 'watchlist';
|
2008-09-16 05:56:43 +00:00
|
|
|
$join_conds = array( 'watchlist' => array('LEFT JOIN',"wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace") );
|
2008-06-17 08:24:00 +00:00
|
|
|
}
|
|
|
|
|
|
2008-09-16 05:56:43 +00:00
|
|
|
wfRunHooks('SpecialRecentChangesQuery', array( &$conds, &$tables, &$join_conds, $opts ) );
|
2008-06-17 08:24:00 +00:00
|
|
|
|
|
|
|
|
// Is there either one namespace selected or excluded?
|
|
|
|
|
// Also, if this is "all" or main namespace, just use timestamp index.
|
|
|
|
|
if( is_null($namespace) || $invert || $namespace == NS_MAIN ) {
|
|
|
|
|
$res = $dbr->select( $tables, '*', $conds, __METHOD__,
|
2008-06-26 19:12:52 +00:00
|
|
|
array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit,
|
2008-06-17 08:24:00 +00:00
|
|
|
'USE INDEX' => array('recentchanges' => 'rc_timestamp') ),
|
|
|
|
|
$join_conds );
|
|
|
|
|
// We have a new_namespace_time index! UNION over new=(0,1) and sort result set!
|
|
|
|
|
} else {
|
|
|
|
|
// New pages
|
|
|
|
|
$sqlNew = $dbr->selectSQLText( $tables, '*',
|
|
|
|
|
array( 'rc_new' => 1 ) + $conds,
|
|
|
|
|
__METHOD__,
|
2008-06-26 19:12:52 +00:00
|
|
|
array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit,
|
2008-06-17 08:24:00 +00:00
|
|
|
'USE INDEX' => array('recentchanges' => 'new_name_timestamp') ),
|
|
|
|
|
$join_conds );
|
|
|
|
|
// Old pages
|
|
|
|
|
$sqlOld = $dbr->selectSQLText( $tables, '*',
|
|
|
|
|
array( 'rc_new' => 0 ) + $conds,
|
|
|
|
|
__METHOD__,
|
2008-06-26 19:12:52 +00:00
|
|
|
array( 'ORDER BY' => 'rc_timestamp DESC', 'LIMIT' => $limit,
|
2008-06-17 08:24:00 +00:00
|
|
|
'USE INDEX' => array('recentchanges' => 'new_name_timestamp') ),
|
|
|
|
|
$join_conds );
|
|
|
|
|
# Join the two fast queries, and sort the result set
|
|
|
|
|
$sql = "($sqlNew) UNION ($sqlOld) ORDER BY rc_timestamp DESC LIMIT $limit";
|
|
|
|
|
$res = $dbr->query( $sql, __METHOD__ );
|
2005-11-15 11:12:21 +00:00
|
|
|
}
|
2005-04-03 08:27:08 +00:00
|
|
|
|
2008-06-17 08:24:00 +00:00
|
|
|
return $res;
|
2004-01-28 15:57:47 +00:00
|
|
|
}
|
2008-06-17 08:24:00 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
/**
|
2008-09-16 05:56:43 +00:00
|
|
|
* Send output to $wgOut, only called if not used feeds
|
2008-06-26 19:12:52 +00:00
|
|
|
*
|
|
|
|
|
* @param $rows array of database rows
|
|
|
|
|
* @param $opts FormOptions
|
|
|
|
|
*/
|
2008-06-17 08:24:00 +00:00
|
|
|
public function webOutput( $rows, $opts ) {
|
|
|
|
|
global $wgOut, $wgUser, $wgRCShowWatchingUsers, $wgShowUpdatedMarker;
|
|
|
|
|
global $wgAllowCategorizedRecentChanges;
|
|
|
|
|
|
|
|
|
|
$limit = $opts['limit'];
|
|
|
|
|
|
|
|
|
|
if ( !$this->including() ) {
|
|
|
|
|
// Output options box
|
|
|
|
|
$this->doHeader( $opts );
|
2005-05-28 11:09:22 +00:00
|
|
|
}
|
2005-05-04 21:56:14 +00:00
|
|
|
|
|
|
|
|
// And now for the content
|
2007-12-19 01:33:44 +00:00
|
|
|
$wgOut->setSyndicated( true );
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2005-09-15 00:40:51 +00:00
|
|
|
$list = ChangesList::newFromUser( $wgUser );
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2006-01-09 14:20:26 +00:00
|
|
|
if ( $wgAllowCategorizedRecentChanges ) {
|
2008-06-26 19:12:52 +00:00
|
|
|
$this->filterByCategories( $rows, $opts );
|
2006-01-09 14:20:26 +00:00
|
|
|
}
|
2005-09-06 18:43:45 +00:00
|
|
|
|
2004-11-25 13:47:17 +00:00
|
|
|
$s = $list->beginRecentChangesList();
|
2004-04-09 23:44:18 +00:00
|
|
|
$counter = 1;
|
2007-12-01 09:48:40 +00:00
|
|
|
|
|
|
|
|
$showWatcherCount = $wgRCShowWatchingUsers && $wgUser->getOption( 'shownumberswatching' );
|
|
|
|
|
$watcherCache = array();
|
|
|
|
|
|
2008-06-17 08:24:00 +00:00
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
|
|
|
|
|
2004-03-19 05:31:18 +00:00
|
|
|
foreach( $rows as $obj ){
|
|
|
|
|
if( $limit == 0) {
|
2004-08-09 05:38:11 +00:00
|
|
|
break;
|
2004-03-19 05:31:18 +00:00
|
|
|
}
|
2004-08-09 05:38:11 +00:00
|
|
|
|
2008-06-17 08:24:00 +00:00
|
|
|
if ( ! ( $opts['hideminor'] && $obj->rc_minor ) &&
|
|
|
|
|
! ( $opts['hidepatrolled'] && $obj->rc_patrolled ) ) {
|
2004-03-19 05:31:18 +00:00
|
|
|
$rc = RecentChange::newFromRow( $obj );
|
2004-04-09 23:44:18 +00:00
|
|
|
$rc->counter = $counter++;
|
2004-12-18 03:47:11 +00:00
|
|
|
|
|
|
|
|
if ($wgShowUpdatedMarker
|
2004-12-18 03:59:06 +00:00
|
|
|
&& !empty( $obj->wl_notificationtimestamp )
|
2004-12-18 03:47:11 +00:00
|
|
|
&& ($obj->rc_timestamp >= $obj->wl_notificationtimestamp)) {
|
|
|
|
|
$rc->notificationtimestamp = true;
|
|
|
|
|
} else {
|
|
|
|
|
$rc->notificationtimestamp = false;
|
|
|
|
|
}
|
|
|
|
|
|
2007-12-01 09:48:40 +00:00
|
|
|
$rc->numberofWatchingusers = 0; // Default
|
|
|
|
|
if ($showWatcherCount && $obj->rc_namespace >= 0) {
|
|
|
|
|
if (!isset($watcherCache[$obj->rc_namespace][$obj->rc_title])) {
|
2007-12-03 16:34:16 +00:00
|
|
|
$watcherCache[$obj->rc_namespace][$obj->rc_title] =
|
|
|
|
|
$dbr->selectField( 'watchlist',
|
|
|
|
|
'COUNT(*)',
|
|
|
|
|
array(
|
|
|
|
|
'wl_namespace' => $obj->rc_namespace,
|
|
|
|
|
'wl_title' => $obj->rc_title,
|
|
|
|
|
),
|
|
|
|
|
__METHOD__ . '-watchers' );
|
2007-12-01 09:48:40 +00:00
|
|
|
}
|
|
|
|
|
$rc->numberofWatchingusers = $watcherCache[$obj->rc_namespace][$obj->rc_title];
|
2004-12-18 03:47:11 +00:00
|
|
|
}
|
2008-08-19 00:15:05 +00:00
|
|
|
$s .= $list->recentChangesLine( $rc, !empty( $obj->wl_user ) );
|
2004-03-19 05:31:18 +00:00
|
|
|
--$limit;
|
|
|
|
|
}
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
2004-11-25 13:47:17 +00:00
|
|
|
$s .= $list->endRecentChangesList();
|
2004-03-19 05:31:18 +00:00
|
|
|
$wgOut->addHTML( $s );
|
2003-04-14 23:10:40 +00:00
|
|
|
}
|
2008-06-17 08:24:00 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
/**
|
|
|
|
|
* Return the text to be displayed above the changes
|
|
|
|
|
*
|
|
|
|
|
* @param $opts FormOptions
|
|
|
|
|
* @return String: XHTML
|
|
|
|
|
*/
|
2008-06-17 08:24:00 +00:00
|
|
|
public function doHeader( $opts ) {
|
|
|
|
|
global $wgScript, $wgOut;
|
2008-06-26 19:12:52 +00:00
|
|
|
|
|
|
|
|
$this->setTopText( $wgOut, $opts );
|
2008-06-17 08:24:00 +00:00
|
|
|
|
|
|
|
|
$defaults = $opts->getAllValues();
|
|
|
|
|
$nondefaults = $opts->getChangedValues();
|
|
|
|
|
$opts->consumeValues( array( 'namespace', 'invert' ) );
|
|
|
|
|
|
|
|
|
|
$panel = array();
|
2008-06-26 19:12:52 +00:00
|
|
|
$panel[] = $this->optionsPanel( $defaults, $nondefaults );
|
2008-07-02 02:31:05 +00:00
|
|
|
$panel[] = '<hr />';
|
2008-06-17 08:24:00 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
$extraOpts = $this->getExtraOptions( $opts );
|
2008-08-28 07:14:33 +00:00
|
|
|
$extraOptsCount = count( $extraOpts );
|
|
|
|
|
$count = 0;
|
|
|
|
|
$submit = ' ' . Xml::submitbutton( wfMsg( 'allpagessubmit' ) );
|
2008-06-17 08:24:00 +00:00
|
|
|
|
2008-08-28 07:14:33 +00:00
|
|
|
$out = Xml::openElement( 'table', array( 'class' => 'mw-recentchanges-table' ) );
|
2008-06-17 08:24:00 +00:00
|
|
|
foreach ( $extraOpts as $optionRow ) {
|
2008-08-28 07:14:33 +00:00
|
|
|
# Add submit button to the last row only
|
|
|
|
|
++$count;
|
|
|
|
|
$addSubmit = $count === $extraOptsCount ? $submit : '';
|
|
|
|
|
|
2008-06-17 08:24:00 +00:00
|
|
|
$out .= Xml::openElement( 'tr' );
|
2008-08-28 07:14:33 +00:00
|
|
|
if ( is_array( $optionRow ) ) {
|
|
|
|
|
$out .= Xml::tags( 'td', array( 'class' => 'mw-label' ), $optionRow[0] );
|
|
|
|
|
$out .= Xml::tags( 'td', array( 'class' => 'mw-input' ), $optionRow[1] . $addSubmit );
|
2008-06-17 08:24:00 +00:00
|
|
|
} else {
|
2008-08-28 07:14:33 +00:00
|
|
|
$out .= Xml::tags( 'td', array( 'class' => 'mw-input', 'colspan' => 2 ), $optionRow . $addSubmit );
|
2008-06-17 08:24:00 +00:00
|
|
|
}
|
|
|
|
|
$out .= Xml::closeElement( 'tr' );
|
|
|
|
|
}
|
|
|
|
|
$out .= Xml::closeElement( 'table' );
|
|
|
|
|
|
|
|
|
|
$unconsumed = $opts->getUnconsumedValues();
|
|
|
|
|
foreach ( $unconsumed as $key => $value ) {
|
|
|
|
|
$out .= Xml::hidden( $key, $value );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$t = $this->getTitle();
|
|
|
|
|
$out .= Xml::hidden( 'title', $t->getPrefixedText() );
|
|
|
|
|
$form = Xml::tags( 'form', array( 'action' => $wgScript ), $out );
|
|
|
|
|
$panel[] = $form;
|
|
|
|
|
$panelString = implode( "\n", $panel );
|
|
|
|
|
|
2008-07-02 02:31:05 +00:00
|
|
|
$wgOut->addHTML(
|
2008-08-27 19:49:09 +00:00
|
|
|
Xml::fieldset( wfMsg( 'recentchanges-legend' ), $panelString, array( 'class' => 'rcoptions' ) )
|
2008-07-02 02:31:05 +00:00
|
|
|
);
|
2008-06-26 19:12:52 +00:00
|
|
|
|
|
|
|
|
$this->setBottomText( $wgOut, $opts );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get options to be displayed in a form
|
|
|
|
|
*
|
|
|
|
|
* @param $opts FormOptions
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
function getExtraOptions( $opts ){
|
|
|
|
|
$extraOpts = array();
|
|
|
|
|
$extraOpts['namespace'] = $this->namespaceFilterForm( $opts );
|
|
|
|
|
|
|
|
|
|
global $wgAllowCategorizedRecentChanges;
|
|
|
|
|
if ( $wgAllowCategorizedRecentChanges ) {
|
|
|
|
|
$extraOpts['category'] = $this->categoryFilterForm( $opts );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
wfRunHooks( 'SpecialRecentChangesPanel', array( &$extraOpts, $opts ) );
|
|
|
|
|
return $extraOpts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Send the text to be displayed above the options
|
|
|
|
|
*
|
|
|
|
|
* @param $out OutputPage
|
|
|
|
|
* @param $opts FormOptions
|
|
|
|
|
*/
|
2008-08-06 17:35:19 +00:00
|
|
|
function setTopText( OutputPage $out, FormOptions $opts ){
|
2008-06-26 19:12:52 +00:00
|
|
|
$out->addWikiText( wfMsgForContentNoTrans( 'recentchangestext' ) );
|
2008-06-17 08:24:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2008-06-26 19:12:52 +00:00
|
|
|
* Send the text to be displayed after the options, for use in
|
|
|
|
|
* Recentchangeslinked
|
|
|
|
|
*
|
|
|
|
|
* @param $out OutputPage
|
|
|
|
|
* @param $opts FormOptions
|
|
|
|
|
*/
|
2008-08-06 17:35:19 +00:00
|
|
|
function setBottomText( OutputPage $out, FormOptions $opts ){}
|
2008-06-26 19:12:52 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates the choose namespace selection
|
|
|
|
|
*
|
|
|
|
|
* @param $opts FormOptions
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
2008-06-17 08:24:00 +00:00
|
|
|
protected function namespaceFilterForm( FormOptions $opts ) {
|
|
|
|
|
$nsSelect = HTMLnamespaceselector( $opts['namespace'], '' );
|
|
|
|
|
$nsLabel = Xml::label( wfMsg('namespace'), 'namespace' );
|
|
|
|
|
$invert = Xml::checkLabel( wfMsg('invert'), 'invert', 'nsinvert', $opts['invert'] );
|
|
|
|
|
return array( $nsLabel, "$nsSelect $invert" );
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
/**
|
|
|
|
|
* Create a input to filter changes by categories
|
|
|
|
|
*
|
|
|
|
|
* @param $opts FormOptions
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
2008-06-17 08:24:00 +00:00
|
|
|
protected function categoryFilterForm( FormOptions $opts ) {
|
|
|
|
|
list( $label, $input ) = Xml::inputLabelSep( wfMsg('rc_categories'),
|
|
|
|
|
'categories', 'mw-categories', false, $opts['categories'] );
|
|
|
|
|
|
|
|
|
|
$input .= ' ' . Xml::checkLabel( wfMsg('rc_categories_any'),
|
|
|
|
|
'categories_any', 'mw-categories_any', $opts['categories_any'] );
|
|
|
|
|
|
|
|
|
|
return array( $label, $input );
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
/**
|
|
|
|
|
* Filter $rows by categories set in $opts
|
|
|
|
|
*
|
|
|
|
|
* @param $rows array of database rows
|
|
|
|
|
* @param $opts FormOptions
|
|
|
|
|
*/
|
|
|
|
|
function filterByCategories( &$rows, FormOptions $opts ) {
|
|
|
|
|
$categories = array_map( 'trim', explode( "|" , $opts['categories'] ) );
|
|
|
|
|
|
|
|
|
|
if( empty($categories) ) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
# Filter categories
|
|
|
|
|
$cats = array();
|
|
|
|
|
foreach ( $categories as $cat ) {
|
|
|
|
|
$cat = trim( $cat );
|
|
|
|
|
if ( $cat == "" ) continue;
|
|
|
|
|
$cats[] = $cat;
|
2006-01-09 14:20:26 +00:00
|
|
|
}
|
2008-06-26 19:12:52 +00:00
|
|
|
|
|
|
|
|
# Filter articles
|
|
|
|
|
$articles = array();
|
|
|
|
|
$a2r = array();
|
|
|
|
|
foreach ( $rows AS $k => $r ) {
|
|
|
|
|
$nt = Title::makeTitle( $r->rc_namespace, $r->rc_title );
|
|
|
|
|
$id = $nt->getArticleID();
|
|
|
|
|
if ( $id == 0 ) continue; # Page might have been deleted...
|
|
|
|
|
if ( !in_array($id, $articles) ) {
|
|
|
|
|
$articles[] = $id;
|
|
|
|
|
}
|
|
|
|
|
if ( !isset($a2r[$id]) ) {
|
|
|
|
|
$a2r[$id] = array();
|
|
|
|
|
}
|
|
|
|
|
$a2r[$id][] = $k;
|
2006-01-09 14:20:26 +00:00
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
# Shortcut?
|
|
|
|
|
if ( !count($articles) || !count($cats) )
|
|
|
|
|
return ;
|
|
|
|
|
|
|
|
|
|
# Look up
|
|
|
|
|
$c = new Categoryfinder ;
|
|
|
|
|
$c->seed( $articles, $cats, $opts['categories_any'] ? "OR" : "AND" ) ;
|
|
|
|
|
$match = $c->run();
|
|
|
|
|
|
|
|
|
|
# Filter
|
|
|
|
|
$newrows = array();
|
|
|
|
|
foreach ( $match AS $id ) {
|
|
|
|
|
foreach ( $a2r[$id] AS $rev ) {
|
|
|
|
|
$k = $rev;
|
|
|
|
|
$newrows[$k] = $rows[$k];
|
|
|
|
|
}
|
2004-12-12 04:13:19 +00:00
|
|
|
}
|
2008-06-26 19:12:52 +00:00
|
|
|
$rows = $newrows;
|
2004-12-12 04:13:19 +00:00
|
|
|
}
|
2003-04-14 23:10:40 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
/**
|
|
|
|
|
* Makes change an option link which carries all the other options
|
|
|
|
|
* @param $title see Title
|
|
|
|
|
* @param $override
|
|
|
|
|
* @param $options
|
|
|
|
|
*/
|
|
|
|
|
function makeOptionsLink( $title, $override, $options, $active = false ) {
|
2008-06-27 11:43:39 +00:00
|
|
|
global $wgUser;
|
2008-06-26 19:12:52 +00:00
|
|
|
$sk = $wgUser->getSkin();
|
2008-08-11 16:24:51 +00:00
|
|
|
$params = wfArrayMerge( $options, $override );
|
|
|
|
|
return $sk->link( $this->getTitle(), htmlspecialchars( $title ),
|
|
|
|
|
( $active ? array( 'style'=>'font-weight: bold;' ) : array() ), $params, array( 'known' ) );
|
2006-04-05 18:31:58 +00:00
|
|
|
}
|
2008-04-14 07:45:50 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
/**
|
|
|
|
|
* Creates the options panel.
|
|
|
|
|
* @param $defaults array
|
|
|
|
|
* @param $nondefaults array
|
|
|
|
|
*/
|
|
|
|
|
function optionsPanel( $defaults, $nondefaults ) {
|
|
|
|
|
global $wgLang, $wgUser, $wgRCLinkLimits, $wgRCLinkDays;
|
|
|
|
|
|
|
|
|
|
$options = $nondefaults + $defaults;
|
|
|
|
|
|
2008-10-29 15:35:18 +00:00
|
|
|
if( $options['from'] )
|
|
|
|
|
$note = wfMsgExt( 'rcnotefrom', array( 'parseinline' ),
|
|
|
|
|
$wgLang->formatNum( $options['limit'] ),
|
|
|
|
|
$wgLang->timeanddate( $options['from'], true ) );
|
|
|
|
|
else
|
|
|
|
|
$note = wfMsgExt( 'rcnote', array( 'parseinline' ),
|
|
|
|
|
$wgLang->formatNum( $options['limit'] ),
|
|
|
|
|
$wgLang->formatNum( $options['days'] ),
|
|
|
|
|
$wgLang->timeAndDate( wfTimestampNow(), true ),
|
|
|
|
|
$wgLang->date( wfTimestampNow(), true ),
|
|
|
|
|
$wgLang->time( wfTimestampNow(), true ) );
|
|
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
# Sort data for display and make sure it's unique after we've added user data.
|
|
|
|
|
$wgRCLinkLimits[] = $options['limit'];
|
|
|
|
|
$wgRCLinkDays[] = $options['days'];
|
2008-08-11 16:24:51 +00:00
|
|
|
sort( $wgRCLinkLimits );
|
|
|
|
|
sort( $wgRCLinkDays );
|
|
|
|
|
$wgRCLinkLimits = array_unique( $wgRCLinkLimits );
|
|
|
|
|
$wgRCLinkDays = array_unique( $wgRCLinkDays );
|
2008-06-26 19:12:52 +00:00
|
|
|
|
|
|
|
|
// limit links
|
|
|
|
|
foreach( $wgRCLinkLimits as $value ) {
|
|
|
|
|
$cl[] = $this->makeOptionsLink( $wgLang->formatNum( $value ),
|
|
|
|
|
array( 'limit' => $value ), $nondefaults, $value == $options['limit'] ) ;
|
|
|
|
|
}
|
2008-09-18 17:11:51 +00:00
|
|
|
$cl = implode( ' | ', $cl );
|
2005-05-03 18:30:46 +00:00
|
|
|
|
2008-06-26 19:12:52 +00:00
|
|
|
// day links, reset 'from' to none
|
|
|
|
|
foreach( $wgRCLinkDays as $value ) {
|
|
|
|
|
$dl[] = $this->makeOptionsLink( $wgLang->formatNum( $value ),
|
|
|
|
|
array( 'days' => $value, 'from' => '' ), $nondefaults, $value == $options['days'] ) ;
|
|
|
|
|
}
|
2008-09-18 17:11:51 +00:00
|
|
|
$dl = implode( ' | ', $dl );
|
2008-06-26 19:12:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
// show/hide links
|
2008-08-11 16:24:51 +00:00
|
|
|
$showhide = array( wfMsg( 'show' ), wfMsg( 'hide' ) );
|
2008-06-26 19:12:52 +00:00
|
|
|
$minorLink = $this->makeOptionsLink( $showhide[1-$options['hideminor']],
|
|
|
|
|
array( 'hideminor' => 1-$options['hideminor'] ), $nondefaults);
|
|
|
|
|
$botLink = $this->makeOptionsLink( $showhide[1-$options['hidebots']],
|
|
|
|
|
array( 'hidebots' => 1-$options['hidebots'] ), $nondefaults);
|
|
|
|
|
$anonsLink = $this->makeOptionsLink( $showhide[ 1 - $options['hideanons'] ],
|
|
|
|
|
array( 'hideanons' => 1 - $options['hideanons'] ), $nondefaults );
|
|
|
|
|
$liuLink = $this->makeOptionsLink( $showhide[1-$options['hideliu']],
|
|
|
|
|
array( 'hideliu' => 1-$options['hideliu'] ), $nondefaults);
|
|
|
|
|
$patrLink = $this->makeOptionsLink( $showhide[1-$options['hidepatrolled']],
|
|
|
|
|
array( 'hidepatrolled' => 1-$options['hidepatrolled'] ), $nondefaults);
|
|
|
|
|
$myselfLink = $this->makeOptionsLink( $showhide[1-$options['hidemyself']],
|
|
|
|
|
array( 'hidemyself' => 1-$options['hidemyself'] ), $nondefaults);
|
|
|
|
|
|
|
|
|
|
$links[] = wfMsgHtml( 'rcshowhideminor', $minorLink );
|
|
|
|
|
$links[] = wfMsgHtml( 'rcshowhidebots', $botLink );
|
|
|
|
|
$links[] = wfMsgHtml( 'rcshowhideanons', $anonsLink );
|
|
|
|
|
$links[] = wfMsgHtml( 'rcshowhideliu', $liuLink );
|
|
|
|
|
if( $wgUser->useRCPatrol() )
|
|
|
|
|
$links[] = wfMsgHtml( 'rcshowhidepatr', $patrLink );
|
|
|
|
|
$links[] = wfMsgHtml( 'rcshowhidemine', $myselfLink );
|
2008-09-18 17:11:51 +00:00
|
|
|
$hl = implode( ' | ', $links );
|
2008-06-26 19:12:52 +00:00
|
|
|
|
|
|
|
|
// show from this onward link
|
|
|
|
|
$now = $wgLang->timeanddate( wfTimestampNow(), true );
|
2008-08-11 16:24:51 +00:00
|
|
|
$tl = $this->makeOptionsLink( $now, array( 'from' => wfTimestampNow() ), $nondefaults );
|
2008-06-26 19:12:52 +00:00
|
|
|
|
2008-08-11 16:24:51 +00:00
|
|
|
$rclinks = wfMsgExt( 'rclinks', array( 'parseinline', 'replaceafter' ),
|
2008-06-26 19:12:52 +00:00
|
|
|
$cl, $dl, $hl );
|
2008-08-11 16:24:51 +00:00
|
|
|
$rclistfrom = wfMsgExt( 'rclistfrom', array( 'parseinline', 'replaceafter' ), $tl );
|
2008-10-29 15:35:18 +00:00
|
|
|
return "$note<br />$rclinks<br />$rclistfrom";
|
2005-05-03 18:30:46 +00:00
|
|
|
}
|
2008-06-26 19:12:52 +00:00
|
|
|
}
|