Merge maintenance-work branch:
authorChad Horohoe <demon@users.mediawiki.org>
Wed, 24 Jun 2009 02:02:37 +0000 (02:02 +0000)
committerChad Horohoe <demon@users.mediawiki.org>
Wed, 24 Jun 2009 02:02:37 +0000 (02:02 +0000)
* (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 :)

57 files changed:
AdminSettings.sample [deleted file]
RELEASE-NOTES
UPGRADE
config/index.php
docs/maintenance.txt [new file with mode: 0644]
docs/scripts.txt
includes/SiteStats.php
maintenance/Doxyfile
maintenance/Maintenance.php [new file with mode: 0644]
maintenance/README
maintenance/attachLatest.php
maintenance/benchmarkPurge.php
maintenance/changePassword.php
maintenance/checkAutoLoader.php
maintenance/checkBadRedirects.php
maintenance/checkImages.php
maintenance/checkUsernames.php
maintenance/clear_interwiki_cache.php
maintenance/clear_stats.php
maintenance/createAndPromote.php
maintenance/deleteBatch.php
maintenance/deleteDefaultMessages.php
maintenance/deleteImageMemcached.php
maintenance/deleteRevision.php
maintenance/doMaintenance.php [new file with mode: 0644]
maintenance/eval.php
maintenance/fetchText.php
maintenance/getLagTimes.php
maintenance/getSlaveServer.php
maintenance/initStats.php
maintenance/mctest.php
maintenance/moveBatch.php
maintenance/nextJobDB.php
maintenance/nukeNS.php
maintenance/nukePage.php
maintenance/populateLogSearch.php
maintenance/populateParentId.php
maintenance/purgeOldText.php
maintenance/reassignEdits.php
maintenance/rebuildFileCache.php
maintenance/refreshImageCount.php
maintenance/removeUnusedAccounts.php
maintenance/renameDbPrefix.php
maintenance/renderDump.php
maintenance/runJobs.php
maintenance/showJobs.php
maintenance/showStats.php
maintenance/sql.php
maintenance/stats.php
maintenance/undelete.php
maintenance/updateArticleCount.php
maintenance/updateSearchIndex.php
maintenance/updateSpecialPages.php
maintenance/updaters.inc
maintenance/waitForSlave.php
profileinfo.php
t/Search.inc

diff --git a/AdminSettings.sample b/AdminSettings.sample
deleted file mode 100644 (file)
index 8b6fe99..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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;
index 06b9ab2..b4a773b 100644 (file)
@@ -41,6 +41,7 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
   appropriate privileges. Creating this user with web-install page requires
   oci8.privileged_connect set to On in php.ini.
 * Removed UserrightsChangeableGroups hook introduced in 1.14
+* AdminSettings.php has been removed completely
 
 === New features in 1.16 ===
 
@@ -91,7 +92,10 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
   stripped from them.
 * Added a PHP port of CDB (constant database), for improved local caching when
   the DBA extension is not available.
-
+* (bug 14201) Create AdminSettings.php during wiki installation, in the same 
+  way as LocalSettings.php
+* (bug 16322) Allow maint scripts to accept DB user/pass over input or params 
+  if no AdminSettings.php
 
 === Bug fixes in 1.16 ===
 
@@ -200,6 +204,8 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
 * (bug 19294) Always show Sp-contributions-footer(-anon)
 * Attempts to restrict reading of pages while anonymous viewing is allowed
   via extensions not using the userCan hook and via $wgRevokePermissions now work.
+* (bug 19157) createAndPromote error on bad password
+* (bug 18768) Remove AdminSettings.php from MediaWiki core
 
 == API changes in 1.16 ==
 
diff --git a/UPGRADE b/UPGRADE
index 9d0e052..f55364f 100644 (file)
--- a/UPGRADE
+++ b/UPGRADE
@@ -42,8 +42,7 @@ You can also obtain the new files directly from our Subversion source code
 repository, via a checkout or export operation.
 
 Replace the existing MediaWiki files with the new. You should preserve the
-LocalSettings.php file, 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),
@@ -51,8 +50,8 @@ deleted file archives, and any custom skins.
 
 === 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
@@ -172,10 +171,10 @@ should be replaced with:
 === 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
@@ -185,8 +184,8 @@ lot of corrupt pages, particularly if your wiki is not in English.
 
 Additionally, as of 1.4.0 you can run an in-place upgrade script from
 the command line, keeping your existing LocalSettings.php. This requires
