* (bug 16322) Allow maint scripts to accept DB user/pass over input or params if no AdminSettings.php
* (bug 18768) Remove AdminSettings.php from MediaWiki core
* (bug 19157) createAndPromote error on bad password
* (bug 14201) Create AdminSettings.php during wiki installation, in the same way as LocalSettings.php
* Introduce new Maintenance class framework and port a good number of scripts over; the ones that are left are a little more complicated. Read the docs.
* Not deleting "unused" files yet, don't want to break everything at once :)
+++ /dev/null
-<?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;
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 ===
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 ===
* (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
== API changes in 1.16 ==
repository, via a checkout or export operation.
Replace the existing MediaWiki files with the new. You should preserve the
-LocalSettings.php file, AdminSettings.php file (if present), and the
-"extensions" and "images" directories.
+LocalSettings.php file and the "extensions" and "images" directories.
Depending upon your configuration, you may also need to preserve additional
directories, including a custom upload directory ($wgUploadDirectory),
=== Perform the database upgrade ===
-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.
+You will need to have $wgDBadminuser and $wgDBadminpass set in your
+LocalSettings.php, see there for more info.
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
=== Web installer ===
You can use the web-based installer wizard if you first remove the
-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.
+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.
You may change some settings during the install, but be very careful!
Changing the encoding in particular will generally leave you with a
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 create an AdminSettings.php giving an appropriate database user
-and password with privileges to modify the database structure.
+that you set $wgDBadminuser and $wgDBadminpassword with 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:
$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" );
<label class="column">Superuser account:</label>
<input type="checkbox" name="useroot" id="useroot" <?php if( $useRoot ) { ?>checked="checked" <?php } ?> />
<label for="useroot">Use superuser account</label>
+ <input type="checkbox" name="populateadmin" id="populateadmin" <?php if( $conf->populateadmin ) { ?>checked="checked" <?php } ?> />
+ <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>
# 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 =
{$dbsettings}
+## Database admin settings, used for maintenance scripts
+\$wgDBadminuser = \"{$slconf['DBadminuser']}\";
+\$wgDBadminpassword = \"{$slconf['DBadminpassword']}\";
+
## Shared memory settings
\$wgMainCacheType = $cacheType;
\$wgMemCachedServers = $mcservers;
--- /dev/null
+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
\ No newline at end of file
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 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.
+ 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.
redirect.php
Script that only redirect to the article passed in the wpDropdown parameter
// clean schema with mwdumper.
wfDebug( __METHOD__ . ": initializing damaged or missing site_stats\n" );
- global $IP;
- require_once "$IP/maintenance/initStats.inc";
-
ob_start();
- wfInitStats();
+ self::init( false );
ob_end_clean();
$row = self::doLoad( wfGetDB( DB_MASTER ) );
}
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" );
+ }
}
RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS = YES
-EXCLUDE_PATTERNS = LocalSettings.php AdminSettings.php
+EXCLUDE_PATTERNS = LocalSettings.php
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
EXAMPLE_RECURSIVE = NO
--- /dev/null
+<?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();
+
+ }
+}
+
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 place their username and password in an AdminSettings.php
-file in the directory above. See AdminSettings.sample for specifics on this.
+database, and then set $wgDBadminuser and $wgDBadminpassword in your
+LocalSettings.php
=== Brief explanation of files ===
* @ingroup Maintenance
*/
-require_once( 'commandLine.inc' );
+require_once( "Maintenance.php" );
-$fixit = isset( $options['fix'] );
-$fname = 'attachLatest';
-
-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;
+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__ );
- $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 = 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" );
+ }
}
- $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 );
* @ingroup Maintenance
*/
-/** */
-require_once( "commandLine.inc" );
+require_once( "Maintenance.php" );
-/**
- * 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 );
+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.";
}
- $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
- */
-function randomUrlList( $length ) {
- $list = array();
- for( $i = 0; $i < $length; $i++ ) {
- $list[] = randomUrl();
+
+ 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" );
+ }
+ }
}
- 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') ) );
+
+ /**
+ * 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 );
}
- 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 );
+
+ /**
+ * 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 );
}
- foreach( $lengths as $length ) {
- $urls = randomUrlList( $length );
- $trial = benchSquid( $urls );
- print "$trial\n";
+
+ /**
+ * 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 );
}
}
+
+$maintClass = "BenchmarkPurge";
+require_once( DO_MAINTENANCE );
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
*/
-$optionsWithArgs = array( 'user', 'password' );
-require_once 'commandLine.inc';
-
-$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 );
+require_once( "Maintenance.php" );
+
+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."
+ }
+
+ public function execute() {
+ if( !$this->hasOption('user') || !$this->hasOption('password') ) {
+ $this->error( "Username or password not provided, halting.", true );
}
-
- $this->user = User::newFromName( $user );
- if ( !$this->user->getId() ) {
- die ( "No such user: $user\n" );
+ $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 );
}
-
- $this->password = $password;
-
- $this->dbw = wfGetDB( DB_MASTER );
- }
-
- function main() {
- $this->user->setPassword( $this->password );
- $this->user->saveSettings();
}
}
+
+$maintClass = "ChangePassword";
+require_once( DO_MAINTENANCE );
<?php
-if ( php_sapi_name() != 'cli' ) exit;
+/**
+ * Check the autoloader
+ */
-$IP = dirname(__FILE__) .'/..';
-require( "$IP/includes/AutoLoader.php" );
-$files = array_unique( $wgAutoloadLocalClasses );
+require_once( "Maintenance.php" );
-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];
+class CheckAutoLoader extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "AutoLoader sanity checks";
}
- 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";
+ 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" );
+ }
+ }
}
}
-
}
-
+$maintClass = "CheckAutoLoader";
+require_once( DO_MAINTENANCE );
<?php
+/**
+ * CheckBadRedirects - See if pages marked as being redirects
+ * really are.
+ */
+
+require_once( "Maintenance.php" );
-require "commandLine.inc";
-
-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";
+class CheckBadRedirects extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Look for bad redirects";
+ }
-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";
+ 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" );
+ }
+ }
}
+ $this->output( "\ndone.\n" );
}
}
-echo "\n";
-echo "done.\n";
+$maintClass = "CheckBadRedirects";
+require_once( DO_MAINTENANCE );
<?php
+/**
+ * Check images to see if they exist, are readable, etc etc
+ */
+require_once( "Maintenance.php" );
-require( 'commandLine.inc' );
+class CheckImages extends Maintenance {
-$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 __construct() {
+ parent::__construct();
+ $this->mDescription = "Check images to see if they exist, are readable, etc";
}
+
+ 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";
* @ingroup Maintenance
*/
-error_reporting(E_ALL ^ E_NOTICE);
-require_once 'commandLine.inc';
-class checkUsernames {
- var $stderr, $log;
+require_once( "Maintenance.php" );
- function checkUsernames() {
- $this->stderr = fopen( 'php://stderr', 'wt' );
+class CheckUsernames extends Maintenance {
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Verify that database usernames are actually valid";
}
- function main() {
- $fname = 'checkUsernames::main';
+ function execute() {
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'user',
array( 'user_id', 'user_name' ),
null,
- $fname
+ __METHOD__
);
while ( $row = $dbr->fetchObject( $res ) ) {
if ( ! User::isValidUserName( $row->user_name ) ) {
- $out = sprintf( "%s: %6d: '%s'\n", wfWikiID(), $row->user_id, $row->user_name );
- fwrite( $this->stderr, $out );
+ $this->error( sprintf( "%s: %6d: '%s'\n", wfWikiID(), $row->user_id, $row->user_name ) );
wfDebugLog( 'checkUsernames', $out );
}
}
}
}
-$cun = new checkUsernames();
-$cun->main();
-
+$maintClass = "CheckUsernames";
+require_once( "doMaintenance.php" );
* This script is used to clear the interwiki links for ALL languages in
* memcached.
*
- * @file
* @ingroup Maintenance
*/
-/** */
-require_once('commandLine.inc');
+require_once( "Maintenance.php" );
-$dbr = wfGetDB( DB_SLAVE );
-$res = $dbr->select( 'interwiki', array( 'iw_prefix' ), false );
-$prefixes = array();
-while ( $row = $dbr->fetchObject( $res ) ) {
- $prefixes[] = $row->iw_prefix;
-}
+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 ) {
- print "$db ";
- foreach ( $prefixes as $prefix ) {
- $wgMemc->delete("$db:interwiki:$prefix");
+ foreach ( $wgLocalDatabases as $db ) {
+ $this->output( "$db..." );
+ foreach ( $prefixes as $prefix ) {
+ $wgMemc->delete("$db:interwiki:$prefix");
+ }
+ $this->output( "done\n" );
+ }
}
}
-print "\n";
+$maintClass = "ClearInterwikiCache";
+require_once( DO_MAINTENANCE );
* @ingroup Maintenance
*/
-require_once('commandLine.inc');
+require_once( 'Maintenance.php' );
-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");
-}
+class clear_stats extends Maintenance {
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Remove all statistics tracking from memcached";
+ }
-function noisyDelete( $key ) {
- global $wgMemc;
- /*
- print "$key ";
- if ( $wgMemc->delete($key) ) {
- print "deleted\n";
- } else {
- print "FAILED\n";
- }*/
- $wgMemc->delete($key);
+ 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");
+ }
+ }
}
+$maintClass = "clear_stats";
+require_once( DO_MAINTENANCE );
* @author Rob Church <robchur@gmail.com>
*/
-$options = array( 'help', 'bureaucrat' );
-require_once( 'commandLine.inc' );
-
-if( isset( $options['help'] ) ) {
- showHelp();
- exit( 1 );
-}
-
-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 );
+require_once( "Maintenance.php" );
+
+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" );
+ }
}
-# 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
- );
-}
\ No newline at end of file
+$maintClass = "CreateAndPromote";
+require_once( DO_MAINTENANCE );
<?php
-
/**
* Deletes a batch of pages
* Usage: php deleteBatch.php [-u <user>] [-r <reason>] [-i <interval>] [listfile]
* @file
* @ingroup Maintenance
*/
-
-$oldCwd = getcwd();
-$optionsWithArgs = array( 'u', 'r', 'i' );
-require_once( 'commandLine.inc' );
-
-chdir( $oldCwd );
-
-# Options processing
-
-$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'];
-}
-
-$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;
+
+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' ) );
}
- 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... ";
+
+ public function execute() {
+ global $wgUser;
+
+ # 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();
}
- } 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 );
+ # Setup
+ if( !$file ) {
+ $this->error( "Unable to read file, exiting\n", true );
+ }
+ $wgUser = User::newFromName( $user );
+ $dbw = wfGetDB( DB_MASTER );
+
+ # 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 );
+}
}
- wfWaitForSlaves( 5 );
}
-
-
+$maintClass = "DeleteBatch";
+require_once( DO_MAINTENANCE );
<?php
-
/**
* Deletes all pages in the MediaWiki namespace which were last edited by
* "MediaWiki default".
*
- * @file
* @ingroup Maintenance
*/
-if ( !defined( 'MEDIAWIKI' ) ) {
- require_once( 'commandLine.inc' );
- deleteDefaultMessages();
-}
+require_once( "Maintenance.php" );
+
+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',
- )
- );
+ global $wgUser;
+ $wgUser = User::newFromName( $user );
+ $wgUser->addGroup( 'bot' );
- $dbw = wfGetDB( DB_MASTER );
+ $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',
+ )
+ );
- while ( $row = $dbr->fetchObject( $res ) ) {
- if ( function_exists( 'wfWaitForSlaves' ) ) {
- wfWaitForSlaves( 5 );
+ $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();
}
- $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 );
* This script delete image information from memcached.
*
* Usage example:
- * php deleteImageMemcached.php --until "2005-09-05 00:00:00" --sleep 0 --report 10
+ * php deleteImageMemcached.php --until "2005-09-05 00:00:00" --sleep 0
*
* @file
* @ingroup Maintenance
*/
-$optionsWithArgs = array( 'until', 'sleep', 'report' );
+require_once( "Maintenance.php" );
-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;
+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 );
}
- function main() {
+ public function execute() {
global $wgMemc;
- $fname = 'DeleteImageCache::main';
+
+ $until = preg_replace( "/[^\d]/", '', $this->getOption('until') );
+ $sleep = (int)$this->getOption('sleep') * 1000; // milliseconds
ini_set( 'display_errors', false );
$res = $dbr->select( 'image',
array( 'img_name' ),
- array( "img_timestamp < {$this->until}" ),
- $fname
+ array( "img_timestamp < {$until}" ),
+ __METHOD__
);
$i = 0;
while ( $row = $dbr->fetchObject( $res ) ) {
if ($i % $this->report == 0)
- printf("%s: %13s done (%s)\n", wfWikiID(), "$i/$total", wfPercent( $i / $total * 100 ));
+ $this->output( sprintf("%s: %13s done (%s)\n", wfWikiID(), "$i/$total", wfPercent( $i / $total * 100 ) ) );
$md5 = md5( $row->img_name );
$wgMemc->delete( wfMemcKey( 'Image', $md5 ) );
- if ($this->sleep != 0)
- usleep( $this->sleep );
+ if ($sleep != 0)
+ usleep( $sleep );
++$i;
}
}
- function getImageCount() {
- $fname = 'DeleteImageCache::getImageCount';
-
+ private function getImageCount() {
$dbr = wfGetDB( DB_SLAVE );
- return $dbr->selectField( 'image', 'COUNT(*)', array(), $fname );
+ return $dbr->selectField( 'image', 'COUNT(*)', array(), __METHOD__ );
}
}
-$until = preg_replace( "/[^\d]/", '', $options['until'] );
-$sleep = (int)$options['sleep'] * 1000; // milliseconds
-$report = (int)$options['report'];
-
-$dic = new DeleteImageCache( $until, $sleep, $report );
-$dic->main();
-
+$maintClass = "DeleteImageCache";
+require_once( DO_MAINTENANCE );
* @ingroup Maintenance
*/
-require_once( 'commandLine.inc' );
+require_once( "Maintenance.php" );
-$dbw = wfGetDB( DB_MASTER );
-
-if ( count( $args ) == 0 ) {
- echo "Usage: php deleteRevision.php <revid> [<revid> ...]\n";
- exit(1);
-}
-
-echo "Deleting revision(s) " . implode( ',', $args ) . " from ".wfWikiID()."...\n";
+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 );
+ }
-$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 ) );
+ $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" );
}
}
-print "Deleted $affected revisions\n";
-
+$maintClass = "DeleteRevision";
+require_once( DO_MAINTENANCE );
--- /dev/null
+<?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() );
+}
\ No newline at end of file
* @ingroup Maintenance
*/
-$wgUseNormalUser = (bool)getenv('MW_WIKIUSER');
+require_once( "Maintenance.php" );
-$optionsWithArgs = array( 'd' );
+class EvalPrompt extends Maintenance {
-/** */
-require_once( "commandLine.inc" );
-
-if ( isset( $options['d'] ) ) {
- $d = $options['d'];
- if ( $d > 0 ) {
- $wgDebugLogFile = '/dev/stdout';
+ 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 );
}
- if ( $d > 1 ) {
- $lb = wfGetLB();
- foreach ( $lb->mServers as $i => $server ) {
- $lb->mServers[$i]['flags'] |= DBO_DEBUG;
+
+ 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 > 2 ) {
- $wgDebugFunctionEntry = true;
- }
-}
-
-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 );
+
+ 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";
}
}
-print "\n";
-
-
+$maintClass = "EvalPrompt";
+require_once( DO_MAINTENANCE );
* @ingroup Maintenance
*/
-require "commandLine.inc";
+require_once( "Maintenance.php" );
-$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;
+class FetchText extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Fetch the revision text from an old_id";
}
- $textId = intval( $line );
- $text = doGetText( $db, $textId );
- echo strlen( $text ) . "\n";
- echo $text;
-}
-/**
- * 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;
+ 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;
}
- return $text;
}
+
+$maintClass = "FetchText";
+require_once( DO_MAINTENANCE );
* @ingroup Maintenance
*/
-require 'commandLine.inc';
+require_once( "Maintenance.php" );
-$lb = wfGetLB();
+class GetLagTimes extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Dump replication lag times";
+ }
+
+ public function execute() {
+ $lb = wfGetLB();
-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 );
+ 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" );
} else {
- $ip = gethostbyname( $host );
+ $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 ) );
+ }
}
- $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 );
* @file
* @ingroup Maintenance
*/
+
+require_once( "Maintenance.php" );
-require_once( dirname(__FILE__).'/commandLine.inc' );
-
-if ( $wgAllDBsAreLocalhost ) {
- # Can't fool the backup script
- print "localhost\n";
- exit;
-}
-
-if( isset( $options['group'] ) ) {
- $db = wfGetDB( DB_SLAVE, $options['group'] );
- $host = $db->getServer();
-} else {
- $lb = wfGetLB();
- $i = $lb->getReaderIndex();
- $host = $lb->getServerName( $i );
+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" );
+ }
}
-print "$host\n";
-
-
+$maintClass = "GetSlaveServer";
+require_once( DO_MAINTENANCE );
* @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" );
-if( isset( $options['help'] ) ) {
- showHelp();
- exit(1);
-}
+require_once( "Maintenance.php" );
-require "$IP/maintenance/initStats.inc";
-wfInitStats( $options );
+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" );
+ }
-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" );
+ public function execute() {
+ $this->output( "Refresh Site Statistics\n\n" );
+ SiteStats::init( $this->hasOption('update'), $this->hasOption('noviews') );
+ }
}
+$maintClass = "InitStats";
+require_once( DO_MAINTENANCE );
* @ingroup Maintenance
*/
-$optionsWithArgs = array( 'i' );
+require_once( "Maintenance.php" );
-require_once('commandLine.inc');
-
-function microtime_float()
-{
- list($usec, $sec) = explode(" ", microtime());
- return ((float)$usec + (float)$sec);
-}
+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' ) );
+ }
+ public function execute() {
+ global $wgMemCachedServers;
-#$wgDebugLogFile = '/dev/stdout';
+ $iterations = $this->getOption( 'i', 100 );
+ if( $this->hasArg() )
+ $wgMemCachedServers = array( $this->getArg() );
-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++;
+ 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" );
}
}
- for ( $i=1; $i<=$iterations; $i++ ) {
- if ( !is_null( $mcc->incr( "test$i", $i ) ) ) {
- $incr++;
- }
+ /**
+ * 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++ ) {
- $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 );
<?php
-
/**
* Maintenance script to move a batch of pages
*
- * @file
* @ingroup Maintenance
* @author Tim Starling
*
* e.g. immobile_namespace for namespaces which can't be moved
*/
-$oldCwd = getcwd();
-$optionsWithArgs = array( 'u', 'r', 'i' );
-require_once( 'commandLine.inc' );
-
-chdir( $oldCwd );
-
-# Options processing
-
-$filename = 'php://stdin';
-$user = 'Move 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'];
-}
-
-$wgUser = User::newFromName( $user );
-
-
-# Setup complete, now start
+require_once( "Maintenance.php" );
-$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;
+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' ) );
}
- $parts = array_map( 'trim', explode( '|', $line ) );
- if ( count( $parts ) != 2 ) {
- print "Error on line $linenum, no pipe character\n";
- continue;
+
+ public function execute() {
+ global $wgUser;
+
+ # Change to current working directory
+ $oldCwd = getcwd();
+ chdir( $oldCwd );
+
+ # 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();
+ }
+
+ # 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 );
+ }
}
- $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 );
}
-
-
+$maintClass = "MoveBatch";
+require_once( DO_MAINTENANCE );
* @ingroup Maintenance
*/
-$options = array( 'type' );
+require_once( "Maintenance.php" );
-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;
+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 );
}
-
- 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)";
+ 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 )
}
- $res = $dbConn->query( $sql, 'nextJobDB.php' );
- $row = $dbConn->fetchRow( $res ); // discard padding row
- while ( $row = $dbConn->fetchRow( $res ) ) {
- $pendingDBs[] = $row[0];
+ # 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];
+ }
}
}
-
- $wgMemc->set( $mckey, $pendingDBs, 300 );
-}
-
-if ( $pendingDBs ) {
- echo $pendingDBs[mt_rand(0, count( $pendingDBs ) - 1)];
}
-
+$maintClass = "nextJobDb";
+require_once( DO_MAINTENANCE );
* based on nukePage by Rob Church
*/
-require_once( 'commandLine.inc' );
-require_once( 'nukePage.inc' );
+require_once( "Maintenance.php" );
-$ns = NS_MEDIAWIKI;
-$delete = false;
+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 );
+ }
-if (isset($options['ns']))
-{
- $ns = $options['ns'];
-}
+ public function execute() {
+ $ns = $this->getOption( 'ns', NS_MEDIAWIKI );
+ $delete = $this->getOption( 'delete', false );
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin();
-if (isset( $options['delete'] ) and $options['delete'])
-{
- $delete = true;
-}
+ $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;
-NukeNS( $ns, $delete);
+ while( $row = $dbw->fetchObject( $res ) ) {
+ //echo "$ns_name:".$row->page_title, "\n";
+ $title = Title::newFromText($row->page_title, $ns);
+ $id = $title->getArticleID();
-function NukeNS($ns_no, $delete) {
+ // Get corresponding revisions
+ $res2 = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_page = $id" );
+ $revs = array();
- $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" );
+ while( $row2 = $dbw->fetchObject( $res2 ) ) {
+ $revs[] = $row2->rev_id;
+ }
+ $count = count( $revs );
- $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();
+ //skip anything that looks modified (i.e. multiple revs)
+ if (($count == 1)) {
+ #echo $title->getPrefixedText(), "\t", $count, "\n";
+ $this->output( "delete: ", $title->getPrefixedText(), "\n" );
- // 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 );
+ //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();
- //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" );
- }
-
+ 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" );
+ }
+ }
}
-
+$maintClass = "NukeNS";
+require_once( DO_MAINTENANCE );
<?php
-
/**
* Erase a page record from the database
* Irreversible (can't use standard undelete) and does not update link tables
* @author Rob Church <robchur@gmail.com>
*/
-require_once( 'commandLine.inc' );
-require_once( 'nukePage.inc' );
+require_once( "Maintenance.php" );
-echo( "Erase Page Record\n\n" );
+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' ) );
+ }
-if( isset( $args[0] ) ) {
- NukePage( $args[0], true );
-} else {
- ShowUsage();
-}
+ 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 )" );
-/** 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" );
+ $dbw->commit();
+ }
}
+$maintClass = "NukePage";
+require_once( DO_MAINTENANCE );
\ No newline at end of file
* @ingroup Maintenance
*/
-require_once 'commandLine.inc';
-require_once 'populateLogSearch.inc';
+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__ );
-$db =& wfGetDB( DB_MASTER );
-if ( !$db->tableExists( 'log_search' ) ) {
- echo "log_search does not exist\n";
- exit( 1 );
+ # 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;
+ }
+ }
}
-migrate_log_params( $db );
+$maintClass = "PopulateLogSearch";
+require_once( DO_MAINTENANCE );
<?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 'commandLine.inc';
-require_once 'populateParentId.inc';
-
-$db =& wfGetDB( DB_MASTER );
-if ( !$db->tableExists( 'revision' ) ) {
- echo "revision table does not exist\n";
- exit( 1 );
+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;
+ }
+ }
}
-populate_rev_parent_id( $db );
+$maintClass = "PopulateParentId";
+require_once( DO_MAINTENANCE );
<?php
-
/**
* Purge old text records from the database
*
- * @file
* @ingroup Maintenance
* @author Rob Church <robchur@gmail.com>
*/
-$options = array( 'purge', 'help' );
-require_once( 'commandLine.inc' );
-require_once( 'purgeOldText.inc' );
-
-echo( "Purge Old Text\n\n" );
-
-if( @$options['help'] ) {
- ShowUsage();
-} else {
- PurgeRedundantText( @$options['purge'] );
-}
+require_once( "Maintenance.php" );
-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" );
+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') );
+ }
}
+$maintClass = "PurgeOldText";
+require_once( DO_MAINTENANCE );
<?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
*/
-$options = array( 'force', 'norc', 'quiet', 'report' );
-require_once( 'commandLine.inc' );
-require_once( 'reassignEdits.inc' );
-
-# 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" );
+require_once( "Maintenance.php" );
+
+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' ) );
}
-} else {
- ShowUsage();
-}
+ 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;
+ }
+
-/** 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" );
}
+$maintClass = "ReassignEdits";
+require_once( DO_MAINTENANCE );
+
* @ingroup Maintenance
*/
-/** */
-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
+require_once( "Maintenance.php" );
-$BATCH_SIZE = 100;
-# Do remaining chunk
-$end += $BATCH_SIZE - 1;
-$blockStart = $start;
-$blockEnd = $start + $BATCH_SIZE - 1;
+class RebuildFileCache extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Build file cache for content pages";
+ $this->addArgs( array( 'start', 'overwrite' ) );
+ }
-$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?
+ 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 );
}
- $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;
+
+ $_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 {
- echo "Page {$row->page_id} already cached\n";
- continue; // done already!
+ $this->output( "Page {$row->page_id} not cacheable\n" );
}
+ $dbw->commit(); // commit any changes
}
- 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";
+ $blockStart += $BATCH_SIZE;
+ $blockEnd += $BATCH_SIZE;
+ wfWaitForSlaves( 5 );
}
- $dbw->commit(); // commit any changes
+ $this->output( "Done!\n" );
+
+ // Remove these to be safe
+ if( isset($wgTitle) )
+ unset($wgTitle);
+ if( isset($wgArticle) )
+ unset($wgArticle);
}
- $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);
+require_once( "commandLine.inc" );
* @ingroup Maintenance
*/
-require_once( "commandLine.inc" );
-
-$dbw = wfGetDB( DB_MASTER );
-
-// Load the current value from the master
-$count = $dbw->selectField( 'site_stats', 'ss_images' );
-
-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 ) );
-
+require_once( "Maintenance.php" );
+
+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 );
+
+ // Load the current value from the master
+ $count = $dbw->selectField( 'site_stats', 'ss_images' );
+
+ $this->output( 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 ) );
+ }
+}
+
+$maintClass = "RefreshImageCount";
+require_once( DO_MAINTENANCE );
* @author Rob Church <robchur@gmail.com>
*/
-$options = array( 'help', 'delete' );
-require_once( 'commandLine.inc' );
-require_once( 'removeUnusedAccounts.inc' );
-echo( "Remove Unused Accounts\n\n" );
-$fname = 'removeUnusedAccounts';
+require_once( "Maintenance.php" );
-if( isset( $options['help'] ) ) {
- showHelp();
- exit(1);
-}
+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() {
-# 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;
+ $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();
}
- } 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" );
+ $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;
}
}
-$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" );
+$maintClass = "RemoveUnusedAccounts";
+require_once( DO_MAINTENANCE );
* @file
* @ingroup Maintenance
*/
-$optionsWithArgs = array( 'old', 'new', 'help' );
+
+require_once( "Maintenance.php" );
-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" );
+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" );
}
- $count++;
}
-print "Done! [$count tables]\n";
\ No newline at end of file
* @file
* @ingroup Maintenance
*/
+
+require_once( "Maintenance.php" );
-$optionsWithArgs = array( 'report' );
+class DumpRenderer extends Maintenance {
-require_once( 'commandLine.inc' );
+ private $count = 0;
+ private $outputDirectory, $startTime;
-class DumpRenderer {
- function __construct( $dir ) {
- $this->stderr = fopen( "php://stderr", "wt" );
- $this->outputDirectory = $dir;
- $this->count = 0;
+ 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 );
}
- function handleRevision( $rev ) {
+ 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 ) {
$title = $rev->getTitle();
if (!$title) {
- fprintf( $this->stderr, "Got bogus revision with null title!" );
+ $this->error( "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 );
- fprintf( $this->stderr, "%s\n", $filename, $display );
-
- // fixme
+ $this->output( sprintf( $this->stderr, "%s\n", $filename, $display ) );
+
+ // fixme (what?)
$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" .
"</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();
- }
}
-if( isset( $options['output-dir'] ) ) {
- $dir = $options['output-dir'];
-} else {
- wfDie( "Must use --output-dir=/some/dir\n" );
-}
-$render = new DumpRenderer( $dir );
-$render->run();
-
-
+$maintClass = "DumpRenderer";
+require_once( DO_MAINTENANCE );
* @ingroup Maintenance
*/
-$optionsWithArgs = array( 'maxjobs', 'type', 'procs' );
-$wgUseNormalUser = true;
-require_once( 'commandLine.inc' );
+require_once( "Maintenance.php" );
-if ( isset( $options['procs'] ) ) {
- $procs = intval( $options['procs'] );
- if ( $procs < 1 || $procs > 1000 ) {
- echo "Invalid argument to --procs\n";
- exit( 1 );
+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;
}
- $fc = new ForkController( $procs );
- if ( $fc->start( $procs ) != 'child' ) {
- exit( 0 );
- }
-}
-
-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" );
+ 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 );
+ }
}
- if ( $maxJobs && ++$n > $maxJobs ) {
- break 2;
+ $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;
+ }
+ }
}
}
-}
-
-function runJobsLog( $msg ) {
- print wfTimestamp( TS_DB ) . " $msg\n";
- wfDebugLog( 'runJobs', $msg );
+ /**
+ * 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 );
+ }
}
-
+$maintClass = "RunJobs";
+require_once( DO_MAINTENANCE );
* @author Tim Starling
* @author Ashar Voultoiz
*/
-require_once( 'commandLine.inc' );
-
-$dbw = wfGetDB( DB_MASTER );
-$count = $dbw->selectField( 'job', 'count(*)', '', 'runJobs.php' );
-print $count."\n";
+
+require_once( "Maintenance.php" );
+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 );
* @license GNU General Public License 2.0 or later
*/
-require_once( 'commandLine.inc' );
+require_once( "Maintenance.php" );
-#
-# 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 )) ;
+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 ) );
+ }
+ }
}
-// Show them
-foreach( $fields as $field => $desc ) {
- printf( "%-{$max_length_desc}s: %{$max_length_value}d\n", $desc, $stats->$field );
-}
+$maintClass = "ShowStats";
+require_once( DO_MAINTENANCE );
* @ingroup Database Maintenance
*/
-require_once( dirname(__FILE__) . '/' . 'commandLine.inc' );
+require_once( "Maintenance.php" );
-if ( isset( $options['help'] ) ) {
- echo "Send SQL queries to a MediaWiki database.\nUsage: php sql.php [<file>]\n";
- exit( 1 );
-}
+class MwSql extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Send SQL queries to a MediaWiki database";
+ }
-if ( isset( $args[0] ) ) {
- $fileName = $args[0];
- $file = fopen( $fileName, 'r' );
- $promptCallback = false;
-} else {
- $file = STDIN;
- $promptObject = new SqlPromptPrinter( "> " );
- $promptCallback = $promptObject->cb();
-}
+ 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 );
-if ( !$file ) {
- echo "Unable to open input file\n";
- exit( 1 );
-}
+ $dbw = wfGetDB( DB_MASTER );
+ $error = $dbw->sourceStream( $file, $promptCallback, array( $this, 'sqlPrintResult' ) );
+ if ( $error !== true ) {
+ $this->error( $error, true );
+ } else {
+ exit( 0 );
+ }
+ }
-$dbw =& wfGetDB( DB_MASTER );
-$error = $dbw->sourceStream( $file, $promptCallback, 'sqlPrintResult' );
-if ( $error !== true ) {
- echo $error;
- exit( 1 );
-} 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" );
+ }
+ }
}
-//-----------------------------------------------------------------------------
class SqlPromptPrinter {
function __construct( $prompt ) {
$this->prompt = $prompt;
}
}
-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";
- }
-}
-
-
+$maintClass = "MwSql";
+require_once( DO_MAINTENANCE );
<?php
/**
* Show statistics from memcached
- *
- * @file
* @ingroup Maintenance
*/
-require_once('commandLine.inc');
+require_once( "Maintenance.php" );
-if( get_class( $wgMemc ) == 'FakeMemCachedClient' ) {
- die("You are running FakeMemCachedClient, I can not provide any statistics.\n");
-}
-$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");
+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 );
+ }
}
-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 );
+
+$maintClass = "MemcachedStats";
+require_once( DO_MAINTENANCE );
+
+
* @ingroup Maintenance
*/
-$usage = <<<EOT
-Undelete a page
-Usage: php undelete.php [-u <user>] [-r <reason>] <pagename>
+require_once( "Maintenance.php" );
-EOT;
+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' ) );
+ }
-$optionsWithArgs = array( 'u', 'r' );
-require_once( 'commandLine.inc' );
+ public function execute() {
+ global $wgUser;
-$user = 'Command line script';
-$reason = '';
+ $user = $this->getOption( 'u', 'Command line script' );
+ $reason = $this->getOption( 'r', '' );
+ $pageName = $this->getArg();
-if ( isset( $options['u'] ) ) {
- $user = $options['u'];
+ $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['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 );
* 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>
*/
-$options = array( 'update', 'help' );
-require_once( 'commandLine.inc' );
-require_once( 'updateArticleCount.inc' );
-echo( "Update Article Count\n\n" );
+require_once( "Maintenance.php" );
-if( isset( $options['help'] ) && $options['help'] ) {
- echo( "Usage: php updateArticleCount.php [--update]\n\n" );
- echo( "--update : Update site statistics table\n" );
- exit( 0 );
-}
+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";
+ }
-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" );
+ /**
+ * 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;
}
-} else {
- echo( "failed.\n" );
}
-echo( "\n" );
+$maintClass = "UpdateArticleCount";
+require_once( DO_MAINTENANCE );
* 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.pos by default
- * LOCKTIME is how long the searchindex and cur tables will be locked for
+ * 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
* -q means quiet
*
- * @file
* @ingroup Maintenance
*/
+
+require_once( "Maintenance.php" );
-/** */
-$optionsWithArgs = array( 's', 'e', 'p' );
+class UpdateSearchIndex extends Maintenance {
-require_once( 'commandLine.inc' );
-require_once( 'updateSearchIndex.inc' );
+ 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 );
+ }
-if ( isset( $options['p'] ) ) {
- $posFile = $options['p'];
-} else {
- $posFile = 'searchUpdate.' . wfWikiId() . '.pos';
-}
+ 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['e'] ) ) {
- $end = $options['e'];
-} else {
- $end = wfTimestampNow();
-}
+ $wgDisableSearchUpdate = false;
-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 );
- }
-}
+ $dbw = wfGetDB( DB_MASTER );
+ $recentchanges = $dbw->tableName( 'recentchanges' );
-if ( isset( $options['l'] ) ) {
- $lockTime = $options['l'];
-} else {
- $lockTime = 20;
-}
+ $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 );
-$quiet = (bool)(@$options['q']);
+ $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__ );
-updateSearchIndex( $start, $end, $lockTime, $quiet );
-$file = fopen( $posFile, 'w' );
-fwrite( $file, $end );
-fclose( $file );
+ # 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 );
+ }
+}
+$maintClass = "UpdateSearchIndex";
+require_once( DO_MAINTENANCE );
* @file
* @ingroup Maintenance
*/
-$options = array('only','help');
+
+require_once( "Maintenance.php" );
-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 ';
+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' );
}
- 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;
+ public function execute() {
+ global $wgOut;
+ $wgOut->disable();
+ $dbw = wfGetDB( DB_MASTER );
- # --list : just show the name of pages
- if( @$options['list'] ) {
- print "$special\n";
- continue;
- }
-
- if ( !isset( $options['override'] ) && $wgDisableQueryPageUpdate && in_array( $special, $wgDisableQueryPageUpdate ) ) {
- printf("%-30s disabled\n", $special);
- 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() ) {
+ foreach( $wgSpecialPageCacheUpdates as $special => $call ) {
+ if( !is_callable($call) ) {
+ $this->error( "Uncallable function $call!\n" );
+ continue;
+ }
$t1 = explode( ' ', microtime() );
- # Do the query
- $num = $queryPage->recache( $limit === null ? $wgQueryCacheLimit : $limit );
+ call_user_func( $call, $dbw );
$t2 = explode( ' ', microtime() );
- 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 ';
+ $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 );
+ }
+
+ 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 ) );
}
- if ( $minutes ) {
- print $minutes . 'm ';
+ # 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();
}
- printf( "%.2fs\n", $seconds );
- }
- # 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";
+ # Wait for the slave to catch up
+ wfWaitForSlaves( 5 );
+ } else {
+ $this->output( "cheap, skipped\n" );
+ }
+ }
}
}
}
+
wfOut( "ok.\n" );
return;
}
-
- global $IP;
- require_once "$IP/maintenance/initStats.inc";
- wfInitStats();
+ SiteStats::init( false );
}
function do_active_users_init() {
* @ingroup Maintenance
*/
-require_once( "commandLine.inc" );
-if ( isset( $args[0] ) ) {
- wfWaitForSlaves($args[0]);
-} else {
- wfWaitForSlaves(10);
-}
+require_once( "Maintenance.php" );
+class WaitForSlave extends Maintenance {
+ public function __construct() {
+ $this->addArgs( array( 'maxlag' ) );
+ }
+ public function execute() {
+ wfWaitForSlaves( $this->getArg( 0, 10 ) );
+ }
+}
+$maintClass = "WaitForSlave";
+require_once( DO_MAINTENANCE );
$wgEnableProfileInfo = $wgProfileToDatabase = false;
require_once( './includes/WebStart.php' );
-@include_once( './AdminSettings.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, $wgDBadminuser, $wgDBadminpassword, $wgDBname, $wgDBtype;
+ global $wgDBprefix, $wgDBserver, $wgDBname, $wgDBtype;
$oldPrefix = $wgDBprefix;
$wgDBprefix = 'parsertest';