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
   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 ===
 
 
 === 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.
   stripped from them.
 * Added a PHP port of CDB (constant database), for improved local caching when
   the DBA extension is not available.
-
+* (bug 14201) Create AdminSettings.php during wiki installation, in the same 
+  way as LocalSettings.php
+* (bug 16322) Allow maint scripts to accept DB user/pass over input or params 
+  if no AdminSettings.php
 
 === Bug fixes in 1.16 ===
 
 
 === Bug 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 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 ==
 
 
 == 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
 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),
 
 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 ===
 
 
 === 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
 
 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
 === 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
 
 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
 
 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:
 
 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->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" );
        $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>
                <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>
        </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;
        }
                # 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 =
 
        if( $conf->DBtype == 'mysql' ) {
                $dbsettings =
@@ -1899,6 +1907,10 @@ if ( \$wgCommandLineMode ) {
 
 {$dbsettings}
 
 
 {$dbsettings}
 
+## Database admin settings, used for maintenance scripts
+\$wgDBadminuser     = \"{$slconf['DBadminuser']}\";
+\$wgDBadminpassword = \"{$slconf['DBadminpassword']}\";
+
 ## Shared memory settings
 \$wgMainCacheType = $cacheType;
 \$wgMemCachedServers = $mcservers;
 ## 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 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
 
   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" );
 
                        // clean schema with mwdumper.
                        wfDebug( __METHOD__ . ": initializing damaged or missing site_stats\n" );
 
-                       global $IP;
-                       require_once "$IP/maintenance/initStats.inc";
-
                        ob_start();
                        ob_start();
-                       wfInitStats();
+                       self::init( false );
                        ob_end_clean();
 
                        $row = self::doLoad( wfGetDB( DB_MASTER ) );
                        ob_end_clean();
 
                        $row = self::doLoad( wfGetDB( DB_MASTER ) );
@@ -177,6 +174,63 @@ class SiteStats {
                }
                return true;
        }
                }
                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
 RECURSIVE              = YES
 EXCLUDE                = 
 EXCLUDE_SYMLINKS       = YES
-EXCLUDE_PATTERNS       = LocalSettings.php AdminSettings.php
+EXCLUDE_PATTERNS       = LocalSettings.php
 EXAMPLE_PATH           = 
 EXAMPLE_PATTERNS       = *
 EXAMPLE_RECURSIVE      = NO
 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
 
 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 ===
 
 
 === Brief explanation of files ===
 
index 8d680af..ca8d1aa 100644 (file)
  * @ingroup Maintenance
  */
 
  * @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
  */
 
  * @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
  */
 
  * @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
 <?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
 <?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
 <?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
  */
 
  * @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,
                $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 ) ) {
                );
 
                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 );
                        }
                }
        }
 }
 
                                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.
  *
  * This script is used to clear the interwiki links for ALL languages in
  * memcached.
  *
- * @file
  * @ingroup Maintenance
  */
 
  * @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
  */
 
  * @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>
  */
 
  * @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
 <?php
-
 /**
  * Deletes a batch of pages
  * Usage: php deleteBatch.php [-u <user>] [-r <reason>] [-i <interval>] [listfile]
 /**
  * Deletes a batch of pages
  * Usage: php deleteBatch.php [-u <user>] [-r <reason>] [-i <interval>] [listfile]
  * @file
  * @ingroup Maintenance
  */
  * @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
 <?php
-
 /**
  * Deletes all pages in the MediaWiki namespace which were last edited by 
  * "MediaWiki default".
  *
 /**
  * Deletes all pages in the MediaWiki namespace which were last edited by 
  * "MediaWiki default".
  *
- * @file
  * @ingroup Maintenance
  */
 
  * @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:
  * 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
  */
 
  *
  * @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;
                global $wgMemc;
-               $fname = 'DeleteImageCache::main';
+
+               $until = preg_replace( "/[^\d]/", '', $this->getOption('until') );
+               $sleep = (int)$this->getOption('sleep') * 1000; // milliseconds
 
                ini_set( 'display_errors', false );
 
 
                ini_set( 'display_errors', false );
 
