Revert r52336 "Merge maintenance-work branch:"

Seems to have broken a bunch of stuff. Don't commit giant non-critical changes that break Setup.php and all maint scripts. Thanks!
This commit is contained in:
Brion Vibber 2009-06-24 02:49:24 +00:00
parent ceedb37941
commit 1c9773bd01
57 changed files with 1681 additions and 3125 deletions

32
AdminSettings.sample Normal file
View file

@ -0,0 +1,32 @@
<?php
/**
* This file should be copied to AdminSettings.php, and modified
* to reflect local settings. It is required for the maintenance
* scripts which run on the command line, as an extra security
* measure to allow using a separate user account with higher
* privileges to do maintenance work.
*
* Developers: Do not check AdminSettings.php into Subversion
*/
/*
* This data is used by all database maintenance scripts
* (see directory maintenance/). The SQL user MUST BE
* MANUALLY CREATED or set to an existing user with
* necessary permissions.
*
* This is not to be confused with sysop accounts for the
* wiki.
*
* NOTE: for PostgreSQL this should be set to the same user and
* password as the web user, that is, the same as $wgDBuser and
* $wgDBpassword in LocalSettings.php. This is necessary to
* ensure that the owner for new tables is set correctly.
*/
$wgDBadminuser = 'wikiadmin';
$wgDBadminpassword = 'adminpass';
/*
* Whether to enable the profileinfo.php script.
*/
$wgEnableProfileInfo = false;

View file

@ -41,7 +41,6 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
appropriate privileges. Creating this user with web-install page requires
oci8.privileged_connect set to On in php.ini.
* Removed UserrightsChangeableGroups hook introduced in 1.14
* AdminSettings.php has been removed completely
=== New features in 1.16 ===
@ -92,10 +91,7 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
stripped from them.
* Added a PHP port of CDB (constant database), for improved local caching when
the DBA extension is not available.
* (bug 14201) Create AdminSettings.php during wiki installation, in the same
way as LocalSettings.php
* (bug 16322) Allow maint scripts to accept DB user/pass over input or params
if no AdminSettings.php
=== Bug fixes in 1.16 ===
@ -204,8 +200,6 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
* (bug 19294) Always show Sp-contributions-footer(-anon)
* Attempts to restrict reading of pages while anonymous viewing is allowed
via extensions not using the userCan hook and via $wgRevokePermissions now work.
* (bug 19157) createAndPromote error on bad password
* (bug 18768) Remove AdminSettings.php from MediaWiki core
* (bug 8445) Multiple-character search terms are now handled properly for Chinese
== API changes in 1.16 ==

19
UPGRADE
View file

@ -42,7 +42,8 @@ You can also obtain the new files directly from our Subversion source code
repository, via a checkout or export operation.
Replace the existing MediaWiki files with the new. You should preserve the
LocalSettings.php file and the "extensions" and "images" directories.
LocalSettings.php file, AdminSettings.php file (if present), and the
"extensions" and "images" directories.
Depending upon your configuration, you may also need to preserve additional
directories, including a custom upload directory ($wgUploadDirectory),
@ -50,8 +51,8 @@ deleted file archives, and any custom skins.
=== Perform the database upgrade ===
You will need to have $wgDBadminuser and $wgDBadminpass set in your
LocalSettings.php, see there for more info.
You will need an AdminSettings.php file set up in the correct format; see
AdminSettings.sample in the wiki root for more information and examples.
From the command line, browse to the "maintenance" directory and run the
update.php script to check and update the schema. This will insert missing
@ -171,10 +172,10 @@ should be replaced with:
=== Web installer ===
You can use the web-based installer wizard if you first remove the
LocalSettings.php file; be sure to give the installer the same
information as you did on the original install (language/encoding,
database name, password, etc). This will also generate a fresh
LocalSettings.php, which you may need to customize.
LocalSettings.php (and AdminSettings.php, if any) files; be sure to
give the installer the same information as you did on the original
install (language/encoding, database name, password, etc). This will
also generate a fresh LocalSettings.php, which you may need to customize.
You may change some settings during the install, but be very careful!
Changing the encoding in particular will generally leave you with a
@ -184,8 +185,8 @@ lot of corrupt pages, particularly if your wiki is not in English.
Additionally, as of 1.4.0 you can run an in-place upgrade script from
the command line, keeping your existing LocalSettings.php. This requires
that you set $wgDBadminuser and $wgDBadminpassword with an appropriate
database user and password with privileges to modify the database structure.
that you create an AdminSettings.php giving an appropriate database user
and password with privileges to modify the database structure.
Once the new files are in place, go into the maintenance subdirectory and
run the script:

View file