-that you 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:
index 1367b52..c017bb8 100644 (file)
@@ -615,6 +615,7 @@ print "<li style='font-weight:bold;color:green;font-size:110%'>Environment check
        $conf->RootUser = importPost( "RootUser", "root" );
        $conf->RootPW = importPost( "RootPW", "" );
        $useRoot = importCheck( 'useroot', false );
+       $conf->populateadmin = importCheck( 'populateadmin', false );
        $conf->LanguageCode = importPost( "LanguageCode", "en" );
        ## MySQL specific:
        $conf->DBprefix     = importPost( "DBprefix" );
@@ -1527,6 +1528,8 @@ if( count( $errs ) ) {
                <label class="column">Superuser account:</label>
                <input type="checkbox" name="useroot" id="useroot" <?php if( $useRoot ) { ?>checked="checked" <?php } ?> />
                &nbsp;<label for="useroot">Use superuser account</label>
+               <input type="checkbox" name="populateadmin" id="populateadmin" <?php if( $conf->populateadmin ) { ?>checked="checked" <?php } ?> />
+               &nbsp;<label for="populateadmin">Set as admin user for maintenance</label>
        </div>
        <div class="config-input"><?php aField( $conf, "RootUser", "Superuser name:", "text" ); ?></div>
        <div class="config-input"><?php aField( $conf, "RootPW", "Superuser password:", "password" ); ?></div>
@@ -1792,6 +1795,11 @@ function writeLocalSettings( $conf ) {
                # Needs literal string interpolation for the current style path
                $slconf['RightsIcon'] = $conf->RightsIcon;
        }
+       
+       if( $conf->populateadmin ) {
+               $slconf['DBadminuser'] = $conf->RootUser;
+               $slconf['DBadminpassword'] = $conf->RootPW;
+       }
 
        if( $conf->DBtype == 'mysql' ) {
                $dbsettings =
@@ -1899,6 +1907,10 @@ if ( \$wgCommandLineMode ) {
 
 {$dbsettings}
 
+## Database admin settings, used for maintenance scripts
+\$wgDBadminuser     = \"{$slconf['DBadminuser']}\";
+\$wgDBadminpassword = \"{$slconf['DBadminpassword']}\";
+
 ## Shared memory settings
 \$wgMainCacheType = $cacheType;
 \$wgMemCachedServers = $mcservers;
diff --git a/docs/maintenance.txt b/docs/maintenance.txt
new file mode 100644 (file)
index 0000000..d46d12e
--- /dev/null
@@ -0,0 +1,54 @@
+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
index f8228a4..2027d17 100644 (file)
@@ -35,10 +35,9 @@ Primary scripts:
     to force the profiler to save the informations in the database and apply the
     maintenance/archives/patch-profiling.sql patch to the database.
 
-    To enable the profileinfo.php itself, you'll need to 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
index 9427536..dd3fff1 100644 (file)
@@ -49,11 +49,8 @@ class SiteStats {
                        // clean schema with mwdumper.
                        wfDebug( __METHOD__ . ": initializing damaged or missing site_stats\n" );
 
-                       global $IP;
-                       require_once "$IP/maintenance/initStats.inc";
-
                        ob_start();
-                       wfInitStats();
+                       self::init( false );
                        ob_end_clean();
 
                        $row = self::doLoad( wfGetDB( DB_MASTER ) );
@@ -177,6 +174,63 @@ class SiteStats {
                }
                return true;
        }
+
+       /**
+        * Ported from initStats.inc.
+        * @param $update bool Whether to update the current stats write fresh
+        * @param $noViews bool When true, do not update the number of page views
+        */
+       function init( $update, $noViews = false ) {
+               $dbr = wfGetDB( DB_SLAVE );
+       
+               wfOut( "Counting total edits..." );
+               $edits = $dbr->selectField( 'revision', 'COUNT(*)', '', __METHOD__ );
+               $edits += $dbr->selectField( 'archive', 'COUNT(*)', '', __METHOD__ );
+               wfOut( "{$edits}\nCounting number of articles..." );
+       
+               global $wgContentNamespaces;
+               $good  = $dbr->selectField( 'page', 'COUNT(*)', array( 'page_namespace' => $wgContentNamespaces, 'page_is_redirect' => 0, 'page_len > 0' ), __METHOD__ );
+               wfOut( "{$good}\nCounting total pages..." );
+       
+               $pages = $dbr->selectField( 'page', 'COUNT(*)', '', __METHOD__ );
+               wfOut( "{$pages}\nCounting number of users..." );
+       
+               $users = $dbr->selectField( 'user', 'COUNT(*)', '', __METHOD__ );
+               wfOut( "{$users}\nCounting number of admins..." );
+       
+               $admin = $dbr->selectField( 'user_groups', 'COUNT(*)', array( 'ug_group' => 'sysop' ), __METHOD__ );
+               wfOut( "{$admin}\nCounting number of images..." );
+       
+               $image = $dbr->selectField( 'image', 'COUNT(*)', '', __METHOD__ );
+               wfOut( "{$image}\n" );
+       
+               if( !$noViews ) {
+                       wfOut( "Counting total page views..." );
+                       $views = $dbr->selectField( 'page', 'SUM(page_counter)', '', __METHOD__ );
+                       wfOut( "{$views}\n" );
+               }
+       
+               wfOut( "\nUpdating site statistics..." );
+       
+               $dbw = wfGetDB( DB_MASTER );
+               $values = array( 'ss_total_edits' => $edits,
+                                               'ss_good_articles' => $good,
+                                               'ss_total_pages' => $pages,
+                                               'ss_users' => $users,
+                                               'ss_admins' => $admin,
+                                               'ss_images' => $image );
+               $conds = array( 'ss_row_id' => 1 );
+               $views = array( 'ss_total_views' => isset( $views ) ? $views : 0 );
+       
+               if( $update ) {
+                       $dbw->update( 'site_stats', $values, $conds, __METHOD__ );
+               } else {
+                       $dbw->delete( 'site_stats', $conds, __METHOD__ );
+                       $dbw->insert( 'site_stats', array_merge( $values, $conds, $views ), __METHOD__ );
+               }
+       
+               wfOut( "done.\n" );
+       }
 }
 
 
index cdc748d..e4014e8 100644 (file)
@@ -135,7 +135,7 @@ FILE_PATTERNS          = *.c \
 RECURSIVE              = YES
 EXCLUDE                = 
 EXCLUDE_SYMLINKS       = YES
-EXCLUDE_PATTERNS       = LocalSettings.php AdminSettings.php
+EXCLUDE_PATTERNS       = LocalSettings.php
 EXAMPLE_PATH           = 
 EXAMPLE_PATTERNS       = *
 EXAMPLE_RECURSIVE      = NO
diff --git a/maintenance/Maintenance.php b/maintenance/Maintenance.php
new file mode 100644 (file)
index 0000000..6b5eda5
--- /dev/null
@@ -0,0 +1,639 @@
+<?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();
+       
+       }
+}
+
index e2215c1..6b23937 100644 (file)
@@ -10,8 +10,8 @@ proper installation.
 
 Certain scripts will require elevated access to the database. In order to
 provide this, first create a MySQL user with "all" permissions on the wiki