@@ -35,8 +31,8 @@ class DeleteImageCache {
 
                $res = $dbr->select( 'image',
                        array( 'img_name' ),
 
                $res = $dbr->select( 'image',
                        array( 'img_name' ),
-                       array( "img_timestamp < {$this->until}" ),
-                       $fname
+                       array( "img_timestamp < {$until}" ),
+                       __METHOD__
                );
 
                $i = 0;
                );
 
                $i = 0;
@@ -44,29 +40,22 @@ class DeleteImageCache {
 
                while ( $row = $dbr->fetchObject( $res ) ) {
                        if ($i % $this->report == 0)
 
                while ( $row = $dbr->fetchObject( $res ) ) {
                        if ($i % $this->report == 0)
-                               printf("%s: %13s done (%s)\n", wfWikiID(), "$i/$total", wfPercent( $i / $total * 100 ));
+                               $this->output( sprintf("%s: %13s done (%s)\n", wfWikiID(), "$i/$total", wfPercent( $i / $total * 100 ) ) );
                        $md5 = md5( $row->img_name );
                        $wgMemc->delete( wfMemcKey( 'Image', $md5 ) );
 
                        $md5 = md5( $row->img_name );
                        $wgMemc->delete( wfMemcKey( 'Image', $md5 ) );
 
-                       if ($this->sleep != 0)
-                               usleep( $this->sleep );
+                       if ($sleep != 0)
+                               usleep( $sleep );
 
                        ++$i;
                }
        }
 
 
                        ++$i;
                }
        }
 
-       function getImageCount() {
-               $fname = 'DeleteImageCache::getImageCount';
-
+       private function getImageCount() {
                $dbr = wfGetDB( DB_SLAVE );
                $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
  */
 
  * @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
  */
 
  * @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
  */
 
  * @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
  */
 
  * @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 {
                } 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
  */
  * @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
  */
  * @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
  */
 
  * @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
 <?php
-
 /**
  * Maintenance script to move a batch of pages
  *
 /**
  * Maintenance script to move a batch of pages
  *
- * @file
  * @ingroup Maintenance
  * @author Tim Starling
  *
  * @ingroup Maintenance
  * @author Tim Starling
  *
  * e.g. immobile_namespace for namespaces which can't be moved
  */
 
  * 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
  */
 
  * @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
  */
 
  * 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
 <?php
-
 /**
  * Erase a page record from the database
  * Irreversible (can't use standard undelete) and does not update link tables
 /**
  * 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>
  */
 
  * @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
  */
 
  * @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
 <?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.
  */
 
 /*
  * 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
 <?php
-
 /**
  * Purge old text records from the database
  *
 /**
  * Purge old text records from the database
  *
- * @file
  * @ingroup Maintenance
  * @author Rob Church <robchur@gmail.com>
  */
 
  * @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
 <?php
-
 /**
  * Reassign edits from a user or IP address to another user
  *
 /**
  * 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
  */
 
  * @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
  */
 
  * @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 {
                                } 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
  */
 
  * @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>
  */
 
  * @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
  */
  * @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
  */
  * @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) {
                $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();
                        return;
                }
                $display = $title->getPrefixedText();
-               
+
                $this->count++;
                $this->count++;
-               
+
                $sanitized = rawurlencode( $display );
                $filename = sprintf( "%s/wiki-%07d-%s.html", 
                        $this->outputDirectory,
                        $this->count,
                        $sanitized );
                $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 );
                $user = new User();
                $parser = new Parser();
                $options = ParserOptions::newFromUser( $user );
-               
+
                $output = $parser->parse( $rev->getText(), $title, $options );
                $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" .
                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>" );
        }
                        "</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
  */
 
  * @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
  */
  * @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
  */
 
  * @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
  */
 
  * @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;
 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
 <?php
 /**
  * Show statistics from memcached
- *
- * @file
  * @ingroup Maintenance
  */
 
  * @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
  */
 
  * @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
  *
  * 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>
  */
 
  * @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
  * 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
  *
  * -q means quiet
  *
- * @file
  * @ingroup Maintenance
  */
  * @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
  */
  * @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() );
                        $t1 = explode( ' ', microtime() );
-                       # Do the query
-                       $num = $queryPage->recache( $limit === null ? $wgQueryCacheLimit : $limit );
+                       call_user_func( $call, $dbw );
                        $t2 = explode( ' ', microtime() );
                        $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;
        }
                wfOut( "ok.\n" );
                return;
        }
-
-       global $IP;
-       require_once "$IP/maintenance/initStats.inc";
-       wfInitStats();
+       SiteStats::init( false );
 }
 
 function do_active_users_init() {
 }
 
 function do_active_users_init() {
index 309d0e7..edeede8 100644 (file)
@@ -5,11 +5,16 @@
  * @ingroup Maintenance
  */
 
  * @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' );
 $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 'includes/Defines.php';
 require 'includes/ProfilerStub.php';
 require 'LocalSettings.php';
-require 'AdminSettings.php';
 require 'includes/Setup.php';
 
 function buildTestDatabase( $tables ) {
 require 'includes/Setup.php';
 
 function buildTestDatabase( $tables ) {
-       global $wgDBprefix, $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname, $wgDBtype;
+       global $wgDBprefix, $wgDBserver, $wgDBname, $wgDBtype;
        $oldPrefix = $wgDBprefix;
        $wgDBprefix = 'parsertest';
 
        $oldPrefix = $wgDBprefix;
        $wgDBprefix = 'parsertest';