@ -615,7 +615,6 @@ print "<li style='font-weight:bold;color:green;font-size:110%'>Environment check
$conf->RootUser = importPost( "RootUser", "root" );
$conf->RootPW = importPost( "RootPW", "" );
$useRoot = importCheck( 'useroot', false );
$conf->populateadmin = importCheck( 'populateadmin', false );
$conf->LanguageCode = importPost( "LanguageCode", "en" );
## MySQL specific:
$conf->DBprefix = importPost( "DBprefix" );
@ -1528,8 +1527,6 @@ if( count( $errs ) ) {
<label class="column">Superuser account:</label>
<input type="checkbox" name="useroot" id="useroot" <?php if( $useRoot ) { ?>checked="checked" <?php } ?> />
&nbsp;<label for="useroot">Use superuser account</label>
<input type="checkbox" name="populateadmin" id="populateadmin" <?php if( $conf->populateadmin ) { ?>checked="checked" <?php } ?> />
&nbsp;<label for="populateadmin">Set as admin user for maintenance</label>
</div>
<div class="config-input"><?php aField( $conf, "RootUser", "Superuser name:", "text" ); ?></div>
<div class="config-input"><?php aField( $conf, "RootPW", "Superuser password:", "password" ); ?></div>
@ -1795,11 +1792,6 @@ function writeLocalSettings( $conf ) {
# Needs literal string interpolation for the current style path
$slconf['RightsIcon'] = $conf->RightsIcon;
}
if( $conf->populateadmin ) {
$slconf['DBadminuser'] = $conf->RootUser;
$slconf['DBadminpassword'] = $conf->RootPW;
}
if( $conf->DBtype == 'mysql' ) {
$dbsettings =
@ -1907,10 +1899,6 @@ if ( \$wgCommandLineMode ) {
{$dbsettings}
## Database admin settings, used for maintenance scripts
\$wgDBadminuser = \"{$slconf['DBadminuser']}\";
\$wgDBadminpassword = \"{$slconf['DBadminpassword']}\";
## Shared memory settings
\$wgMainCacheType = $cacheType;
\$wgMemCachedServers = $mcservers;

View file

@ -1,54 +0,0 @@
Prior to version 1.16, maintenance scripts were a hodgepodge of code that
had no cohesion or formal method of action. Beginning in 1.16, maintenance
scripts have been cleaned up to use a unified class.
1. Directory structure
2. How to run a script
3. How to write your own
1. DIRECTORY STRUCTURE
The /maintenance directory of a MediaWiki installation contains several
subdirectories, all of which have unique purposes.
2. HOW TO RUN A SCRIPT
Ridiculously simple, just call 'php someScript.php' that's in the top-
level /maintenance directory.
Example:
php clear_stats.php
The following parameters are available to all maintenance scripts
--help : Print a help message
--quiet : Quiet non-error output
--dbuser : The database user to use for the script (if needed)
--dbpass : Same as above (if needed)
3. HOW TO WRITE YOUR OWN
Make a file in the maintenance directory called myScript.php or something.
In it, write the following:
==BEGIN==
<?php
require_once( "Maintenance.php" );
class DemoMaint extends Maintenance {
public function __construct() {
parent::__construct();
}
protected function execute() {
}
}
$maintClass = "DemoMaint";
require_once( DO_MAINTENANCE );
==END==
That's it. In the execute() method, you have access to all of the normal
MediaWiki functions, so you can get a DB connection, use the cache, etc.
For full docs on the Maintenance class, see the auto-generated docs at
http://svn.wikimedia.org/doc/classMaintenance.html

View file

@ -35,9 +35,10 @@ Primary scripts:
to force the profiler to save the informations in the database and apply the
maintenance/archives/patch-profiling.sql patch to the database.
To enable the profileinfo.php itself, you'll need to set $wgDBadminuser
and $wgDBadminpassword in your LocalSettings.php, as well as $wgEnableProfileInfo
See also http://www.mediawiki.org/wiki/How_to_debug#Profiling.
To enable the profileinfo.php itself, you'll need to create the
AdminSettings.php file (see AdminSettings.sample for more information) and
set $wgEnableProfileInfo to true in that file. See also
http://www.mediawiki.org/wiki/How_to_debug#Profiling.
redirect.php
Script that only redirect to the article passed in the wpDropdown parameter

View file

@ -49,8 +49,11 @@ class SiteStats {
// clean schema with mwdumper.
wfDebug( __METHOD__ . ": initializing damaged or missing site_stats\n" );
global $IP;
require_once "$IP/maintenance/initStats.inc";
ob_start();
self::init( false );
wfInitStats();
ob_end_clean();
$row = self::doLoad( wfGetDB( DB_MASTER ) );
@ -174,63 +177,6 @@ class SiteStats {
}
return true;
}
/**
* Ported from initStats.inc.
* @param $update bool Whether to update the current stats write fresh
* @param $noViews bool When true, do not update the number of page views
*/
function init( $update, $noViews = false ) {
$dbr = wfGetDB( DB_SLAVE );
wfOut( "Counting total edits..." );
$edits = $dbr->selectField( 'revision', 'COUNT(*)', '', __METHOD__ );
$edits += $dbr->selectField( 'archive', 'COUNT(*)', '', __METHOD__ );
wfOut( "{$edits}\nCounting number of articles..." );
global $wgContentNamespaces;
$good = $dbr->selectField( 'page', 'COUNT(*)', array( 'page_namespace' => $wgContentNamespaces, 'page_is_redirect' => 0, 'page_len > 0' ), __METHOD__ );
wfOut( "{$good}\nCounting total pages..." );
$pages = $dbr->selectField( 'page', 'COUNT(*)', '', __METHOD__ );
wfOut( "{$pages}\nCounting number of users..." );
$users = $dbr->selectField( 'user', 'COUNT(*)', '', __METHOD__ );
wfOut( "{$users}\nCounting number of admins..." );
$admin = $dbr->selectField( 'user_groups', 'COUNT(*)', array( 'ug_group' => 'sysop' ), __METHOD__ );
wfOut( "{$admin}\nCounting number of images..." );
$image = $dbr->selectField( 'image', 'COUNT(*)', '', __METHOD__ );
wfOut( "{$image}\n" );
if( !$noViews ) {
wfOut( "Counting total page views..." );
$views = $dbr->selectField( 'page', 'SUM(page_counter)', '', __METHOD__ );
wfOut( "{$views}\n" );
}
wfOut( "\nUpdating site statistics..." );
$dbw = wfGetDB( DB_MASTER );
$values = array( 'ss_total_edits' => $edits,
'ss_good_articles' => $good,
'ss_total_pages' => $pages,
'ss_users' => $users,
'ss_admins' => $admin,
'ss_images' => $image );
$conds = array( 'ss_row_id' => 1 );
$views = array( 'ss_total_views' => isset( $views ) ? $views : 0 );
if( $update ) {
$dbw->update( 'site_stats', $values, $conds, __METHOD__ );
} else {
$dbw->delete( 'site_stats', $conds, __METHOD__ );
$dbw->insert( 'site_stats', array_merge( $values, $conds, $views ), __METHOD__ );
}
wfOut( "done.\n" );
}
}

View file

@ -135,7 +135,7 @@ FILE_PATTERNS = *.c \
RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS = YES
EXCLUDE_PATTERNS = LocalSettings.php
EXCLUDE_PATTERNS = LocalSettings.php AdminSettings.php
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
EXAMPLE_RECURSIVE = NO

View file

@ -1,639 +0,0 @@
<?php
// Define this so scripts can easily find doMaintenance.php
define( 'DO_MAINTENANCE', dirname(__FILE__) . '/doMaintenance.php' );
/**
* Abstract maintenance class for quickly writing and churning out
* maintenance scripts with minimal effort. All that _must_ be defined
* is the execute() method. See docs/maintenance.txt for more info
* and a quick demo of how to use it.
*
* @author Chad Horohoe <chad@anyonecanedit.org>
* @since 1.16
* @ingroup Maintenance
*/
abstract class Maintenance {
/**
* Constants for DB access type
* @see Maintenance::getDbType()
*/
const NO_DB = 0;
const NORMAL_DB = 1;
const ADMIN_DB = 2;
// This is the desired params
private $mParams = array();
// Array of desired args
private $mArgList = array();
// This is the list of options that were actually passed
private $mOptions = array();
// This is the list of arguments that were actually passed
protected $mArgs = array();
// Name of the script currently running
protected $mSelf;
// Special vars for params that are always used
private $mQuiet = false;
private $mDbUser, $mDbPass;
// A description of the script, children should change this
protected $mDescription = '';
// Have we already loaded our user input?
private $inputLoaded = false;
// Batch size
protected $mBatchSize = 100;
/**
* Default constructor. Children should call this if implementing
* their own constructors
*/
public function __construct() {
$this->addDefaultParams();
}
/**
* Do the actual work. All child classes will need to implement this
*/
abstract public function execute();
/**
* Add a parameter to the script. Will be displayed on --help
* with the associated description
*
* @param $name String The name of the param (help, version, etc)
* @param $description String The description of the param to show on --help
* @param $required boolean Is the param required?
* @param $withArg Boolean Is an argument required with this option?
*/
protected function addParam( $name, $description, $required = false, $withArg = false ) {
$this->mParams[ $name ] = array( 'desc' => $description, 'require' => $required, 'withArg' => $withArg );
}
/**
* Checks to see if a particular param exists.
* @param $name String The name of the param
* @return boolean
*/
protected function hasOption( $name ) {
return isset( $this->mOptions[ $name ] );
}
/**
* Get an option, or return the default
* @param $name String The name of the param
* @param $default mixed Anything you want, default null
* @return mixed
*/
protected function getOption( $name, $default = null ) {
if( $this->hasOption($name) ) {
return $this->mOptions[$name];
} else {
// Set it so we don't have to provide the default again
$this->mOptions[$name] = $default;
return $this->mOptions[$name];
}
}
/**
* Add some args that are needed. Used in formatting help
*/
protected function addArgs( $args ) {
$this->mArgList = array_merge( $this->mArgList, $args );
}
/**
* Does a given argument exist?
* @param $argId int The integer value (from zero) for the arg
* @return boolean
*/
protected function hasArg( $argId = 0 ) {
return isset( $this->mArgs[ $argId ] ) ;
}
/**
* Get an argument.
* @param $argId int The integer value (from zero) for the arg
* @param $default mixed The default if it doesn't exist
* @return mixed
*/
protected function getArg( $argId = 0, $default = null ) {
return $this->hasArg($name) ? $this->mArgs[$name] : $default;
}
/**
* Set the batch size.
* @param $s int The number of operations to do in a batch
*/
protected function setBatchSize( $s = 0 ) {
$this->mBatchSize = $s;
}
/**
* Return input from stdin.
* @param $length int The number of bytes to read. If null,
* just return the handle
* @return mixed
*/
protected function getStdin( $len = null ) {
$f = fopen( 'php://stdin', 'rt' );
if( !$len ) {
return $f;
}
$input = fgets( $f, $len );
fclose ( $f );
return rtrim( $input );
}
/**
* Throw some output to the user. Scripts can call this with no fears,
* as we handle all --quiet stuff here
* @param $out String The text to show to the user
*/
protected function output( $out ) {
if( $this->mQuiet ) {
return;
}
$f = fopen( 'php://stdout', 'w' );
fwrite( $f, $out );
fclose( $f );
}
/**
* Throw an error to the user. Doesn't respect --quiet, so don't use
* this for non-error output
* @param $err String The error to display
* @param $die boolean If true, go ahead and die out.
*/
protected function error( $err, $die = false ) {
$f = fopen( 'php://stderr', 'w' );
fwrite( $f, $err );
fclose( $f );
if( $die ) die();
}
/**
* Does the script need normal DB access? By default, we give Maintenance
* scripts admin rights to the DB (when available). Sometimes, a script needs
* normal access for a reason and sometimes they want no access. Subclasses
* should override and return one of the following values, as needed:
* Maintenance::NO_DB - For no DB access at all
* Maintenance::NORMAL_DB - For normal DB access
* Maintenance::ADMIN_DB - For admin DB access, default
* @return int
*/
protected function getDbType() {
return Maintenance :: ADMIN_DB;
}
/**
* Add the default parameters to the scripts
*/
private function addDefaultParams() {
$this->addParam( 'help', "Display this help message" );
$this->addParam( 'quiet', "Whether to supress non-error output" );
$this->addParam( 'conf', "Location of LocalSettings.php, if not default", false, true );
$this->addParam( 'wiki', "For specifying the wiki ID", false, true );
if( $this->getDbType() > 0 ) {
$this->addParam( 'dbuser', "The DB user to use for this script", false, true );
$this->addParam( 'dbpass', "The password to use for this script", false, true );
}
}
/**
* Spawn a child maintenance script. Pass all of the current arguments
* to it.
* @param $maintClass String A name of a child maintenance class
* @param $classFile String Full path of where the child is
* @return Maintenance child
*/
protected function spawnChild( $maintClass, $classFile = null ) {
// If we haven't already specified, kill setup procedures
// for child scripts, we've already got a sane environment
if( !defined( 'MW_NO_SETUP' ) ) {
define( 'MW_NO_SETUP', true );
}
// Make sure the class is loaded first
if( !class_exists( $maintClass ) ) {
if( $classFile ) {
require_once( $classFile );
}
if( !class_exists( $maintClass ) ) {
$this->error( "Cannot spawn child: $maintClass\n" );
}
}
$child = new $maintClass();
$child->loadParamsAndArgs( $this->mSelf, $this->mOptions, $this->mArgs );
return $child;
}
/**
* Do some sanity checking and basic setup
*/
public function setup() {
global $IP, $wgCommandLineMode, $wgUseNormalUser, $wgRequestTime;
# Abort if called from a web server
if ( isset( $_SERVER ) && array_key_exists( 'REQUEST_METHOD', $_SERVER ) ) {
$this->error( "This script must be run from the command line\n", true );
}
# Make sure we can handle script parameters
if( !ini_get( 'register_argc_argv' ) ) {
$this->error( "Cannot get command line arguments, register_argc_argv is set to false", true );
}
# Make sure we're on PHP5 or better
if( version_compare( PHP_VERSION, '5.0.0' ) < 0 ) {
$this->error( "Sorry! This version of MediaWiki requires PHP 5; you are running " .
PHP_VERSION . ".\n\n" .
"If you are sure you already have PHP 5 installed, it may be installed\n" .
"in a different path from PHP 4. Check with your system administrator.\n", true );
}
if( version_compare( phpversion(), '5.2.4' ) >= 0 ) {
// Send PHP warnings and errors to stderr instead of stdout.
// This aids in diagnosing problems, while keeping messages
// out of redirected output.
if( ini_get( 'display_errors' ) ) {
ini_set( 'display_errors', 'stderr' );
}
// Don't touch the setting on earlier versions of PHP,
// as setting it would disable output if you'd wanted it.
// Note that exceptions are also sent to stderr when
// command-line mode is on, regardless of PHP version.
}
# Set the memory limit
ini_set( 'memory_limit', -1 );
$wgRequestTime = microtime(true);
# Define us as being in Mediawiki
define( 'MEDIAWIKI', true );
# Setup $IP, using MW_INSTALL_PATH if it exists
$IP = strval( getenv('MW_INSTALL_PATH') ) !== ''
? getenv('MW_INSTALL_PATH')
: realpath( dirname( __FILE__ ) . '/..' );
$wgCommandLineMode = true;
# Turn off output buffering if it's on
@ob_end_flush();
if (!isset( $wgUseNormalUser ) ) {
$wgUseNormalUser = false;
}
$this->loadParamsAndArgs();
$this->maybeHelp();
}
/**
* Clear all params and arguments.
*/
public function clearParamsAndArgs() {
$this->mOptions = array();
$this->mArgs = array();
$this->inputLoaded = false;
}
/**
* Process command line arguments
* $mOptions becomes an array with keys set to the option names
* $mArgs becomes a zero-based array containing the non-option arguments
*
* @param $self String The name of the script, if any
* @param $opts Array An array of options, in form of key=>value
* @param $args Array An array of command line arguments
*/
public function loadParamsAndArgs( $self = null, $opts = null, $args = null ) {
# If we were given opts or args, set those and return early
if( $self ) {
$this->mSelf = $self;
$this->inputLoaded = true;
}
if( $opts ) {
$this->mOptions = $opts;
$this->inputLoaded = true;
}
if( $args ) {
$this->mArgs = $args;
$this->inputLoaded = true;
}
# If we've already loaded input (either by user values or from $argv)
# skip on loading it again. The array_shift() will corrupt values if
# it's run again and again
if( $this->inputLoaded ) {
$this->loadSpecialVars();
return;
}
global $argv;
$this->mSelf = array_shift( $argv );
$options = array();
$args = array();
# Parse arguments
for( $arg = reset( $argv ); $arg !== false; $arg = next( $argv ) ) {
if ( $arg == '--' ) {
# End of options, remainder should be considered arguments
$arg = next( $argv );
while( $arg !== false ) {
$args[] = $arg;
$arg = next( $argv );
}
break;
} elseif ( substr( $arg, 0, 2 ) == '--' ) {
# Long options
$option = substr( $arg, 2 );
if ( isset( $this->mParams[$option] ) && $this->mParams[$option]['withArg'] ) {
$param = next( $argv );
if ( $param === false ) {
$this->error( "$arg needs a value after it\n", true );
}
$options[$option] = $param;
} else {
$bits = explode( '=', $option, 2 );
if( count( $bits ) > 1 ) {
$option = $bits[0];
$param = $bits[1];
} else {
$param = 1;
}
$options[$option] = $param;
}
} elseif ( substr( $arg, 0, 1 ) == '-' ) {
# Short options
for ( $p=1; $p<strlen( $arg ); $p++ ) {
$option = $arg{$p};
if ( isset( $this->mParams[$option]['withArg'] ) ) {
$param = next( $argv );
if ( $param === false ) {
$this->error( "$arg needs a value after it\n", true );
}
$options[$option] = $param;
} else {
$options[$option] = 1;
}
}
} else {
$args[] = $arg;
}
}
# Check to make sure we've got all the required ones
foreach( $this->mParams as $opt => $info ) {
if( $info['require'] && !$this->hasOption($opt) ) {
$this->error( "Param $opt required.\n", true );
}
}
# Also make sure we've got enough arguments
if ( count( $args ) < count( $this->mArgList ) ) {
$this->error( "Not enough arguments passed", true );
}
$this->mOptions = $options;
$this->mArgs = $args;
$this->loadSpecialVars();
$this->inputLoaded = true;
}
/**
* Handle the special variables that are global to all scripts
*/
private function loadSpecialVars() {
if( $this->hasOption( 'dbuser' ) )
$this->mDbUser = $this->getOption( 'dbuser' );
if( $this->hasOption( 'dbpass' ) )
$this->mDbPass = $this->getOption( 'dbpass' );
if( $this->hasOption( 'quiet' ) )
$this->mQuiet = true;
}
/**
* Maybe show the help.
* @param $force boolean Whether to force the help to show, default false
*/
private function maybeHelp( $force = false ) {
if( $this->hasOption('help') || in_array( 'help', $this->mArgs ) || $force ) {
$this->mQuiet = false;
if( $this->mDescription ) {
$this->output( $this->mDescription . "\n" );
}
$this->output( "\nUsage: php " . $this->mSelf );
if( $this->mParams ) {
$this->output( " [--" . implode( array_keys( $this->mParams ), "|--" ) . "]" );
}
if( $this->mArgList ) {
$this->output( " <" . implode( $this->mArgList, "> <" ) . ">" );
}
$this->output( "\n" );
foreach( $this->mParams as $par => $info ) {
$this->output( "\t$par : " . $info['desc'] . "\n" );
}
die( 1 );
}
}
/**
* Handle some last-minute setup here.
*/
private function finalSetup() {
global $wgCommandLineMode, $wgUseNormalUser, $wgShowSQLErrors;
global $wgTitle, $wgProfiling, $IP, $wgDBadminuser, $wgDBadminpassword;
global $wgDBuser, $wgDBpassword, $wgDBservers, $wgLBFactoryConf;
# Turn off output buffering again, it might have been turned on in the settings files
if( ob_get_level() ) {
ob_end_flush();
}
# Same with these
$wgCommandLineMode = true;
# If these were passed, use them
if( $this->mDbUser )
$wgDBadminuser = $this->mDbUser;
if( $this->mDbPass )
$wgDBadminpass = $this->mDbPass;
if ( empty( $wgUseNormalUser ) && isset( $wgDBadminuser ) ) {
$wgDBuser = $wgDBadminuser;
$wgDBpassword = $wgDBadminpassword;
if( $wgDBservers ) {
foreach ( $wgDBservers as $i => $server ) {
$wgDBservers[$i]['user'] = $wgDBuser;
$wgDBservers[$i]['password'] = $wgDBpassword;
}
}
if( isset( $wgLBFactoryConf['serverTemplate'] ) ) {
$wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser;
$wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword;
}
}
if ( defined( 'MW_CMDLINE_CALLBACK' ) ) {
$fn = MW_CMDLINE_CALLBACK;
$fn();
}
$wgShowSQLErrors = true;
@set_time_limit( 0 );
$wgProfiling = false; // only for Profiler.php mode; avoids OOM errors
}
/**
* Do setup specific to WMF
*/
public function loadWikimediaSettings() {
global $IP, $wgNoDBParam, $wgUseNormalUser, $wgConf;
if ( empty( $wgNoDBParam ) ) {
# Check if we were passed a db name
if ( isset( $this->mOptions['wiki'] ) ) {
$db = $this->mOptions['wiki'];
} else {
$db = array_shift( $this->mArgs );
}
list( $site, $lang ) = $wgConf->siteFromDB( $db );
# If not, work out the language and site the old way
if ( is_null( $site ) || is_null( $lang ) ) {
if ( !$db ) {
$lang = 'aa';
} else {
$lang = $db;
}
if ( isset( $this->mArgs[0] ) ) {
$site = array_shift( $this->mArgs );
} else {
$site = 'wikipedia';
}
}
} else {
$lang = 'aa';
$site = 'wikipedia';
}
# This is for the IRC scripts, which now run as the apache user
# The apache user doesn't have access to the wikiadmin_pass command
if ( $_ENV['USER'] == 'apache' ) {
#if ( posix_geteuid() == 48 ) {
$wgUseNormalUser = true;
}
putenv( 'wikilang=' . $lang );
$DP = $IP;
ini_set( 'include_path', ".:$IP:$IP/includes:$IP/languages:$IP/maintenance" );
if ( $lang == 'test' && $site == 'wikipedia' ) {
define( 'TESTWIKI', 1 );
}
}
/**
* Generic setup for most installs. Returns the location of LocalSettings
* @return String
*/
public function loadSettings() {
global $wgWikiFarm, $wgCommandLineMode, $IP, $DP;
$wgWikiFarm = false;
if ( isset( $this->mOptions['conf'] ) ) {
$settingsFile = $this->mOptions['conf'];
} else {
$settingsFile = "$IP/LocalSettings.php";
}
if ( isset( $this->mOptions['wiki'] ) ) {
$bits = explode( '-', $this->mOptions['wiki'] );
if ( count( $bits ) == 1 ) {
$bits[] = '';
}
define( 'MW_DB', $bits[0] );
define( 'MW_PREFIX', $bits[1] );
}
if ( ! is_readable( $settingsFile ) ) {
$this->error( "A copy of your installation's LocalSettings.php\n" .
"must exist and be readable in the source directory.\n", true );
}
$wgCommandLineMode = true;
$DP = $IP;
$this->finalSetup();
return $settingsFile;
}
/**
* Support function for cleaning up redundant text records
* @param $delete boolean Whether or not to actually delete the records
* @author Rob Church <robchur@gmail.com>
*/
protected function purgeRedundantText( $delete = true ) {
# Data should come off the master, wrapped in a transaction
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
$tbl_arc = $dbw->tableName( 'archive' );
$tbl_rev = $dbw->tableName( 'revision' );
$tbl_txt = $dbw->tableName( 'text' );
# Get "active" text records from the revisions table
$this->output( "Searching for active text records in revisions table..." );
$res = $dbw->query( "SELECT DISTINCT rev_text_id FROM $tbl_rev" );
while( $row = $dbw->fetchObject( $res ) ) {
$cur[] = $row->rev_text_id;
}
$this->output( "done.\n" );
# Get "active" text records from the archive table
$this->output( "Searching for active text records in archive table..." );
$res = $dbw->query( "SELECT DISTINCT ar_text_id FROM $tbl_arc" );
while( $row = $dbw->fetchObject( $res ) ) {
$cur[] = $row->ar_text_id;
}
$this->output( "done.\n" );
# Get the IDs of all text records not in these sets
$this->output( "Searching for inactive text records..." );
$set = implode( ', ', $cur );
$res = $dbw->query( "SELECT old_id FROM $tbl_txt WHERE old_id NOT IN ( $set )" );
$old = array();
while( $row = $dbw->fetchObject( $res ) ) {
$old[] = $row->old_id;
}
$this->output( "done.\n" );
# Inform the user of what we're going to do
$count = count( $old );
$this->output( "$count inactive items found.\n" );
# Delete as appropriate
if( $delete && $count ) {
$this->output( "Deleting..." );
$set = implode( ', ', $old );
$dbw->query( "DELETE FROM $tbl_txt WHERE old_id IN ( $set )" );
$this->output( "done.\n" );
}
# Done
$dbw->commit();
}
}

View file

@ -10,8 +10,8 @@ proper installation.
Certain scripts will require elevated access to the database. In order to
provide this, first create a MySQL user with "all" permissions on the wiki
database, and then set $wgDBadminuser and $wgDBadminpassword in your
LocalSettings.php
database, and then place their username and password in an AdminSettings.php
file in the directory above. See AdminSettings.sample for specifics on this.
=== Brief explanation of files ===

View file

@ -25,58 +25,49 @@
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
require_once( 'commandLine.inc' );
class AttachLatest extends Maintenance {
public function __construct() {
parent::__construct();
$this->addParam( "fix", "Actually fix the entries, will dry run otherwise" );
$this->mDescription = "Fix page_latest entries in the page table";
}
public function execute() {
$this->output( "Looking for pages with page_latest set to 0...\n" );
$dbw = wfGetDB( DB_MASTER );
$result = $dbw->select( 'page',
array( 'page_id', 'page_namespace', 'page_title' ),
array( 'page_latest' => 0 ),
__METHOD__ );
$fixit = isset( $options['fix'] );
$fname = 'attachLatest';
$n = 0;
while( $row = $dbw->fetchObject( $result ) ) {
$pageId = intval( $row->page_id );
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
$name = $title->getPrefixedText();
$latestTime = $dbw->selectField( 'revision',
'MAX(rev_timestamp)',
array( 'rev_page' => $pageId ),
__METHOD__ );
if( !$latestTime ) {
$this->output( wfWikiID()." $pageId [[$name]] can't find latest rev time?!\n" );
continue;
}
$revision = Revision::loadFromTimestamp( $dbw, $title, $latestTime );
if( is_null( $revision ) ) {
$this->output( wfWikiID()." $pageId [[$name]] latest time $latestTime, can't find revision id\n" );
continue;
}
$id = $revision->getId();
$this->output( wfWikiID()." $pageId [[$name]] latest time $latestTime, rev id $id\n" );
if( $this->hasOption('fix') ) {
$article = new Article( $title );
$article->updateRevisionOn( $dbw, $revision );
}
$n++;
}
$dbw->freeResult( $result );
$this->output( "Done! Processed $n pages.\n" );
if( !$this->hasOption('fix') ) {
$this->output( "This was a dry run; rerun with --fix to update page_latest.\n" );
}
echo "Looking for pages with page_latest set to 0...\n";
$dbw = wfGetDB( DB_MASTER );
$result = $dbw->select( 'page',
array( 'page_id', 'page_namespace', 'page_title' ),
array( 'page_latest' => 0 ),
$fname );
$n = 0;
while( $row = $dbw->fetchObject( $result ) ) {
$pageId = intval( $row->page_id );
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
$name = $title->getPrefixedText();
$latestTime = $dbw->selectField( 'revision',
'MAX(rev_timestamp)',
array( 'rev_page' => $pageId ),
$fname );
if( !$latestTime ) {
echo wfWikiID()." $pageId [[$name]] can't find latest rev time?!\n";
continue;
}
$revision = Revision::loadFromTimestamp( $dbw, $title, $latestTime );
if( is_null( $revision ) ) {
echo wfWikiID()." $pageId [[$name]] latest time $latestTime, can't find revision id\n";
continue;
}
$id = $revision->getId();
echo wfWikiID()." $pageId [[$name]] latest time $latestTime, rev id $id\n";
if( $fixit ) {
$article = new Article( $title );
$article->updateRevisionOn( $dbw, $revision );
}
$n++;
}
$dbw->freeResult( $result );
echo "Done! Processed $n pages.\n";
if( !$fixit ) {
echo "This was a dry run; rerun with --fix to update page_latest.\n";
}
$maintClass = "AttachLatest";
require_once( DO_MAINTENANCE );

View file

@ -6,87 +6,74 @@
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
/** */
require_once( "commandLine.inc" );
class BenchmarkPurge extends Maintenance {
public function __construct() {
parent::__construct();
$this->addParams( "count", "How many URLs to feed to Squid for purging", false, true );
$this->mDescription = "Benchmark the Squid purge functions.";
}
public function execute() {
global $wgUseSquid;
if( !$wgUseSquid ) {
$this->error( "Squid purge benchmark doesn't do much without squid support on.\n". true );
} else {
$this->output( "There are " . count( $wgSquidServers ) . " defined squid servers:\n" );
if( $this->hasOption( 'count' ) ) {
$lengths = array( intval( $this->getOption('count') ) );
} else {
$lengths = array( 1, 10, 100 );
}
foreach( $lengths as $length ) {
$urls = $this->randomUrlList( $length );
$trial = $this->benchSquid( $urls );
$this->output( $trial . "\n" );
}
}
}
/**
* Run a bunch of URLs through SquidUpdate::purge()
* to benchmark Squid response times.
* @param $urls array A bunch of URLs to purge
* @param $trials int How many times to run the test?
*/
private function benchSquid( $urls, $trials = 1 ) {
$start = wfTime();
for( $i = 0; $i < $trials; $i++) {
SquidUpdate::purge( $urls );
}
$delta = wfTime() - $start;
$pertrial = $delta / $trials;
$pertitle = $pertrial / count( $urls );
return sprintf( "%4d titles in %6.2fms (%6.2fms each)",
count( $urls ), $pertrial * 1000.0, $pertitle * 1000.0 );
}
/**
* Get an array of randomUrl()'s.
* @param $length int How many urls to add to the array
*/
private function randomUrlList( $length ) {
$list = array();
for( $i = 0; $i < $length; $i++ ) {
$list[] = $this->randomUrl();
}
return $list;
}
/**
* Return a random URL of the wiki. Not necessarily an actual title in the
* database, but at least a URL that looks like one.
*/
private function randomUrl() {
global $wgServer, $wgArticlePath;
return $wgServer . str_replace( '$1', $this->randomTitle(), $wgArticlePath );
}
/**
* Create a random title string (not necessarily a Title object).
* For use with randomUrl().
*/
private function randomTitle() {
$str = '';
$length = mt_rand( 1, 20 );
for( $i = 0; $i < $length; $i++ ) {
$str .= chr( mt_rand( ord('a'), ord('z') ) );
}
return ucfirst( $str );
/**
* Run a bunch of URLs through SquidUpdate::purge()
* to benchmark Squid response times.
* @param $urls array A bunch of URLs to purge
* @param $trials int How many times to run the test?
*/
function benchSquid( $urls, $trials = 1 ) {
$start = wfTime();
for( $i = 0; $i < $trials; $i++) {
SquidUpdate::purge( $urls );
}
$delta = wfTime() - $start;
$pertrial = $delta / $trials;
$pertitle = $pertrial / count( $urls );
return sprintf( "%4d titles in %6.2fms (%6.2fms each)",
count( $urls ), $pertrial * 1000.0, $pertitle * 1000.0 );
}
$maintClass = "BenchmarkPurge";
require_once( DO_MAINTENANCE );
/**
* Get an array of randomUrl()'s.
* @param $length int How many urls to add to the array
*/
function randomUrlList( $length ) {
$list = array();
for( $i = 0; $i < $length; $i++ ) {
$list[] = randomUrl();
}
return $list;
}
/**
* Return a random URL of the wiki. Not necessarily an actual title in the
* database, but at least a URL that looks like one.
*/
function randomUrl() {
global $wgServer, $wgArticlePath;
return $wgServer . str_replace( '$1', randomTitle(), $wgArticlePath );
}
/**
* Create a random title string (not necessarily a Title object).
* For use with randomUrl().
*/
function randomTitle() {
$str = '';
$length = mt_rand( 1, 20 );
for( $i = 0; $i < $length; $i++ ) {
$str .= chr( mt_rand( ord('a'), ord('z') ) );
}
return ucfirst( $str );
}
if( !$wgUseSquid ) {
wfDie( "Squid purge benchmark doesn't do much without squid support on.\n" );
} else {
printf( "There are %d defined squid servers:\n", count( $wgSquidServers ) );
#echo implode( "\n", $wgSquidServers ) . "\n";
if( isset( $options['count'] ) ) {
$lengths = array( intval( $options['count'] ) );
} else {
$lengths = array( 1, 10, 100 );
}
foreach( $lengths as $length ) {
$urls = randomUrlList( $length );
$trial = benchSquid( $urls );
print "$trial\n";
}
}

View file

@ -10,32 +10,47 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
*/
require_once( "Maintenance.php" );
$optionsWithArgs = array( 'user', 'password' );
require_once 'commandLine.inc';
class ChangePassword extends Maintenance {
public function __construct() {
parent::__construct();
$this->addParam( "user", "The username to operate on", true, true );
$this->addParam( "password", "The password to use", true, true );
$this->mDescription = "Change a user's password."
$USAGE =
"Usage: php changePassword.php [--user=user --password=password | --help]\n" .
"\toptions:\n" .
"\t\t--help show this message\n" .
"\t\t--user the username to operate on\n" .
"\t\t--password the password to use\n";
if( in_array( '--help', $argv ) )
wfDie( $USAGE );
$cp = new ChangePassword( @$options['user'], @$options['password'] );
$cp->main();
/**
* @ingroup Maintenance
*/
class ChangePassword {
var $dbw;
var $user, $password;
function ChangePassword( $user, $password ) {
global $USAGE;
if( !strlen( $user ) or !strlen( $password ) ) {
wfDie( $USAGE );
}
$this->user = User::newFromName( $user );
if ( !$this->user->getId() ) {
die ( "No such user: $user\n" );
}
$this->password = $password;
$this->dbw = wfGetDB( DB_MASTER );
}
public function execute() {
if( !$this->hasOption('user') || !$this->hasOption('password') ) {
$this->error( "Username or password not provided, halting.", true );
}
$user = User::newFromName( $this->getOption('user') );
if( !$user->getId() ) {
$this->error( "No such user: " . $this->getOption('user') . "\n", true );
}
try {
$user->setPassword( $this->getOption('password') );
$user->saveSettings();
} catch( PasswordError $pwe ) {
$this->error( $pwe->getText(), true );
}
function main() {
$this->user->setPassword( $this->password );
$this->user->saveSettings();
}
}
$maintClass = "ChangePassword";
require_once( DO_MAINTENANCE );

View file

@ -1,40 +1,29 @@
<?php
/**
* Check the autoloader
*/
if ( php_sapi_name() != 'cli' ) exit;
require_once( "Maintenance.php" );
$IP = dirname(__FILE__) .'/..';
require( "$IP/includes/AutoLoader.php" );
$files = array_unique( $wgAutoloadLocalClasses );
class CheckAutoLoader extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "AutoLoader sanity checks";
foreach ( $files as $file ) {
if( function_exists( 'parsekit_compile_file' ) ){
$parseInfo = parsekit_compile_file( "$IP/$file" );
$classes = array_keys( $parseInfo['class_table'] );
} else {
$contents = file_get_contents( "$IP/$file" );
$m = array();
preg_match_all( '/\n\s*class\s+([a-zA-Z0-9_]+)/', $contents, $m, PREG_PATTERN_ORDER );
$classes = $m[1];
}
public function execute() {
global $wgAutoloadLocalClasses, $IP;
$files = array_unique( $wgAutoloadLocalClasses );
foreach( $files as $file ) {
if( function_exists( 'parsekit_compile_file' ) ){
$parseInfo = parsekit_compile_file( "$IP/$file" );
$classes = array_keys( $parseInfo['class_table'] );
} else {
$contents = file_get_contents( "$IP/$file" );
$m = array();
preg_match_all( '/\n\s*class\s+([a-zA-Z0-9_]+)/', $contents, $m, PREG_PATTERN_ORDER );
$classes = $m[1];
}
foreach ( $classes as $class ) {
if ( !isset( $wgAutoloadLocalClasses[$class] ) ) {
//printf( "%-50s Unlisted, in %s\n", $class, $file );
$this->output( "\t'$class' => '$file',\n" );
} elseif ( $wgAutoloadLocalClasses[$class] !== $file ) {
$this->output( "$class: Wrong file: found in $file, listed in " . $wgAutoloadLocalClasses[$class] . "\n" );
}
}
foreach ( $classes as $class ) {
if ( !isset( $wgAutoloadLocalClasses[$class] ) ) {
//printf( "%-50s Unlisted, in %s\n", $class, $file );
echo " '$class' => '$file',\n";
} elseif ( $wgAutoloadLocalClasses[$class] !== $file ) {
echo "$class: Wrong file: found in $file, listed in " . $wgAutoloadLocalClasses[$class] . "\n";
}
}
}
$maintClass = "CheckAutoLoader";
require_once( DO_MAINTENANCE );

View file

@ -1,42 +1,30 @@
<?php
/**
* CheckBadRedirects - See if pages marked as being redirects
* really are.
*/
require_once( "Maintenance.php" );
class CheckBadRedirects extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Look for bad redirects";
}
require "commandLine.inc";
public function execute() {
$this->output( "Fetching redirects...\n" );
$dbr = wfGetDB( DB_SLAVE );
$result = $dbr->select(
array( 'page' ),
array( 'page_namespace','page_title', 'page_latest' ),
array( 'page_is_redirect' => 1 ) );
$count = $result->numRows();
$this->output( "Found $count total redirects.\n" .
"Looking for bad redirects:\n\n" );
foreach( $result as $row ) {
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
$rev = Revision::newFromId( $row->page_latest );
if( $rev ) {
$target = Title::newFromRedirect( $rev->getText() );
if( !$target ) {
$this->output( $title->getPrefixedText() . "\n" );
}
}
echo "Fetching redirects...\n";
$dbr = wfGetDB( DB_SLAVE );
$result = $dbr->select(
array( 'page' ),
array( 'page_namespace','page_title', 'page_latest' ),
array( 'page_is_redirect' => 1 ) );
$count = $result->numRows();
echo "Found $count total redirects.\n";
echo "Looking for bad redirects:\n";
echo "\n";
foreach( $result as $row ) {
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
$rev = Revision::newFromId( $row->page_latest );
if( $rev ) {
$target = Title::newFromRedirect( $rev->getText() );
if( !$target ) {
echo $title->getPrefixedText();
echo "\n";
}
$this->output( "\ndone.\n" );
}
}
$maintClass = "CheckBadRedirects";
require_once( DO_MAINTENANCE );
echo "\n";
echo "done.\n";

View file

@ -1,63 +1,51 @@
<?php
/**
* Check images to see if they exist, are readable, etc etc
*/
require_once( "Maintenance.php" );
class CheckImages extends Maintenance {
require( 'commandLine.inc' );
public function __construct() {
parent::__construct();
$this->mDescription = "Check images to see if they exist, are readable, etc";
$batchSize = 1000;
$start = '';
$dbr = wfGetDB( DB_SLAVE );
$localRepo = RepoGroup::singleton()->getLocalRepo();
$numImages = 0;
$numGood = 0;
do {
$res = $dbr->select( 'image', '*', array( 'img_name > ' . $dbr->addQuotes( $start ) ),
'checkImages.php', array( 'LIMIT' => $batchSize ) );
foreach ( $res as $row ) {
$numImages++;
$start = $row->img_name;
$file = $localRepo->newFileFromRow( $row );
$path = $file->getPath();
if ( !$path ) {
echo "{$row->img_name}: not locally accessible\n";
continue;
}
$stat = @stat( $file->getPath() );
if ( !$stat ) {
echo "{$row->img_name}: missing\n";
continue;
}
if ( $stat['mode'] & 040000 ) {
echo "{$row->img_name}: is a directory\n";
continue;
}
if ( $stat['size'] == 0 && $row->img_size != 0 ) {
echo "{$row->img_name}: truncated, was {$row->img_size}\n";
continue;
}
if ( $stat['size'] != $row->img_size ) {
echo "{$row->img_name}: size mismatch DB={$row->img_size}, actual={$stat['size']}\n";
continue;
}
$numGood++;
}
public function execute() {
$batchSize = 1000;
$start = '';
$dbr = wfGetDB( DB_SLAVE );
$numImages = 0;
$numGood = 0;
do {
$res = $dbr->select( 'image', '*', array( 'img_name > ' . $dbr->addQuotes( $start ) ),
__METHOD__, array( 'LIMIT' => $batchSize ) );
foreach ( $res as $row ) {
$numImages++;
$start = $row->img_name;
$file = RepoGroup::singleton()->getLocalRepo()->newFileFromRow( $row );
$path = $file->getPath();
if ( !$path ) {
$this->output( "{$row->img_name}: not locally accessible\n";
continue;
}
$stat = @stat( $file->getPath() );
if ( !$stat ) {
$this->output( "{$row->img_name}: missing\n" );
continue;
}
if ( $stat['mode'] & 040000 ) {
$this->output( "{$row->img_name}: is a directory\n" );
continue;
}
if ( $stat['size'] == 0 && $row->img_size != 0 ) {
$this->output( "{$row->img_name}: truncated, was {$row->img_size}\n" );
continue;
}
if ( $stat['size'] != $row->img_size ) {
$this->output( "{$row->img_name}: size mismatch DB={$row->img_size}, actual={$stat['size']}\n" );
continue;
}
$numGood++;
}
} while ( $res->numRows() );
$this->output( "Good images: $numGood/$numImages\n" );
}
}
} while ( $res->numRows() );
echo "Good images: $numGood/$numImages\n";

View file

@ -7,33 +7,36 @@
* @ingroup Maintenance
*/
error_reporting(E_ALL ^ E_NOTICE);
require_once 'commandLine.inc';
require_once( "Maintenance.php" );
class checkUsernames {
var $stderr, $log;
class CheckUsernames extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Verify that database usernames are actually valid";
function checkUsernames() {
$this->stderr = fopen( 'php://stderr', 'wt' );
}
function main() {
$fname = 'checkUsernames::main';
function execute() {
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'user',
array( 'user_id', 'user_name' ),
null,
__METHOD__
$fname
);
while ( $row = $dbr->fetchObject( $res ) ) {
if ( ! User::isValidUserName( $row->user_name ) ) {
$this->error( sprintf( "%s: %6d: '%s'\n", wfWikiID(), $row->user_id, $row->user_name ) );
$out = sprintf( "%s: %6d: '%s'\n", wfWikiID(), $row->user_id, $row->user_name );
fwrite( $this->stderr, $out );
wfDebugLog( 'checkUsernames', $out );
}
}
}
}
$maintClass = "CheckUsernames";
require_once( "doMaintenance.php" );
$cun = new checkUsernames();
$cun->main();

View file

@ -3,36 +3,25 @@
* This script is used to clear the interwiki links for ALL languages in
* memcached.
*
* @file
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
/** */
require_once('commandLine.inc');
class ClearInterwikiCache extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Clear all interwiki links for all languages from the cache";
}
public function execute() {
global $wgLocalDatabases;
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'interwiki', array( 'iw_prefix' ), false );
$prefixes = array();
while ( $row = $dbr->fetchObject( $res ) ) {
$prefixes[] = $row->iw_prefix;
}
foreach ( $wgLocalDatabases as $db ) {
$this->output( "$db..." );
foreach ( $prefixes as $prefix ) {
$wgMemc->delete("$db:interwiki:$prefix");
}
$this->output( "done\n" );
}
}
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'interwiki', array( 'iw_prefix' ), false );
$prefixes = array();
while ( $row = $dbr->fetchObject( $res ) ) {
$prefixes[] = $row->iw_prefix;
}
$maintClass = "ClearInterwikiCache";
require_once( DO_MAINTENANCE );
foreach ( $wgLocalDatabases as $db ) {
print "$db ";
foreach ( $prefixes as $prefix ) {
$wgMemc->delete("$db:interwiki:$prefix");
}
}
print "\n";

View file

@ -6,34 +6,33 @@
* @ingroup Maintenance
*/
require_once( 'Maintenance.php' );
require_once('commandLine.inc');
class clear_stats extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Remove all statistics tracking from memcached";
}
public function execute() {
global $wgLocalDatabases, $wgMemc;
foreach ( $wgLocalDatabases as $db ) {
$wgMemc->delete("$db:stats:request_with_session");
$wgMemc->delete("$db:stats:request_without_session");
$wgMemc->delete("$db:stats:pcache_hit");
$wgMemc->delete("$db:stats:pcache_miss_invalid");
$wgMemc->delete("$db:stats:pcache_miss_expired");
$wgMemc->delete("$db:stats:pcache_miss_absent");
$wgMemc->delete("$db:stats:pcache_miss_stub");
$wgMemc->delete("$db:stats:image_cache_hit");
$wgMemc->delete("$db:stats:image_cache_miss");
$wgMemc->delete("$db:stats:image_cache_update");
$wgMemc->delete("$db:stats:diff_cache_hit");
$wgMemc->delete("$db:stats:diff_cache_miss");
$wgMemc->delete("$db:stats:diff_uncacheable");
}
}
foreach ( $wgLocalDatabases as $db ) {
noisyDelete("$db:stats:request_with_session");
noisyDelete("$db:stats:request_without_session");
noisyDelete("$db:stats:pcache_hit");
noisyDelete("$db:stats:pcache_miss_invalid");
noisyDelete("$db:stats:pcache_miss_expired");
noisyDelete("$db:stats:pcache_miss_absent");
noisyDelete("$db:stats:pcache_miss_stub");
noisyDelete("$db:stats:image_cache_hit");
noisyDelete("$db:stats:image_cache_miss");
noisyDelete("$db:stats:image_cache_update");
noisyDelete("$db:stats:diff_cache_hit");
noisyDelete("$db:stats:diff_cache_miss");
noisyDelete("$db:stats:diff_uncacheable");
}
function noisyDelete( $key ) {
global $wgMemc;
/*
print "$key ";
if ( $wgMemc->delete($key) ) {
print "deleted\n";
} else {
print "FAILED\n";
}*/
$wgMemc->delete($key);
}
$maintClass = "clear_stats";
require_once( DO_MAINTENANCE );

View file

@ -8,53 +8,61 @@
* @author Rob Church <robchur@gmail.com>
*/
require_once( "Maintenance.php" );
$options = array( 'help', 'bureaucrat' );
require_once( 'commandLine.inc' );
class CreateAndPromote extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Create a new user account with administrator rights";
$this->addParam( "bureaucrat", "Grant the account bureaucrat rights" );
$this->addArgs( array( "username", "password" ) );
}
public function execute() {
$username = $this->getArg(0);
$password = $this->getArg(1);
$this->output( wfWikiID() . ": Creating and promoting User:{$username}..." );
$user = User::newFromName( $username );
if( !is_object( $user ) ) {
$this->error( "invalid username.\n", true );
} elseif( 0 != $user->idForName() ) {
$this->error( "account exists.\n", true );
}
# Try to set the password
try {
$user->setPassword( $password );
} catch( PasswordError $pwe ) {
$this->error( $pwe->getText(), true );
}
# Insert the account into the database
$user->addToDatabase();
$user->saveSettings();
# Promote user
$user->addGroup( 'sysop' );
if( $this->hasOption( 'bureaucrat' ) )
$user->addGroup( 'bureaucrat' );
# Increment site_stats.ss_users
$ssu = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
$ssu->doUpdate();
$this->output( "done.\n" );
}
if( isset( $options['help'] ) ) {
showHelp();
exit( 1 );
}
$maintClass = "CreateAndPromote";
require_once( DO_MAINTENANCE );
if( count( $args ) < 2 ) {
echo( "Please provide a username and password for the new account.\n" );
die( 1 );
}
$username = $args[0];
$password = $args[1];
echo( wfWikiID() . ": Creating and promoting User:{$username}..." );
# Validate username and check it doesn't exist
$user = User::newFromName( $username );
if( !is_object( $user ) ) {
echo( "invalid username.\n" );
die( 1 );
} elseif( 0 != $user->idForName() ) {
echo( "account exists.\n" );
die( 1 );
}
# Insert the account into the database
$user->addToDatabase();
$user->setPassword( $password );
$user->saveSettings();
# Promote user
$user->addGroup( 'sysop' );
if( isset( $option['bureaucrat'] ) )
$user->addGroup( 'bureaucrat' );
# Increment site_stats.ss_users
$ssu = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
$ssu->doUpdate();
echo( "done.\n" );
function showHelp() {
echo( <<<EOT
Create a new user account with administrator rights
USAGE: php createAndPromote.php [--bureaucrat|--help] <username> <password>
--bureaucrat
Grant the account bureaucrat rights
--help
Show this help information
EOT
);
}

View file

@ -1,4 +1,5 @@
<?php
/**
* Deletes a batch of pages
* Usage: php deleteBatch.php [-u <user>] [-r <reason>] [-i <interval>] [listfile]
@ -12,87 +13,86 @@
* @file
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
class DeleteBatch extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Deletes a batch of pages";
$this->addParam( 'u', "User to perform deletion", false, true );
$this->addParam( 'r', "Reason to delete page", false, true );
$this->addParam( 'i', "Interval to sleep between deletions" );
$this->addArgs( array( 'listfile' ) );
}
public function execute() {
global $wgUser;
$oldCwd = getcwd();
$optionsWithArgs = array( 'u', 'r', 'i' );
require_once( 'commandLine.inc' );
# Change to current working directory
$oldCwd = getcwd();
chdir( $oldCwd );
# Options processing
$user = $this->getOption( 'u', 'Delete page script' );
$reason = $this->getOption( 'r', '' );
$interval = $this->getOption( 'i', 0 );
if( $this->hasArg() ) {
$file = fopen( $this->getArg(), 'r' );
} else {
$file = $this->getStdin();
}
chdir( $oldCwd );
# Setup
if( !$file ) {
$this->error( "Unable to read file, exiting\n", true );
}
$wgUser = User::newFromName( $user );
$dbw = wfGetDB( DB_MASTER );
# Options processing
# Handle each entry
for ( $linenum = 1; !feof( $file ); $linenum++ ) {
$line = trim( fgets( $file ) );
if ( $line == '' ) {
continue;
}
$page = Title::newFromText( $line );
if ( is_null( $page ) ) {
$this->output( "Invalid title '$line' on line $linenum\n" );
continue;
}
if( !$page->exists() ) {
$this->output( "Skipping nonexistent page '$line'\n" );
continue;
}
$this->output( $page->getPrefixedText() );
$dbw->begin();
if( $page->getNamespace() == NS_FILE ) {
$art = new ImagePage( $page );
$img = wfFindFile( $art->mTitle );
if( !$img || !$img->delete( $reason ) ) {
$this->output( "FAILED to delete image file... " );
}
} else {
$art = new Article( $page );
}
$success = $art->doDeleteArticle( $reason );
$dbw->immediateCommit();
if ( $success ) {
$this->output( "\n" );
} else {
$this->output( " FAILED to delete article\n" );
}
if ( $interval ) {
sleep( $interval );
}
wfWaitForSlaves( 5 );
$filename = 'php://stdin';
$user = 'Delete page script';
$reason = '';
$interval = 0;
if ( isset( $args[0] ) ) {
$filename = $args[0];
}
}
if ( isset( $options['u'] ) ) {
$user = $options['u'];
}
if ( isset( $options['r'] ) ) {
$reason = $options['r'];
}
if ( isset( $options['i'] ) ) {
$interval = $options['i'];
}
$maintClass = "DeleteBatch";
require_once( DO_MAINTENANCE );
$wgUser = User::newFromName( $user );
# Setup complete, now start
$file = fopen( $filename, 'r' );
if ( !$file ) {
print "Unable to read file, exiting\n";
exit;
}
$dbw = wfGetDB( DB_MASTER );
for ( $linenum = 1; !feof( $file ); $linenum++ ) {
$line = trim( fgets( $file ) );
if ( $line == '' ) {
continue;
}
$page = Title::newFromText( $line );
if ( is_null( $page ) ) {
print "Invalid title '$line' on line $linenum\n";
continue;
}
if( !$page->exists() ) {
print "Skipping nonexistent page '$line'\n";
continue;
}
print $page->getPrefixedText();
$dbw->begin();
if( $page->getNamespace() == NS_FILE ) {
$art = new ImagePage( $page );
$img = wfFindFile( $art->mTitle );
if( !$img || !$img->delete( $reason ) ) {
print "FAILED to delete image file... ";
}
} else {
$art = new Article( $page );
}
$success = $art->doDeleteArticle( $reason );
$dbw->immediateCommit();
if ( $success ) {
print "\n";
} else {
print " FAILED to delete image page\n";
}
if ( $interval ) {
sleep( $interval );
}
wfWaitForSlaves( 5 );
}

View file

@ -1,53 +1,48 @@
<?php
/**
* Deletes all pages in the MediaWiki namespace which were last edited by
* "MediaWiki default".
*
* @file
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
if ( !defined( 'MEDIAWIKI' ) ) {
require_once( 'commandLine.inc' );
deleteDefaultMessages();
}
class DeleteDefaultMessages extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Deletes all pages in the MediaWiki namespace" .
" which were last edited by \"MediaWiki default\"";
}
function deleteDefaultMessages() {
$user = 'MediaWiki default';
$reason = 'No longer required';
public function execute() {
$user = 'MediaWiki default';
$reason = 'No longer required';
global $wgUser;
$wgUser = User::newFromName( $user );
$wgUser->addGroup( 'bot' );
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( array( 'page', 'revision' ),
array( 'page_namespace', 'page_title' ),
array(
'page_namespace' => NS_MEDIAWIKI,
'page_latest=rev_id',
'rev_user_text' => 'MediaWiki default',
)
);
$dbw = wfGetDB( DB_MASTER );
global $wgUser;
$wgUser = User::newFromName( $user );
$wgUser->addGroup( 'bot' );
while ( $row = $dbr->fetchObject( $res ) ) {
if ( function_exists( 'wfWaitForSlaves' ) ) {
wfWaitForSlaves( 5 );
}
$dbw->ping();
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
$article = new Article( $title );
$dbw->begin();
$article->doDeleteArticle( $reason );
$dbw->commit();
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( array( 'page', 'revision' ),
array( 'page_namespace', 'page_title' ),
array(
'page_namespace' => NS_MEDIAWIKI,
'page_latest=rev_id',
'rev_user_text' => 'MediaWiki default',
)
);
$dbw = wfGetDB( DB_MASTER );
while ( $row = $dbr->fetchObject( $res ) ) {
if ( function_exists( 'wfWaitForSlaves' ) ) {
wfWaitForSlaves( 5 );
}
$dbw->ping();
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
$article = new Article( $title );
$dbw->begin();
$article->doDeleteArticle( $reason );
$dbw->commit();
}
}
$maintClass = "DeleteDefaultMessages";
require_once( DO_MAINTENANCE );

View file

@ -3,27 +3,31 @@
* This script delete image information from memcached.
*
* Usage example:
* php deleteImageMemcached.php --until "2005-09-05 00:00:00" --sleep 0
* php deleteImageMemcached.php --until "2005-09-05 00:00:00" --sleep 0 --report 10
*
* @file
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
$optionsWithArgs = array( 'until', 'sleep', 'report' );
class DeleteImageCache extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Delete image information from memcached";
$this->addParam( 'sleep', 'How many seconds to sleep between deletions', true, true );
$this->addParam( 'until', 'Timestamp to delete all entries prior to', true, true );
require_once 'commandLine.inc';
/**
* @ingroup Maintenance
*/
class DeleteImageCache {
var $until, $sleep, $report;
function DeleteImageCache( $until, $sleep, $report ) {
$this->until = $until;
$this->sleep = $sleep;
$this->report = $report;
}
public function execute() {
function main() {
global $wgMemc;
$until = preg_replace( "/[^\d]/", '', $this->getOption('until') );
$sleep = (int)$this->getOption('sleep') * 1000; // milliseconds
$fname = 'DeleteImageCache::main';
ini_set( 'display_errors', false );
@ -31,8 +35,8 @@ class DeleteImageCache extends Maintenance {
$res = $dbr->select( 'image',
array( 'img_name' ),
array( "img_timestamp < {$until}" ),
__METHOD__
array( "img_timestamp < {$this->until}" ),
$fname
);
$i = 0;
@ -40,22 +44,29 @@ class DeleteImageCache extends Maintenance {
while ( $row = $dbr->fetchObject( $res ) ) {
if ($i % $this->report == 0)
$this->output( sprintf("%s: %13s done (%s)\n", wfWikiID(), "$i/$total", wfPercent( $i / $total * 100 ) ) );
printf("%s: %13s done (%s)\n", wfWikiID(), "$i/$total", wfPercent( $i / $total * 100 ));
$md5 = md5( $row->img_name );
$wgMemc->delete( wfMemcKey( 'Image', $md5 ) );
if ($sleep != 0)
usleep( $sleep );
if ($this->sleep != 0)
usleep( $this->sleep );
++$i;
}
}
private function getImageCount() {
function getImageCount() {
$fname = 'DeleteImageCache::getImageCount';
$dbr = wfGetDB( DB_SLAVE );
return $dbr->selectField( 'image', 'COUNT(*)', array(), __METHOD__ );
return $dbr->selectField( 'image', 'COUNT(*)', array(), $fname );
}
}
$maintClass = "DeleteImageCache";
require_once( DO_MAINTENANCE );
$until = preg_replace( "/[^\d]/", '', $options['until'] );
$sleep = (int)$options['sleep'] * 1000; // milliseconds
$report = (int)$options['report'];
$dic = new DeleteImageCache( $until, $sleep, $report );
$dic->main();

View file

@ -6,52 +6,42 @@
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
require_once( 'commandLine.inc' );
class DeleteRevision extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Delete one or more revisions by moving them to the archive table";
}
public function execute() {
if( count( $this->mArgs ) == 0 ) {
$this->error( "No revisions specified", true );
}
$dbw = wfGetDB( DB_MASTER );
$this->output( "Deleting revision(s) " . implode( ',', $this->mArgs ) .
" from " . wfWikiID() . "...\n" );
$dbw = wfGetDB( DB_MASTER );
$affected = 0;
foreach ( $this->mArgs as $revID ) {
$dbw->insertSelect( 'archive', array( 'page', 'revision' ),
array(
'ar_namespace' => 'page_namespace',
'ar_title' => 'page_title',
'ar_comment' => 'rev_comment',
'ar_user' => 'rev_user',
'ar_user_text' => 'rev_user_text',
'ar_timestamp' => 'rev_timestamp',
'ar_minor_edit' => 'rev_minor_edit',
'ar_rev_id' => 'rev_id',
'ar_text_id' => 'rev_text_id',
), array(
'rev_id' => $revID,
'page_id = rev_page'
), __METHOD__
);
if ( !$dbw->affectedRows() ) {
$this->output( "Revision $revID not found\n" );
} else {
$affected += $dbw->affectedRows();
$dbw->delete( 'revision', array( 'rev_id' => $revID ) );
}
}
$this->output( "Deleted $affected revisions\n" );
if ( count( $args ) == 0 ) {
echo "Usage: php deleteRevision.php <revid> [<revid> ...]\n";
exit(1);
}
echo "Deleting revision(s) " . implode( ',', $args ) . " from ".wfWikiID()."...\n";
$affected = 0;
foreach ( $args as $revID ) {
$dbw->insertSelect( 'archive', array( 'page', 'revision' ),
array(
'ar_namespace' => 'page_namespace',
'ar_title' => 'page_title',
'ar_comment' => 'rev_comment',
'ar_user' => 'rev_user',
'ar_user_text' => 'rev_user_text',
'ar_timestamp' => 'rev_timestamp',
'ar_minor_edit' => 'rev_minor_edit',
'ar_rev_id' => 'rev_id',
'ar_text_id' => 'rev_text_id',
), array(
'rev_id' => $revID,
'page_id = rev_page'
), $fname
);
if ( !$dbw->affectedRows() ) {
echo "Revision $revID not found\n";
} else {
$affected += $dbw->affectedRows();
$dbw->delete( 'revision', array( 'rev_id' => $revID ) );
}
}
$maintClass = "DeleteRevision";
require_once( DO_MAINTENANCE );
print "Deleted $affected revisions\n";

View file

@ -1,60 +0,0 @@
<?php
/**
* We want to make this whole thing as seamless as possible to the
* end-user. Unfortunately, we can't do _all_ of the work in the class
* because A) included files are not in global scope, but in the scope
* of their caller, and B) MediaWiki has way too many globals. So instead
* we'll kinda fake it, and do the requires() inline. <3 PHP
*/
if( !isset( $maintClass ) || !class_exists( $maintClass ) ) {
echo "\$maintClass is not set or is set to a non-existent class.";
die();
}
if( defined( 'MW_NO_SETUP' ) ) {
return;
}
// Get an object to start us off
$maintenance = new $maintClass();
// Basic sanity checks and such
$maintenance->setup();
# Setup the profiler
if ( file_exists( "$IP/StartProfiler.php" ) ) {
require_once( "$IP/StartProfiler.php" );
} else {
require_once( "$IP/includes/ProfilerStub.php" );
}
// Load settings, using wikimedia-mode if needed
if( file_exists( dirname(__FILE__).'/wikimedia-mode' ) ) {
# TODO FIXME! Wikimedia-specific stuff needs to go away to an ext
# Maybe a hook?
global $cluster;
$wgWikiFarm = true;
$cluster = 'pmtma';
require_once( "$IP/includes/AutoLoader.php" );
require_once( "$IP/includes/SiteConfiguration.php" );
require( "$IP/wgConf.php" );
$maintenance->loadWikimediaSettings();
require( $IP.'/includes/Defines.php' );
require( $IP.'/CommonSettings.php' );
} else {
require_once( "$IP/includes/AutoLoader.php" );
require_once( "$IP/includes/Defines.php" );
require_once( $maintenance->loadSettings() );
}
// Some last includes
require_once( "$IP/includes/Setup.php" );
require_once( "$IP/install-utils.inc" );
$wgTitle = null; # Much much faster startup than creating a title object
try {
$maintenance->execute();
} catch( MWException $mwe ) {
echo( $mwe->getText() );
}

View file

@ -16,66 +16,57 @@
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
$wgUseNormalUser = (bool)getenv('MW_WIKIUSER');
class EvalPrompt extends Maintenance {
$optionsWithArgs = array( 'd' );
public function __construct() {
parent::__construct();
$this->mDescription = "This script lets a command-line user start up the wiki engine and then poke\n" .
"about by issuing PHP commands directly.";
$this->addParam( 'd', "Enable MediaWiki debug output", false, true );
/** */
require_once( "commandLine.inc" );
if ( isset( $options['d'] ) ) {
$d = $options['d'];
if ( $d > 0 ) {
$wgDebugLogFile = '/dev/stdout';
}
public function execute() {
global $wgUseNormalUser;
$wgUseNormalUser = (bool)getenv('MW_WIKIUSER');
if ( $this->hasOption('d') ) {
$d = $this->getOption('d');
if ( $d > 0 ) {
$wgDebugLogFile = '/dev/stdout';
}
if ( $d > 1 ) {
$lb = wfGetLB();
foreach ( $lb->mServers as $i => $server ) {
$lb->mServers[$i]['flags'] |= DBO_DEBUG;
}
}
if ( $d > 2 ) {
$wgDebugFunctionEntry = true;
}
if ( $d > 1 ) {
$lb = wfGetLB();
foreach ( $lb->mServers as $i => $server ) {
$lb->mServers[$i]['flags'] |= DBO_DEBUG;
}
if ( function_exists( 'readline_add_history' )
&& function_exists( 'posix_isatty' ) && posix_isatty( 0 /*STDIN*/ ) )
{
$useReadline = true;
} else {
$useReadline = false;
}
if ( $useReadline ) {
$historyFile = "{$_ENV['HOME']}/.mweval_history";
readline_read_history( $historyFile );
}
while ( ( $line = readconsole( '> ' ) ) !== false ) {
if ( $useReadline ) {
readline_add_history( $line );
readline_write_history( $historyFile );
}
$val = eval( $line . ";" );
if( is_null( $val ) ) {
echo "\n";
} elseif( is_string( $val ) || is_numeric( $val ) ) {
echo "$val\n";
} else {
var_dump( $val );
}
}
print "\n";
}
if ( $d > 2 ) {
$wgDebugFunctionEntry = true;
}
}
$maintClass = "EvalPrompt";
require_once( DO_MAINTENANCE );
if ( function_exists( 'readline_add_history' )
&& function_exists( 'posix_isatty' ) && posix_isatty( 0 /*STDIN*/ ) )
{
$useReadline = true;
} else {
$useReadline = false;
}
if ( $useReadline ) {
$historyFile = "{$_ENV['HOME']}/.mweval_history";
readline_read_history( $historyFile );
}
while ( ( $line = readconsole( '> ' ) ) !== false ) {
if ( $useReadline ) {
readline_add_history( $line );
readline_write_history( $historyFile );
}
$val = eval( $line . ";" );
if( is_null( $val ) ) {
echo "\n";
} elseif( is_string( $val ) || is_numeric( $val ) ) {
echo "$val\n";
} else {
var_dump( $val );
}
}
print "\n";

View file

@ -6,48 +6,34 @@
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
require "commandLine.inc";
class FetchText extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Fetch the revision text from an old_id";
}
public function execute() {
$db = wfGetDB( DB_SLAVE );
$stdin = $this->getStdin();
while( !feof( $stdin ) ) {
$line = fgets( $stdin );
if( $line === false ) {
// We appear to have lost contact...
break;
}
$textId = intval( $line );
$text = $this->doGetText( $db, $textId );
$this->output( strlen( $text ) . "\n". $text );
}
}
/**
* May throw a database error if, say, the server dies during query.
* @param $db Database object
* @param $id int The old_id
* @return String
*/
private function doGetText( $db, $id ) {
$id = intval( $id );
$row = $db->selectRow( 'text',
array( 'old_text', 'old_flags' ),
array( 'old_id' => $id ),
'TextPassDumper::getText' );
$text = Revision::getRevisionText( $row );
if( $text === false ) {
return false;
}
return $text;
$db = wfGetDB( DB_SLAVE );
$stdin = fopen( "php://stdin", "rt" );
while( !feof( $stdin ) ) {
$line = fgets( $stdin );
if( $line === false ) {
// We appear to have lost contact...
break;
}
$textId = intval( $line );
$text = doGetText( $db, $textId );
echo strlen( $text ) . "\n";
echo $text;
}
$maintClass = "FetchText";
require_once( DO_MAINTENANCE );
/**
* May throw a database error if, say, the server dies during query.
*/
function doGetText( $db, $id ) {
$id = intval( $id );
$row = $db->selectRow( 'text',
array( 'old_text', 'old_flags' ),
array( 'old_id' => $id ),
'TextPassDumper::getText' );
$text = Revision::getRevisionText( $row );
if( $text === false ) {
return false;
}
return $text;
}

View file

@ -4,37 +4,26 @@
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
require 'commandLine.inc';
class GetLagTimes extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Dump replication lag times";
}
$lb = wfGetLB();
public function execute() {
$lb = wfGetLB();
if( $lb->getServerCount() == 1 ) {
$this->error( "This script dumps replication lag times, but you don't seem to have\n"
. "a multi-host db server configuration.\n" );
if( $lb->getServerCount() == 1 ) {
echo "This script dumps replication lag times, but you don't seem to have\n";
echo "a multi-host db server configuration.\n";
} else {
$lags = $lb->getLagTimes();
foreach( $lags as $n => $lag ) {
$host = $lb->getServerName( $n );
if( IP::isValid( $host ) ) {
$ip = $host;
$host = gethostbyaddr( $host );
} else {
$lags = $lb->getLagTimes();
foreach( $lags as $n => $lag ) {
$host = $lb->getServerName( $n );
if( IP::isValid( $host ) ) {
$ip = $host;
$host = gethostbyaddr( $host );
} else {
$ip = gethostbyname( $host );
}
$starLen = min( intval( $lag ), 40 );
$stars = str_repeat( '*', $starLen );
$this->output( sprintf( "%10s %20s %3d %s\n", $ip, $host, $lag, $stars ) );
}
$ip = gethostbyname( $host );
}
$starLen = min( intval( $lag ), 40 );
$stars = str_repeat( '*', $starLen );
printf( "%10s %20s %3d %s\n", $ip, $host, $lag, $stars );
}
}
$maintClass = "GetLagTimes";
require_once( DO_MAINTENANCE );

View file

@ -5,32 +5,24 @@
* @file
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
class GetSlaveServer extends Maintenance {
public function __construct() {
parent::__construct();
$this->addParam( "group", "Query group to check specifically" );
$this->mDescription = "Report the hostname of a slave server";
}
public function execute() {
global $wgAllDBsAreLocalhost;
if( $wgAllDBsAreLocalhost ) {
$host = 'localhost';
} else {
if( $this->hasOption('group') ) {
$db = wfGetDB( DB_SLAVE, $this->getOption('group') );
$host = $db->getServer();
} else {
$lb = wfGetLB();
$i = $lb->getReaderIndex();
$host = $lb->getServerName( $i );
}
}
$this->output( "$host\n" );
}
require_once( dirname(__FILE__).'/commandLine.inc' );
if ( $wgAllDBsAreLocalhost ) {
# Can't fool the backup script
print "localhost\n";
exit;
}
$maintClass = "GetSlaveServer";
require_once( DO_MAINTENANCE );
if( isset( $options['group'] ) ) {
$db = wfGetDB( DB_SLAVE, $options['group'] );
$host = $db->getServer();
} else {
$lb = wfGetLB();
$i = $lb->getReaderIndex();
$host = $lb->getServerName( $i );
}
print "$host\n";

View file

@ -9,22 +9,23 @@
* @author Rob Church <robchur@gmail.com>
* @licence GNU General Public Licence 2.0 or later
*/
$options = array( 'help', 'update', 'noviews' );
require_once( 'commandLine.inc' );
echo( "Refresh Site Statistics\n\n" );
require_once( "Maintenance.php" );
class InitStats extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Re-initialise the site statistics tables";
$this->addParam( 'update', 'Update the existing statistics (preserves the ss_total_views field)' );
$this->addParam( 'noviews', "Don't update the page view counter" );
}
public function execute() {
$this->output( "Refresh Site Statistics\n\n" );
SiteStats::init( $this->hasOption('update'), $this->hasOption('noviews') );
}
if( isset( $options['help'] ) ) {
showHelp();
exit(1);
}
require "$IP/maintenance/initStats.inc";
wfInitStats( $options );
function showHelp() {
echo( "Re-initialise the site statistics tables.\n\n" );
echo( "Usage: php initStats.php [--update|--noviews]\n\n" );
echo( " --update : Update the existing statistics (preserves the ss_total_views field)\n" );
echo( "--noviews : Don't update the page view counter\n\n" );
}
$maintClass = "InitStats";
require_once( DO_MAINTENANCE );

View file

@ -8,63 +8,58 @@
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
$optionsWithArgs = array( 'i' );
class mcTest extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Makes several 'set', 'incr' and 'get' requests on every"
. " memcached server and shows a report";
$this->addParam( 'i', 'Number of iterations', false, true );
$this->addArgs( array( 'server' ) );
}
require_once('commandLine.inc');
public function execute() {
global $wgMemCachedServers;
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
$iterations = $this->getOption( 'i', 100 );
if( $this->hasArg() )
$wgMemCachedServers = array( $this->getArg() );
foreach ( $wgMemCachedServers as $server ) {
$this->output( $server . " " );
$mcc = new MemCachedClientforWiki( array('persistant' => true) );
$mcc->set_servers( array( $server ) );
$set = 0;
$incr = 0;
$get = 0;
$time_start = $this->microtime_float();
for ( $i=1; $i<=$iterations; $i++ ) {
if ( !is_null( $mcc->set( "test$i", $i ) ) ) {
$set++;
}
}
for ( $i=1; $i<=$iterations; $i++ ) {
if ( !is_null( $mcc->incr( "test$i", $i ) ) ) {
$incr++;
}
}
for ( $i=1; $i<=$iterations; $i++ ) {
$value = $mcc->get( "test$i" );
if ( $value == $i*2 ) {
$get++;
}
}
$exectime = $this->microtime_float() - $time_start;
$this->output( "set: $set incr: $incr get: $get time: $exectime\n" );
#$wgDebugLogFile = '/dev/stdout';
if ( isset( $args[0] ) ) {
$wgMemCachedServers = array( $args[0] );
}
if ( isset( $options['i'] ) ) {
$iterations = $options['i'];
} else {
$iterations = 100;
}
foreach ( $wgMemCachedServers as $server ) {
print "$server ";
$mcc = new MemCachedClientforWiki( array('persistant' => true) );
$mcc->set_servers( array( $server ) );
$set = 0;
$incr = 0;
$get = 0;
$time_start=microtime_float();
for ( $i=1; $i<=$iterations; $i++ ) {
if ( !is_null( $mcc->set( "test$i", $i ) ) ) {
$set++;
}
}
/**
* Return microtime() as a float
* @return float
*/
private function microtime_float() {
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
for ( $i=1; $i<=$iterations; $i++ ) {
if ( !is_null( $mcc->incr( "test$i", $i ) ) ) {
$incr++;
}
}
for ( $i=1; $i<=$iterations; $i++ ) {
$value = $mcc->get( "test$i" );
if ( $value == $i*2 ) {
$get++;
}
}
$exectime=microtime_float()-$time_start;
print "set: $set incr: $incr get: $get time: $exectime\n";
}
$maintClass = "mcTest";
require_once( DO_MAINTENANCE );

View file

@ -1,7 +1,9 @@
<?php
/**
* Maintenance script to move a batch of pages
*
* @file
* @ingroup Maintenance
* @author Tim Starling
*
@ -18,77 +20,77 @@
* e.g. immobile_namespace for namespaces which can't be moved
*/
require_once( "Maintenance.php" );
$oldCwd = getcwd();
$optionsWithArgs = array( 'u', 'r', 'i' );
require_once( 'commandLine.inc' );
class MoveBatch extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Moves a batch of pages";
$this->addParam( 'u', "User to perform move", false, true );
$this->addParam( 'r', "Reason to move page", false, true );
$this->addParam( 'i', "Interval to sleep between moves" );
$this->addArgs( array( 'listfile' ) );
}
public function execute() {
global $wgUser;
chdir( $oldCwd );
# Change to current working directory
$oldCwd = getcwd();
chdir( $oldCwd );
# Options processing
# Options processing
$user = $this->getOption( 'u', 'Move page script' );
$reason = $this->getOption( 'r', '' );
$interval = $this->getOption( 'i', 0 );
if( $this->hasArg() ) {
$file = fopen( $this->getArg(), 'r' );
} else {
$file = $this->getStdin();
}
$filename = 'php://stdin';
$user = 'Move page script';
$reason = '';
$interval = 0;
# Setup
if( !$file ) {
$this->error( "Unable to read file, exiting\n", true );
}
$wgUser = User::newFromName( $user );
# Setup complete, now start
$dbw = wfGetDB( DB_MASTER );
for ( $linenum = 1; !feof( $file ); $linenum++ ) {
$line = fgets( $file );
if ( $line === false ) {
break;
}
$parts = array_map( 'trim', explode( '|', $line ) );
if ( count( $parts ) != 2 ) {
$this->error( "Error on line $linenum, no pipe character\n" );
continue;
}
$source = Title::newFromText( $parts[0] );
$dest = Title::newFromText( $parts[1] );
if ( is_null( $source ) || is_null( $dest ) ) {
$this->error( "Invalid title on line $linenum\n" );
continue;
}
$this->output( $source->getPrefixedText() . ' --> ' . $dest->getPrefixedText() );
$dbw->begin();
$err = $source->moveTo( $dest, false, $reason );
if( $err !== true ) {
$this->output( "\nFAILED: $err" );
}
$dbw->immediateCommit();
$this->output( "\n" );
if ( $interval ) {
sleep( $interval );
}
wfWaitForSlaves( 5 );
}
}
if ( isset( $args[0] ) ) {
$filename = $args[0];
}
if ( isset( $options['u'] ) ) {
$user = $options['u'];
}
if ( isset( $options['r'] ) ) {
$reason = $options['r'];
}
if ( isset( $options['i'] ) ) {
$interval = $options['i'];
}
$maintClass = "MoveBatch";
require_once( DO_MAINTENANCE );
$wgUser = User::newFromName( $user );
# Setup complete, now start
$file = fopen( $filename, 'r' );
if ( !$file ) {
print "Unable to read file, exiting\n";
exit;
}
$dbw = wfGetDB( DB_MASTER );
for ( $linenum = 1; !feof( $file ); $linenum++ ) {
$line = fgets( $file );
if ( $line === false ) {
break;
}
$parts = array_map( 'trim', explode( '|', $line ) );
if ( count( $parts ) != 2 ) {
print "Error on line $linenum, no pipe character\n";
continue;
}
$source = Title::newFromText( $parts[0] );
$dest = Title::newFromText( $parts[1] );
if ( is_null( $source ) || is_null( $dest ) ) {
print "Invalid title on line $linenum\n";
continue;
}
print $source->getPrefixedText() . ' --> ' . $dest->getPrefixedText();
$dbw->begin();
$err = $source->moveTo( $dest, false, $reason );
if( $err !== true ) {
print "\nFAILED: $err";
}
$dbw->immediateCommit();
print "\n";
if ( $interval ) {
sleep( $interval );
}
wfWaitForSlaves( 5 );
}

View file

@ -6,71 +6,55 @@
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
$options = array( 'type' );
class nextJobDB extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Pick a database that has pending jobs";
$this->addParam( 'type', "The type of job to search for", false, true );
require_once( 'commandLine.inc' );
$type = isset($options['type'])
? $options['type']
: false;
$mckey = $type === false
? "jobqueue:dbs"
: "jobqueue:dbs:$type";
$pendingDBs = $wgMemc->get( $mckey );
if ( !$pendingDBs ) {
$pendingDBs = array();
# Cross-reference DBs by master DB server
$dbsByMaster = array();
foreach ( $wgLocalDatabases as $db ) {
$lb = wfGetLB( $db );
$dbsByMaster[$lb->getServerName(0)][] = $db;
}
public function execute() {
global $wgMemc;
$type = $this->getParam( 'type', false );
$mckey = $type === false
? "jobqueue:dbs"
: "jobqueue:dbs:$type";
$pendingDBs = $wgMemcKey->get( $mckey );
# If we didn't get it from the cache
if( !$pendingDBs ) {
$pendingDBs = $this->getPendingDbs( $type );
$wgMemc->get( $mckey, $pendingDBs, 300 )
}
# If we've got a pending job in a db, display it.
if ( $pendingDBs ) {
$this->output( $pendingDBs[mt_rand(0, count( $pendingDBs ) - 1)] );
}
}
/**
* Get all databases that have a pending job
* @param $type String Job type
* @return array
*/
private function getPendingDbs( $type ) {
$pendingDBs = array();
# Cross-reference DBs by master DB server
$dbsByMaster = array();
foreach ( $wgLocalDatabases as $db ) {
$lb = wfGetLB( $db );
$dbsByMaster[$lb->getServerName(0)][] = $db;
}
foreach ( $dbsByMaster as $master => $dbs ) {
$dbConn = wfGetDB( DB_MASTER, array(), $dbs[0] );
$stype = $dbConn->addQuotes($type);
$jobTable = $dbConn->getTable( 'job' );
# Padding row for MySQL bug
$sql = "(SELECT '-------------------------------------------')";
foreach ( $dbs as $dbName ) {
if ( $sql != '' ) {
$sql .= ' UNION ';
}
if ($type === false)
$sql .= "(SELECT '$dbName' FROM `$dbName`.$jobTable LIMIT 1)";
else
$sql .= "(SELECT '$dbName' FROM `$dbName`.$jobTable WHERE job_cmd=$stype LIMIT 1)";
}
$res = $dbConn->query( $sql, __METHOD__ );
$row = $dbConn->fetchRow( $res ); // discard padding row
while ( $row = $dbConn->fetchRow( $res ) ) {
$pendingDBs[] = $row[0];
foreach ( $dbsByMaster as $master => $dbs ) {
$dbConn = wfGetDB( DB_MASTER, array(), $dbs[0] );
$stype = $dbConn->addQuotes($type);
# Padding row for MySQL bug
$sql = "(SELECT '-------------------------------------------')";
foreach ( $dbs as $dbName ) {
if ( $sql != '' ) {
$sql .= ' UNION ';
}
if ($type === false)
$sql .= "(SELECT '$dbName' FROM `$dbName`.job LIMIT 1)";
else
$sql .= "(SELECT '$dbName' FROM `$dbName`.job WHERE job_cmd=$stype LIMIT 1)";
}
$res = $dbConn->query( $sql, 'nextJobDB.php' );
$row = $dbConn->fetchRow( $res ); // discard padding row
while ( $row = $dbConn->fetchRow( $res ) ) {
$pendingDBs[] = $row[0];
}
}
$wgMemc->set( $mckey, $pendingDBs, 300 );
}
$maintClass = "nextJobDb";
require_once( DO_MAINTENANCE );
if ( $pendingDBs ) {
echo $pendingDBs[mt_rand(0, count( $pendingDBs ) - 1)];
}

View file

@ -19,80 +19,90 @@
* based on nukePage by Rob Church
*/
require_once( "Maintenance.php" );
require_once( 'commandLine.inc' );
require_once( 'nukePage.inc' );
class NukeNS extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Remove pages with only 1 revision from any namespace";
$this->addParam( 'delete', "Actually delete the page" );
$this->addParam( 'ns', 'Namespace to delete from, default NS_MEDIAWIKI', false, true );
}
$ns = NS_MEDIAWIKI;
$delete = false;
public function execute() {
$ns = $this->getOption( 'ns', NS_MEDIAWIKI );
$delete = $this->getOption( 'delete', false );
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
$tbl_pag = $dbw->tableName( 'page' );
$tbl_rev = $dbw->tableName( 'revision' );
$res = $dbw->query( "SELECT page_title FROM $tbl_pag WHERE page_namespace = $ns" );
$n_deleted = 0;
while( $row = $dbw->fetchObject( $res ) ) {
//echo "$ns_name:".$row->page_title, "\n";
$title = Title::newFromText($row->page_title, $ns);
$id = $title->getArticleID();
// Get corresponding revisions
$res2 = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_page = $id" );
$revs = array();
while( $row2 = $dbw->fetchObject( $res2 ) ) {
$revs[] = $row2->rev_id;
}
$count = count( $revs );
//skip anything that looks modified (i.e. multiple revs)
if (($count == 1)) {
#echo $title->getPrefixedText(), "\t", $count, "\n";
$this->output( "delete: ", $title->getPrefixedText(), "\n" );
//as much as I hate to cut & paste this, it's a little different, and
//I already have the id & revs
if( $delete ) {
$dbw->query( "DELETE FROM $tbl_pag WHERE page_id = $id" );
$dbw->commit();
// Delete revisions as appropriate
$child = $this->spawnChild( 'NukePage', 'NukePage.php' );
$child->deleteRevisions( $revs );
$this->purgeRedundantText( true );
$n_deleted ++;
}
} else {
$this->output( "skip: ", $title->getPrefixedText(), "\n" );
}
}
$dbw->commit();
if ($n_deleted > 0) {
#update statistics - better to decrement existing count, or just count
#the page table?
$pages = $dbw->selectField('site_stats', 'ss_total_pages');
$pages -= $n_deleted;
$dbw->update( 'site_stats',
array('ss_total_pages' => $pages ),
array( 'ss_row_id' => 1),
__METHOD__ );
}
if (!$delete) {
$this->output( "To update the database, run the script with the --delete option.\n" );
}
}
if (isset($options['ns']))
{
$ns = $options['ns'];
}
$maintClass = "NukeNS";
require_once( DO_MAINTENANCE );
if (isset( $options['delete'] ) and $options['delete'])
{
$delete = true;
}
NukeNS( $ns, $delete);
function NukeNS($ns_no, $delete) {
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
$tbl_pag = $dbw->tableName( 'page' );
$tbl_rev = $dbw->tableName( 'revision' );
$res = $dbw->query( "SELECT page_title FROM $tbl_pag WHERE page_namespace = $ns_no" );
$n_deleted = 0;
while( $row = $dbw->fetchObject( $res ) ) {
//echo "$ns_name:".$row->page_title, "\n";
$title = Title::newFromText($row->page_title, $ns_no);
$id = $title->getArticleID();
// Get corresponding revisions
$res2 = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_page = $id" );
$revs = array();
while( $row2 = $dbw->fetchObject( $res2 ) ) {
$revs[] = $row2->rev_id;
}
$count = count( $revs );
//skip anything that looks modified (i.e. multiple revs)
if (($count == 1)) {
#echo $title->getPrefixedText(), "\t", $count, "\n";
echo "delete: ", $title->getPrefixedText(), "\n";
//as much as I hate to cut & paste this, it's a little different, and
//I already have the id & revs
if( $delete ) {
$dbw->query( "DELETE FROM $tbl_pag WHERE page_id = $id" );
$dbw->commit();
// Delete revisions as appropriate
DeleteRevisions( $revs );
PurgeRedundantText( true );
$n_deleted ++;
}
} else {
echo "skip: ", $title->getPrefixedText(), "\n";
}
}
$dbw->commit();
if ($n_deleted > 0) {
#update statistics - better to decrement existing count, or just count
#the page table?
$pages = $dbw->selectField('site_stats', 'ss_total_pages');
$pages -= $n_deleted;
$dbw->update( 'site_stats',
array('ss_total_pages' => $pages ),
array( 'ss_row_id' => 1),
__METHOD__ );
}
if (!$delete) {
echo( "To update the database, run the script with the --delete option.\n" );
}
}

View file

@ -1,4 +1,5 @@
<?php
/**
* Erase a page record from the database
* Irreversible (can't use standard undelete) and does not update link tables
@ -8,92 +9,21 @@
* @author Rob Church <robchur@gmail.com>
*/
require_once( "Maintenance.php" );
require_once( 'commandLine.inc' );
require_once( 'nukePage.inc' );
class NukePage extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Remove a page record from the database";
$this->addParam( 'delete', "Actually delete the page" );
$this->addArgs( array( 'title' ) );
}
echo( "Erase Page Record\n\n" );
public function execute() {
$name = $this->getArg();
$delete = $this->getOption( 'delete', false );
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
$tbl_pag = $dbw->tableName( 'page' );
$tbl_rec = $dbw->tableName( 'recentchanges' );
$tbl_rev = $dbw->tableName( 'revision' );
# Get page ID
$this->output( "Searching for \"$name\"..." );
$title = Title::newFromText( $name );
if( $title ) {
$id = $title->getArticleID();
$real = $title->getPrefixedText();
$isGoodArticle = $title->isContentPage();
$this->output( "found \"$real\" with ID $id.\n" );
# Get corresponding revisions
$this->output( "Searching for revisions..." );
$res = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_page = $id" );
while( $row = $dbw->fetchObject( $res ) ) {
$revs[] = $row->rev_id;
}
$count = count( $revs );
$this->output( "found $count.\n" );
# Delete the page record and associated recent changes entries
if( $delete ) {
$this->output( "Deleting page record..." );
$dbw->query( "DELETE FROM $tbl_pag WHERE page_id = $id" );
$this->output( "done.\n" );
$this->output( "Cleaning up recent changes..." );
$dbw->query( "DELETE FROM $tbl_rec WHERE rc_cur_id = $id" );
$this->output( "done.\n" );
}
$dbw->commit();
# Delete revisions as appropriate
if( $delete && $count ) {
$this->output( "Deleting revisions..." );
$this->deleteRevisions( $revs );
$this->output( "done.\n" );
$this->purgeRedundantText( true );
}
# Update stats as appropriate
if ( $delete ) {
$this->output( "Updating site stats..." );
$ga = $isGoodArticle ? -1 : 0; // if it was good, decrement that too
$stats = new SiteStatsUpdate( 0, -$count, $ga, -1 );
$stats->doUpdate();
$this->output( "done.\n" );
}
} else {
$this->output( "not found in database.\n" );
$dbw->commit();
}
}
public function deleteRevisions( $ids ) {
$dbw = wfGetDB( DB_MASTER );
$dbw->begin();
$tbl_rev = $dbw->tableName( 'revision' );
$set = implode( ', ', $ids );
$dbw->query( "DELETE FROM $tbl_rev WHERE rev_id IN ( $set )" );
$dbw->commit();
}
if( isset( $args[0] ) ) {
NukePage( $args[0], true );
} else {
ShowUsage();
}
/** Show script usage information */
function ShowUsage() {
echo( "Remove a page record from the database.\n\n" );
echo( "Usage: php nukePage.php <title>\n\n" );
echo( " <title> : Page title; spaces escaped with underscores\n\n" );
}
$maintClass = "NukePage";
require_once( DO_MAINTENANCE );

View file

@ -9,88 +9,13 @@
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
class PopulateLogSearch extends Maintenance {
const LOG_SEARCH_BATCH_SIZE = 100;
public function __construct() {
parent::__construct();
$this->mDescription = "Migrate log params to new table and index for searching";
}
public function execute() {
$db = wfGetDB( DB_MASTER );
if ( !$db->tableExists( 'log_search' ) ) {
$this->error( "log_search does not exist\n", true );
}
$start = $db->selectField( 'logging', 'MIN(log_id)', false, __FUNCTION__ );
if( !$start ) {
$this->output( "Nothing to do.\n" );
return true;
}
$end = $db->selectField( 'logging', 'MAX(log_id)', false, __FUNCTION__ );
require_once 'commandLine.inc';
require_once 'populateLogSearch.inc';
# Do remaining chunk
$end += self::LOG_SEARCH_BATCH_SIZE - 1;
$blockStart = $start;
$blockEnd = $start + self::LOG_SEARCH_BATCH_SIZE - 1;
while( $blockEnd <= $end ) {
$this->output( "...doing log_id from $blockStart to $blockEnd\n" );
$cond = "log_id BETWEEN $blockStart AND $blockEnd";
$res = $db->select( 'logging', '*', $cond, __FUNCTION__ );
$batch = array();
while( $row = $db->fetchObject( $res ) ) {
// RevisionDelete logs - revisions
if( LogEventsList::typeAction( $row, array('delete','suppress'), 'revision' ) ) {
$params = LogPage::extractParams( $row->log_params );
// Param format: <urlparam> <item CSV> [<ofield> <nfield>]
if( count($params) >= 2 ) {
$field = RevisionDeleter::getRelationType($params[0]);
// B/C, the params may start with a title key
if( $field == null ) {
array_shift($params);
$field = RevisionDeleter::getRelationType($params[0]);
}
if( $field == null ) {
$this->output( "Invalid param type for $row->log_id\n" );
continue; // skip this row
}
$items = explode(',',$params[1]);
$log = new LogPage( $row->log_type );
$log->addRelations( $field, $items, $row->log_id );
}
// RevisionDelete logs - log events
} else if( LogEventsList::typeAction( $row, array('delete','suppress'), 'event' ) ) {
$params = LogPage::extractParams( $row->log_params );
// Param format: <item CSV> [<ofield> <nfield>]
if( count($params) >= 1 ) {
$items = explode(',',$params[0]);
$log = new LogPage( $row->log_type );
$log->addRelations( 'log_id', $items, $row->log_id );
}
}
}
$blockStart += self::LOG_SEARCH_BATCH_SIZE;
$blockEnd += self::LOG_SEARCH_BATCH_SIZE;
wfWaitForSlaves( 5 );
}
if( $db->insert(
'updatelog',
array( 'ul_key' => 'populate log_search' ),
__FUNCTION__,
'IGNORE'
)
) {
$this->output( "log_search population complete.\n" );
return true;
} else {
$this->output( "Could not insert log_search population row.\n" );
return false;
}
}
$db =& wfGetDB( DB_MASTER );
if ( !$db->tableExists( 'log_search' ) ) {
echo "log_search does not exist\n";
exit( 1 );
}
$maintClass = "PopulateLogSearch";
require_once( DO_MAINTENANCE );
migrate_log_params( $db );

View file

@ -1,105 +1,18 @@
<?php
/*
* Makes the required database updates for rev_parent_id
* to be of any use. It can be used for some simple tracking
* and to find new page edits by users.
*/
require_once( "Maintenance.php" );
class PopulateParentId extends Maintenance {
// Batch size
const BATCH_SIZE = 200;
public function __construct() {
parent::__construct();
$this->mDescription = "Populates rev_parent_id";
}
public function execute() {
$db = wfGetDB( DB_MASTER );
if ( !$db->tableExists( 'revision' ) ) {
$this->error( "revision table does not exist\n", true );
}
$this->output( "Populating rev_parent_id column\n" );
$start = $db->selectField( 'revision', 'MIN(rev_id)', false, __FUNCTION__ );
$end = $db->selectField( 'revision', 'MAX(rev_id)', false, __FUNCTION__ );
if( is_null( $start ) || is_null( $end ) ){
$this->output( "...revision table seems to be empty.\n" );
$db->insert( 'updatelog',
array( 'ul_key' => 'populate rev_parent_id' ),
__FUNCTION__,
'IGNORE' );
return;
}
# Do remaining chunk
$end += self::BATCH_SIZE - 1;
$blockStart = intval( $start );
$blockEnd = intval( $start ) + self::BATCH_SIZE - 1;
$count = 0;
$changed = 0;
while( $blockEnd <= $end ) {
$this->output( "...doing rev_id from $blockStart to $blockEnd\n" );
$cond = "rev_id BETWEEN $blockStart AND $blockEnd";
$res = $db->select( 'revision',
array('rev_id','rev_page','rev_timestamp','rev_parent_id'),
$cond, __FUNCTION__ );
# Go through and update rev_parent_id from these rows.
# Assume that the previous revision of the title was
# the original previous revision of the title when the
# edit was made...
foreach( $res as $row ) {
# First, check rows with the same timestamp other than this one
# with a smaller rev ID. The highest ID "wins". This avoids loops
# as timestamp can only decrease and never loops with IDs (from parent to parent)
$previousID = $db->selectField( 'revision', 'rev_id',
array( 'rev_page' => $row->rev_page, 'rev_timestamp' => $row->rev_timestamp,
"rev_id < " . intval( $row->rev_id ) ),
__FUNCTION__,
array( 'ORDER BY' => 'rev_id DESC' ) );
# If there are none, check the the highest ID with a lower timestamp
if( !$previousID ) {
# Get the highest older timestamp
$lastTimestamp = $db->selectField( 'revision', 'rev_timestamp',
array( 'rev_page' => $row->rev_page, "rev_timestamp < " . $db->addQuotes( $row->rev_timestamp ) ),
__FUNCTION__,
array( 'ORDER BY' => 'rev_timestamp DESC' ) );
# If there is one, let the highest rev ID win
if( $lastTimestamp ) {
$previousID = $db->selectField( 'revision', 'rev_id',
array( 'rev_page' => $row->rev_page, 'rev_timestamp' => $lastTimestamp ),
__FUNCTION__,
array( 'ORDER BY' => 'rev_id DESC' ) );
}
}
$previousID = intval($previousID);
if( $previousID != $row->rev_parent_id )
$changed++;
# Update the row...
$db->update( 'revision',
array( 'rev_parent_id' => $previousID ),
array( 'rev_id' => $row->rev_id ),
__FUNCTION__ );
$count++;
}
$blockStart += self::BATCH_SIZE - 1;
$blockEnd += self::BATCH_SIZE - 1;
wfWaitForSlaves( 5 );
}
$logged = $db->insert( 'updatelog',
array( 'ul_key' => 'populate rev_parent_id' ),
__FUNCTION__,
'IGNORE' );
if( $logged ) {
$this->output( "rev_parent_id population complete ... {$count} rows [{$changed} changed]\n" );
return true;
} else {
$this->output( "Could not insert rev_parent_id population row.\n" );
return false;
}
}
require_once 'commandLine.inc';
require_once 'populateParentId.inc';
$db =& wfGetDB( DB_MASTER );
if ( !$db->tableExists( 'revision' ) ) {
echo "revision table does not exist\n";
exit( 1 );
}
$maintClass = "PopulateParentId";
require_once( DO_MAINTENANCE );
populate_rev_parent_id( $db );

View file

@ -1,24 +1,29 @@
<?php
/**
* Purge old text records from the database
*
* @file
* @ingroup Maintenance
* @author Rob Church <robchur@gmail.com>
*/
require_once( "Maintenance.php" );
$options = array( 'purge', 'help' );
require_once( 'commandLine.inc' );
require_once( 'purgeOldText.inc' );
class PurgeOldText extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Purge old text records from the database";
$this->addOption( 'purge', 'Performs the deletion' );
}
public function execute() {
$this->purgeRedundantText( $this->hasOption('purge') );
}
echo( "Purge Old Text\n\n" );
if( @$options['help'] ) {
ShowUsage();
} else {
PurgeRedundantText( @$options['purge'] );
}
function ShowUsage() {
echo( "Prunes unused text records from the database.\n\n" );
echo( "Usage: php purgeOldText.php [--purge]\n\n" );
echo( "purge : Performs the deletion\n" );
echo( " help : Show this usage information\n" );
}
$maintClass = "PurgeOldText";
require_once( DO_MAINTENANCE );

View file

@ -1,153 +1,56 @@
<?php
/**
* Reassign edits from a user or IP address to another user
*
* @file
* @ingroup Maintenance
* @author Rob Church <robchur@gmail.com>
* @licence GNU General Public Licence 2.0 or later
*/
require_once( "Maintenance.php" );
$options = array( 'force', 'norc', 'quiet', 'report' );
require_once( 'commandLine.inc' );
require_once( 'reassignEdits.inc' );
class ReassignEdits extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Reassign edits from one user to another";
$this->addParam( "force", "Reassign even if the target user doesn't exist" );
$this->addParam( "norc", "Don't update the recent changes table" );
$this->addParam( "report", "Print out details of what would be changed, but don't update it" );
$this->addArgs( array( 'from', 'to' ) );
# Set silent mode; --report overrides --quiet
if( !@$options['report'] && @$options['quiet'] )
setSilent();
out( "Reassign Edits\n\n" );
if( @$args[0] && @$args[1] ) {
# Set up the users involved
$from =& initialiseUser( $args[0] );
$to =& initialiseUser( $args[1] );
# If the target doesn't exist, and --force is not set, stop here
if( $to->getId() || @$options['force'] ) {
# Reassign the edits
$report = @$options['report'];
$count = reassignEdits( $from, $to, !@$options['norc'], $report );
# If reporting, and there were items, advise the user to run without --report
if( $report )
out( "Run the script again without --report to update.\n" );
} else {
$ton = $to->getName();
echo( "User '{$ton}' not found.\n" );
}
public function execute() {
if( $this->hasArg(0) && $this->hasArg(1) ) {
# Set up the users involved
$from =& $this->initialiseUser( $this->getArg(0) );
$to =& $this->initialiseUser( $this->getArg(1) );
# If the target doesn't exist, and --force is not set, stop here
if( $to->getId() || $this->hasOption('force') ) {
# Reassign the edits
$report = $this->hasOption('report');
$count = $this->reassignEdits( $from, $to, !$this->hasOption('norc'), $report );
# If reporting, and there were items, advise the user to run without --report
if( $report )
$this->output( "Run the script again without --report to update.\n" );
} else {
$ton = $to->getName();
$this->error( "User '{$ton}' not found.\n" );
}
}
}
/**
* Reassign edits from one user to another
*
* @param $from User to take edits from
* @param $to User to assign edits to
* @param $rc Update the recent changes table
* @param $report Don't change things; just echo numbers
* @return integer Number of entries changed, or that would be changed
*/
private function reassignEdits( &$from, &$to, $rc = false, $report = false ) {
$dbw = wfGetDB( DB_MASTER );
$dbw->immediateBegin();
# Count things
$this->output( "Checking current edits..." );
$res = $dbw->select( 'revision', 'COUNT(*) AS count', $this->userConditions( $from, 'rev_user', 'rev_user_text' ), __METHOD__ );
$row = $dbw->fetchObject( $res );
$cur = $row->count;
$this->output( "found {$cur}.\n" );
$this->output( "Checking deleted edits..." );
$res = $dbw->select( 'archive', 'COUNT(*) AS count', $this->userConditions( $from, 'ar_user', 'ar_user_text' ), __METHOD__ );
$row = $dbw->fetchObject( $res );
$del = $row->count;
$this->output( "found {$del}.\n" );
# Don't count recent changes if we're not supposed to
if( $rc ) {
$this->output( "Checking recent changes..." );
$res = $dbw->select( 'recentchanges', 'COUNT(*) AS count', $this->userConditions( $from, 'rc_user', 'rc_user_text' ), __METHOD__ );
$row = $dbw->fetchObject( $res );
$rec = $row->count;
$this->output( "found {$rec}.\n" );
} else {
$rec = 0;
}
$total = $cur + $del + $rec;
$this->output( "\nTotal entries to change: {$total}\n" );
if( !$report ) {
if( $total ) {
# Reassign edits
$this->output( "\nReassigning current edits..." );
$res = $dbw->update( 'revision', userSpecification( $to, 'rev_user', 'rev_user_text' ), $this->userConditions( $from, 'rev_user', 'rev_user_text' ), __METHOD__ );
$this->output( "done.\nReassigning deleted edits..." );
$res = $dbw->update( 'archive', userSpecification( $to, 'ar_user', 'ar_user_text' ), $this->userConditions( $from, 'ar_user', 'ar_user_text' ), __METHOD__ );
$this->output( "done.\n" );
# Update recent changes if required
if( $rc ) {
$this->output( "Updating recent changes..." );
$res = $dbw->update( 'recentchanges', $this->userSpecification( $to, 'rc_user', 'rc_user_text' ), $this->userConditions( $from, 'rc_user', 'rc_user_text' ), __METHOD__ );
$this->output( "done.\n" );
}
}
}
$dbw->immediateCommit();
return (int)$total;
}
/**
* Return the most efficient set of user conditions
* i.e. a user => id mapping, or a user_text => text mapping
*
* @param $user User for the condition
* @param $idfield Field name containing the identifier
* @param $utfield Field name containing the user text
* @return array
*/
private function userConditions( &$user, $idfield, $utfield ) {
return $user->getId() ? array( $idfield => $user->getId() ) : array( $utfield => $user->getName() );
}
/**
* Return user specifications
* i.e. user => id, user_text => text
*
* @param $user User for the spec
* @param $idfield Field name containing the identifier
* @param $utfield Field name containing the user text
* @return array
*/
private function userSpecification( &$user, $idfield, $utfield ) {
return array( $idfield => $user->getId(), $utfield => $user->getName() );
}
/**
* Initialise the user object
*
* @param $username Username or IP address
* @return User
*/
private function initialiseUser( $username ) {
if( User::isIP( $username ) ) {
$user = new User();
$user->setId( 0 );
$user->setName( $username );
} else {
$user = User::newFromName( $username );
}
$user->load();
return $user;
}
} else {
ShowUsage();
}
$maintClass = "ReassignEdits";
require_once( DO_MAINTENANCE );
/** Show script usage information */
function ShowUsage() {
echo( "Reassign edits from one user to another.\n\n" );
echo( "Usage: php reassignEdits.php [--force|--quiet|--norc|--report] <from> <to>\n\n" );
echo( " <from> : Name of the user to assign edits from\n" );
echo( " <to> : Name of the user to assign edits to\n" );
echo( " --force : Reassign even if the target user doesn't exist\n" );
echo( " --quiet : Don't print status information (except for errors)\n" );
echo( " --norc : Don't update the recent changes table\n" );
echo( " --report : Print out details of what would be changed, but don't update it\n\n" );
}

View file

@ -6,96 +6,87 @@
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
class RebuildFileCache extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Build file cache for content pages";
$this->addArgs( array( 'start', 'overwrite' ) );
}
public function execute() {
global $wgUseFileCache, $wgDisableCounters, $wgTitle, $wgArticle, $wgOut;
if( !$wgUseFileCache ) {
$this->error( "Nothing to do -- \$wgUseFileCache is disabled.\n", true );
}
$wgDisableCounters = false;
$start = intval( $this->getArg( 0, 0 ) );
$overwrite = $this->hasArg(1) && $this->getArg(1) === 'overwrite';
$this->output( "Building content page file cache from page {$start}!\n" );
$dbr = wfGetDB( DB_SLAVE );
$start = $start > 0 ? $start : $dbr->selectField( 'page', 'MIN(page_id)', false, __FUNCTION__ );
$end = $dbr->selectField( 'page', 'MAX(page_id)', false, __FUNCTION__ );
if( !$start ) {
$this->error( "Nothing to do.\n", true );
}
$_SERVER['HTTP_ACCEPT_ENCODING'] = 'bgzip'; // hack, no real client
OutputPage::setEncodings(); # Not really used yet
$BATCH_SIZE = 100;
# Do remaining chunk
$end += $BATCH_SIZE - 1;
$blockStart = $start;
$blockEnd = $start + $BATCH_SIZE - 1;
$dbw = wfGetDB( DB_MASTER );
// Go through each page and save the output
while( $blockEnd <= $end ) {
// Get the pages
$res = $dbr->select( 'page', array('page_namespace','page_title','page_id'),
array('page_namespace' => $wgContentNamespaces,
"page_id BETWEEN $blockStart AND $blockEnd" ),
array('ORDER BY' => 'page_id ASC','USE INDEX' => 'PRIMARY')
);
while( $row = $dbr->fetchObject( $res ) ) {
$rebuilt = false;
$wgTitle = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
if( null == $wgTitle ) {
$this->output( "Page {$row->page_id} bad title\n" );
continue; // broken title?
}
$wgArticle = new Article( $wgTitle );
// If the article is cacheable, then load it
if( $wgArticle->isFileCacheable() ) {
$cache = new HTMLFileCache( $wgTitle );
if( $cache->isFileCacheGood() ) {
if( $overwrite ) {
$rebuilt = true;
} else {
$this->output( "Page {$row->page_id} already cached\n" );
continue; // done already!
}
}
ob_start( array(&$cache, 'saveToFileCache' ) ); // save on ob_end_clean()
$wgUseFileCache = false; // hack, we don't want $wgArticle fiddling with filecache
$wgArticle->view();
@$wgOut->output(); // header notices
$wgUseFileCache = true;
ob_end_clean(); // clear buffer
$wgOut = new OutputPage(); // empty out any output page garbage
if( $rebuilt )
$this->output( "Re-cached page {$row->page_id}\n" );
else
$this->output( "Cached page {$row->page_id}\n" );
} else {
$this->output( "Page {$row->page_id} not cacheable\n" );
}
$dbw->commit(); // commit any changes
}
$blockStart += $BATCH_SIZE;
$blockEnd += $BATCH_SIZE;
wfWaitForSlaves( 5 );
}
$this->output( "Done!\n" );
// Remove these to be safe
if( isset($wgTitle) )
unset($wgTitle);
if( isset($wgArticle) )
unset($wgArticle);
}
}
/** */
require_once( "commandLine.inc" );
if( !$wgUseFileCache ) {
echo "Nothing to do -- \$wgUseFileCache is disabled.\n";
exit(0);
}
$wgDisableCounters = false; // no real hits here
$start = isset($args[0]) ? intval($args[0]) : 0;
$overwrite = isset( $args[1] ) && $args[1] === 'overwrite';
echo "Building content page file cache from page {$start}!\n";
echo "Format: <start> [overwrite]\n";
$dbr = wfGetDB( DB_SLAVE );
$start = $start > 0 ? $start : $dbr->selectField( 'page', 'MIN(page_id)', false, __FUNCTION__ );
$end = $dbr->selectField( 'page', 'MAX(page_id)', false, __FUNCTION__ );
if( !$start ) {
die("Nothing to do.\n");
}
$_SERVER['HTTP_ACCEPT_ENCODING'] = 'bgzip'; // hack, no real client
OutputPage::setEncodings(); # Not really used yet
$BATCH_SIZE = 100;
# Do remaining chunk
$end += $BATCH_SIZE - 1;
$blockStart = $start;
$blockEnd = $start + $BATCH_SIZE - 1;
$dbw = wfGetDB( DB_MASTER );
// Go through each page and save the output
while( $blockEnd <= $end ) {
// Get the pages
$res = $dbr->select( 'page', array('page_namespace','page_title','page_id'),
array('page_namespace' => $wgContentNamespaces,
"page_id BETWEEN $blockStart AND $blockEnd" ),
array('ORDER BY' => 'page_id ASC','USE INDEX' => 'PRIMARY')
);
while( $row = $dbr->fetchObject( $res ) ) {
$rebuilt = false;
$wgTitle = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
if( null == $wgTitle ) {
echo "Page {$row->page_id} bad title\n";
continue; // broken title?
}
$wgArticle = new Article( $wgTitle );
// If the article is cacheable, then load it
if( $wgArticle->isFileCacheable() ) {
$cache = new HTMLFileCache( $wgTitle );
if( $cache->isFileCacheGood() ) {
if( $overwrite ) {
$rebuilt = true;
} else {
echo "Page {$row->page_id} already cached\n";
continue; // done already!
}
}
ob_start( array(&$cache, 'saveToFileCache' ) ); // save on ob_end_clean()
$wgUseFileCache = false; // hack, we don't want $wgArticle fiddling with filecache
$wgArticle->view();
@$wgOut->output(); // header notices
$wgUseFileCache = true;
ob_end_clean(); // clear buffer
$wgOut = new OutputPage(); // empty out any output page garbage
if( $rebuilt )
echo "Re-cached page {$row->page_id}\n";
else
echo "Cached page {$row->page_id}\n";
} else {
echo "Page {$row->page_id} not cacheable\n";
}
$dbw->commit(); // commit any changes
}
$blockStart += $BATCH_SIZE;
$blockEnd += $BATCH_SIZE;
wfWaitForSlaves( 5 );
}
echo "Done!\n";
// Remove these to be safe
if( isset($wgTitle) )
unset($wgTitle);
if( isset($wgArticle) )
unset($wgArticle);

View file

@ -7,34 +7,23 @@
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
require_once( "commandLine.inc" );
class RefreshImageCount extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Resets ss_image count, forcing slaves to pick it up.";
}
public function execute() {
$dbw = wfGetDB( DB_MASTER );
$dbw = wfGetDB( DB_MASTER );
// Load the current value from the master
$count = $dbw->selectField( 'site_stats', 'ss_images' );
// Load the current value from the master
$count = $dbw->selectField( 'site_stats', 'ss_images' );
$this->output( wfWikiID() . ": forcing ss_images to $count\n" );
echo wfWikiID().": forcing ss_images to $count\n";
// First set to NULL so that it changes on the master
$dbw->update( 'site_stats',
array( 'ss_images' => null ),
array( 'ss_row_id' => 1 ) );
// Now this update will be forced to go out
$dbw->update( 'site_stats',
array( 'ss_images' => $count ),
array( 'ss_row_id' => 1 ) );
}
}
// First set to NULL so that it changes on the master
$dbw->update( 'site_stats',
array( 'ss_images' => null ),
array( 'ss_row_id' => 1 ) );
// Now this update will be forced to go out
$dbw->update( 'site_stats',
array( 'ss_images' => $count ),
array( 'ss_row_id' => 1 ) );
$maintClass = "RefreshImageCount";
require_once( DO_MAINTENANCE );

View file

@ -8,89 +8,63 @@
* @author Rob Church <robchur@gmail.com>
*/
require_once( "Maintenance.php" );
$options = array( 'help', 'delete' );
require_once( 'commandLine.inc' );
require_once( 'removeUnusedAccounts.inc' );
echo( "Remove Unused Accounts\n\n" );
$fname = 'removeUnusedAccounts';
class RemoveUnusedAccounts extends Maintenance {
public function __construct() {
parent::__construct();
$this->addParam( 'delete', 'Actually delete the account' );
$this->addParam( 'ignore-groups', 'List of comma-separated groups to exclude', false, true );
$this->addParam( 'ignore-touched', 'Skip accounts touched in last N days', false, true );
}
public function execute() {
$this->output( "Remove unused accounts\n\n" );
# Do an initial scan for inactive accounts and report the result
$this->output( "Checking for unused user accounts...\n" );
$del = array();
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'user', array( 'user_id', 'user_name', 'user_touched' ), '', __METHOD__ );
if( $this->hasOption('ignore-groups') ) {
$excludedGroups = explode( ',', $this->getOption('ignore-groups') );
} else {
$excludedGroups = array();
}
$touched = $this->getOption( 'ignore-touched', "1" );
if( !ctype_digit( $touched ) ) {
$this->error( "Please put a valid positive integer on the --ignore-touched parameter.\n", true );
}
$touchedSeconds = 86400 * $touched;
while( $row = $dbr->fetchObject( $res ) ) {
# Check the account, but ignore it if it's within a $excludedGroups group or if it's touched within the $touchedSeconds seconds.
$instance = User::newFromId( $row->user_id );
if( count( array_intersect( $instance->getEffectiveGroups(), $excludedGroups ) ) == 0
&& $this->isInactiveAccount( $row->user_id, true )
&& wfTimestamp( TS_UNIX, $row->user_touched ) < wfTimestamp( TS_UNIX, time() - $touchedSeconds )
) {
# Inactive; print out the name and flag it
$del[] = $row->user_id;
$this->output( $row->user_name . "\n" );
}
}
$count = count( $del );
$this->output( "...found {$count}.\n" );
# If required, go back and delete each marked account
if( $count > 0 && $this->hasOption('delete') ) {
$this->output( "\nDeleting inactive accounts..." );
$dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'user', array( 'user_id' => $del ), __METHOD__ );
$this->output( "done.\n" );
# Update the site_stats.ss_users field
$users = $dbw->selectField( 'user', 'COUNT(*)', array(), __METHOD__ );
$dbw->update( 'site_stats', array( 'ss_users' => $users ), array( 'ss_row_id' => 1 ), __METHOD__ );
} elseif( $count > 0 ) {
$this->output( "\nRun the script again with --delete to remove them from the database.\n" );
}
$this->output( "\n" );
}
/**
* Could the specified user account be deemed inactive?
* (No edits, no deleted edits, no log entries, no current/old uploads)
*
* @param $id User's ID
* @param $master Perform checking on the master
* @return bool
*/
private function isInactiveAccount( $id, $master = false ) {
$dbo = wfGetDB( $master ? DB_MASTER : DB_SLAVE );
$checks = array( 'revision' => 'rev', 'archive' => 'ar', 'logging' => 'log',
'image' => 'img', 'oldimage' => 'oi' );
$count = 0;
$dbo->immediateBegin();
foreach( $checks as $table => $fprefix ) {
$conds = array( $fprefix . '_user' => $id );
$count += (int)$dbo->selectField( $table, 'COUNT(*)', $conds, __METHOD__ );
}
$dbo->immediateCommit();
return $count == 0;
}
if( isset( $options['help'] ) ) {
showHelp();
exit(1);
}
$maintClass = "RemoveUnusedAccounts";
require_once( DO_MAINTENANCE );
# Do an initial scan for inactive accounts and report the result
echo( "Checking for unused user accounts...\n" );
$del = array();
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'user', array( 'user_id', 'user_name', 'user_touched' ), '', $fname );
if( isset( $options['ignore-groups'] ) ) {
$excludedGroups = explode( ',', $options['ignore-groups'] );
} else { $excludedGroups = array(); }
$touchedSeconds = 0;
if( isset( $options['ignore-touched'] ) ) {
$touchedParamError = 0;
if( ctype_digit( $options['ignore-touched'] ) ) {
if( $options['ignore-touched'] <= 0 ) {
$touchedParamError = 1;
}
} else { $touchedParamError = 1; }
if( $touchedParamError == 1 ) {
die( "Please put a valid positive integer on the --ignore-touched parameter.\n" );
} else { $touchedSeconds = 86400 * $options['ignore-touched']; }
}
while( $row = $dbr->fetchObject( $res ) ) {
# Check the account, but ignore it if it's within a $excludedGroups group or if it's touched within the $touchedSeconds seconds.
$instance = User::newFromId( $row->user_id );
if( count( array_intersect( $instance->getEffectiveGroups(), $excludedGroups ) ) == 0
&& isInactiveAccount( $row->user_id, true )
&& wfTimestamp( TS_UNIX, $row->user_touched ) < wfTimestamp( TS_UNIX, time() - $touchedSeconds )
) {
# Inactive; print out the name and flag it
$del[] = $row->user_id;
echo( $row->user_name . "\n" );
}
}
$count = count( $del );
echo( "...found {$count}.\n" );
# If required, go back and delete each marked account
if( $count > 0 && isset( $options['delete'] ) ) {
echo( "\nDeleting inactive accounts..." );
$dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'user', array( 'user_id' => $del ), $fname );
echo( "done.\n" );
# Update the site_stats.ss_users field
$users = $dbw->selectField( 'user', 'COUNT(*)', array(), $fname );
$dbw->update( 'site_stats', array( 'ss_users' => $users ), array( 'ss_row_id' => 1 ), $fname );
} else {
if( $count > 0 )
echo( "\nRun the script again with --delete to remove them from the database.\n" );
}
echo( "\n" );

View file

@ -6,59 +6,62 @@
* @file
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
$optionsWithArgs = array( 'old', 'new', 'help' );
class RenameDbPrefix extends Maintenance {
public function __construct() {
parent::__construct();
$this->addParam( "old", "Old db prefix [0 for none]", true, true );
$this->addParam( "new", "New db prefix [0 for none]", true, true );
}
public function execute() {
// Allow for no old prefix
if( $this->getOption( 'old', 0 ) === '0' ) {
$old = '';
} else {
// Use nice safe, sane, prefixes
preg_match( '/^[a-zA-Z]+_$/', $this->getOption('old'), $m );
$old = isset( $m[0] ) ? $m[0] : false;
}
// Allow for no new prefix
if( $this->getOption( 'new', 0 ) === '0' ) {
$new = '';
} else {
// Use nice safe, sane, prefixes
preg_match( '/^[a-zA-Z]+_$/', $this->getOption('new'), $m );
$new = isset( $m[0] ) ? $m[0] : false;
}
if( $old === false || $new === false ) {
$this->error( "Invalid prefix!\n", true );
}
if( $old === $new ) {
$this->( "Same prefix. Nothing to rename!\n", true );
}
$this->output( "Renaming DB prefix for tables of $wgDBname from '$old' to '$new'\n" );
$count = 0;
$dbw = wfGetDB( DB_MASTER );
$res = $dbw->query( "SHOW TABLES LIKE '".$dbw->escapeLike( $old )."%'" );
foreach( $res as $row ) {
// XXX: odd syntax. MySQL outputs an oddly cased "Tables of X"
// sort of message. Best not to try $row->x stuff...
$fields = get_object_vars( $row );
// Silly for loop over one field...
foreach( $fields as $resName => $table ) {
// $old should be regexp safe ([a-zA-Z_])
$newTable = preg_replace( '/^'.$old.'/', $new, $table );
$this->output( "Renaming table $table to $newTable\n" );
$dbw->query( "RENAME TABLE $table TO $newTable" );
}
$count++;
}
$this->output( "Done! [$count tables]\n" );
}
require_once( 'commandLine.inc' );
if( @$options['help'] || !isset( $options['old'] ) || !isset( $options['new'] ) ) {
print "usage: renameDbPrefix.php [--help] [--old x] [new y]\n";
print " --help : this help message\n";
print " --old x : old db prefix x\n";
print " --old 0 : EMPTY old db prefix x\n";
print " --new y : new db prefix y\n";
print " --new 0 : EMPTY new db prefix\n";
wfDie();
}
// Allow for no old prefix
if( $options['old'] === '0' ) {
$old = '';
} else {
// Use nice safe, sane, prefixes
preg_match( '/^[a-zA-Z]+_$/', $options['old'], $m );
$old = isset( $m[0] ) ? $m[0] : false;
}
// Allow for no new prefix
if( $options['new'] === '0' ) {
$new = '';
} else {
// Use nice safe, sane, prefixes
preg_match( '/^[a-zA-Z]+_$/', $options['new'], $m );
$new = isset( $m[0] ) ? $m[0] : false;
}
if( $old === false || $new === false ) {
print "Invalid prefix!\n";
wfDie();
}
if( $old === $new ) {
print "Same prefix. Nothing to rename!\n";
wfDie();
}
print "Renaming DB prefix for tables of $wgDBname from '$old' to '$new'\n";
$count = 0;
$dbw = wfGetDB( DB_MASTER );
$res = $dbw->query( "SHOW TABLES LIKE '".$dbw->escapeLike( $old )."%'" );
foreach( $res as $row ) {
// XXX: odd syntax. MySQL outputs an oddly cased "Tables of X"
// sort of message. Best not to try $row->x stuff...
$fields = get_object_vars( $row );
// Silly for loop over one field...
foreach( $fields as $resName => $table ) {
// $old should be regexp safe ([a-zA-Z_])
$newTable = preg_replace( '/^'.$old.'/', $new, $table );
print "Renaming table $table to $newTable\n";
$dbw->query( "RENAME TABLE $table TO $newTable" );
}
$count++;
}
print "Done! [$count tables]\n";

View file

@ -27,61 +27,42 @@
* @file
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
class DumpRenderer extends Maintenance {
$optionsWithArgs = array( 'report' );
private $count = 0;
private $outputDirectory, $startTime;
require_once( 'commandLine.inc' );
public function __construct() {
parent::__construct();
$this->mDescription = "Take page text out of an XML dump file and render basic HTML out to files";
$this->addParam( 'output-dir', 'The directory to output the HTML files to', true, true );
class DumpRenderer {
function __construct( $dir ) {
$this->stderr = fopen( "php://stderr", "wt" );
$this->outputDirectory = $dir;
$this->count = 0;
}
public function execute() {
$this->outputDirectory = $this->getOption( 'output-dir' );
$this->startTime = wfTime();
$source = new ImportStreamSource( $this->getStdin() );
$importer = new WikiImporter( $source );
$importer->setRevisionCallback(
array( &$this, 'handleRevision' ) );
return $importer->doImport();
}
/**
* Callback function for each revision, turn into HTML and save
* @param $rev Revision
*/
private function handleRevision( $rev ) {
function handleRevision( $rev ) {
$title = $rev->getTitle();
if (!$title) {
$this->error( "Got bogus revision with null title!" );
fprintf( $this->stderr, "Got bogus revision with null title!" );
return;
}
$display = $title->getPrefixedText();
$this->count++;
$sanitized = rawurlencode( $display );
$filename = sprintf( "%s/wiki-%07d-%s.html",
$this->outputDirectory,
$this->count,
$sanitized );
$this->output( sprintf( $this->stderr, "%s\n", $filename, $display ) );
// fixme (what?)
fprintf( $this->stderr, "%s\n", $filename, $display );
// fixme
$user = new User();
$parser = new Parser();
$options = ParserOptions::newFromUser( $user );
$output = $parser->parse( $rev->getText(), $title, $options );
file_put_contents( $filename,
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" " .
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" .
@ -95,7 +76,27 @@ class DumpRenderer extends Maintenance {
"</body>\n" .
"</html>" );
}
function run() {
$this->startTime = wfTime();
$file = fopen( 'php://stdin', 'rt' );
$source = new ImportStreamSource( $file );
$importer = new WikiImporter( $source );
$importer->setRevisionCallback(
array( &$this, 'handleRevision' ) );
return $importer->doImport();
}
}
$maintClass = "DumpRenderer";
require_once( DO_MAINTENANCE );
if( isset( $options['output-dir'] ) ) {
$dir = $options['output-dir'];
} else {
wfDie( "Must use --output-dir=/some/dir\n" );
}
$render = new DumpRenderer( $dir );
$render->run();

View file

@ -10,77 +10,71 @@
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
$optionsWithArgs = array( 'maxjobs', 'type', 'procs' );
$wgUseNormalUser = true;
require_once( 'commandLine.inc' );
class RunJobs extends Maintenance {
public function __construct() {
global $wgUseNormalUser;
parent::__construct();
$this->mDescription = "Run pending jobs";
$this->addParam( 'maxjobs', 'Maximum number of jobs to run', false, true );
$this->addParam( 'type', 'Type of job to run', false, true );
$this->addParam( 'procs', 'Number of processes to use', false, true );
$wgUseNormalUser = true;
if ( isset( $options['procs'] ) ) {
$procs = intval( $options['procs'] );
if ( $procs < 1 || $procs > 1000 ) {
echo "Invalid argument to --procs\n";
exit( 1 );
}
public function execute() {
global $wgTitle;
if ( $this->hasOption( 'procs' ) ) {
$procs = intval( $this->getOption('procs') );
if ( $procs < 1 || $procs > 1000 ) {
$this->error( "Invalid argument to --procs\n", true );
}
$fc = new ForkController( $procs );
if ( $fc->start( $procs ) != 'child' ) {
exit( 0 );
}
}
$maxJobs = $this->getOption( 'maxjobs', 10000 );
$type = $this->getOption( 'type', false );
$wgTitle = Title::newFromText( 'RunJobs.php' );
$dbw = wfGetDB( DB_MASTER );
$n = 0;
$conds = '';
if ($type !== false)
$conds = "job_cmd = " . $dbw->addQuotes($type);
while ( $dbw->selectField( 'job', 'job_id', $conds, 'runJobs.php' ) ) {
$offset=0;
for (;;) {
$job = ($type == false) ?
Job::pop($offset)
: Job::pop_type($type);
if ($job == false)
break;
wfWaitForSlaves( 5 );
$t = microtime( true );
$offset=$job->id;
$status = $job->run();
$t = microtime( true ) - $t;
$timeMs = intval( $t * 1000 );
if ( !$status ) {
$this->runJobsLog( $job->toString() . " t=$timeMs error={$job->error}" );
} else {
$this->runJobsLog( $job->toString() . " t=$timeMs good" );
}
if ( $maxJobs && ++$n > $maxJobs ) {
break 2;
}
}
}
}
/**
* Log the job message
* @param $msg String The message to log
*/
private function runJobsLog( $msg ) {
$this->output( wfTimestamp( TS_DB ) . " $msg\n" );
wfDebugLog( 'runJobs', $msg );
$fc = new ForkController( $procs );
if ( $fc->start( $procs ) != 'child' ) {
exit( 0 );
}
}
$maintClass = "RunJobs";
require_once( DO_MAINTENANCE );
if ( isset( $options['maxjobs'] ) ) {
$maxJobs = $options['maxjobs'];
} else {
$maxJobs = 10000;
}
$type = false;
if ( isset( $options['type'] ) )
$type = $options['type'];
$wgTitle = Title::newFromText( 'RunJobs.php' );
$dbw = wfGetDB( DB_MASTER );
$n = 0;
$conds = '';
if ($type !== false)
$conds = "job_cmd = " . $dbw->addQuotes($type);
while ( $dbw->selectField( 'job', 'job_id', $conds, 'runJobs.php' ) ) {
$offset=0;
for (;;) {
$job = ($type == false) ?
Job::pop($offset)
: Job::pop_type($type);
if ($job == false)
break;
wfWaitForSlaves( 5 );
$t = microtime( true );
$offset=$job->id;
$status = $job->run();
$t = microtime( true ) - $t;
$timeMs = intval( $t * 1000 );
if ( !$status ) {
runJobsLog( $job->toString() . " t=$timeMs error={$job->error}" );
} else {
runJobsLog( $job->toString() . " t=$timeMs good" );
}
if ( $maxJobs && ++$n > $maxJobs ) {
break 2;
}
}
}
function runJobsLog( $msg ) {
print wfTimestamp( TS_DB ) . " $msg\n";
wfDebugLog( 'runJobs', $msg );
}

View file

@ -9,19 +9,10 @@
* @author Tim Starling
* @author Ashar Voultoiz
*/
require_once( "Maintenance.php" );
require_once( 'commandLine.inc' );
$dbw = wfGetDB( DB_MASTER );
$count = $dbw->selectField( 'job', 'count(*)', '', 'runJobs.php' );
print $count."\n";
class ShowJobs extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Show number of jobs waiting in master database";
}
public function execute() {
$dbw = wfGetDB( DB_MASTER );
$this->output( $dbw->selectField( 'job', 'count(*)', '', 'runJobs.php' ) . "\n" );
}
}
$maintClass = "ShowJobs";
require_once( DO_MAINTENANCE );

View file

@ -14,41 +14,35 @@
* @license GNU General Public License 2.0 or later
*/
require_once( "Maintenance.php" );
require_once( 'commandLine.inc' );
class ShowStats extends Maintenance {
public function __construct() {
$this->mDescription = "Show the cached statistics";
}
public function execute() {
$fields = array(
'ss_total_views' => 'Total views',
'ss_total_edits' => 'Total edits',
'ss_good_articles' => 'Number of articles',
'ss_total_pages' => 'Total pages',
'ss_users' => 'Number of users',
'ss_admins' => 'Number of admins',
'ss_images' => 'Number of images',
);
// Get cached stats from slave database
$dbr = wfGetDB( DB_SLAVE );
$stats = $dbr->selectRow( 'site_stats', '*', '', __METHOD__ );
// Get maximum size for each column
$max_length_value = $max_length_desc = 0;
foreach( $fields as $field => $desc ) {
$max_length_value = max( $max_length_value, strlen( $stats->$field ) );
$max_length_desc = max( $max_length_desc , strlen( $desc )) ;
}
// Show them
foreach( $fields as $field => $desc ) {
$this->output( sprintf( "%-{$max_length_desc}s: %{$max_length_value}d\n", $desc, $stats->$field ) );
}
}
#
# Configuration
#
$fields = array(
'ss_total_views' => 'Total views',
'ss_total_edits' => 'Total edits',
'ss_good_articles' => 'Number of articles',
'ss_total_pages' => 'Total pages',
'ss_users' => 'Number of users',
'ss_admins' => 'Number of admins',
'ss_images' => 'Number of images',
);
// Get cached stats from slave database
$dbr = wfGetDB( DB_SLAVE );
$fname = 'showStats';
$stats = $dbr->selectRow( 'site_stats', '*', '' );
// Get maximum size for each column
$max_length_value = $max_length_desc = 0;
foreach( $fields as $field => $desc ) {
$max_length_value = max( $max_length_value, strlen( $stats->$field ) );
$max_length_desc = max( $max_length_desc , strlen( $desc )) ;
}
$maintClass = "ShowStats";
require_once( DO_MAINTENANCE );
// Show them
foreach( $fields as $field => $desc ) {
printf( "%-{$max_length_desc}s: %{$max_length_value}d\n", $desc, $stats->$field );
}

View file

@ -7,56 +7,38 @@
* @ingroup Database Maintenance
*/
require_once( "Maintenance.php" );
require_once( dirname(__FILE__) . '/' . 'commandLine.inc' );
class MwSql extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Send SQL queries to a MediaWiki database";
}
public function execute() {
if ( $this->hasArg() ) {
$fileName = $this->getArg();
$file = fopen( $fileName, 'r' );
$promptCallback = false;
} else {
$file = $this->getStdin();
$promptObject = new SqlPromptPrinter( "> " );
$promptCallback = $promptObject->cb();
}
if ( !$file )
$this->error( "Unable to open input file\n", true );
$dbw = wfGetDB( DB_MASTER );
$error = $dbw->sourceStream( $file, $promptCallback, array( $this, 'sqlPrintResult' ) );
if ( $error !== true ) {
$this->error( $error, true );
} else {
exit( 0 );
}
}
/**
* Print the results, callback for $db->sourceStream()
* @param $res The results object
* @param $db Database object
*/
public function sqlPrintResult( $res, $db ) {
if ( !$res ) {
// Do nothing
} elseif ( is_object( $res ) && $res->numRows() ) {
while ( $row = $res->fetchObject() ) {
$this->output( print_r( $row, true ) );
}
} else {
$affected = $db->affectedRows();
$this->output( "Query OK, $affected row(s) affected\n" );
}
}
if ( isset( $options['help'] ) ) {
echo "Send SQL queries to a MediaWiki database.\nUsage: php sql.php [<file>]\n";
exit( 1 );
}
if ( isset( $args[0] ) ) {
$fileName = $args[0];
$file = fopen( $fileName, 'r' );
$promptCallback = false;
} else {
$file = STDIN;
$promptObject = new SqlPromptPrinter( "> " );
$promptCallback = $promptObject->cb();
}
if ( !$file ) {
echo "Unable to open input file\n";
exit( 1 );
}
$dbw =& wfGetDB( DB_MASTER );
$error = $dbw->sourceStream( $file, $promptCallback, 'sqlPrintResult' );
if ( $error !== true ) {
echo $error;
exit( 1 );
} else {
exit( 0 );
}
//-----------------------------------------------------------------------------
class SqlPromptPrinter {
function __construct( $prompt ) {
$this->prompt = $prompt;
@ -71,5 +53,17 @@ class SqlPromptPrinter {
}
}
$maintClass = "MwSql";
require_once( DO_MAINTENANCE );
function sqlPrintResult( $res, $db ) {
if ( !$res ) {
// Do nothing
} elseif ( is_object( $res ) && $res->numRows() ) {
while ( $row = $res->fetchObject() ) {
print_r( $row );
}
} else {
$affected = $db->affectedRows();
echo "Query OK, $affected row(s) affected\n";
}
}

View file

@ -1,73 +1,58 @@
<?php
/**
* Show statistics from memcached
*
* @file
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
require_once('commandLine.inc');
class MemcachedStats extends Maintenance {
public function __construct() {
$this->mDescription = "Show statistics from memcached";
}
public function execute() {
global $wgMemc;
// Can't do stats if
if( get_class( $wgMemc ) == 'FakeMemCachedClient' ) {
$this->error( "You are running FakeMemCachedClient, I can not provide any statistics.\n", true );
}
$session = intval($wgMemc->get(wfMemcKey('stats','request_with_session')));
$noSession = intval($wgMemc->get(wfMemcKey('stats','request_without_session')));
$total = $session + $noSession;
if ( $total == 0 ) {
$this->error( "You either have no stats or memcached isn't running. Aborting.\n", true );
}
$this->output( "Requests\n" );
$this->output( sprintf( "with session: %-10d %6.2f%%\n", $session, $session/$total*100 );
$this->output( sprintf( "without session: %-10d %6.2f%%\n", $noSession, $noSession/$total*100 );
$this->output( sprintf( "total: %-10d %6.2f%%\n", $total, 100 );
$this->output( "\nParser cache\n" );
$hits = intval($wgMemc->get(wfMemcKey('stats','pcache_hit')));
$invalid = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_invalid')));
$expired = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_expired')));
$absent = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_absent')));
$stub = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_stub')));
$total = $hits + $invalid + $expired + $absent + $stub;
$this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits/$total*100 ) );
$this->output( sprintf( "invalid: %-10d %6.2f%%\n", $invalid, $invalid/$total*100 ) );
$this->output( sprintf( "expired: %-10d %6.2f%%\n", $expired, $expired/$total*100 ) );
$this->output( sprintf( "absent: %-10d %6.2f%%\n", $absent, $absent/$total*100 ) );
$this->output( sprintf( "stub threshold: %-10d %6.2f%%\n", $stub, $stub/$total*100 ) );
$this->output( sprintf( "total: %-10d %6.2f%%\n", $total, 100 ) );
$hits = intval($wgMemc->get(wfMemcKey('stats','image_cache_hit')));
$misses = intval($wgMemc->get(wfMemcKey('stats','image_cache_miss')));
$updates = intval($wgMemc->get(wfMemcKey('stats','image_cache_update')));
$total = $hits + $misses;
$this->output("\nImage cache\n");
$this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits/$total*100 ) );
$this->output( sprintf( "misses: %-10d %6.2f%%\n", $misses, $misses/$total*100 ) );
$this->output( sprintf( "updates: %-10d\n", $updates ) );
$hits = intval($wgMemc->get(wfMemcKey('stats','diff_cache_hit')));
$misses = intval($wgMemc->get(wfMemcKey('stats','diff_cache_miss')));
$uncacheable = intval($wgMemc->get(wfMemcKey('stats','diff_uncacheable')));
$total = $hits + $misses + $uncacheable;
$this->output("\nDiff cache\n");
$this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits/$total*100 );
$this->output( sprintf( "misses: %-10d %6.2f%%\n", $misses, $misses/$total*100 );
$this->output( sprintf( "uncacheable: %-10d %6.2f%%\n", $uncacheable, $uncacheable/$total*100 );
}
if( get_class( $wgMemc ) == 'FakeMemCachedClient' ) {
die("You are running FakeMemCachedClient, I can not provide any statistics.\n");
}
$maintClass = "MemcachedStats";
require_once( DO_MAINTENANCE );
$session = intval($wgMemc->get(wfMemcKey('stats','request_with_session')));
$noSession = intval($wgMemc->get(wfMemcKey('stats','request_without_session')));
$total = $session + $noSession;
if ( $total == 0 ) {
die("You either have no stats or memcached isn't running. Aborting.\n");
}
print "Requests\n";
printf( "with session: %-10d %6.2f%%\n", $session, $session/$total*100 );
printf( "without session: %-10d %6.2f%%\n", $noSession, $noSession/$total*100 );
printf( "total: %-10d %6.2f%%\n", $total, 100 );
print "\nParser cache\n";
$hits = intval($wgMemc->get(wfMemcKey('stats','pcache_hit')));
$invalid = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_invalid')));
$expired = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_expired')));
$absent = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_absent')));
$stub = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_stub')));
$total = $hits + $invalid + $expired + $absent + $stub;
printf( "hits: %-10d %6.2f%%\n", $hits, $hits/$total*100 );
printf( "invalid: %-10d %6.2f%%\n", $invalid, $invalid/$total*100 );
printf( "expired: %-10d %6.2f%%\n", $expired, $expired/$total*100 );
printf( "absent: %-10d %6.2f%%\n", $absent, $absent/$total*100 );
printf( "stub threshold: %-10d %6.2f%%\n", $stub, $stub/$total*100 );
printf( "total: %-10d %6.2f%%\n", $total, 100 );
$hits = intval($wgMemc->get(wfMemcKey('stats','image_cache_hit')));
$misses = intval($wgMemc->get(wfMemcKey('stats','image_cache_miss')));
$updates = intval($wgMemc->get(wfMemcKey('stats','image_cache_update')));
$total = $hits + $misses;
print("\nImage cache\n");
printf( "hits: %-10d %6.2f%%\n", $hits, $hits/$total*100 );
printf( "misses: %-10d %6.2f%%\n", $misses, $misses/$total*100 );
printf( "updates: %-10d\n", $updates );
$hits = intval($wgMemc->get(wfMemcKey('stats','diff_cache_hit')));
$misses = intval($wgMemc->get(wfMemcKey('stats','diff_cache_miss')));
$uncacheable = intval($wgMemc->get(wfMemcKey('stats','diff_uncacheable')));
$total = $hits + $misses + $uncacheable;
print("\nDiff cache\n");
printf( "hits: %-10d %6.2f%%\n", $hits, $hits/$total*100 );
printf( "misses: %-10d %6.2f%%\n", $misses, $misses/$total*100 );
printf( "uncacheable: %-10d %6.2f%%\n", $uncacheable, $uncacheable/$total*100 );

View file

@ -6,35 +6,34 @@
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
$usage = <<<EOT
Undelete a page
Usage: php undelete.php [-u <user>] [-r <reason>] <pagename>
class Undelete extends Maintenance {
public function __construct() {
parent::__construct();
$this->mDescription = "Undelete a page";
$this->addParam( 'u', 'The user to perform the undeletion', false, true );
$this->addParam( 'r', 'The reason to undelete', false, true );
$this->addArgs( array( 'pagename' ) );
}
EOT;
public function execute() {
global $wgUser;
$optionsWithArgs = array( 'u', 'r' );
require_once( 'commandLine.inc' );
$user = $this->getOption( 'u', 'Command line script' );
$reason = $this->getOption( 'r', '' );
$pageName = $this->getArg();
$user = 'Command line script';
$reason = '';
$title = Title::newFromText( $pageName );
if ( !$title ) {
$this->error( "Invalid title", true );
}
$wgUser = User::newFromName( $user );
$archive = new PageArchive( $title );
$this->output( "Undeleting " . $title->getPrefixedDBkey() . '...' );
$archive->undelete( array(), $reason );
$this->output( "done\n" );
}
if ( isset( $options['u'] ) ) {
$user = $options['u'];
}
if ( isset( $options['r'] ) ) {
$reason = $options['r'];
}
$pageName = @$args[0];
$title = Title::newFromText( $pageName );
if ( !$title ) {
echo $usage;
exit( 1 );
}
$wgUser = User::newFromName( $user );
$archive = new PageArchive( $title );
echo "Undeleting " . $title->getPrefixedDBkey() . '...';
$archive->undelete( array(), $reason );
echo "done\n";
$maintClass = "Undelete";
require_once( DO_MAINTENANCE );

View file

@ -3,84 +3,38 @@
* Maintenance script to provide a better count of the number of articles
* and update the site statistics table, if desired
*
* @file
* @ingroup Maintenance
* @author Rob Church <robchur@gmail.com>
*/
require_once( "Maintenance.php" );
$options = array( 'update', 'help' );
require_once( 'commandLine.inc' );
require_once( 'updateArticleCount.inc' );
echo( "Update Article Count\n\n" );
class UpdateArticleCount extends Maintenance {
// Content namespaces
private $namespaces;
public function __construct() {
global $wgContentNamespaces;
parent::__construct();
$this->mDescription = "Count of the number of articles and update the site statistics table";
$this->addParam( 'update', 'Update the site_stats table with the new count' );
$this->namespaces = $wgContentNamespaces;
}
public function execute() {
$this->output( "Counting articles..." );
$result = $this->count();
if( $result !== false ) {
$this->output( "found {$result}.\n" );
if( isset( $options['update'] ) && $options['update'] ) {
$this->output( "Updating site statistics table... " );
$dbw = wfGetDB( DB_MASTER );
$dbw->update( 'site_stats', array( 'ss_good_articles' => $result ), array( 'ss_row_id' => 1 ), __METHOD__ );
$this->output( "done.\n" );
} else {
$this->output( "To update the site statistics table, run the script with the --update option.\n" );
}
} else {
$this->output( "failed.\n" );
}
}
/**
* Produce a comma-delimited set of namespaces
* Includes paranoia
*
* @return string
*/
private function makeNsSet() {
foreach( $this->namespaces as $namespace )
$namespaces[] = intval( $namespace );
return implode( ', ', $namespaces );
}
/**
* Produce SQL for the query
*
* @param $dbr Database handle
* @return string
*/
private function makeSql( $dbr ) {
list( $page, $pagelinks ) = $dbr->tableNamesN( 'page', 'pagelinks' );
$nsset = $this->makeNsSet();
return "SELECT COUNT(DISTINCT page_namespace, page_title) AS pagecount " .
"FROM $page, $pagelinks " .
"WHERE pl_from=page_id and page_namespace IN ( $nsset ) " .
"AND page_is_redirect = 0 AND page_len > 0";
}
/**
* Count the number of valid content pages in the wiki
*
* @return mixed Integer, or false if there's a problem
*/
private function count() {
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->query( $this->makeSql( $dbr ), __METHOD__ );
$row = $dbr->fetchObject( $res );
$dbr->freeResult( $res );
return $row->pagecount;
}
if( isset( $options['help'] ) && $options['help'] ) {
echo( "Usage: php updateArticleCount.php [--update]\n\n" );
echo( "--update : Update site statistics table\n" );
exit( 0 );
}
$maintClass = "UpdateArticleCount";
require_once( DO_MAINTENANCE );
echo( "Counting articles..." );
$counter = new ArticleCounter();
$result = $counter->count();
if( $result !== false ) {
echo( "found {$result}.\n" );
if( isset( $options['update'] ) && $options['update'] ) {
echo( "Updating site statistics table... " );
$dbw = wfGetDB( DB_MASTER );
$dbw->update( 'site_stats', array( 'ss_good_articles' => $result ), array( 'ss_row_id' => 1 ), __METHOD__ );
echo( "done.\n" );
} else {
echo( "To update the site statistics table, run the script with the --update option.\n" );
}
} else {
echo( "failed.\n" );
}
echo( "\n" );

View file

@ -5,159 +5,58 @@
* Usage: php updateSearchIndex.php [-s START] [-e END] [-p POSFILE] [-l LOCKTIME] [-q]
* Where START is the starting timestamp
* END is the ending timestamp
* POSFILE is a file to load timestamps from and save them to, searchUpdate.WIKI_ID.pos by default
* LOCKTIME is how long the searchindex and revision tables will be locked for
* POSFILE is a file to load timestamps from and save them to, searchUpdate.pos by default
* LOCKTIME is how long the searchindex and cur tables will be locked for
* -q means quiet
*
* @file
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
class UpdateSearchIndex extends Maintenance {
/** */
$optionsWithArgs = array( 's', 'e', 'p' );
public function __construct() {
parent::__construct();
$this->mDescription = "Script for periodic off-peak updating of the search index";
$this->addParam( 's', 'starting timestamp', false, true );
$this->addParam( 'e', 'Ending timestamp', false, true );
$this->addParam( 'p', 'File for saving/loading timestamps, searchUpdate.WIKI_ID.pos by default', false, true );
$this->addParam( 'l', 'How long the searchindex and revision tables will be locked for', false, true );
}
require_once( 'commandLine.inc' );
require_once( 'updateSearchIndex.inc' );
public function execute() {
$posFile = $this->getOption( 'p', 'searchUpdate.' . wfWikiId() . '.pos' );
$end = $this->getOption( 'e', wfTimestampNow() );
if ( $this->hasOption( 's' ) ) {
$start = $this->getOption('s');
} elseif( is_readable( 'searchUpdate.pos' ) ) {
# B/c to the old position file name which was hardcoded
# We can safely delete the file when we're done though.
$start = file_get_contents( 'searchUpdate.pos' );
unlink( 'searchUpdate.pos' );
} else {
$start = @file_get_contents( $posFile );
if ( !$start ) {
$start = wfTimestamp( TS_MW, time() - 86400 );
}
}
$lockTime = $this->getOption( 'l', 20 );
$this->updateSearchIndex( $start, $end, $lockTime );
$file = fopen( $posFile, 'w' );
fwrite( $file, $end );
fclose( $file );
}
private function updateSearchIndex( $start, $end, $maxLockTime ) {
global $wgDisableSearchUpdate;
if ( isset( $options['p'] ) ) {
$posFile = $options['p'];
} else {
$posFile = 'searchUpdate.' . wfWikiId() . '.pos';
}
$wgDisableSearchUpdate = false;
if ( isset( $options['e'] ) ) {
$end = $options['e'];
} else {
$end = wfTimestampNow();
}
$dbw = wfGetDB( DB_MASTER );
$recentchanges = $dbw->tableName( 'recentchanges' );
$this->output( "Updating searchindex between $start and $end\n" );
# Select entries from recentchanges which are on top and between the specified times
$start = $dbw->strencode( $start );
$end = $dbw->strencode( $end );
$page = $dbw->tableName( 'page' );
$sql = "SELECT rc_cur_id,rc_type,rc_moved_to_ns,rc_moved_to_title FROM $recentchanges
JOIN $page ON rc_cur_id=page_id AND rc_this_oldid=page_latest
WHERE rc_timestamp BETWEEN '$start' AND '$end'
";
$res = $dbw->query( $sql, __METHOD__ );
# Lock searchindex
if ( $maxLockTime ) {
$this->output( " --- Waiting for lock ---" );
$this->lockSearchindex( $dbw );
$lockTime = time();
$this->output( "\n" );
}
# Loop through the results and do a search update
while ( $row = $dbw->fetchObject( $res ) ) {
# Allow reads to be processed
if ( $maxLockTime && time() > $lockTime + $maxLockTime ) {
$this->output( " --- Relocking ---" );
$this->relockSearchindex( $dbw );
$lockTime = time();
$this->output( "\n" );
}
if ( $row->rc_type == RC_LOG ) {
continue;
} elseif ( $row->rc_type == RC_MOVE || $row->rc_type == RC_MOVE_OVER_REDIRECT ) {
# Rename searchindex entry
$titleObj = Title::makeTitle( $row->rc_moved_to_ns, $row->rc_moved_to_title );
$title = $titleObj->getPrefixedDBkey();
$this->output( "$title..." );
$u = new SearchUpdate( $row->rc_cur_id, $title, false );
$this->output( "\n" );
} else {
// Get current revision
$rev = Revision::loadFromPageId( $dbw, $row->rc_cur_id );
if( $rev ) {
$titleObj = $rev->getTitle();
$title = $titleObj->getPrefixedDBkey();
$this->output( $title );
# Update searchindex
$u = new SearchUpdate( $row->rc_cur_id, $titleObj->getText(), $rev->getText() );
$u->doUpdate();
$this->output( "\n" );
}
}
}
# Unlock searchindex
if ( $maxLockTime ) {
$this->output( " --- Unlocking --" );
$this->unlockSearchindex( $dbw );
$this->output( "\n" );
}
$this->output( "Done\n" );
}
/**
* Lock the search index
* @param &$db Database object
*/
private function lockSearchindex( &$db ) {
$write = array( 'searchindex' );
$read = array( 'page', 'revision', 'text', 'interwiki' );
$items = array();
foreach( $write as $table ) {
$items[] = $db->tableName( $table ) . ' LOW_PRIORITY WRITE';
}
foreach( $read as $table ) {
$items[] = $db->tableName( $table ) . ' READ';
}
$sql = "LOCK TABLES " . implode( ',', $items );
$db->query( $sql, 'updateSearchIndex.php ' . __METHOD__ );
}
/**
* Unlock the tables
* @param &$db Database object
*/
private function unlockSearchindex( &$db ) {
$db->query( "UNLOCK TABLES", 'updateSearchIndex.php ' . __METHOD__ );
}
/**
* Unlock and lock again
* Since the lock is low-priority, queued reads will be able to complete
* @param &$db Database object
*/
private function relockSearchindex( &$db ) {
$this->unlockSearchindex( $db );
$this->lockSearchindex( $db );
if ( isset( $options['s'] ) ) {
$start = $options['s'];
} elseif( is_readable( 'searchUpdate.pos' ) ) {
# B/c to the old position file name which was hardcoded
# We can safely delete the file when we're done though.
$start = file_get_contents( 'searchUpdate.pos' );
unlink( 'searchUpdate.pos' );
} else {
$start = @file_get_contents( $posFile );
if ( !$start ) {
$start = wfTimestamp( TS_MW, time() - 86400 );
}
}
$maintClass = "UpdateSearchIndex";
require_once( DO_MAINTENANCE );
if ( isset( $options['l'] ) ) {
$lockTime = $options['l'];
} else {
$lockTime = 20;
}
$quiet = (bool)(@$options['q']);
updateSearchIndex( $start, $end, $lockTime, $quiet );
$file = fopen( $posFile, 'w' );
fwrite( $file, $end );
fclose( $file );

View file

@ -6,114 +6,114 @@
* @file
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
$options = array('only','help');
class UpdateSpecialPages extends Maintenance {
public function __construct() {
parent::__construct();
$this->addParam( 'list', 'List special page names' );
$this->addParam( 'only', 'Only update "page". Ex: --only=BrokenRedirects', false, true );
$this->addParam( 'override', 'Also update pages that have updates disabled' );
require_once( 'commandLine.inc' );
require_once( "$IP/includes/SpecialPage.php" );
require_once( "$IP/includes/QueryPage.php" );
if(@$options['help']) {
print "usage:updateSpecialPages.php [--help] [--only=page]\n";
print " --help : this help message\n";
print " --list : list special pages names\n";
print " --only=page : only update 'page'. Ex: --only=BrokenRedirects\n";
print " --override : update even pages which have had updates disabled\n";
wfDie();
}
$wgOut->disable();
$dbw = wfGetDB( DB_MASTER );
foreach( $wgSpecialPageCacheUpdates as $special => $call ) {
if( !is_callable($call) ) {
print "Uncallable function $call!\n";
continue;
}
$t1 = explode( ' ', microtime() );
call_user_func( $call, $dbw );
$t2 = explode( ' ', microtime() );
printf( '%-30s ', $special );
$elapsed = ($t2[0] - $t1[0]) + ($t2[1] - $t1[1]);
$hours = intval( $elapsed / 3600 );
$minutes = intval( $elapsed % 3600 / 60 );
$seconds = $elapsed - $hours * 3600 - $minutes * 60;
if ( $hours ) {
print $hours . 'h ';
}
if ( $minutes ) {
print $minutes . 'm ';
}
printf( "completed in %.2fs\n", $seconds );
# Wait for the slave to catch up
wfWaitForSlaves( 5 );
}
foreach( $wgQueryPages as $page ) {
@list( $class, $special, $limit ) = $page;
# --list : just show the name of pages
if( @$options['list'] ) {
print "$special\n";
continue;
}
public function execute() {
global $wgOut;
$wgOut->disable();
$dbw = wfGetDB( DB_MASTER );
if ( !isset( $options['override'] ) && $wgDisableQueryPageUpdate && in_array( $special, $wgDisableQueryPageUpdate ) ) {
printf("%-30s disabled\n", $special);
continue;
}
foreach( $wgSpecialPageCacheUpdates as $special => $call ) {
if( !is_callable($call) ) {
$this->error( "Uncallable function $call!\n" );
continue;
}
$specialObj = SpecialPage::getPage( $special );
if ( !$specialObj ) {
print "No such special page: $special\n";
exit;
}
if ( !class_exists( $class ) ) {
$file = $specialObj->getFile();
require_once( $file );
}
$queryPage = new $class;
if( !isset($options['only']) or $options['only'] == $queryPage->getName() ) {
printf( '%-30s ', $special );
if ( $queryPage->isExpensive() ) {
$t1 = explode( ' ', microtime() );
call_user_func( $call, $dbw );
# Do the query
$num = $queryPage->recache( $limit === null ? $wgQueryCacheLimit : $limit );
$t2 = explode( ' ', microtime() );
$this->output( sprintf( '%-30s ', $special ) );
$elapsed = ($t2[0] - $t1[0]) + ($t2[1] - $t1[1]);
$hours = intval( $elapsed / 3600 );
$minutes = intval( $elapsed % 3600 / 60 );
$seconds = $elapsed - $hours * 3600 - $minutes * 60;
if ( $hours ) {
$this->output( $hours . 'h ' );
}
if ( $minutes ) {
$this->output( $minutes . 'm ' );
}
$this->output( sprintf( "completed in %.2fs\n", $seconds ) );
# Wait for the slave to catch up
wfWaitForSlaves( 5 );
if ( $num === false ) {
print "FAILED: database error\n";
} else {
print "got $num rows in ";
$elapsed = ($t2[0] - $t1[0]) + ($t2[1] - $t1[1]);
$hours = intval( $elapsed / 3600 );
$minutes = intval( $elapsed % 3600 / 60 );
$seconds = $elapsed - $hours * 3600 - $minutes * 60;
if ( $hours ) {
print $hours . 'h ';
}
if ( $minutes ) {
print $minutes . 'm ';
}
printf( "%.2fs\n", $seconds );
}
foreach( $wgQueryPages as $page ) {
@list( $class, $special, $limit ) = $page;
# --list : just show the name of pages
if( $this->hasOption('list') ) {
$this->output( "$special\n" );
continue;
}
if ( $this->hasOption('override') && $wgDisableQueryPageUpdate && in_array( $special, $wgDisableQueryPageUpdate ) ) {
$this->output( sprintf( "%-30s disabled\n", $special ) );
continue;
}
$specialObj = SpecialPage::getPage( $special );
if ( !$specialObj ) {
$this->output( "No such special page: $special\n" );
exit;
}
if ( !class_exists( $class ) ) {
$file = $specialObj->getFile();
require_once( $file );
}
$queryPage = new $class;
if( !$this->hasOption('only') || $this->getOption('only') == $queryPage->getName() ) {
$this->output( sprintf( '%-30s ', $special ) );
if ( $queryPage->isExpensive() ) {
$t1 = explode( ' ', microtime() );
# Do the query
$num = $queryPage->recache( $limit === null ? $wgQueryCacheLimit : $limit );
$t2 = explode( ' ', microtime() );
if ( $num === false ) {
$this->output( "FAILED: database error\n" );
} else {
$this->output( "got $num rows in " );
$elapsed = ($t2[0] - $t1[0]) + ($t2[1] - $t1[1]);
$hours = intval( $elapsed / 3600 );
$minutes = intval( $elapsed % 3600 / 60 );
$seconds = $elapsed - $hours * 3600 - $minutes * 60;
if ( $hours ) {
$this->output( $hours . 'h ' );
}
if ( $minutes ) {
$this->output( $minutes . 'm ' );
}
$this->output( sprintf( "%.2fs\n", $seconds ) );
}
# Reopen any connections that have closed
if ( !wfGetLB()->pingAll()) {
$this->output( "\n" );
do {
$this->error( "Connection failed, reconnecting in 10 seconds...\n" );
sleep(10);
} while ( !wfGetLB()->pingAll() );
$this->output( "Reconnected\n\n" );
} else {
# Commit the results
$dbw->immediateCommit();
}
# Wait for the slave to catch up
wfWaitForSlaves( 5 );
} else {
$this->output( "cheap, skipped\n" );
}
}
# Reopen any connections that have closed
if ( !wfGetLB()->pingAll()) {
print "\n";
do {
print "Connection failed, reconnecting in 10 seconds...\n";
sleep(10);
} while ( !wfGetLB()->pingAll() );
print "Reconnected\n\n";
} else {
# Commit the results
$dbw->immediateCommit();
}
# Wait for the slave to catch up
wfWaitForSlaves( 5 );
} else {
print "cheap, skipped\n";
}
}
}

View file

@ -1038,7 +1038,10 @@ function do_stats_init() {
wfOut( "ok.\n" );
return;
}
SiteStats::init( false );
global $IP;
require_once "$IP/maintenance/initStats.inc";
wfInitStats();
}
function do_active_users_init() {

View file

@ -5,16 +5,11 @@
* @ingroup Maintenance
*/
require_once( "Maintenance.php" );
class WaitForSlave extends Maintenance {
public function __construct() {
$this->addArgs( array( 'maxlag' ) );
}
public function execute() {
wfWaitForSlaves( $this->getArg( 0, 10 ) );
}
require_once( "commandLine.inc" );
if ( isset( $args[0] ) ) {
wfWaitForSlaves($args[0]);
} else {
wfWaitForSlaves(10);
}
$maintClass = "WaitForSlave";
require_once( DO_MAINTENANCE );

View file

@ -4,6 +4,7 @@ ini_set( 'zlib.output_compression', 'off' );
$wgEnableProfileInfo = $wgProfileToDatabase = false;
require_once( './includes/WebStart.php' );
@include_once( './AdminSettings.php' );
?>
<!--

View file

@ -7,10 +7,11 @@ require 't/Test.php';
require 'includes/Defines.php';
require 'includes/ProfilerStub.php';
require 'LocalSettings.php';
require 'AdminSettings.php';
require 'includes/Setup.php';
function buildTestDatabase( $tables ) {
global $wgDBprefix, $wgDBserver, $wgDBname, $wgDBtype;
global $wgDBprefix, $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname, $wgDBtype;
$oldPrefix = $wgDBprefix;
$wgDBprefix = 'parsertest';