-database, and then 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 ===
 
index 8d680af..ca8d1aa 100644 (file)
  * @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 );
index 796e1da..f67c6c8 100644 (file)
@@ -6,74 +6,87 @@
  * @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 );
index 0fe8c0b..006562a 100644 (file)
  * @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 );
index 554395c..30e9069 100644 (file)
@@ -1,29 +1,40 @@
 <?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 );
index 48a4b0e..6a49f25 100644 (file)
@@ -1,30 +1,42 @@
 <?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 );
index 378caa3..2102bcf 100644 (file)
@@ -1,51 +1,63 @@
 <?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";
index 77565b9..e530a3b 100644 (file)
@@ -7,36 +7,33 @@
  * @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" );
index ce15477..88d08ba 100644 (file)
@@ -3,25 +3,36 @@
  * 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 );
index 4cacd74..f2a128f 100644 (file)
@@ -6,33 +6,34 @@
  * @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 );
index a5a8f88..a424336 100644 (file)
@@ -8,61 +8,53 @@
  * @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 );
index 5aeea78..6e44d63 100644 (file)
@@ -1,5 +1,4 @@
 <?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 );
index 77e8574..868fe7d 100644 (file)
@@ -1,48 +1,53 @@
 <?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 );
index 2c3afa8..caa48a1 100644 (file)
@@ -3,31 +3,27 @@
  * 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 );
 
@@ -35,8 +31,8 @@ class DeleteImageCache {
 
                $res = $dbr->select( 'image',
                        array( 'img_name' ),
-                       array( "img_timestamp < {$this->until}" ),
-                       $fname
+                       array( "img_timestamp < {$until}" ),
+                       __METHOD__
                );
 
                $i = 0;
@@ -44,29 +40,22 @@ class DeleteImageCache {
 
                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 );
index 0c203ab..6623de1 100644 (file)
@@ -6,42 +6,52 @@
  * @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 );
diff --git a/maintenance/doMaintenance.php b/maintenance/doMaintenance.php
new file mode 100644 (file)
index 0000000..dbb0352
--- /dev/null
@@ -0,0 +1,60 @@
+<?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
index a990a4d..7b90012 100644 (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 );
index 91b78be..f300318 100644 (file)
@@ -6,34 +6,48 @@
  * @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 );
index 0f750ca..49197e6 100644 (file)
@@ -4,26 +4,37 @@
  * @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 );
index 2525826..72bc138 100644 (file)
@@ -5,24 +5,32 @@
  * @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 );
index bb0cf30..2365718 100644 (file)
@@ -9,23 +9,22 @@
  * @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 );
index 64fea06..7cd5172 100644 (file)
@@ -8,58 +8,63 @@
  * @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 );
index 427e5d0..13dd40a 100644 (file)
@@ -1,9 +1,7 @@
 <?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 );
index 6af5cbe..fde6b49 100644 (file)
@@ -6,55 +6,71 @@
  * @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 );
index 4f9fe92..bd4eae0 100644 (file)
  * 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 );
index b3bfc76..ac2059f 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 /**
  * Erase a page record from the database
  * Irreversible (can't use standard undelete) and does not update link tables
@@ -9,21 +8,92 @@
  * @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
index 3679f7e..01b04b5 100644 (file)
@@ -9,13 +9,88 @@
  * @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 );
index 0173003..dd114fd 100644 (file)
 <?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 );
index 4a4be48..08f5ef1 100644 (file)
@@ -1,29 +1,24 @@
 <?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 );
index 5825644..daed219 100644 (file)
 <?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 );
+
index 8c01b90..c24ad61 100644 (file)
@@ -6,87 +6,96 @@
  * @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" );
index 14f842b..e0f22b8 100644 (file)
@@ -7,23 +7,34 @@
  * @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 );
 
index 5f74b65..bf03154 100644 (file)
@@ -8,63 +8,89 @@
  * @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 );
index 277f3b9..562f143 100644 (file)
@@ -6,62 +6,59 @@
  * @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
index 9779779..fe2d22b 100644 (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" .
@@ -76,27 +95,7 @@ class DumpRenderer {
                        "</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 );
index 1340a85..202c1e7 100644 (file)
  * @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 );
index 6e38c85..49b825f 100644 (file)
@@ -9,10 +9,19 @@
  * @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 );
index ef13f65..fc0a141 100644 (file)
  * @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 );
 
index ab6546b..1dbbe55 100644 (file)
@@ -7,38 +7,56 @@
  * @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;
@@ -53,17 +71,5 @@ class SqlPromptPrinter {
        }
 }
 
-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 );
index 00f79de..0e53033 100644 (file)
@@ -1,58 +1,73 @@
 <?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 );
+
+
 
 
index b7b7df9..9328f12 100644 (file)
@@ -6,34 +6,35 @@
  * @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 );
index 15033ad..3b704eb 100644 (file)
@@ -3,38 +3,84 @@
  * 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 );
index 5fa24ad..ee1bb2f 100644 (file)
  * 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 );
index 3eaa620..bbe4727 100644 (file)
  * @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" );
+                               }
+                       }
                }
        }
 }
+
index 4b77af4..5d3d22e 100644 (file)
@@ -1038,10 +1038,7 @@ function do_stats_init() {
                wfOut( "ok.\n" );
                return;
        }
-
-       global $IP;
-       require_once "$IP/maintenance/initStats.inc";
-       wfInitStats();
+       SiteStats::init( false );
 }
 
 function do_active_users_init() {
index 309d0e7..edeede8 100644 (file)
@@ -5,11 +5,16 @@
  * @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 );
index fcce6d7..afc05eb 100644 (file)
@@ -4,7 +4,6 @@ ini_set( 'zlib.output_compression', 'off' );
 $wgEnableProfileInfo = $wgProfileToDatabase = false;
 
 require_once( './includes/WebStart.php' );
-@include_once( './AdminSettings.php' );
 
 ?>
 <!--
index 2522930..a76acfd 100644 (file)
@@ -7,11 +7,10 @@ require 't/Test.php';
 require 'includes/Defines.php';
 require 'includes/ProfilerStub.php';
 require 'LocalSettings.php';
-require 'AdminSettings.php';
 require 'includes/Setup.php';
 
 function buildTestDatabase( $tables ) {
-       global $wgDBprefix, $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname, $wgDBtype;
+       global $wgDBprefix, $wgDBserver, $wgDBname, $wgDBtype;
        $oldPrefix = $wgDBprefix;
        $wgDBprefix = 'parsertest';