r52336 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r52335‎ | r52336 | r52337 >
Date:02:02, 24 June 2009
Author:demon
Status:reverted (Comments)
Tags:
Comment:
Merge maintenance-work branch:
* (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 :)
Modified paths:
  • /trunk/phase3 (modified) (history)
  • /trunk/phase3/AdminSettings.sample (deleted) (history)
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/UPGRADE (modified) (history)
  • /trunk/phase3/config/index.php (modified) (history)
  • /trunk/phase3/docs/maintenance.txt (added) (history)
  • /trunk/phase3/docs/scripts.txt (modified) (history)
  • /trunk/phase3/includes/SiteStats.php (modified) (history)
  • /trunk/phase3/maintenance/Doxyfile (modified) (history)
  • /trunk/phase3/maintenance/Maintenance.php (added) (history)
  • /trunk/phase3/maintenance/README (modified) (history)
  • /trunk/phase3/maintenance/attachLatest.php (modified) (history)
  • /trunk/phase3/maintenance/benchmarkPurge.php (modified) (history)
  • /trunk/phase3/maintenance/changePassword.php (modified) (history)
  • /trunk/phase3/maintenance/checkAutoLoader.php (modified) (history)
  • /trunk/phase3/maintenance/checkBadRedirects.php (modified) (history)
  • /trunk/phase3/maintenance/checkImages.php (modified) (history)
  • /trunk/phase3/maintenance/checkUsernames.php (modified) (history)
  • /trunk/phase3/maintenance/clear_interwiki_cache.php (modified) (history)
  • /trunk/phase3/maintenance/clear_stats.php (modified) (history)
  • /trunk/phase3/maintenance/createAndPromote.php (modified) (history)
  • /trunk/phase3/maintenance/deleteBatch.php (modified) (history)
  • /trunk/phase3/maintenance/deleteDefaultMessages.php (modified) (history)
  • /trunk/phase3/maintenance/deleteImageMemcached.php (modified) (history)
  • /trunk/phase3/maintenance/deleteRevision.php (modified) (history)
  • /trunk/phase3/maintenance/doMaintenance.php (added) (history)
  • /trunk/phase3/maintenance/eval.php (modified) (history)
  • /trunk/phase3/maintenance/fetchText.php (modified) (history)
  • /trunk/phase3/maintenance/getLagTimes.php (modified) (history)
  • /trunk/phase3/maintenance/getSlaveServer.php (modified) (history)
  • /trunk/phase3/maintenance/initStats.php (modified) (history)
  • /trunk/phase3/maintenance/mctest.php (modified) (history)
  • /trunk/phase3/maintenance/moveBatch.php (modified) (history)
  • /trunk/phase3/maintenance/nextJobDB.php (modified) (history)
  • /trunk/phase3/maintenance/nukeNS.php (modified) (history)
  • /trunk/phase3/maintenance/nukePage.php (modified) (history)
  • /trunk/phase3/maintenance/populateLogSearch.php (modified) (history)
  • /trunk/phase3/maintenance/populateParentId.php (modified) (history)
  • /trunk/phase3/maintenance/purgeOldText.php (modified) (history)
  • /trunk/phase3/maintenance/reassignEdits.php (modified) (history)
  • /trunk/phase3/maintenance/rebuildFileCache.php (modified) (history)
  • /trunk/phase3/maintenance/refreshImageCount.php (modified) (history)
  • /trunk/phase3/maintenance/removeUnusedAccounts.php (modified) (history)
  • /trunk/phase3/maintenance/renameDbPrefix.php (modified) (history)
  • /trunk/phase3/maintenance/renderDump.php (modified) (history)
  • /trunk/phase3/maintenance/runJobs.php (modified) (history)
  • /trunk/phase3/maintenance/showJobs.php (modified) (history)
  • /trunk/phase3/maintenance/showStats.php (modified) (history)
  • /trunk/phase3/maintenance/sql.php (modified) (history)
  • /trunk/phase3/maintenance/stats.php (modified) (history)
  • /trunk/phase3/maintenance/undelete.php (modified) (history)
  • /trunk/phase3/maintenance/updateArticleCount.php (modified) (history)
  • /trunk/phase3/maintenance/updateSearchIndex.php (modified) (history)
  • /trunk/phase3/maintenance/updateSpecialPages.php (modified) (history)
  • /trunk/phase3/maintenance/updaters.inc (modified) (history)
  • /trunk/phase3/maintenance/waitForSlave.php (modified) (history)
  • /trunk/phase3/profileinfo.php (modified) (history)
  • /trunk/phase3/t/Search.inc (modified) (history)

Diff [purge]

Index: trunk/phase3/AdminSettings.sample
@@ -1,32 +0,0 @@
2 -<?php
3 -/**
4 - * This file should be copied to AdminSettings.php, and modified
5 - * to reflect local settings. It is required for the maintenance
6 - * scripts which run on the command line, as an extra security
7 - * measure to allow using a separate user account with higher
8 - * privileges to do maintenance work.
9 - *
10 - * Developers: Do not check AdminSettings.php into Subversion
11 - */
12 -
13 -/*
14 - * This data is used by all database maintenance scripts
15 - * (see directory maintenance/). The SQL user MUST BE
16 - * MANUALLY CREATED or set to an existing user with
17 - * necessary permissions.
18 - *
19 - * This is not to be confused with sysop accounts for the
20 - * wiki.
21 - *
22 - * NOTE: for PostgreSQL this should be set to the same user and
23 - * password as the web user, that is, the same as $wgDBuser and
24 - * $wgDBpassword in LocalSettings.php. This is necessary to
25 - * ensure that the owner for new tables is set correctly.
26 - */
27 -$wgDBadminuser = 'wikiadmin';
28 -$wgDBadminpassword = 'adminpass';
29 -
30 -/*
31 - * Whether to enable the profileinfo.php script.
32 - */
33 -$wgEnableProfileInfo = false;
Index: trunk/phase3/docs/scripts.txt
@@ -35,10 +35,9 @@
3636 to force the profiler to save the informations in the database and apply the
3737 maintenance/archives/patch-profiling.sql patch to the database.
3838
39 - To enable the profileinfo.php itself, you'll need to create the
40 - AdminSettings.php file (see AdminSettings.sample for more information) and
41 - set $wgEnableProfileInfo to true in that file. See also
42 - http://www.mediawiki.org/wiki/How_to_debug#Profiling.
 39+ To enable the profileinfo.php itself, you'll need to set $wgDBadminuser
 40+ and $wgDBadminpassword in your LocalSettings.php, as well as $wgEnableProfileInfo
 41+ See also http://www.mediawiki.org/wiki/How_to_debug#Profiling.
4342
4443 redirect.php
4544 Script that only redirect to the article passed in the wpDropdown parameter
Index: trunk/phase3/docs/maintenance.txt
@@ -0,0 +1,54 @@
 2+Prior to version 1.16, maintenance scripts were a hodgepodge of code that
 3+had no cohesion or formal method of action. Beginning in 1.16, maintenance
 4+scripts have been cleaned up to use a unified class.
 5+
 6+1. Directory structure
 7+2. How to run a script
 8+3. How to write your own
 9+
 10+1. DIRECTORY STRUCTURE
 11+ The /maintenance directory of a MediaWiki installation contains several
 12+subdirectories, all of which have unique purposes.
 13+
 14+2. HOW TO RUN A SCRIPT
 15+ Ridiculously simple, just call 'php someScript.php' that's in the top-
 16+level /maintenance directory.
 17+
 18+Example:
 19+ php clear_stats.php
 20+
 21+The following parameters are available to all maintenance scripts
 22+--help : Print a help message
 23+--quiet : Quiet non-error output
 24+--dbuser : The database user to use for the script (if needed)
 25+--dbpass : Same as above (if needed)
 26+
 27+3. HOW TO WRITE YOUR OWN
 28+Make a file in the maintenance directory called myScript.php or something.
 29+In it, write the following:
 30+
 31+==BEGIN==
 32+
 33+<?php
 34+
 35+require_once( "Maintenance.php" );
 36+
 37+class DemoMaint extends Maintenance {
 38+
 39+ public function __construct() {
 40+ parent::__construct();
 41+ }
 42+
 43+ protected function execute() {
 44+ }
 45+}
 46+
 47+$maintClass = "DemoMaint";
 48+require_once( DO_MAINTENANCE );
 49+
 50+==END==
 51+
 52+That's it. In the execute() method, you have access to all of the normal
 53+MediaWiki functions, so you can get a DB connection, use the cache, etc.
 54+For full docs on the Maintenance class, see the auto-generated docs at
 55+http://svn.wikimedia.org/doc/classMaintenance.html
\ No newline at end of file
Property changes on: trunk/phase3/docs/maintenance.txt
___________________________________________________________________
Added: svn:eol-style
156 + native
Index: trunk/phase3/profileinfo.php
@@ -4,7 +4,6 @@
55 $wgEnableProfileInfo = $wgProfileToDatabase = false;
66
77 require_once( './includes/WebStart.php' );
8 -@include_once( './AdminSettings.php' );
98
109 ?>
1110 <!--
Index: trunk/phase3/t/Search.inc
@@ -7,11 +7,10 @@
88 require 'includes/Defines.php';
99 require 'includes/ProfilerStub.php';
1010 require 'LocalSettings.php';
11 -require 'AdminSettings.php';
1211 require 'includes/Setup.php';
1312
1413 function buildTestDatabase( $tables ) {
15 - global $wgDBprefix, $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname, $wgDBtype;
 14+ global $wgDBprefix, $wgDBserver, $wgDBname, $wgDBtype;
1615 $oldPrefix = $wgDBprefix;
1716 $wgDBprefix = 'parsertest';
1817
Index: trunk/phase3/includes/SiteStats.php
@@ -49,11 +49,8 @@
5050 // clean schema with mwdumper.
5151 wfDebug( __METHOD__ . ": initializing damaged or missing site_stats\n" );
5252
53 - global $IP;
54 - require_once "$IP/maintenance/initStats.inc";
55 -
5653 ob_start();
57 - wfInitStats();
 54+ self::init( false );
5855 ob_end_clean();
5956
6057 $row = self::doLoad( wfGetDB( DB_MASTER ) );
@@ -177,6 +174,63 @@
178175 }
179176 return true;
180177 }
 178+
 179+ /**
 180+ * Ported from initStats.inc.
 181+ * @param $update bool Whether to update the current stats write fresh
 182+ * @param $noViews bool When true, do not update the number of page views
 183+ */
 184+ function init( $update, $noViews = false ) {
 185+ $dbr = wfGetDB( DB_SLAVE );
 186+
 187+ wfOut( "Counting total edits..." );
 188+ $edits = $dbr->selectField( 'revision', 'COUNT(*)', '', __METHOD__ );
 189+ $edits += $dbr->selectField( 'archive', 'COUNT(*)', '', __METHOD__ );
 190+ wfOut( "{$edits}\nCounting number of articles..." );
 191+
 192+ global $wgContentNamespaces;
 193+ $good = $dbr->selectField( 'page', 'COUNT(*)', array( 'page_namespace' => $wgContentNamespaces, 'page_is_redirect' => 0, 'page_len > 0' ), __METHOD__ );
 194+ wfOut( "{$good}\nCounting total pages..." );
 195+
 196+ $pages = $dbr->selectField( 'page', 'COUNT(*)', '', __METHOD__ );
 197+ wfOut( "{$pages}\nCounting number of users..." );
 198+
 199+ $users = $dbr->selectField( 'user', 'COUNT(*)', '', __METHOD__ );
 200+ wfOut( "{$users}\nCounting number of admins..." );
 201+
 202+ $admin = $dbr->selectField( 'user_groups', 'COUNT(*)', array( 'ug_group' => 'sysop' ), __METHOD__ );
 203+ wfOut( "{$admin}\nCounting number of images..." );
 204+
 205+ $image = $dbr->selectField( 'image', 'COUNT(*)', '', __METHOD__ );
 206+ wfOut( "{$image}\n" );
 207+
 208+ if( !$noViews ) {
 209+ wfOut( "Counting total page views..." );
 210+ $views = $dbr->selectField( 'page', 'SUM(page_counter)', '', __METHOD__ );
 211+ wfOut( "{$views}\n" );
 212+ }
 213+
 214+ wfOut( "\nUpdating site statistics..." );
 215+
 216+ $dbw = wfGetDB( DB_MASTER );
 217+ $values = array( 'ss_total_edits' => $edits,
 218+ 'ss_good_articles' => $good,
 219+ 'ss_total_pages' => $pages,
 220+ 'ss_users' => $users,
 221+ 'ss_admins' => $admin,
 222+ 'ss_images' => $image );
 223+ $conds = array( 'ss_row_id' => 1 );
 224+ $views = array( 'ss_total_views' => isset( $views ) ? $views : 0 );
 225+
 226+ if( $update ) {
 227+ $dbw->update( 'site_stats', $values, $conds, __METHOD__ );
 228+ } else {
 229+ $dbw->delete( 'site_stats', $conds, __METHOD__ );
 230+ $dbw->insert( 'site_stats', array_merge( $values, $conds, $views ), __METHOD__ );
 231+ }
 232+
 233+ wfOut( "done.\n" );
 234+ }
181235 }
182236
183237
Index: trunk/phase3/RELEASE-NOTES
@@ -41,6 +41,7 @@
4242 appropriate privileges. Creating this user with web-install page requires
4343 oci8.privileged_connect set to On in php.ini.
4444 * Removed UserrightsChangeableGroups hook introduced in 1.14
 45+* AdminSettings.php has been removed completely
4546
4647 === New features in 1.16 ===
4748
@@ -91,8 +92,11 @@
9293 stripped from them.
9394 * Added a PHP port of CDB (constant database), for improved local caching when
9495 the DBA extension is not available.
 96+* (bug 14201) Create AdminSettings.php during wiki installation, in the same
 97+ way as LocalSettings.php
 98+* (bug 16322) Allow maint scripts to accept DB user/pass over input or params
 99+ if no AdminSettings.php
95100
96 -
97101 === Bug fixes in 1.16 ===
98102
99103 * (bug 18031) Make namespace selector on Special:Export remember the previous
@@ -200,6 +204,8 @@
201205 * (bug 19294) Always show Sp-contributions-footer(-anon)
202206 * Attempts to restrict reading of pages while anonymous viewing is allowed
203207 via extensions not using the userCan hook and via $wgRevokePermissions now work.
 208+* (bug 19157) createAndPromote error on bad password
 209+* (bug 18768) Remove AdminSettings.php from MediaWiki core
204210
205211 == API changes in 1.16 ==
206212
Index: trunk/phase3/maintenance/showJobs.php
@@ -9,10 +9,19 @@
1010 * @author Tim Starling
1111 * @author Ashar Voultoiz
1212 */
13 -require_once( 'commandLine.inc' );
 13+
 14+require_once( "Maintenance.php" );
1415
15 -$dbw = wfGetDB( DB_MASTER );
16 -$count = $dbw->selectField( 'job', 'count(*)', '', 'runJobs.php' );
17 -print $count."\n";
 16+class ShowJobs extends Maintenance {
 17+ public function __construct() {
 18+ parent::__construct();
 19+ $this->mDescription = "Show number of jobs waiting in master database";
 20+ }
 21+ public function execute() {
 22+ $dbw = wfGetDB( DB_MASTER );
 23+ $this->output( $dbw->selectField( 'job', 'count(*)', '', 'runJobs.php' ) . "\n" );
 24+ }
 25+}
1826
19 -
 27+$maintClass = "ShowJobs";
 28+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/updateSearchIndex.php
@@ -5,58 +5,159 @@
66 * Usage: php updateSearchIndex.php [-s START] [-e END] [-p POSFILE] [-l LOCKTIME] [-q]
77 * Where START is the starting timestamp
88 * END is the ending timestamp
9 - * POSFILE is a file to load timestamps from and save them to, searchUpdate.pos by default
10 - * LOCKTIME is how long the searchindex and cur tables will be locked for
 9+ * POSFILE is a file to load timestamps from and save them to, searchUpdate.WIKI_ID.pos by default
 10+ * LOCKTIME is how long the searchindex and revision tables will be locked for
1111 * -q means quiet
1212 *
13 - * @file
1413 * @ingroup Maintenance
1514 */
 15+
 16+require_once( "Maintenance.php" );
1617
17 -/** */
18 -$optionsWithArgs = array( 's', 'e', 'p' );
 18+class UpdateSearchIndex extends Maintenance {
1919
20 -require_once( 'commandLine.inc' );
21 -require_once( 'updateSearchIndex.inc' );
 20+ public function __construct() {
 21+ parent::__construct();
 22+ $this->mDescription = "Script for periodic off-peak updating of the search index";
 23+ $this->addParam( 's', 'starting timestamp', false, true );
 24+ $this->addParam( 'e', 'Ending timestamp', false, true );
 25+ $this->addParam( 'p', 'File for saving/loading timestamps, searchUpdate.WIKI_ID.pos by default', false, true );
 26+ $this->addParam( 'l', 'How long the searchindex and revision tables will be locked for', false, true );
 27+ }
2228
23 -if ( isset( $options['p'] ) ) {
24 - $posFile = $options['p'];
25 -} else {
26 - $posFile = 'searchUpdate.' . wfWikiId() . '.pos';
27 -}
 29+ public function execute() {
 30+ $posFile = $this->getOption( 'p', 'searchUpdate.' . wfWikiId() . '.pos' );
 31+ $end = $this->getOption( 'e', wfTimestampNow() );
 32+ if ( $this->hasOption( 's' ) ) {
 33+ $start = $this->getOption('s');
 34+ } elseif( is_readable( 'searchUpdate.pos' ) ) {
 35+ # B/c to the old position file name which was hardcoded
 36+ # We can safely delete the file when we're done though.
 37+ $start = file_get_contents( 'searchUpdate.pos' );
 38+ unlink( 'searchUpdate.pos' );
 39+ } else {
 40+ $start = @file_get_contents( $posFile );
 41+ if ( !$start ) {
 42+ $start = wfTimestamp( TS_MW, time() - 86400 );
 43+ }
 44+ }
 45+ $lockTime = $this->getOption( 'l', 20 );
 46+
 47+ $this->updateSearchIndex( $start, $end, $lockTime );
 48+ $file = fopen( $posFile, 'w' );
 49+ fwrite( $file, $end );
 50+ fclose( $file );
 51+ }
 52+
 53+ private function updateSearchIndex( $start, $end, $maxLockTime ) {
 54+ global $wgDisableSearchUpdate;
2855
29 -if ( isset( $options['e'] ) ) {
30 - $end = $options['e'];
31 -} else {
32 - $end = wfTimestampNow();
33 -}
 56+ $wgDisableSearchUpdate = false;
3457
35 -if ( isset( $options['s'] ) ) {
36 - $start = $options['s'];
37 -} elseif( is_readable( 'searchUpdate.pos' ) ) {
38 - # B/c to the old position file name which was hardcoded
39 - # We can safely delete the file when we're done though.
40 - $start = file_get_contents( 'searchUpdate.pos' );
41 - unlink( 'searchUpdate.pos' );
42 -} else {
43 - $start = @file_get_contents( $posFile );
44 - if ( !$start ) {
45 - $start = wfTimestamp( TS_MW, time() - 86400 );
46 - }
47 -}
 58+ $dbw = wfGetDB( DB_MASTER );
 59+ $recentchanges = $dbw->tableName( 'recentchanges' );
4860
49 -if ( isset( $options['l'] ) ) {
50 - $lockTime = $options['l'];
51 -} else {
52 - $lockTime = 20;
53 -}
 61+ $this->output( "Updating searchindex between $start and $end\n" );
5462
55 -$quiet = (bool)(@$options['q']);
 63+ # Select entries from recentchanges which are on top and between the specified times
 64+ $start = $dbw->strencode( $start );
 65+ $end = $dbw->strencode( $end );
5666
57 -updateSearchIndex( $start, $end, $lockTime, $quiet );
 67+ $page = $dbw->tableName( 'page' );
 68+ $sql = "SELECT rc_cur_id,rc_type,rc_moved_to_ns,rc_moved_to_title FROM $recentchanges
 69+ JOIN $page ON rc_cur_id=page_id AND rc_this_oldid=page_latest
 70+ WHERE rc_timestamp BETWEEN '$start' AND '$end'
 71+ ";
 72+ $res = $dbw->query( $sql, __METHOD__ );
5873
59 -$file = fopen( $posFile, 'w' );
60 -fwrite( $file, $end );
61 -fclose( $file );
6274
 75+ # Lock searchindex
 76+ if ( $maxLockTime ) {
 77+ $this->output( " --- Waiting for lock ---" );
 78+ $this->lockSearchindex( $dbw );
 79+ $lockTime = time();
 80+ $this->output( "\n" );
 81+ }
6382
 83+ # Loop through the results and do a search update
 84+ while ( $row = $dbw->fetchObject( $res ) ) {
 85+ # Allow reads to be processed
 86+ if ( $maxLockTime && time() > $lockTime + $maxLockTime ) {
 87+ $this->output( " --- Relocking ---" );
 88+ $this->relockSearchindex( $dbw );
 89+ $lockTime = time();
 90+ $this->output( "\n" );
 91+ }
 92+ if ( $row->rc_type == RC_LOG ) {
 93+ continue;
 94+ } elseif ( $row->rc_type == RC_MOVE || $row->rc_type == RC_MOVE_OVER_REDIRECT ) {
 95+ # Rename searchindex entry
 96+ $titleObj = Title::makeTitle( $row->rc_moved_to_ns, $row->rc_moved_to_title );
 97+ $title = $titleObj->getPrefixedDBkey();
 98+ $this->output( "$title..." );
 99+ $u = new SearchUpdate( $row->rc_cur_id, $title, false );
 100+ $this->output( "\n" );
 101+ } else {
 102+ // Get current revision
 103+ $rev = Revision::loadFromPageId( $dbw, $row->rc_cur_id );
 104+ if( $rev ) {
 105+ $titleObj = $rev->getTitle();
 106+ $title = $titleObj->getPrefixedDBkey();
 107+ $this->output( $title );
 108+ # Update searchindex
 109+ $u = new SearchUpdate( $row->rc_cur_id, $titleObj->getText(), $rev->getText() );
 110+ $u->doUpdate();
 111+ $this->output( "\n" );
 112+ }
 113+ }
 114+ }
 115+
 116+ # Unlock searchindex
 117+ if ( $maxLockTime ) {
 118+ $this->output( " --- Unlocking --" );
 119+ $this->unlockSearchindex( $dbw );
 120+ $this->output( "\n" );
 121+ }
 122+ $this->output( "Done\n" );
 123+ }
 124+
 125+ /**
 126+ * Lock the search index
 127+ * @param &$db Database object
 128+ */
 129+ private function lockSearchindex( &$db ) {
 130+ $write = array( 'searchindex' );
 131+ $read = array( 'page', 'revision', 'text', 'interwiki' );
 132+ $items = array();
 133+
 134+ foreach( $write as $table ) {
 135+ $items[] = $db->tableName( $table ) . ' LOW_PRIORITY WRITE';
 136+ }
 137+ foreach( $read as $table ) {
 138+ $items[] = $db->tableName( $table ) . ' READ';
 139+ }
 140+ $sql = "LOCK TABLES " . implode( ',', $items );
 141+ $db->query( $sql, 'updateSearchIndex.php ' . __METHOD__ );
 142+ }
 143+
 144+ /**
 145+ * Unlock the tables
 146+ * @param &$db Database object
 147+ */
 148+ private function unlockSearchindex( &$db ) {
 149+ $db->query( "UNLOCK TABLES", 'updateSearchIndex.php ' . __METHOD__ );
 150+ }
 151+
 152+ /**
 153+ * Unlock and lock again
 154+ * Since the lock is low-priority, queued reads will be able to complete
 155+ * @param &$db Database object
 156+ */
 157+ private function relockSearchindex( &$db ) {
 158+ $this->unlockSearchindex( $db );
 159+ $this->lockSearchindex( $db );
 160+ }
 161+}
 162+
 163+$maintClass = "UpdateSearchIndex";
 164+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/getLagTimes.php
@@ -4,26 +4,37 @@
55 * @ingroup Maintenance
66 */
77
8 -require 'commandLine.inc';
 8+require_once( "Maintenance.php" );
99
10 -$lb = wfGetLB();
 10+class GetLagTimes extends Maintenance {
 11+ public function __construct() {
 12+ parent::__construct();
 13+ $this->mDescription = "Dump replication lag times";
 14+ }
1115
12 -if( $lb->getServerCount() == 1 ) {
13 - echo "This script dumps replication lag times, but you don't seem to have\n";
14 - echo "a multi-host db server configuration.\n";
15 -} else {
16 - $lags = $lb->getLagTimes();
17 - foreach( $lags as $n => $lag ) {
18 - $host = $lb->getServerName( $n );
19 - if( IP::isValid( $host ) ) {
20 - $ip = $host;
21 - $host = gethostbyaddr( $host );
 16+ public function execute() {
 17+ $lb = wfGetLB();
 18+
 19+ if( $lb->getServerCount() == 1 ) {
 20+ $this->error( "This script dumps replication lag times, but you don't seem to have\n"
 21+ . "a multi-host db server configuration.\n" );
2222 } else {
23 - $ip = gethostbyname( $host );
 23+ $lags = $lb->getLagTimes();
 24+ foreach( $lags as $n => $lag ) {
 25+ $host = $lb->getServerName( $n );
 26+ if( IP::isValid( $host ) ) {
 27+ $ip = $host;
 28+ $host = gethostbyaddr( $host );
 29+ } else {
 30+ $ip = gethostbyname( $host );
 31+ }
 32+ $starLen = min( intval( $lag ), 40 );
 33+ $stars = str_repeat( '*', $starLen );
 34+ $this->output( sprintf( "%10s %20s %3d %s\n", $ip, $host, $lag, $stars ) );
 35+ }
2436 }
25 - $starLen = min( intval( $lag ), 40 );
26 - $stars = str_repeat( '*', $starLen );
27 - printf( "%10s %20s %3d %s\n", $ip, $host, $lag, $stars );
2837 }
2938 }
3039
 40+$maintClass = "GetLagTimes";
 41+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/renderDump.php
@@ -27,42 +27,61 @@
2828 * @file
2929 * @ingroup Maintenance
3030 */
 31+
 32+require_once( "Maintenance.php" );
3133
32 -$optionsWithArgs = array( 'report' );
 34+class DumpRenderer extends Maintenance {
3335
34 -require_once( 'commandLine.inc' );
 36+ private $count = 0;
 37+ private $outputDirectory, $startTime;
3538
36 -class DumpRenderer {
37 - function __construct( $dir ) {
38 - $this->stderr = fopen( "php://stderr", "wt" );
39 - $this->outputDirectory = $dir;
40 - $this->count = 0;
 39+ public function __construct() {
 40+ parent::__construct();
 41+ $this->mDescription = "Take page text out of an XML dump file and render basic HTML out to files";
 42+ $this->addParam( 'output-dir', 'The directory to output the HTML files to', true, true );
4143 }
4244
43 - function handleRevision( $rev ) {
 45+ public function execute() {
 46+ $this->outputDirectory = $this->getOption( 'output-dir' );
 47+ $this->startTime = wfTime();
 48+
 49+ $source = new ImportStreamSource( $this->getStdin() );
 50+ $importer = new WikiImporter( $source );
 51+
 52+ $importer->setRevisionCallback(
 53+ array( &$this, 'handleRevision' ) );
 54+
 55+ return $importer->doImport();
 56+ }
 57+
 58+ /**
 59+ * Callback function for each revision, turn into HTML and save
 60+ * @param $rev Revision
 61+ */
 62+ private function handleRevision( $rev ) {
4463 $title = $rev->getTitle();
4564 if (!$title) {
46 - fprintf( $this->stderr, "Got bogus revision with null title!" );
 65+ $this->error( "Got bogus revision with null title!" );
4766 return;
4867 }
4968 $display = $title->getPrefixedText();
50 -
 69+
5170 $this->count++;
52 -
 71+
5372 $sanitized = rawurlencode( $display );
5473 $filename = sprintf( "%s/wiki-%07d-%s.html",
5574 $this->outputDirectory,
5675 $this->count,
5776 $sanitized );
58 - fprintf( $this->stderr, "%s\n", $filename, $display );
59 -
60 - // fixme
 77+ $this->output( sprintf( $this->stderr, "%s\n", $filename, $display ) );
 78+
 79+ // fixme (what?)
6180 $user = new User();
6281 $parser = new Parser();
6382 $options = ParserOptions::newFromUser( $user );
64 -
 83+
6584 $output = $parser->parse( $rev->getText(), $title, $options );
66 -
 85+
6786 file_put_contents( $filename,
6887 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" " .
6988 "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" .
@@ -76,27 +95,7 @@
7796 "</body>\n" .
7897 "</html>" );
7998 }
80 -
81 - function run() {
82 - $this->startTime = wfTime();
83 -
84 - $file = fopen( 'php://stdin', 'rt' );
85 - $source = new ImportStreamSource( $file );
86 - $importer = new WikiImporter( $source );
87 -
88 - $importer->setRevisionCallback(
89 - array( &$this, 'handleRevision' ) );
90 -
91 - return $importer->doImport();
92 - }
9399 }
94100
95 -if( isset( $options['output-dir'] ) ) {
96 - $dir = $options['output-dir'];
97 -} else {
98 - wfDie( "Must use --output-dir=/some/dir\n" );
99 -}
100 -$render = new DumpRenderer( $dir );
101 -$render->run();
102 -
103 -
 101+$maintClass = "DumpRenderer";
 102+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/waitForSlave.php
@@ -5,11 +5,16 @@
66 * @ingroup Maintenance
77 */
88
9 -require_once( "commandLine.inc" );
10 -if ( isset( $args[0] ) ) {
11 - wfWaitForSlaves($args[0]);
12 -} else {
13 - wfWaitForSlaves(10);
 9+require_once( "Maintenance.php" );
 10+
 11+class WaitForSlave extends Maintenance {
 12+ public function __construct() {
 13+ $this->addArgs( array( 'maxlag' ) );
 14+ }
 15+ public function execute() {
 16+ wfWaitForSlaves( $this->getArg( 0, 10 ) );
 17+ }
1418 }
1519
16 -
 20+$maintClass = "WaitForSlave";
 21+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/moveBatch.php
@@ -1,9 +1,7 @@
22 <?php
3 -
43 /**
54 * Maintenance script to move a batch of pages
65 *
7 - * @file
86 * @ingroup Maintenance
97 * @author Tim Starling
108 *
@@ -20,77 +18,77 @@
2119 * e.g. immobile_namespace for namespaces which can't be moved
2220 */
2321
24 -$oldCwd = getcwd();
25 -$optionsWithArgs = array( 'u', 'r', 'i' );
26 -require_once( 'commandLine.inc' );
 22+require_once( "Maintenance.php" );
2723
28 -chdir( $oldCwd );
29 -
30 -# Options processing
31 -
32 -$filename = 'php://stdin';
33 -$user = 'Move page script';
34 -$reason = '';
35 -$interval = 0;
36 -
37 -if ( isset( $args[0] ) ) {
38 - $filename = $args[0];
39 -}
40 -if ( isset( $options['u'] ) ) {
41 - $user = $options['u'];
42 -}
43 -if ( isset( $options['r'] ) ) {
44 - $reason = $options['r'];
45 -}
46 -if ( isset( $options['i'] ) ) {
47 - $interval = $options['i'];
48 -}
49 -
50 -$wgUser = User::newFromName( $user );
51 -
52 -
53 -# Setup complete, now start
54 -
55 -$file = fopen( $filename, 'r' );
56 -if ( !$file ) {
57 - print "Unable to read file, exiting\n";
58 - exit;
59 -}
60 -
61 -$dbw = wfGetDB( DB_MASTER );
62 -
63 -for ( $linenum = 1; !feof( $file ); $linenum++ ) {
64 - $line = fgets( $file );
65 - if ( $line === false ) {
66 - break;
 24+class MoveBatch extends Maintenance {
 25+ public function __construct() {
 26+ parent::__construct();
 27+ $this->mDescription = "Moves a batch of pages";
 28+ $this->addParam( 'u', "User to perform move", false, true );
 29+ $this->addParam( 'r', "Reason to move page", false, true );
 30+ $this->addParam( 'i', "Interval to sleep between moves" );
 31+ $this->addArgs( array( 'listfile' ) );
6732 }
68 - $parts = array_map( 'trim', explode( '|', $line ) );
69 - if ( count( $parts ) != 2 ) {
70 - print "Error on line $linenum, no pipe character\n";
71 - continue;
72 - }
73 - $source = Title::newFromText( $parts[0] );
74 - $dest = Title::newFromText( $parts[1] );
75 - if ( is_null( $source ) || is_null( $dest ) ) {
76 - print "Invalid title on line $linenum\n";
77 - continue;
78 - }
 33+
 34+ public function execute() {
 35+ global $wgUser;
7936
 37+ # Change to current working directory
 38+ $oldCwd = getcwd();
 39+ chdir( $oldCwd );
8040
81 - print $source->getPrefixedText() . ' --> ' . $dest->getPrefixedText();
82 - $dbw->begin();
83 - $err = $source->moveTo( $dest, false, $reason );
84 - if( $err !== true ) {
85 - print "\nFAILED: $err";
86 - }
87 - $dbw->immediateCommit();
88 - print "\n";
 41+ # Options processing
 42+ $user = $this->getOption( 'u', 'Move page script' );
 43+ $reason = $this->getOption( 'r', '' );
 44+ $interval = $this->getOption( 'i', 0 );
 45+ if( $this->hasArg() ) {
 46+ $file = fopen( $this->getArg(), 'r' );
 47+ } else {
 48+ $file = $this->getStdin();
 49+ }
8950
90 - if ( $interval ) {
91 - sleep( $interval );
 51+ # Setup
 52+ if( !$file ) {
 53+ $this->error( "Unable to read file, exiting\n", true );
 54+ }
 55+ $wgUser = User::newFromName( $user );
 56+
 57+ # Setup complete, now start
 58+ $dbw = wfGetDB( DB_MASTER );
 59+ for ( $linenum = 1; !feof( $file ); $linenum++ ) {
 60+ $line = fgets( $file );
 61+ if ( $line === false ) {
 62+ break;
 63+ }
 64+ $parts = array_map( 'trim', explode( '|', $line ) );
 65+ if ( count( $parts ) != 2 ) {
 66+ $this->error( "Error on line $linenum, no pipe character\n" );
 67+ continue;
 68+ }
 69+ $source = Title::newFromText( $parts[0] );
 70+ $dest = Title::newFromText( $parts[1] );
 71+ if ( is_null( $source ) || is_null( $dest ) ) {
 72+ $this->error( "Invalid title on line $linenum\n" );
 73+ continue;
 74+ }
 75+
 76+
 77+ $this->output( $source->getPrefixedText() . ' --> ' . $dest->getPrefixedText() );
 78+ $dbw->begin();
 79+ $err = $source->moveTo( $dest, false, $reason );
 80+ if( $err !== true ) {
 81+ $this->output( "\nFAILED: $err" );
 82+ }
 83+ $dbw->immediateCommit();
 84+ $this->output( "\n" );
 85+
 86+ if ( $interval ) {
 87+ sleep( $interval );
 88+ }
 89+ wfWaitForSlaves( 5 );
 90+ }
9291 }
93 - wfWaitForSlaves( 5 );
9492 }
9593
96 -
97 -
 94+$maintClass = "MoveBatch";
 95+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/refreshImageCount.php
@@ -7,23 +7,34 @@
88 * @ingroup Maintenance
99 */
1010
11 -require_once( "commandLine.inc" );
 11+require_once( "Maintenance.php" );
1212
13 -$dbw = wfGetDB( DB_MASTER );
 13+class RefreshImageCount extends Maintenance {
 14+ public function __construct() {
 15+ parent::__construct();
 16+ $this->mDescription = "Resets ss_image count, forcing slaves to pick it up.";
 17+ }
 18+
 19+ public function execute() {
 20+ $dbw = wfGetDB( DB_MASTER );
1421
15 -// Load the current value from the master
16 -$count = $dbw->selectField( 'site_stats', 'ss_images' );
 22+ // Load the current value from the master
 23+ $count = $dbw->selectField( 'site_stats', 'ss_images' );
1724
18 -echo wfWikiID().": forcing ss_images to $count\n";
 25+ $this->output( wfWikiID() . ": forcing ss_images to $count\n" );
1926
20 -// First set to NULL so that it changes on the master
21 -$dbw->update( 'site_stats',
22 - array( 'ss_images' => null ),
23 - array( 'ss_row_id' => 1 ) );
 27+ // First set to NULL so that it changes on the master
 28+ $dbw->update( 'site_stats',
 29+ array( 'ss_images' => null ),
 30+ array( 'ss_row_id' => 1 ) );
 31+
 32+ // Now this update will be forced to go out
 33+ $dbw->update( 'site_stats',
 34+ array( 'ss_images' => $count ),
 35+ array( 'ss_row_id' => 1 ) );
 36+ }
 37+}
2438
25 -// Now this update will be forced to go out
26 -$dbw->update( 'site_stats',
27 - array( 'ss_images' => $count ),
28 - array( 'ss_row_id' => 1 ) );
 39+$maintClass = "RefreshImageCount";
 40+require_once( DO_MAINTENANCE );
2941
30 -
Index: trunk/phase3/maintenance/eval.php
@@ -16,57 +16,66 @@
1717 * @ingroup Maintenance
1818 */
1919
20 -$wgUseNormalUser = (bool)getenv('MW_WIKIUSER');
 20+require_once( "Maintenance.php" );
2121
22 -$optionsWithArgs = array( 'd' );
 22+class EvalPrompt extends Maintenance {
2323
24 -/** */
25 -require_once( "commandLine.inc" );
26 -
27 -if ( isset( $options['d'] ) ) {
28 - $d = $options['d'];
29 - if ( $d > 0 ) {
30 - $wgDebugLogFile = '/dev/stdout';
 24+ public function __construct() {
 25+ parent::__construct();
 26+ $this->mDescription = "This script lets a command-line user start up the wiki engine and then poke\n" .
 27+ "about by issuing PHP commands directly.";
 28+ $this->addParam( 'd', "Enable MediaWiki debug output", false, true );
3129 }
32 - if ( $d > 1 ) {
33 - $lb = wfGetLB();
34 - foreach ( $lb->mServers as $i => $server ) {
35 - $lb->mServers[$i]['flags'] |= DBO_DEBUG;
 30+
 31+ public function execute() {
 32+ global $wgUseNormalUser;
 33+ $wgUseNormalUser = (bool)getenv('MW_WIKIUSER');
 34+ if ( $this->hasOption('d') ) {
 35+ $d = $this->getOption('d');
 36+ if ( $d > 0 ) {
 37+ $wgDebugLogFile = '/dev/stdout';
 38+ }
 39+ if ( $d > 1 ) {
 40+ $lb = wfGetLB();
 41+ foreach ( $lb->mServers as $i => $server ) {
 42+ $lb->mServers[$i]['flags'] |= DBO_DEBUG;
 43+ }
 44+ }
 45+ if ( $d > 2 ) {
 46+ $wgDebugFunctionEntry = true;
 47+ }
3648 }
 49+
 50+ if ( function_exists( 'readline_add_history' )
 51+ && function_exists( 'posix_isatty' ) && posix_isatty( 0 /*STDIN*/ ) )
 52+ {
 53+ $useReadline = true;
 54+ } else {
 55+ $useReadline = false;
 56+ }
 57+
 58+ if ( $useReadline ) {
 59+ $historyFile = "{$_ENV['HOME']}/.mweval_history";
 60+ readline_read_history( $historyFile );
 61+ }
 62+
 63+ while ( ( $line = readconsole( '> ' ) ) !== false ) {
 64+ if ( $useReadline ) {
 65+ readline_add_history( $line );
 66+ readline_write_history( $historyFile );
 67+ }
 68+ $val = eval( $line . ";" );
 69+ if( is_null( $val ) ) {
 70+ echo "\n";
 71+ } elseif( is_string( $val ) || is_numeric( $val ) ) {
 72+ echo "$val\n";
 73+ } else {
 74+ var_dump( $val );
 75+ }
 76+ }
 77+ print "\n";
3778 }
38 - if ( $d > 2 ) {
39 - $wgDebugFunctionEntry = true;
40 - }
4179 }
4280
43 -if ( function_exists( 'readline_add_history' )
44 - && function_exists( 'posix_isatty' ) && posix_isatty( 0 /*STDIN*/ ) )
45 -{
46 - $useReadline = true;
47 -} else {
48 - $useReadline = false;
49 -}
50 -
51 -if ( $useReadline ) {
52 - $historyFile = "{$_ENV['HOME']}/.mweval_history";
53 - readline_read_history( $historyFile );
54 -}
55 -
56 -while ( ( $line = readconsole( '> ' ) ) !== false ) {
57 - if ( $useReadline ) {
58 - readline_add_history( $line );
59 - readline_write_history( $historyFile );
60 - }
61 - $val = eval( $line . ";" );
62 - if( is_null( $val ) ) {
63 - echo "\n";
64 - } elseif( is_string( $val ) || is_numeric( $val ) ) {
65 - echo "$val\n";
66 - } else {
67 - var_dump( $val );
68 - }
69 -}
70 -
71 -print "\n";
72 -
73 -
 81+$maintClass = "EvalPrompt";
 82+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/clear_interwiki_cache.php
@@ -3,25 +3,36 @@
44 * This script is used to clear the interwiki links for ALL languages in
55 * memcached.
66 *
7 - * @file
87 * @ingroup Maintenance
98 */
109
11 -/** */
12 -require_once('commandLine.inc');
 10+require_once( "Maintenance.php" );
1311
14 -$dbr = wfGetDB( DB_SLAVE );
15 -$res = $dbr->select( 'interwiki', array( 'iw_prefix' ), false );
16 -$prefixes = array();
17 -while ( $row = $dbr->fetchObject( $res ) ) {
18 - $prefixes[] = $row->iw_prefix;
19 -}
 12+class ClearInterwikiCache extends Maintenance {
2013
21 -foreach ( $wgLocalDatabases as $db ) {
22 - print "$db ";
23 - foreach ( $prefixes as $prefix ) {
24 - $wgMemc->delete("$db:interwiki:$prefix");
 14+ public function __construct() {
 15+ parent::__construct();
 16+ $this->mDescription = "Clear all interwiki links for all languages from the cache";
2517 }
 18+
 19+ public function execute() {
 20+ global $wgLocalDatabases;
 21+ $dbr = wfGetDB( DB_SLAVE );
 22+ $res = $dbr->select( 'interwiki', array( 'iw_prefix' ), false );
 23+ $prefixes = array();
 24+ while ( $row = $dbr->fetchObject( $res ) ) {
 25+ $prefixes[] = $row->iw_prefix;
 26+ }
 27+
 28+ foreach ( $wgLocalDatabases as $db ) {
 29+ $this->output( "$db..." );
 30+ foreach ( $prefixes as $prefix ) {
 31+ $wgMemc->delete("$db:interwiki:$prefix");
 32+ }
 33+ $this->output( "done\n" );
 34+ }
 35+ }
2636 }
27 -print "\n";
2837
 38+$maintClass = "ClearInterwikiCache";
 39+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/deleteBatch.php
@@ -1,5 +1,4 @@
22 <?php
3 -
43 /**
54 * Deletes a batch of pages
65 * Usage: php deleteBatch.php [-u <user>] [-r <reason>] [-i <interval>] [listfile]
@@ -13,86 +12,87 @@
1413 * @file
1514 * @ingroup Maintenance
1615 */
 16+
 17+require_once( "Maintenance.php" );
1718
18 -$oldCwd = getcwd();
19 -$optionsWithArgs = array( 'u', 'r', 'i' );
20 -require_once( 'commandLine.inc' );
21 -
22 -chdir( $oldCwd );
23 -
24 -# Options processing
25 -
26 -$filename = 'php://stdin';
27 -$user = 'Delete page script';
28 -$reason = '';
29 -$interval = 0;
30 -
31 -if ( isset( $args[0] ) ) {
32 - $filename = $args[0];
33 -}
34 -if ( isset( $options['u'] ) ) {
35 - $user = $options['u'];
36 -}
37 -if ( isset( $options['r'] ) ) {
38 - $reason = $options['r'];
39 -}
40 -if ( isset( $options['i'] ) ) {
41 - $interval = $options['i'];
42 -}
43 -
44 -$wgUser = User::newFromName( $user );
45 -
46 -
47 -# Setup complete, now start
48 -
49 -$file = fopen( $filename, 'r' );
50 -if ( !$file ) {
51 - print "Unable to read file, exiting\n";
52 - exit;
53 -}
54 -
55 -$dbw = wfGetDB( DB_MASTER );
56 -
57 -for ( $linenum = 1; !feof( $file ); $linenum++ ) {
58 - $line = trim( fgets( $file ) );
59 - if ( $line == '' ) {
60 - continue;
 19+class DeleteBatch extends Maintenance {
 20+
 21+ public function __construct() {
 22+ parent::__construct();
 23+ $this->mDescription = "Deletes a batch of pages";
 24+ $this->addParam( 'u', "User to perform deletion", false, true );
 25+ $this->addParam( 'r', "Reason to delete page", false, true );
 26+ $this->addParam( 'i', "Interval to sleep between deletions" );
 27+ $this->addArgs( array( 'listfile' ) );
6128 }
62 - $page = Title::newFromText( $line );
63 - if ( is_null( $page ) ) {
64 - print "Invalid title '$line' on line $linenum\n";
65 - continue;
66 - }
67 - if( !$page->exists() ) {
68 - print "Skipping nonexistent page '$line'\n";
69 - continue;
70 - }
 29+
 30+ public function execute() {
 31+ global $wgUser;
7132
 33+ # Change to current working directory
 34+ $oldCwd = getcwd();
 35+ chdir( $oldCwd );
 36+
 37+ # Options processing
 38+ $user = $this->getOption( 'u', 'Delete page script' );
 39+ $reason = $this->getOption( 'r', '' );
 40+ $interval = $this->getOption( 'i', 0 );
 41+ if( $this->hasArg() ) {
 42+ $file = fopen( $this->getArg(), 'r' );
 43+ } else {
 44+ $file = $this->getStdin();
 45+ }
7246
73 - print $page->getPrefixedText();
74 - $dbw->begin();
75 - if( $page->getNamespace() == NS_FILE ) {
76 - $art = new ImagePage( $page );
77 - $img = wfFindFile( $art->mTitle );
78 - if( !$img || !$img->delete( $reason ) ) {
79 - print "FAILED to delete image file... ";
 47+ # Setup
 48+ if( !$file ) {
 49+ $this->error( "Unable to read file, exiting\n", true );
8050 }
81 - } else {
82 - $art = new Article( $page );
83 - }
84 - $success = $art->doDeleteArticle( $reason );
85 - $dbw->immediateCommit();
86 - if ( $success ) {
87 - print "\n";
88 - } else {
89 - print " FAILED to delete image page\n";
90 - }
 51+ $wgUser = User::newFromName( $user );
 52+ $dbw = wfGetDB( DB_MASTER );
9153
92 - if ( $interval ) {
93 - sleep( $interval );
 54+ # Handle each entry
 55+ for ( $linenum = 1; !feof( $file ); $linenum++ ) {
 56+ $line = trim( fgets( $file ) );
 57+ if ( $line == '' ) {
 58+ continue;
 59+ }
 60+ $page = Title::newFromText( $line );
 61+ if ( is_null( $page ) ) {
 62+ $this->output( "Invalid title '$line' on line $linenum\n" );
 63+ continue;
 64+ }
 65+ if( !$page->exists() ) {
 66+ $this->output( "Skipping nonexistent page '$line'\n" );
 67+ continue;
 68+ }
 69+
 70+
 71+ $this->output( $page->getPrefixedText() );
 72+ $dbw->begin();
 73+ if( $page->getNamespace() == NS_FILE ) {
 74+ $art = new ImagePage( $page );
 75+ $img = wfFindFile( $art->mTitle );
 76+ if( !$img || !$img->delete( $reason ) ) {
 77+ $this->output( "FAILED to delete image file... " );
 78+ }
 79+ } else {
 80+ $art = new Article( $page );
 81+ }
 82+ $success = $art->doDeleteArticle( $reason );
 83+ $dbw->immediateCommit();
 84+ if ( $success ) {
 85+ $this->output( "\n" );
 86+ } else {
 87+ $this->output( " FAILED to delete article\n" );
 88+ }
 89+
 90+ if ( $interval ) {
 91+ sleep( $interval );
 92+ }
 93+ wfWaitForSlaves( 5 );
 94+}
9495 }
95 - wfWaitForSlaves( 5 );
9696 }
9797
98 -
99 -
 98+$maintClass = "DeleteBatch";
 99+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/runJobs.php
@@ -10,71 +10,77 @@
1111 * @ingroup Maintenance
1212 */
1313
14 -$optionsWithArgs = array( 'maxjobs', 'type', 'procs' );
15 -$wgUseNormalUser = true;
16 -require_once( 'commandLine.inc' );
 14+require_once( "Maintenance.php" );
1715
18 -if ( isset( $options['procs'] ) ) {
19 - $procs = intval( $options['procs'] );
20 - if ( $procs < 1 || $procs > 1000 ) {
21 - echo "Invalid argument to --procs\n";
22 - exit( 1 );
 16+class RunJobs extends Maintenance {
 17+ public function __construct() {
 18+ global $wgUseNormalUser;
 19+ parent::__construct();
 20+ $this->mDescription = "Run pending jobs";
 21+ $this->addParam( 'maxjobs', 'Maximum number of jobs to run', false, true );
 22+ $this->addParam( 'type', 'Type of job to run', false, true );
 23+ $this->addParam( 'procs', 'Number of processes to use', false, true );
 24+ $wgUseNormalUser = true;
2325 }
24 - $fc = new ForkController( $procs );
25 - if ( $fc->start( $procs ) != 'child' ) {
26 - exit( 0 );
27 - }
28 -}
2926
30 -if ( isset( $options['maxjobs'] ) ) {
31 - $maxJobs = $options['maxjobs'];
32 -} else {
33 - $maxJobs = 10000;
34 -}
 27+ public function execute() {
 28+ global $wgTitle;
 29+ if ( $this->hasOption( 'procs' ) ) {
 30+ $procs = intval( $this->getOption('procs') );
 31+ if ( $procs < 1 || $procs > 1000 ) {
 32+ $this->error( "Invalid argument to --procs\n", true );
 33+ }
 34+ $fc = new ForkController( $procs );
 35+ if ( $fc->start( $procs ) != 'child' ) {
 36+ exit( 0 );
 37+ }
 38+ }
 39+ $maxJobs = $this->getOption( 'maxjobs', 10000 );
 40+ $type = $this->getOption( 'type', false );
 41+ $wgTitle = Title::newFromText( 'RunJobs.php' );
 42+ $dbw = wfGetDB( DB_MASTER );
 43+ $n = 0;
 44+ $conds = '';
 45+ if ($type !== false)
 46+ $conds = "job_cmd = " . $dbw->addQuotes($type);
3547
36 -$type = false;
37 -if ( isset( $options['type'] ) )
38 - $type = $options['type'];
39 -
40 -$wgTitle = Title::newFromText( 'RunJobs.php' );
41 -
42 -$dbw = wfGetDB( DB_MASTER );
43 -$n = 0;
44 -$conds = '';
45 -if ($type !== false)
46 - $conds = "job_cmd = " . $dbw->addQuotes($type);
47 -
48 -while ( $dbw->selectField( 'job', 'job_id', $conds, 'runJobs.php' ) ) {
49 - $offset=0;
50 - for (;;) {
51 - $job = ($type == false) ?
52 - Job::pop($offset)
53 - : Job::pop_type($type);
54 -
55 - if ($job == false)
56 - break;
57 -
58 - wfWaitForSlaves( 5 );
59 - $t = microtime( true );
60 - $offset=$job->id;
61 - $status = $job->run();
62 - $t = microtime( true ) - $t;
63 - $timeMs = intval( $t * 1000 );
64 - if ( !$status ) {
65 - runJobsLog( $job->toString() . " t=$timeMs error={$job->error}" );
66 - } else {
67 - runJobsLog( $job->toString() . " t=$timeMs good" );
 48+ while ( $dbw->selectField( 'job', 'job_id', $conds, 'runJobs.php' ) ) {
 49+ $offset=0;
 50+ for (;;) {
 51+ $job = ($type == false) ?
 52+ Job::pop($offset)
 53+ : Job::pop_type($type);
 54+
 55+ if ($job == false)
 56+ break;
 57+
 58+ wfWaitForSlaves( 5 );
 59+ $t = microtime( true );
 60+ $offset=$job->id;
 61+ $status = $job->run();
 62+ $t = microtime( true ) - $t;
 63+ $timeMs = intval( $t * 1000 );
 64+ if ( !$status ) {
 65+ $this->runJobsLog( $job->toString() . " t=$timeMs error={$job->error}" );
 66+ } else {
 67+ $this->runJobsLog( $job->toString() . " t=$timeMs good" );
 68+ }
 69+ if ( $maxJobs && ++$n > $maxJobs ) {
 70+ break 2;
 71+ }
 72+ }
6873 }
69 - if ( $maxJobs && ++$n > $maxJobs ) {
70 - break 2;
71 - }
7274 }
73 -}
7475
75 -
76 -function runJobsLog( $msg ) {
77 - print wfTimestamp( TS_DB ) . " $msg\n";
78 - wfDebugLog( 'runJobs', $msg );
 76+ /**
 77+ * Log the job message
 78+ * @param $msg String The message to log
 79+ */
 80+ private function runJobsLog( $msg ) {
 81+ $this->output( wfTimestamp( TS_DB ) . " $msg\n" );
 82+ wfDebugLog( 'runJobs', $msg );
 83+ }
7984 }
8085
81 -
 86+$maintClass = "RunJobs";
 87+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/mctest.php
@@ -8,58 +8,63 @@
99 * @ingroup Maintenance
1010 */
1111
12 -$optionsWithArgs = array( 'i' );
 12+require_once( "Maintenance.php" );
1313
14 -require_once('commandLine.inc');
 14+class mcTest extends Maintenance {
 15+ public function __construct() {
 16+ parent::__construct();
 17+ $this->mDescription = "Makes several 'set', 'incr' and 'get' requests on every"
 18+ . " memcached server and shows a report";
 19+ $this->addParam( 'i', 'Number of iterations', false, true );
 20+ $this->addArgs( array( 'server' ) );
 21+ }
1522
16 -function microtime_float()
17 -{
18 - list($usec, $sec) = explode(" ", microtime());
19 - return ((float)$usec + (float)$sec);
20 -}
 23+ public function execute() {
 24+ global $wgMemCachedServers;
2125
 26+ $iterations = $this->getOption( 'i', 100 );
 27+ if( $this->hasArg() )
 28+ $wgMemCachedServers = array( $this->getArg() );
2229
23 -#$wgDebugLogFile = '/dev/stdout';
24 -
25 -if ( isset( $args[0] ) ) {
26 - $wgMemCachedServers = array( $args[0] );
27 -}
28 -if ( isset( $options['i'] ) ) {
29 - $iterations = $options['i'];
30 -} else {
31 - $iterations = 100;
32 -}
33 -
34 -foreach ( $wgMemCachedServers as $server ) {
35 - print "$server ";
36 - $mcc = new MemCachedClientforWiki( array('persistant' => true) );
37 - $mcc->set_servers( array( $server ) );
38 - $set = 0;
39 - $incr = 0;
40 - $get = 0;
41 - $time_start=microtime_float();
42 - for ( $i=1; $i<=$iterations; $i++ ) {
43 - if ( !is_null( $mcc->set( "test$i", $i ) ) ) {
44 - $set++;
 30+ foreach ( $wgMemCachedServers as $server ) {
 31+ $this->output( $server . " " );
 32+ $mcc = new MemCachedClientforWiki( array('persistant' => true) );
 33+ $mcc->set_servers( array( $server ) );
 34+ $set = 0;
 35+ $incr = 0;
 36+ $get = 0;
 37+ $time_start = $this->microtime_float();
 38+ for ( $i=1; $i<=$iterations; $i++ ) {
 39+ if ( !is_null( $mcc->set( "test$i", $i ) ) ) {
 40+ $set++;
 41+ }
 42+ }
 43+ for ( $i=1; $i<=$iterations; $i++ ) {
 44+ if ( !is_null( $mcc->incr( "test$i", $i ) ) ) {
 45+ $incr++;
 46+ }
 47+ }
 48+ for ( $i=1; $i<=$iterations; $i++ ) {
 49+ $value = $mcc->get( "test$i" );
 50+ if ( $value == $i*2 ) {
 51+ $get++;
 52+ }
 53+ }
 54+ $exectime = $this->microtime_float() - $time_start;
 55+
 56+ $this->output( "set: $set incr: $incr get: $get time: $exectime\n" );
4557 }
4658 }
4759
48 - for ( $i=1; $i<=$iterations; $i++ ) {
49 - if ( !is_null( $mcc->incr( "test$i", $i ) ) ) {
50 - $incr++;
51 - }
 60+ /**
 61+ * Return microtime() as a float
 62+ * @return float
 63+ */
 64+ private function microtime_float() {
 65+ list($usec, $sec) = explode(" ", microtime());
 66+ return ((float)$usec + (float)$sec);
5267 }
53 -
54 - for ( $i=1; $i<=$iterations; $i++ ) {
55 - $value = $mcc->get( "test$i" );
56 - if ( $value == $i*2 ) {
57 - $get++;
58 - }
59 - }
60 - $exectime=microtime_float()-$time_start;
61 -
62 - print "set: $set incr: $incr get: $get time: $exectime\n";
6368 }
6469
65 -
66 -
 70+$maintClass = "mcTest";
 71+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/deleteDefaultMessages.php
@@ -1,48 +1,53 @@
22 <?php
3 -
43 /**
54 * Deletes all pages in the MediaWiki namespace which were last edited by
65 * "MediaWiki default".
76 *
8 - * @file
97 * @ingroup Maintenance
108 */
119
12 -if ( !defined( 'MEDIAWIKI' ) ) {
13 - require_once( 'commandLine.inc' );
14 - deleteDefaultMessages();
15 -}
 10+require_once( "Maintenance.php" );
1611
17 -function deleteDefaultMessages() {
18 - $user = 'MediaWiki default';
19 - $reason = 'No longer required';
 12+class DeleteDefaultMessages extends Maintenance {
 13+ public function __construct() {
 14+ parent::__construct();
 15+ $this->mDescription = "Deletes all pages in the MediaWiki namespace" .
 16+ " which were last edited by \"MediaWiki default\"";
 17+ }
2018
21 - global $wgUser;
22 - $wgUser = User::newFromName( $user );
23 - $wgUser->addGroup( 'bot' );
24 -
25 - $dbr = wfGetDB( DB_SLAVE );
26 - $res = $dbr->select( array( 'page', 'revision' ),
27 - array( 'page_namespace', 'page_title' ),
28 - array(
29 - 'page_namespace' => NS_MEDIAWIKI,
30 - 'page_latest=rev_id',
31 - 'rev_user_text' => 'MediaWiki default',
32 - )
33 - );
 19+ public function execute() {
 20+ $user = 'MediaWiki default';
 21+ $reason = 'No longer required';
3422
35 - $dbw = wfGetDB( DB_MASTER );
 23+ global $wgUser;
 24+ $wgUser = User::newFromName( $user );
 25+ $wgUser->addGroup( 'bot' );
3626
37 - while ( $row = $dbr->fetchObject( $res ) ) {
38 - if ( function_exists( 'wfWaitForSlaves' ) ) {
39 - wfWaitForSlaves( 5 );
 27+ $dbr = wfGetDB( DB_SLAVE );
 28+ $res = $dbr->select( array( 'page', 'revision' ),
 29+ array( 'page_namespace', 'page_title' ),
 30+ array(
 31+ 'page_namespace' => NS_MEDIAWIKI,
 32+ 'page_latest=rev_id',
 33+ 'rev_user_text' => 'MediaWiki default',
 34+ )
 35+ );
 36+
 37+ $dbw = wfGetDB( DB_MASTER );
 38+
 39+ while ( $row = $dbr->fetchObject( $res ) ) {
 40+ if ( function_exists( 'wfWaitForSlaves' ) ) {
 41+ wfWaitForSlaves( 5 );
 42+ }
 43+ $dbw->ping();
 44+ $title = Title::makeTitle( $row->page_namespace, $row->page_title );
 45+ $article = new Article( $title );
 46+ $dbw->begin();
 47+ $article->doDeleteArticle( $reason );
 48+ $dbw->commit();
4049 }
41 - $dbw->ping();
42 - $title = Title::makeTitle( $row->page_namespace, $row->page_title );
43 - $article = new Article( $title );
44 - $dbw->begin();
45 - $article->doDeleteArticle( $reason );
46 - $dbw->commit();
4750 }
4851 }
4952
 53+$maintClass = "DeleteDefaultMessages";
 54+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/checkImages.php
@@ -1,51 +1,63 @@
22 <?php
 3+/**
 4+ * Check images to see if they exist, are readable, etc etc
 5+ */
 6+require_once( "Maintenance.php" );
37
4 -require( 'commandLine.inc' );
 8+class CheckImages extends Maintenance {
59
6 -$batchSize = 1000;
7 -$start = '';
8 -$dbr = wfGetDB( DB_SLAVE );
9 -$localRepo = RepoGroup::singleton()->getLocalRepo();
 10+ public function __construct() {
 11+ parent::__construct();
 12+ $this->mDescription = "Check images to see if they exist, are readable, etc";
 13+ }
 14+
 15+ public function execute() {
 16+ $batchSize = 1000;
 17+ $start = '';
 18+ $dbr = wfGetDB( DB_SLAVE );
1019
11 -$numImages = 0;
12 -$numGood = 0;
13 -
14 -do {
15 - $res = $dbr->select( 'image', '*', array( 'img_name > ' . $dbr->addQuotes( $start ) ),
16 - 'checkImages.php', array( 'LIMIT' => $batchSize ) );
17 - foreach ( $res as $row ) {
18 - $numImages++;
19 - $start = $row->img_name;
20 - $file = $localRepo->newFileFromRow( $row );
21 - $path = $file->getPath();
22 - if ( !$path ) {
23 - echo "{$row->img_name}: not locally accessible\n";
24 - continue;
25 - }
26 - $stat = @stat( $file->getPath() );
27 - if ( !$stat ) {
28 - echo "{$row->img_name}: missing\n";
29 - continue;
30 - }
31 -
32 - if ( $stat['mode'] & 040000 ) {
33 - echo "{$row->img_name}: is a directory\n";
34 - continue;
35 - }
36 -
37 - if ( $stat['size'] == 0 && $row->img_size != 0 ) {
38 - echo "{$row->img_name}: truncated, was {$row->img_size}\n";
39 - continue;
40 - }
41 -
42 - if ( $stat['size'] != $row->img_size ) {
43 - echo "{$row->img_name}: size mismatch DB={$row->img_size}, actual={$stat['size']}\n";
44 - continue;
45 - }
46 -
47 - $numGood++;
 20+ $numImages = 0;
 21+ $numGood = 0;
 22+
 23+ do {
 24+ $res = $dbr->select( 'image', '*', array( 'img_name > ' . $dbr->addQuotes( $start ) ),
 25+ __METHOD__, array( 'LIMIT' => $batchSize ) );
 26+ foreach ( $res as $row ) {
 27+ $numImages++;
 28+ $start = $row->img_name;
 29+ $file = RepoGroup::singleton()->getLocalRepo()->newFileFromRow( $row );
 30+ $path = $file->getPath();
 31+ if ( !$path ) {
 32+ $this->output( "{$row->img_name}: not locally accessible\n";
 33+ continue;
 34+ }
 35+ $stat = @stat( $file->getPath() );
 36+ if ( !$stat ) {
 37+ $this->output( "{$row->img_name}: missing\n" );
 38+ continue;
 39+ }
 40+
 41+ if ( $stat['mode'] & 040000 ) {
 42+ $this->output( "{$row->img_name}: is a directory\n" );
 43+ continue;
 44+ }
 45+
 46+ if ( $stat['size'] == 0 && $row->img_size != 0 ) {
 47+ $this->output( "{$row->img_name}: truncated, was {$row->img_size}\n" );
 48+ continue;
 49+ }
 50+
 51+ if ( $stat['size'] != $row->img_size ) {
 52+ $this->output( "{$row->img_name}: size mismatch DB={$row->img_size}, actual={$stat['size']}\n" );
 53+ continue;
 54+ }
 55+
 56+ $numGood++;
 57+ }
 58+
 59+ } while ( $res->numRows() );
 60+
 61+ $this->output( "Good images: $numGood/$numImages\n" );
4862 }
 63+}
4964
50 -} while ( $res->numRows() );
51 -
52 -echo "Good images: $numGood/$numImages\n";
Index: trunk/phase3/maintenance/clear_stats.php
@@ -6,33 +6,34 @@
77 * @ingroup Maintenance
88 */
99
10 -require_once('commandLine.inc');
 10+require_once( 'Maintenance.php' );
1111
12 -foreach ( $wgLocalDatabases as $db ) {
13 - noisyDelete("$db:stats:request_with_session");
14 - noisyDelete("$db:stats:request_without_session");
15 - noisyDelete("$db:stats:pcache_hit");
16 - noisyDelete("$db:stats:pcache_miss_invalid");
17 - noisyDelete("$db:stats:pcache_miss_expired");
18 - noisyDelete("$db:stats:pcache_miss_absent");
19 - noisyDelete("$db:stats:pcache_miss_stub");
20 - noisyDelete("$db:stats:image_cache_hit");
21 - noisyDelete("$db:stats:image_cache_miss");
22 - noisyDelete("$db:stats:image_cache_update");
23 - noisyDelete("$db:stats:diff_cache_hit");
24 - noisyDelete("$db:stats:diff_cache_miss");
25 - noisyDelete("$db:stats:diff_uncacheable");
26 -}
 12+class clear_stats extends Maintenance {
2713
28 -function noisyDelete( $key ) {
29 - global $wgMemc;
30 - /*
31 - print "$key ";
32 - if ( $wgMemc->delete($key) ) {
33 - print "deleted\n";
34 - } else {
35 - print "FAILED\n";
36 - }*/
37 - $wgMemc->delete($key);
 14+ public function __construct() {
 15+ parent::__construct();
 16+ $this->mDescription = "Remove all statistics tracking from memcached";
 17+ }
 18+
 19+ public function execute() {
 20+ global $wgLocalDatabases, $wgMemc;
 21+ foreach ( $wgLocalDatabases as $db ) {
 22+ $wgMemc->delete("$db:stats:request_with_session");
 23+ $wgMemc->delete("$db:stats:request_without_session");
 24+ $wgMemc->delete("$db:stats:pcache_hit");
 25+ $wgMemc->delete("$db:stats:pcache_miss_invalid");
 26+ $wgMemc->delete("$db:stats:pcache_miss_expired");
 27+ $wgMemc->delete("$db:stats:pcache_miss_absent");
 28+ $wgMemc->delete("$db:stats:pcache_miss_stub");
 29+ $wgMemc->delete("$db:stats:image_cache_hit");
 30+ $wgMemc->delete("$db:stats:image_cache_miss");
 31+ $wgMemc->delete("$db:stats:image_cache_update");
 32+ $wgMemc->delete("$db:stats:diff_cache_hit");
 33+ $wgMemc->delete("$db:stats:diff_cache_miss");
 34+ $wgMemc->delete("$db:stats:diff_uncacheable");
 35+ }
 36+ }
3837 }
3938
 39+$maintClass = "clear_stats";
 40+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/doMaintenance.php
@@ -0,0 +1,60 @@
 2+<?php
 3+/**
 4+ * We want to make this whole thing as seamless as possible to the
 5+ * end-user. Unfortunately, we can't do _all_ of the work in the class
 6+ * because A) included files are not in global scope, but in the scope
 7+ * of their caller, and B) MediaWiki has way too many globals. So instead
 8+ * we'll kinda fake it, and do the requires() inline. <3 PHP
 9+ */
 10+
 11+if( !isset( $maintClass ) || !class_exists( $maintClass ) ) {
 12+ echo "\$maintClass is not set or is set to a non-existent class.";
 13+ die();
 14+}
 15+
 16+if( defined( 'MW_NO_SETUP' ) ) {
 17+ return;
 18+}
 19+
 20+// Get an object to start us off
 21+$maintenance = new $maintClass();
 22+
 23+// Basic sanity checks and such
 24+$maintenance->setup();
 25+
 26+# Setup the profiler
 27+if ( file_exists( "$IP/StartProfiler.php" ) ) {
 28+ require_once( "$IP/StartProfiler.php" );
 29+} else {
 30+ require_once( "$IP/includes/ProfilerStub.php" );
 31+}
 32+
 33+// Load settings, using wikimedia-mode if needed
 34+if( file_exists( dirname(__FILE__).'/wikimedia-mode' ) ) {
 35+ # TODO FIXME! Wikimedia-specific stuff needs to go away to an ext
 36+ # Maybe a hook?
 37+ global $cluster;
 38+ $wgWikiFarm = true;
 39+ $cluster = 'pmtma';
 40+ require_once( "$IP/includes/AutoLoader.php" );
 41+ require_once( "$IP/includes/SiteConfiguration.php" );
 42+ require( "$IP/wgConf.php" );
 43+ $maintenance->loadWikimediaSettings();
 44+ require( $IP.'/includes/Defines.php' );
 45+ require( $IP.'/CommonSettings.php' );
 46+} else {
 47+ require_once( "$IP/includes/AutoLoader.php" );
 48+ require_once( "$IP/includes/Defines.php" );
 49+ require_once( $maintenance->loadSettings() );
 50+}
 51+// Some last includes
 52+require_once( "$IP/includes/Setup.php" );
 53+require_once( "$IP/install-utils.inc" );
 54+
 55+$wgTitle = null; # Much much faster startup than creating a title object
 56+
 57+try {
 58+ $maintenance->execute();
 59+} catch( MWException $mwe ) {
 60+ echo( $mwe->getText() );
 61+}
\ No newline at end of file
Property changes on: trunk/phase3/maintenance/doMaintenance.php
___________________________________________________________________
Added: svn:eol-style
162 + native
Index: trunk/phase3/maintenance/nextJobDB.php
@@ -6,55 +6,71 @@
77 * @ingroup Maintenance
88 */
99
10 -$options = array( 'type' );
 10+require_once( "Maintenance.php" );
1111
12 -require_once( 'commandLine.inc' );
13 -
14 -$type = isset($options['type'])
15 - ? $options['type']
16 - : false;
17 -
18 -$mckey = $type === false
19 - ? "jobqueue:dbs"
20 - : "jobqueue:dbs:$type";
21 -
22 -$pendingDBs = $wgMemc->get( $mckey );
23 -if ( !$pendingDBs ) {
24 - $pendingDBs = array();
25 - # Cross-reference DBs by master DB server
26 - $dbsByMaster = array();
27 - foreach ( $wgLocalDatabases as $db ) {
28 - $lb = wfGetLB( $db );
29 - $dbsByMaster[$lb->getServerName(0)][] = $db;
 12+class nextJobDB extends Maintenance {
 13+ public function __construct() {
 14+ parent::__construct();
 15+ $this->mDescription = "Pick a database that has pending jobs";
 16+ $this->addParam( 'type', "The type of job to search for", false, true );
3017 }
31 -
32 - foreach ( $dbsByMaster as $master => $dbs ) {
33 - $dbConn = wfGetDB( DB_MASTER, array(), $dbs[0] );
34 - $stype = $dbConn->addQuotes($type);
35 -
36 - # Padding row for MySQL bug
37 - $sql = "(SELECT '-------------------------------------------')";
38 - foreach ( $dbs as $dbName ) {
39 - if ( $sql != '' ) {
40 - $sql .= ' UNION ';
41 - }
42 - if ($type === false)
43 - $sql .= "(SELECT '$dbName' FROM `$dbName`.job LIMIT 1)";
44 - else
45 - $sql .= "(SELECT '$dbName' FROM `$dbName`.job WHERE job_cmd=$stype LIMIT 1)";
 18+ public function execute() {
 19+ global $wgMemc;
 20+ $type = $this->getParam( 'type', false );
 21+ $mckey = $type === false
 22+ ? "jobqueue:dbs"
 23+ : "jobqueue:dbs:$type";
 24+ $pendingDBs = $wgMemcKey->get( $mckey );
 25+
 26+ # If we didn't get it from the cache
 27+ if( !$pendingDBs ) {
 28+ $pendingDBs = $this->getPendingDbs( $type );
 29+ $wgMemc->get( $mckey, $pendingDBs, 300 )
4630 }
47 - $res = $dbConn->query( $sql, 'nextJobDB.php' );
48 - $row = $dbConn->fetchRow( $res ); // discard padding row
49 - while ( $row = $dbConn->fetchRow( $res ) ) {
50 - $pendingDBs[] = $row[0];
 31+ # If we've got a pending job in a db, display it.
 32+ if ( $pendingDBs ) {
 33+ $this->output( $pendingDBs[mt_rand(0, count( $pendingDBs ) - 1)] );
5134 }
5235 }
53 -
54 - $wgMemc->set( $mckey, $pendingDBs, 300 );
 36+
 37+ /**
 38+ * Get all databases that have a pending job
 39+ * @param $type String Job type
 40+ * @return array
 41+ */
 42+ private function getPendingDbs( $type ) {
 43+ $pendingDBs = array();
 44+ # Cross-reference DBs by master DB server
 45+ $dbsByMaster = array();
 46+ foreach ( $wgLocalDatabases as $db ) {
 47+ $lb = wfGetLB( $db );
 48+ $dbsByMaster[$lb->getServerName(0)][] = $db;
 49+ }
 50+
 51+ foreach ( $dbsByMaster as $master => $dbs ) {
 52+ $dbConn = wfGetDB( DB_MASTER, array(), $dbs[0] );
 53+ $stype = $dbConn->addQuotes($type);
 54+ $jobTable = $dbConn->getTable( 'job' );
 55+
 56+ # Padding row for MySQL bug
 57+ $sql = "(SELECT '-------------------------------------------')";
 58+ foreach ( $dbs as $dbName ) {
 59+ if ( $sql != '' ) {
 60+ $sql .= ' UNION ';
 61+ }
 62+ if ($type === false)
 63+ $sql .= "(SELECT '$dbName' FROM `$dbName`.$jobTable LIMIT 1)";
 64+ else
 65+ $sql .= "(SELECT '$dbName' FROM `$dbName`.$jobTable WHERE job_cmd=$stype LIMIT 1)";
 66+ }
 67+ $res = $dbConn->query( $sql, __METHOD__ );
 68+ $row = $dbConn->fetchRow( $res ); // discard padding row
 69+ while ( $row = $dbConn->fetchRow( $res ) ) {
 70+ $pendingDBs[] = $row[0];
 71+ }
 72+ }
 73+ }
5574 }
5675
57 -if ( $pendingDBs ) {
58 - echo $pendingDBs[mt_rand(0, count( $pendingDBs ) - 1)];
59 -}
60 -
61 -
 76+$maintClass = "nextJobDb";
 77+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/Maintenance.php
@@ -0,0 +1,639 @@
 2+<?php
 3+// Define this so scripts can easily find doMaintenance.php
 4+define( 'DO_MAINTENANCE', dirname(__FILE__) . '/doMaintenance.php' );
 5+
 6+/**
 7+ * Abstract maintenance class for quickly writing and churning out
 8+ * maintenance scripts with minimal effort. All that _must_ be defined
 9+ * is the execute() method. See docs/maintenance.txt for more info
 10+ * and a quick demo of how to use it.
 11+ *
 12+ * @author Chad Horohoe <chad@anyonecanedit.org>
 13+ * @since 1.16
 14+ * @ingroup Maintenance
 15+ */
 16+abstract class Maintenance {
 17+
 18+ /**
 19+ * Constants for DB access type
 20+ * @see Maintenance::getDbType()
 21+ */
 22+ const NO_DB = 0;
 23+ const NORMAL_DB = 1;
 24+ const ADMIN_DB = 2;
 25+
 26+ // This is the desired params
 27+ private $mParams = array();
 28+
 29+ // Array of desired args
 30+ private $mArgList = array();
 31+
 32+ // This is the list of options that were actually passed
 33+ private $mOptions = array();
 34+
 35+ // This is the list of arguments that were actually passed
 36+ protected $mArgs = array();
 37+
 38+ // Name of the script currently running
 39+ protected $mSelf;
 40+
 41+ // Special vars for params that are always used
 42+ private $mQuiet = false;
 43+ private $mDbUser, $mDbPass;
 44+
 45+ // A description of the script, children should change this
 46+ protected $mDescription = '';
 47+
 48+ // Have we already loaded our user input?
 49+ private $inputLoaded = false;
 50+
 51+ // Batch size
 52+ protected $mBatchSize = 100;
 53+
 54+ /**
 55+ * Default constructor. Children should call this if implementing
 56+ * their own constructors
 57+ */
 58+ public function __construct() {
 59+ $this->addDefaultParams();
 60+ }
 61+
 62+ /**
 63+ * Do the actual work. All child classes will need to implement this
 64+ */
 65+ abstract public function execute();
 66+
 67+ /**
 68+ * Add a parameter to the script. Will be displayed on --help
 69+ * with the associated description
 70+ *
 71+ * @param $name String The name of the param (help, version, etc)
 72+ * @param $description String The description of the param to show on --help
 73+ * @param $required boolean Is the param required?
 74+ * @param $withArg Boolean Is an argument required with this option?
 75+ */
 76+ protected function addParam( $name, $description, $required = false, $withArg = false ) {
 77+ $this->mParams[ $name ] = array( 'desc' => $description, 'require' => $required, 'withArg' => $withArg );
 78+ }
 79+
 80+ /**
 81+ * Checks to see if a particular param exists.
 82+ * @param $name String The name of the param
 83+ * @return boolean
 84+ */
 85+ protected function hasOption( $name ) {
 86+ return isset( $this->mOptions[ $name ] );
 87+ }
 88+
 89+ /**
 90+ * Get an option, or return the default
 91+ * @param $name String The name of the param
 92+ * @param $default mixed Anything you want, default null
 93+ * @return mixed
 94+ */
 95+ protected function getOption( $name, $default = null ) {
 96+ if( $this->hasOption($name) ) {
 97+ return $this->mOptions[$name];
 98+ } else {
 99+ // Set it so we don't have to provide the default again
 100+ $this->mOptions[$name] = $default;
 101+ return $this->mOptions[$name];
 102+ }
 103+ }
 104+
 105+ /**
 106+ * Add some args that are needed. Used in formatting help
 107+ */
 108+ protected function addArgs( $args ) {
 109+ $this->mArgList = array_merge( $this->mArgList, $args );
 110+ }
 111+
 112+ /**
 113+ * Does a given argument exist?
 114+ * @param $argId int The integer value (from zero) for the arg
 115+ * @return boolean
 116+ */
 117+ protected function hasArg( $argId = 0 ) {
 118+ return isset( $this->mArgs[ $argId ] ) ;
 119+ }
 120+
 121+ /**
 122+ * Get an argument.
 123+ * @param $argId int The integer value (from zero) for the arg
 124+ * @param $default mixed The default if it doesn't exist
 125+ * @return mixed
 126+ */
 127+ protected function getArg( $argId = 0, $default = null ) {
 128+ return $this->hasArg($name) ? $this->mArgs[$name] : $default;
 129+ }
 130+
 131+ /**
 132+ * Set the batch size.
 133+ * @param $s int The number of operations to do in a batch
 134+ */
 135+ protected function setBatchSize( $s = 0 ) {
 136+ $this->mBatchSize = $s;
 137+ }
 138+
 139+ /**
 140+ * Return input from stdin.
 141+ * @param $length int The number of bytes to read. If null,
 142+ * just return the handle
 143+ * @return mixed
 144+ */
 145+ protected function getStdin( $len = null ) {
 146+ $f = fopen( 'php://stdin', 'rt' );
 147+ if( !$len ) {
 148+ return $f;
 149+ }
 150+ $input = fgets( $f, $len );
 151+ fclose ( $f );
 152+ return rtrim( $input );
 153+ }
 154+
 155+ /**
 156+ * Throw some output to the user. Scripts can call this with no fears,
 157+ * as we handle all --quiet stuff here
 158+ * @param $out String The text to show to the user
 159+ */
 160+ protected function output( $out ) {
 161+ if( $this->mQuiet ) {
 162+ return;
 163+ }
 164+ $f = fopen( 'php://stdout', 'w' );
 165+ fwrite( $f, $out );
 166+ fclose( $f );
 167+ }
 168+
 169+ /**
 170+ * Throw an error to the user. Doesn't respect --quiet, so don't use
 171+ * this for non-error output
 172+ * @param $err String The error to display
 173+ * @param $die boolean If true, go ahead and die out.
 174+ */
 175+ protected function error( $err, $die = false ) {
 176+ $f = fopen( 'php://stderr', 'w' );
 177+ fwrite( $f, $err );
 178+ fclose( $f );
 179+ if( $die ) die();
 180+ }
 181+
 182+ /**
 183+ * Does the script need normal DB access? By default, we give Maintenance
 184+ * scripts admin rights to the DB (when available). Sometimes, a script needs
 185+ * normal access for a reason and sometimes they want no access. Subclasses
 186+ * should override and return one of the following values, as needed:
 187+ * Maintenance::NO_DB - For no DB access at all
 188+ * Maintenance::NORMAL_DB - For normal DB access
 189+ * Maintenance::ADMIN_DB - For admin DB access, default
 190+ * @return int
 191+ */
 192+ protected function getDbType() {
 193+ return Maintenance :: ADMIN_DB;
 194+ }
 195+
 196+ /**
 197+ * Add the default parameters to the scripts
 198+ */
 199+ private function addDefaultParams() {
 200+ $this->addParam( 'help', "Display this help message" );
 201+ $this->addParam( 'quiet', "Whether to supress non-error output" );
 202+ $this->addParam( 'conf', "Location of LocalSettings.php, if not default", false, true );
 203+ $this->addParam( 'wiki', "For specifying the wiki ID", false, true );
 204+ if( $this->getDbType() > 0 ) {
 205+ $this->addParam( 'dbuser', "The DB user to use for this script", false, true );
 206+ $this->addParam( 'dbpass', "The password to use for this script", false, true );
 207+ }
 208+ }
 209+
 210+ /**
 211+ * Spawn a child maintenance script. Pass all of the current arguments
 212+ * to it.
 213+ * @param $maintClass String A name of a child maintenance class
 214+ * @param $classFile String Full path of where the child is
 215+ * @return Maintenance child
 216+ */
 217+ protected function spawnChild( $maintClass, $classFile = null ) {
 218+ // If we haven't already specified, kill setup procedures
 219+ // for child scripts, we've already got a sane environment
 220+ if( !defined( 'MW_NO_SETUP' ) ) {
 221+ define( 'MW_NO_SETUP', true );
 222+ }
 223+
 224+ // Make sure the class is loaded first
 225+ if( !class_exists( $maintClass ) ) {
 226+ if( $classFile ) {
 227+ require_once( $classFile );
 228+ }
 229+ if( !class_exists( $maintClass ) ) {
 230+ $this->error( "Cannot spawn child: $maintClass\n" );
 231+ }
 232+ }
 233+
 234+ $child = new $maintClass();
 235+ $child->loadParamsAndArgs( $this->mSelf, $this->mOptions, $this->mArgs );
 236+ return $child;
 237+ }
 238+
 239+ /**
 240+ * Do some sanity checking and basic setup
 241+ */
 242+ public function setup() {
 243+ global $IP, $wgCommandLineMode, $wgUseNormalUser, $wgRequestTime;
 244+
 245+ # Abort if called from a web server
 246+ if ( isset( $_SERVER ) && array_key_exists( 'REQUEST_METHOD', $_SERVER ) ) {
 247+ $this->error( "This script must be run from the command line\n", true );
 248+ }
 249+
 250+ # Make sure we can handle script parameters
 251+ if( !ini_get( 'register_argc_argv' ) ) {
 252+ $this->error( "Cannot get command line arguments, register_argc_argv is set to false", true );
 253+ }
 254+
 255+ # Make sure we're on PHP5 or better
 256+ if( version_compare( PHP_VERSION, '5.0.0' ) < 0 ) {
 257+ $this->error( "Sorry! This version of MediaWiki requires PHP 5; you are running " .
 258+ PHP_VERSION . ".\n\n" .
 259+ "If you are sure you already have PHP 5 installed, it may be installed\n" .
 260+ "in a different path from PHP 4. Check with your system administrator.\n", true );
 261+ }
 262+
 263+ if( version_compare( phpversion(), '5.2.4' ) >= 0 ) {
 264+ // Send PHP warnings and errors to stderr instead of stdout.
 265+ // This aids in diagnosing problems, while keeping messages
 266+ // out of redirected output.
 267+ if( ini_get( 'display_errors' ) ) {
 268+ ini_set( 'display_errors', 'stderr' );
 269+ }
 270+
 271+ // Don't touch the setting on earlier versions of PHP,
 272+ // as setting it would disable output if you'd wanted it.
 273+
 274+ // Note that exceptions are also sent to stderr when
 275+ // command-line mode is on, regardless of PHP version.
 276+ }
 277+
 278+ # Set the memory limit
 279+ ini_set( 'memory_limit', -1 );
 280+
 281+ $wgRequestTime = microtime(true);
 282+
 283+ # Define us as being in Mediawiki
 284+ define( 'MEDIAWIKI', true );
 285+
 286+ # Setup $IP, using MW_INSTALL_PATH if it exists
 287+ $IP = strval( getenv('MW_INSTALL_PATH') ) !== ''
 288+ ? getenv('MW_INSTALL_PATH')
 289+ : realpath( dirname( __FILE__ ) . '/..' );
 290+
 291+ $wgCommandLineMode = true;
 292+ # Turn off output buffering if it's on
 293+ @ob_end_flush();
 294+
 295+ if (!isset( $wgUseNormalUser ) ) {
 296+ $wgUseNormalUser = false;
 297+ }
 298+
 299+ $this->loadParamsAndArgs();
 300+ $this->maybeHelp();
 301+ }
 302+
 303+ /**
 304+ * Clear all params and arguments.
 305+ */
 306+ public function clearParamsAndArgs() {
 307+ $this->mOptions = array();
 308+ $this->mArgs = array();
 309+ $this->inputLoaded = false;
 310+ }
 311+
 312+ /**
 313+ * Process command line arguments
 314+ * $mOptions becomes an array with keys set to the option names
 315+ * $mArgs becomes a zero-based array containing the non-option arguments
 316+ *
 317+ * @param $self String The name of the script, if any
 318+ * @param $opts Array An array of options, in form of key=>value
 319+ * @param $args Array An array of command line arguments
 320+ */
 321+ public function loadParamsAndArgs( $self = null, $opts = null, $args = null ) {
 322+ # If we were given opts or args, set those and return early
 323+ if( $self ) {
 324+ $this->mSelf = $self;
 325+ $this->inputLoaded = true;
 326+ }
 327+ if( $opts ) {
 328+ $this->mOptions = $opts;
 329+ $this->inputLoaded = true;
 330+ }
 331+ if( $args ) {
 332+ $this->mArgs = $args;
 333+ $this->inputLoaded = true;
 334+ }
 335+
 336+ # If we've already loaded input (either by user values or from $argv)
 337+ # skip on loading it again. The array_shift() will corrupt values if
 338+ # it's run again and again
 339+ if( $this->inputLoaded ) {
 340+ $this->loadSpecialVars();
 341+ return;
 342+ }
 343+
 344+ global $argv;
 345+ $this->mSelf = array_shift( $argv );
 346+
 347+ $options = array();
 348+ $args = array();
 349+
 350+ # Parse arguments
 351+ for( $arg = reset( $argv ); $arg !== false; $arg = next( $argv ) ) {
 352+ if ( $arg == '--' ) {
 353+ # End of options, remainder should be considered arguments
 354+ $arg = next( $argv );
 355+ while( $arg !== false ) {
 356+ $args[] = $arg;
 357+ $arg = next( $argv );
 358+ }
 359+ break;
 360+ } elseif ( substr( $arg, 0, 2 ) == '--' ) {
 361+ # Long options
 362+ $option = substr( $arg, 2 );
 363+ if ( isset( $this->mParams[$option] ) && $this->mParams[$option]['withArg'] ) {
 364+ $param = next( $argv );
 365+ if ( $param === false ) {
 366+ $this->error( "$arg needs a value after it\n", true );
 367+ }
 368+ $options[$option] = $param;
 369+ } else {
 370+ $bits = explode( '=', $option, 2 );
 371+ if( count( $bits ) > 1 ) {
 372+ $option = $bits[0];
 373+ $param = $bits[1];
 374+ } else {
 375+ $param = 1;
 376+ }
 377+ $options[$option] = $param;
 378+ }
 379+ } elseif ( substr( $arg, 0, 1 ) == '-' ) {
 380+ # Short options
 381+ for ( $p=1; $p<strlen( $arg ); $p++ ) {
 382+ $option = $arg{$p};
 383+ if ( isset( $this->mParams[$option]['withArg'] ) ) {
 384+ $param = next( $argv );
 385+ if ( $param === false ) {
 386+ $this->error( "$arg needs a value after it\n", true );
 387+ }
 388+ $options[$option] = $param;
 389+ } else {
 390+ $options[$option] = 1;
 391+ }
 392+ }
 393+ } else {
 394+ $args[] = $arg;
 395+ }
 396+ }
 397+
 398+ # Check to make sure we've got all the required ones
 399+ foreach( $this->mParams as $opt => $info ) {
 400+ if( $info['require'] && !$this->hasOption($opt) ) {
 401+ $this->error( "Param $opt required.\n", true );
 402+ }
 403+ }
 404+
 405+ # Also make sure we've got enough arguments
 406+ if ( count( $args ) < count( $this->mArgList ) ) {
 407+ $this->error( "Not enough arguments passed", true );
 408+ }
 409+
 410+ $this->mOptions = $options;
 411+ $this->mArgs = $args;
 412+ $this->loadSpecialVars();
 413+ $this->inputLoaded = true;
 414+ }
 415+
 416+ /**
 417+ * Handle the special variables that are global to all scripts
 418+ */
 419+ private function loadSpecialVars() {
 420+ if( $this->hasOption( 'dbuser' ) )
 421+ $this->mDbUser = $this->getOption( 'dbuser' );
 422+ if( $this->hasOption( 'dbpass' ) )
 423+ $this->mDbPass = $this->getOption( 'dbpass' );
 424+ if( $this->hasOption( 'quiet' ) )
 425+ $this->mQuiet = true;
 426+ }
 427+
 428+ /**
 429+ * Maybe show the help.
 430+ * @param $force boolean Whether to force the help to show, default false
 431+ */
 432+ private function maybeHelp( $force = false ) {
 433+ if( $this->hasOption('help') || in_array( 'help', $this->mArgs ) || $force ) {
 434+ $this->mQuiet = false;
 435+ if( $this->mDescription ) {
 436+ $this->output( $this->mDescription . "\n" );
 437+ }
 438+ $this->output( "\nUsage: php " . $this->mSelf );
 439+ if( $this->mParams ) {
 440+ $this->output( " [--" . implode( array_keys( $this->mParams ), "|--" ) . "]" );
 441+ }
 442+ if( $this->mArgList ) {
 443+ $this->output( " <" . implode( $this->mArgList, "> <" ) . ">" );
 444+ }
 445+ $this->output( "\n" );
 446+ foreach( $this->mParams as $par => $info ) {
 447+ $this->output( "\t$par : " . $info['desc'] . "\n" );
 448+ }
 449+ die( 1 );
 450+ }
 451+ }
 452+
 453+ /**
 454+ * Handle some last-minute setup here.
 455+ */
 456+ private function finalSetup() {
 457+ global $wgCommandLineMode, $wgUseNormalUser, $wgShowSQLErrors;
 458+ global $wgTitle, $wgProfiling, $IP, $wgDBadminuser, $wgDBadminpassword;
 459+ global $wgDBuser, $wgDBpassword, $wgDBservers, $wgLBFactoryConf;
 460+
 461+ # Turn off output buffering again, it might have been turned on in the settings files
 462+ if( ob_get_level() ) {
 463+ ob_end_flush();
 464+ }
 465+ # Same with these
 466+ $wgCommandLineMode = true;
 467+
 468+ # If these were passed, use them
 469+ if( $this->mDbUser )
 470+ $wgDBadminuser = $this->mDbUser;
 471+ if( $this->mDbPass )
 472+ $wgDBadminpass = $this->mDbPass;
 473+
 474+ if ( empty( $wgUseNormalUser ) && isset( $wgDBadminuser ) ) {
 475+ $wgDBuser = $wgDBadminuser;
 476+ $wgDBpassword = $wgDBadminpassword;
 477+
 478+ if( $wgDBservers ) {
 479+ foreach ( $wgDBservers as $i => $server ) {
 480+ $wgDBservers[$i]['user'] = $wgDBuser;
 481+ $wgDBservers[$i]['password'] = $wgDBpassword;
 482+ }
 483+ }
 484+ if( isset( $wgLBFactoryConf['serverTemplate'] ) ) {
 485+ $wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser;
 486+ $wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword;
 487+ }
 488+ }
 489+
 490+ if ( defined( 'MW_CMDLINE_CALLBACK' ) ) {
 491+ $fn = MW_CMDLINE_CALLBACK;
 492+ $fn();
 493+ }
 494+
 495+ $wgShowSQLErrors = true;
 496+ @set_time_limit( 0 );
 497+
 498+ $wgProfiling = false; // only for Profiler.php mode; avoids OOM errors
 499+ }
 500+
 501+ /**
 502+ * Do setup specific to WMF
 503+ */
 504+ public function loadWikimediaSettings() {
 505+ global $IP, $wgNoDBParam, $wgUseNormalUser, $wgConf;
 506+
 507+ if ( empty( $wgNoDBParam ) ) {
 508+ # Check if we were passed a db name
 509+ if ( isset( $this->mOptions['wiki'] ) ) {
 510+ $db = $this->mOptions['wiki'];
 511+ } else {
 512+ $db = array_shift( $this->mArgs );
 513+ }
 514+ list( $site, $lang ) = $wgConf->siteFromDB( $db );
 515+
 516+ # If not, work out the language and site the old way
 517+ if ( is_null( $site ) || is_null( $lang ) ) {
 518+ if ( !$db ) {
 519+ $lang = 'aa';
 520+ } else {
 521+ $lang = $db;
 522+ }
 523+ if ( isset( $this->mArgs[0] ) ) {
 524+ $site = array_shift( $this->mArgs );
 525+ } else {
 526+ $site = 'wikipedia';
 527+ }
 528+ }
 529+ } else {
 530+ $lang = 'aa';
 531+ $site = 'wikipedia';
 532+ }
 533+
 534+ # This is for the IRC scripts, which now run as the apache user
 535+ # The apache user doesn't have access to the wikiadmin_pass command
 536+ if ( $_ENV['USER'] == 'apache' ) {
 537+ #if ( posix_geteuid() == 48 ) {
 538+ $wgUseNormalUser = true;
 539+ }
 540+
 541+ putenv( 'wikilang=' . $lang );
 542+
 543+ $DP = $IP;
 544+ ini_set( 'include_path', ".:$IP:$IP/includes:$IP/languages:$IP/maintenance" );
 545+
 546+ if ( $lang == 'test' && $site == 'wikipedia' ) {
 547+ define( 'TESTWIKI', 1 );
 548+ }
 549+ }
 550+
 551+ /**
 552+ * Generic setup for most installs. Returns the location of LocalSettings
 553+ * @return String
 554+ */
 555+ public function loadSettings() {
 556+ global $wgWikiFarm, $wgCommandLineMode, $IP, $DP;
 557+
 558+ $wgWikiFarm = false;
 559+ if ( isset( $this->mOptions['conf'] ) ) {
 560+ $settingsFile = $this->mOptions['conf'];
 561+ } else {
 562+ $settingsFile = "$IP/LocalSettings.php";
 563+ }
 564+ if ( isset( $this->mOptions['wiki'] ) ) {
 565+ $bits = explode( '-', $this->mOptions['wiki'] );
 566+ if ( count( $bits ) == 1 ) {
 567+ $bits[] = '';
 568+ }
 569+ define( 'MW_DB', $bits[0] );
 570+ define( 'MW_PREFIX', $bits[1] );
 571+ }
 572+
 573+ if ( ! is_readable( $settingsFile ) ) {
 574+ $this->error( "A copy of your installation's LocalSettings.php\n" .
 575+ "must exist and be readable in the source directory.\n", true );
 576+ }
 577+ $wgCommandLineMode = true;
 578+ $DP = $IP;
 579+ $this->finalSetup();
 580+ return $settingsFile;
 581+ }
 582+
 583+ /**
 584+ * Support function for cleaning up redundant text records
 585+ * @param $delete boolean Whether or not to actually delete the records
 586+ * @author Rob Church <robchur@gmail.com>
 587+ */
 588+ protected function purgeRedundantText( $delete = true ) {
 589+ # Data should come off the master, wrapped in a transaction
 590+ $dbw = wfGetDB( DB_MASTER );
 591+ $dbw->begin();
 592+
 593+ $tbl_arc = $dbw->tableName( 'archive' );
 594+ $tbl_rev = $dbw->tableName( 'revision' );
 595+ $tbl_txt = $dbw->tableName( 'text' );
 596+
 597+ # Get "active" text records from the revisions table
 598+ $this->output( "Searching for active text records in revisions table..." );
 599+ $res = $dbw->query( "SELECT DISTINCT rev_text_id FROM $tbl_rev" );
 600+ while( $row = $dbw->fetchObject( $res ) ) {
 601+ $cur[] = $row->rev_text_id;
 602+ }
 603+ $this->output( "done.\n" );
 604+
 605+ # Get "active" text records from the archive table
 606+ $this->output( "Searching for active text records in archive table..." );
 607+ $res = $dbw->query( "SELECT DISTINCT ar_text_id FROM $tbl_arc" );
 608+ while( $row = $dbw->fetchObject( $res ) ) {
 609+ $cur[] = $row->ar_text_id;
 610+ }
 611+ $this->output( "done.\n" );
 612+
 613+ # Get the IDs of all text records not in these sets
 614+ $this->output( "Searching for inactive text records..." );
 615+ $set = implode( ', ', $cur );
 616+ $res = $dbw->query( "SELECT old_id FROM $tbl_txt WHERE old_id NOT IN ( $set )" );
 617+ $old = array();
 618+ while( $row = $dbw->fetchObject( $res ) ) {
 619+ $old[] = $row->old_id;
 620+ }
 621+ $this->output( "done.\n" );
 622+
 623+ # Inform the user of what we're going to do
 624+ $count = count( $old );
 625+ $this->output( "$count inactive items found.\n" );
 626+
 627+ # Delete as appropriate
 628+ if( $delete && $count ) {
 629+ $this->output( "Deleting..." );
 630+ $set = implode( ', ', $old );
 631+ $dbw->query( "DELETE FROM $tbl_txt WHERE old_id IN ( $set )" );
 632+ $this->output( "done.\n" );
 633+ }
 634+
 635+ # Done
 636+ $dbw->commit();
 637+
 638+ }
 639+}
 640+
Property changes on: trunk/phase3/maintenance/Maintenance.php
___________________________________________________________________
Added: svn:eol-style
1641 + native
Index: trunk/phase3/maintenance/updateArticleCount.php
@@ -3,38 +3,84 @@
44 * Maintenance script to provide a better count of the number of articles
55 * and update the site statistics table, if desired
66 *
7 - * @file
87 * @ingroup Maintenance
98 * @author Rob Church <robchur@gmail.com>
109 */
1110
12 -$options = array( 'update', 'help' );
13 -require_once( 'commandLine.inc' );
14 -require_once( 'updateArticleCount.inc' );
15 -echo( "Update Article Count\n\n" );
 11+require_once( "Maintenance.php" );
1612
17 -if( isset( $options['help'] ) && $options['help'] ) {
18 - echo( "Usage: php updateArticleCount.php [--update]\n\n" );
19 - echo( "--update : Update site statistics table\n" );
20 - exit( 0 );
21 -}
 13+class UpdateArticleCount extends Maintenance {
2214
23 -echo( "Counting articles..." );
24 -$counter = new ArticleCounter();
25 -$result = $counter->count();
 15+ // Content namespaces
 16+ private $namespaces;
2617
27 -if( $result !== false ) {
28 - echo( "found {$result}.\n" );
29 - if( isset( $options['update'] ) && $options['update'] ) {
30 - echo( "Updating site statistics table... " );
31 - $dbw = wfGetDB( DB_MASTER );
32 - $dbw->update( 'site_stats', array( 'ss_good_articles' => $result ), array( 'ss_row_id' => 1 ), __METHOD__ );
33 - echo( "done.\n" );
34 - } else {
35 - echo( "To update the site statistics table, run the script with the --update option.\n" );
 18+ public function __construct() {
 19+ global $wgContentNamespaces;
 20+ parent::__construct();
 21+ $this->mDescription = "Count of the number of articles and update the site statistics table";
 22+ $this->addParam( 'update', 'Update the site_stats table with the new count' );
 23+ $this->namespaces = $wgContentNamespaces;
3624 }
37 -} else {
38 - echo( "failed.\n" );
 25+
 26+ public function execute() {
 27+ $this->output( "Counting articles..." );
 28+ $result = $this->count();
 29+
 30+ if( $result !== false ) {
 31+ $this->output( "found {$result}.\n" );
 32+ if( isset( $options['update'] ) && $options['update'] ) {
 33+ $this->output( "Updating site statistics table... " );
 34+ $dbw = wfGetDB( DB_MASTER );
 35+ $dbw->update( 'site_stats', array( 'ss_good_articles' => $result ), array( 'ss_row_id' => 1 ), __METHOD__ );
 36+ $this->output( "done.\n" );
 37+ } else {
 38+ $this->output( "To update the site statistics table, run the script with the --update option.\n" );
 39+ }
 40+ } else {
 41+ $this->output( "failed.\n" );
 42+ }
 43+ }
 44+
 45+ /**
 46+ * Produce a comma-delimited set of namespaces
 47+ * Includes paranoia
 48+ *
 49+ * @return string
 50+ */
 51+ private function makeNsSet() {
 52+ foreach( $this->namespaces as $namespace )
 53+ $namespaces[] = intval( $namespace );
 54+ return implode( ', ', $namespaces );
 55+ }
 56+
 57+ /**
 58+ * Produce SQL for the query
 59+ *
 60+ * @param $dbr Database handle
 61+ * @return string
 62+ */
 63+ private function makeSql( $dbr ) {
 64+ list( $page, $pagelinks ) = $dbr->tableNamesN( 'page', 'pagelinks' );
 65+ $nsset = $this->makeNsSet();
 66+ return "SELECT COUNT(DISTINCT page_namespace, page_title) AS pagecount " .
 67+ "FROM $page, $pagelinks " .
 68+ "WHERE pl_from=page_id and page_namespace IN ( $nsset ) " .
 69+ "AND page_is_redirect = 0 AND page_len > 0";
 70+ }
 71+
 72+ /**
 73+ * Count the number of valid content pages in the wiki
 74+ *
 75+ * @return mixed Integer, or false if there's a problem
 76+ */
 77+ private function count() {
 78+ $dbr = wfGetDB( DB_SLAVE );
 79+ $res = $dbr->query( $this->makeSql( $dbr ), __METHOD__ );
 80+ $row = $dbr->fetchObject( $res );
 81+ $dbr->freeResult( $res );
 82+ return $row->pagecount;
 83+ }
3984 }
40 -echo( "\n" );
4185
 86+$maintClass = "UpdateArticleCount";
 87+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/reassignEdits.php
@@ -1,56 +1,153 @@
22 <?php
3 -
43 /**
54 * Reassign edits from a user or IP address to another user
65 *
7 - * @file
86 * @ingroup Maintenance
97 * @author Rob Church <robchur@gmail.com>
108 * @licence GNU General Public Licence 2.0 or later
119 */
1210
13 -$options = array( 'force', 'norc', 'quiet', 'report' );
14 -require_once( 'commandLine.inc' );
15 -require_once( 'reassignEdits.inc' );
 11+require_once( "Maintenance.php" );
1612
17 -# Set silent mode; --report overrides --quiet
18 -if( !@$options['report'] && @$options['quiet'] )
19 - setSilent();
 13+class ReassignEdits extends Maintenance {
 14+ public function __construct() {
 15+ parent::__construct();
 16+ $this->mDescription = "Reassign edits from one user to another";
 17+ $this->addParam( "force", "Reassign even if the target user doesn't exist" );
 18+ $this->addParam( "norc", "Don't update the recent changes table" );
 19+ $this->addParam( "report", "Print out details of what would be changed, but don't update it" );
 20+ $this->addArgs( array( 'from', 'to' ) );
 21+ }
2022
21 -out( "Reassign Edits\n\n" );
 23+ public function execute() {
 24+ if( $this->hasArg(0) && $this->hasArg(1) ) {
 25+ # Set up the users involved
 26+ $from =& $this->initialiseUser( $this->getArg(0) );
 27+ $to =& $this->initialiseUser( $this->getArg(1) );
 28+
 29+ # If the target doesn't exist, and --force is not set, stop here
 30+ if( $to->getId() || $this->hasOption('force') ) {
 31+ # Reassign the edits
 32+ $report = $this->hasOption('report');
 33+ $count = $this->reassignEdits( $from, $to, !$this->hasOption('norc'), $report );
 34+ # If reporting, and there were items, advise the user to run without --report
 35+ if( $report )
 36+ $this->output( "Run the script again without --report to update.\n" );
 37+ } else {
 38+ $ton = $to->getName();
 39+ $this->error( "User '{$ton}' not found.\n" );
 40+ }
 41+ }
 42+ }
2243
23 -if( @$args[0] && @$args[1] ) {
 44+ /**
 45+ * Reassign edits from one user to another
 46+ *
 47+ * @param $from User to take edits from
 48+ * @param $to User to assign edits to
 49+ * @param $rc Update the recent changes table
 50+ * @param $report Don't change things; just echo numbers
 51+ * @return integer Number of entries changed, or that would be changed
 52+ */
 53+ private function reassignEdits( &$from, &$to, $rc = false, $report = false ) {
 54+ $dbw = wfGetDB( DB_MASTER );
 55+ $dbw->immediateBegin();
2456
25 - # Set up the users involved
26 - $from =& initialiseUser( $args[0] );
27 - $to =& initialiseUser( $args[1] );
 57+ # Count things
 58+ $this->output( "Checking current edits..." );
 59+ $res = $dbw->select( 'revision', 'COUNT(*) AS count', $this->userConditions( $from, 'rev_user', 'rev_user_text' ), __METHOD__ );
 60+ $row = $dbw->fetchObject( $res );
 61+ $cur = $row->count;
 62+ $this->output( "found {$cur}.\n" );
 63+
 64+ $this->output( "Checking deleted edits..." );
 65+ $res = $dbw->select( 'archive', 'COUNT(*) AS count', $this->userConditions( $from, 'ar_user', 'ar_user_text' ), __METHOD__ );
 66+ $row = $dbw->fetchObject( $res );
 67+ $del = $row->count;
 68+ $this->output( "found {$del}.\n" );
 69+
 70+ # Don't count recent changes if we're not supposed to
 71+ if( $rc ) {
 72+ $this->output( "Checking recent changes..." );
 73+ $res = $dbw->select( 'recentchanges', 'COUNT(*) AS count', $this->userConditions( $from, 'rc_user', 'rc_user_text' ), __METHOD__ );
 74+ $row = $dbw->fetchObject( $res );
 75+ $rec = $row->count;
 76+ $this->output( "found {$rec}.\n" );
 77+ } else {
 78+ $rec = 0;
 79+ }
2880
29 - # If the target doesn't exist, and --force is not set, stop here
30 - if( $to->getId() || @$options['force'] ) {
31 - # Reassign the edits
32 - $report = @$options['report'];
33 - $count = reassignEdits( $from, $to, !@$options['norc'], $report );
34 - # If reporting, and there were items, advise the user to run without --report
35 - if( $report )
36 - out( "Run the script again without --report to update.\n" );
37 - } else {
38 - $ton = $to->getName();
39 - echo( "User '{$ton}' not found.\n" );
 81+ $total = $cur + $del + $rec;
 82+ $this->output( "\nTotal entries to change: {$total}\n" );
 83+
 84+ if( !$report ) {
 85+ if( $total ) {
 86+ # Reassign edits
 87+ $this->output( "\nReassigning current edits..." );
 88+ $res = $dbw->update( 'revision', userSpecification( $to, 'rev_user', 'rev_user_text' ), $this->userConditions( $from, 'rev_user', 'rev_user_text' ), __METHOD__ );
 89+ $this->output( "done.\nReassigning deleted edits..." );
 90+ $res = $dbw->update( 'archive', userSpecification( $to, 'ar_user', 'ar_user_text' ), $this->userConditions( $from, 'ar_user', 'ar_user_text' ), __METHOD__ );
 91+ $this->output( "done.\n" );
 92+ # Update recent changes if required
 93+ if( $rc ) {
 94+ $this->output( "Updating recent changes..." );
 95+ $res = $dbw->update( 'recentchanges', $this->userSpecification( $to, 'rc_user', 'rc_user_text' ), $this->userConditions( $from, 'rc_user', 'rc_user_text' ), __METHOD__ );
 96+ $this->output( "done.\n" );
 97+ }
 98+ }
 99+ }
 100+
 101+ $dbw->immediateCommit();
 102+ return (int)$total;
40103 }
41104
42 -} else {
43 - ShowUsage();
44 -}
 105+ /**
 106+ * Return the most efficient set of user conditions
 107+ * i.e. a user => id mapping, or a user_text => text mapping
 108+ *
 109+ * @param $user User for the condition
 110+ * @param $idfield Field name containing the identifier
 111+ * @param $utfield Field name containing the user text
 112+ * @return array
 113+ */
 114+ private function userConditions( &$user, $idfield, $utfield ) {
 115+ return $user->getId() ? array( $idfield => $user->getId() ) : array( $utfield => $user->getName() );
 116+ }
 117+
 118+ /**
 119+ * Return user specifications
 120+ * i.e. user => id, user_text => text
 121+ *
 122+ * @param $user User for the spec
 123+ * @param $idfield Field name containing the identifier
 124+ * @param $utfield Field name containing the user text
 125+ * @return array
 126+ */
 127+ private function userSpecification( &$user, $idfield, $utfield ) {
 128+ return array( $idfield => $user->getId(), $utfield => $user->getName() );
 129+ }
 130+
 131+ /**
 132+ * Initialise the user object
 133+ *
 134+ * @param $username Username or IP address
 135+ * @return User
 136+ */
 137+ private function initialiseUser( $username ) {
 138+ if( User::isIP( $username ) ) {
 139+ $user = new User();
 140+ $user->setId( 0 );
 141+ $user->setName( $username );
 142+ } else {
 143+ $user = User::newFromName( $username );
 144+ }
 145+ $user->load();
 146+ return $user;
 147+ }
45148
46 -/** Show script usage information */
47 -function ShowUsage() {
48 - echo( "Reassign edits from one user to another.\n\n" );
49 - echo( "Usage: php reassignEdits.php [--force|--quiet|--norc|--report] <from> <to>\n\n" );
50 - echo( " <from> : Name of the user to assign edits from\n" );
51 - echo( " <to> : Name of the user to assign edits to\n" );
52 - echo( " --force : Reassign even if the target user doesn't exist\n" );
53 - echo( " --quiet : Don't print status information (except for errors)\n" );
54 - echo( " --norc : Don't update the recent changes table\n" );
55 - echo( " --report : Print out details of what would be changed, but don't update it\n\n" );
 149+
56150 }
57151
 152+$maintClass = "ReassignEdits";
 153+require_once( DO_MAINTENANCE );
 154+
Index: trunk/phase3/maintenance/undelete.php
@@ -6,34 +6,35 @@
77 * @ingroup Maintenance
88 */
99
10 -$usage = <<<EOT
11 -Undelete a page
12 -Usage: php undelete.php [-u <user>] [-r <reason>] <pagename>
 10+require_once( "Maintenance.php" );
1311
14 -EOT;
 12+class Undelete extends Maintenance {
 13+ public function __construct() {
 14+ parent::__construct();
 15+ $this->mDescription = "Undelete a page";
 16+ $this->addParam( 'u', 'The user to perform the undeletion', false, true );
 17+ $this->addParam( 'r', 'The reason to undelete', false, true );
 18+ $this->addArgs( array( 'pagename' ) );
 19+ }
1520
16 -$optionsWithArgs = array( 'u', 'r' );
17 -require_once( 'commandLine.inc' );
 21+ public function execute() {
 22+ global $wgUser;
1823
19 -$user = 'Command line script';
20 -$reason = '';
 24+ $user = $this->getOption( 'u', 'Command line script' );
 25+ $reason = $this->getOption( 'r', '' );
 26+ $pageName = $this->getArg();
2127
22 -if ( isset( $options['u'] ) ) {
23 - $user = $options['u'];
 28+ $title = Title::newFromText( $pageName );
 29+ if ( !$title ) {
 30+ $this->error( "Invalid title", true );
 31+ }
 32+ $wgUser = User::newFromName( $user );
 33+ $archive = new PageArchive( $title );
 34+ $this->output( "Undeleting " . $title->getPrefixedDBkey() . '...' );
 35+ $archive->undelete( array(), $reason );
 36+ $this->output( "done\n" );
 37+ }
2438 }
25 -if ( isset( $options['r'] ) ) {
26 - $reason = $options['r'];
27 -}
28 -$pageName = @$args[0];
29 -$title = Title::newFromText( $pageName );
30 -if ( !$title ) {
31 - echo $usage;
32 - exit( 1 );
33 -}
34 -$wgUser = User::newFromName( $user );
35 -$archive = new PageArchive( $title );
36 -echo "Undeleting " . $title->getPrefixedDBkey() . '...';
37 -$archive->undelete( array(), $reason );
38 -echo "done\n";
3939
40 -
 40+$maintClass = "Undelete";
 41+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/rebuildFileCache.php
@@ -6,87 +6,96 @@
77 * @ingroup Maintenance
88 */
99
10 -/** */
11 -require_once( "commandLine.inc" );
12 -if( !$wgUseFileCache ) {
13 - echo "Nothing to do -- \$wgUseFileCache is disabled.\n";
14 - exit(0);
15 -}
16 -$wgDisableCounters = false; // no real hits here
 10+require_once( "Maintenance.php" );
1711
18 -$start = isset($args[0]) ? intval($args[0]) : 0;
19 -$overwrite = isset( $args[1] ) && $args[1] === 'overwrite';
20 -echo "Building content page file cache from page {$start}!\n";
21 -echo "Format: <start> [overwrite]\n";
 12+class RebuildFileCache extends Maintenance {
 13+ public function __construct() {
 14+ parent::__construct();
 15+ $this->mDescription = "Build file cache for content pages";
 16+ $this->addArgs( array( 'start', 'overwrite' ) );
 17+ }
2218
23 -$dbr = wfGetDB( DB_SLAVE );
24 -$start = $start > 0 ? $start : $dbr->selectField( 'page', 'MIN(page_id)', false, __FUNCTION__ );
25 -$end = $dbr->selectField( 'page', 'MAX(page_id)', false, __FUNCTION__ );
26 -if( !$start ) {
27 - die("Nothing to do.\n");
28 -}
29 -
30 -$_SERVER['HTTP_ACCEPT_ENCODING'] = 'bgzip'; // hack, no real client
31 -OutputPage::setEncodings(); # Not really used yet
32 -
33 -$BATCH_SIZE = 100;
34 -# Do remaining chunk
35 -$end += $BATCH_SIZE - 1;
36 -$blockStart = $start;
37 -$blockEnd = $start + $BATCH_SIZE - 1;
38 -
39 -$dbw = wfGetDB( DB_MASTER );
40 -// Go through each page and save the output
41 -while( $blockEnd <= $end ) {
42 - // Get the pages
43 - $res = $dbr->select( 'page', array('page_namespace','page_title','page_id'),
44 - array('page_namespace' => $wgContentNamespaces,
45 - "page_id BETWEEN $blockStart AND $blockEnd" ),
46 - array('ORDER BY' => 'page_id ASC','USE INDEX' => 'PRIMARY')
47 - );
48 - while( $row = $dbr->fetchObject( $res ) ) {
49 - $rebuilt = false;
50 - $wgTitle = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
51 - if( null == $wgTitle ) {
52 - echo "Page {$row->page_id} bad title\n";
53 - continue; // broken title?
 19+ public function execute() {
 20+ global $wgUseFileCache, $wgDisableCounters, $wgTitle, $wgArticle, $wgOut;
 21+ if( !$wgUseFileCache ) {
 22+ $this->error( "Nothing to do -- \$wgUseFileCache is disabled.\n", true );
5423 }
55 - $wgArticle = new Article( $wgTitle );
56 - // If the article is cacheable, then load it
57 - if( $wgArticle->isFileCacheable() ) {
58 - $cache = new HTMLFileCache( $wgTitle );
59 - if( $cache->isFileCacheGood() ) {
60 - if( $overwrite ) {
61 - $rebuilt = true;
 24+ $wgDisableCounters = false;
 25+ $start = intval( $this->getArg( 0, 0 ) );
 26+ $overwrite = $this->hasArg(1) && $this->getArg(1) === 'overwrite';
 27+ $this->output( "Building content page file cache from page {$start}!\n" );
 28+
 29+ $dbr = wfGetDB( DB_SLAVE );
 30+ $start = $start > 0 ? $start : $dbr->selectField( 'page', 'MIN(page_id)', false, __FUNCTION__ );
 31+ $end = $dbr->selectField( 'page', 'MAX(page_id)', false, __FUNCTION__ );
 32+ if( !$start ) {
 33+ $this->error( "Nothing to do.\n", true );
 34+ }
 35+
 36+ $_SERVER['HTTP_ACCEPT_ENCODING'] = 'bgzip'; // hack, no real client
 37+ OutputPage::setEncodings(); # Not really used yet
 38+
 39+ $BATCH_SIZE = 100;
 40+ # Do remaining chunk
 41+ $end += $BATCH_SIZE - 1;
 42+ $blockStart = $start;
 43+ $blockEnd = $start + $BATCH_SIZE - 1;
 44+
 45+ $dbw = wfGetDB( DB_MASTER );
 46+ // Go through each page and save the output
 47+ while( $blockEnd <= $end ) {
 48+ // Get the pages
 49+ $res = $dbr->select( 'page', array('page_namespace','page_title','page_id'),
 50+ array('page_namespace' => $wgContentNamespaces,
 51+ "page_id BETWEEN $blockStart AND $blockEnd" ),
 52+ array('ORDER BY' => 'page_id ASC','USE INDEX' => 'PRIMARY')
 53+ );
 54+ while( $row = $dbr->fetchObject( $res ) ) {
 55+ $rebuilt = false;
 56+ $wgTitle = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
 57+ if( null == $wgTitle ) {
 58+ $this->output( "Page {$row->page_id} bad title\n" );
 59+ continue; // broken title?
 60+ }
 61+ $wgArticle = new Article( $wgTitle );
 62+ // If the article is cacheable, then load it
 63+ if( $wgArticle->isFileCacheable() ) {
 64+ $cache = new HTMLFileCache( $wgTitle );
 65+ if( $cache->isFileCacheGood() ) {
 66+ if( $overwrite ) {
 67+ $rebuilt = true;
 68+ } else {
 69+ $this->output( "Page {$row->page_id} already cached\n" );
 70+ continue; // done already!
 71+ }
 72+ }
 73+ ob_start( array(&$cache, 'saveToFileCache' ) ); // save on ob_end_clean()
 74+ $wgUseFileCache = false; // hack, we don't want $wgArticle fiddling with filecache
 75+ $wgArticle->view();
 76+ @$wgOut->output(); // header notices
 77+ $wgUseFileCache = true;
 78+ ob_end_clean(); // clear buffer
 79+ $wgOut = new OutputPage(); // empty out any output page garbage
 80+ if( $rebuilt )
 81+ $this->output( "Re-cached page {$row->page_id}\n" );
 82+ else
 83+ $this->output( "Cached page {$row->page_id}\n" );
6284 } else {
63 - echo "Page {$row->page_id} already cached\n";
64 - continue; // done already!
 85+ $this->output( "Page {$row->page_id} not cacheable\n" );
6586 }
 87+ $dbw->commit(); // commit any changes
6688 }
67 - ob_start( array(&$cache, 'saveToFileCache' ) ); // save on ob_end_clean()
68 - $wgUseFileCache = false; // hack, we don't want $wgArticle fiddling with filecache
69 - $wgArticle->view();
70 - @$wgOut->output(); // header notices
71 - $wgUseFileCache = true;
72 - ob_end_clean(); // clear buffer
73 - $wgOut = new OutputPage(); // empty out any output page garbage
74 - if( $rebuilt )
75 - echo "Re-cached page {$row->page_id}\n";
76 - else
77 - echo "Cached page {$row->page_id}\n";
78 - } else {
79 - echo "Page {$row->page_id} not cacheable\n";
 89+ $blockStart += $BATCH_SIZE;
 90+ $blockEnd += $BATCH_SIZE;
 91+ wfWaitForSlaves( 5 );
8092 }
81 - $dbw->commit(); // commit any changes
 93+ $this->output( "Done!\n" );
 94+
 95+ // Remove these to be safe
 96+ if( isset($wgTitle) )
 97+ unset($wgTitle);
 98+ if( isset($wgArticle) )
 99+ unset($wgArticle);
82100 }
83 - $blockStart += $BATCH_SIZE;
84 - $blockEnd += $BATCH_SIZE;
85 - wfWaitForSlaves( 5 );
86101 }
87 -echo "Done!\n";
88 -
89 -// Remove these to be safe
90 -if( isset($wgTitle) )
91 - unset($wgTitle);
92 -if( isset($wgArticle) )
93 - unset($wgArticle);
 102+require_once( "commandLine.inc" );
Index: trunk/phase3/maintenance/attachLatest.php
@@ -25,49 +25,58 @@
2626 * @ingroup Maintenance
2727 */
2828
29 -require_once( 'commandLine.inc' );
 29+require_once( "Maintenance.php" );
3030
31 -$fixit = isset( $options['fix'] );
32 -$fname = 'attachLatest';
33 -
34 -echo "Looking for pages with page_latest set to 0...\n";
35 -$dbw = wfGetDB( DB_MASTER );
36 -$result = $dbw->select( 'page',
37 - array( 'page_id', 'page_namespace', 'page_title' ),
38 - array( 'page_latest' => 0 ),
39 - $fname );
40 -
41 -$n = 0;
42 -while( $row = $dbw->fetchObject( $result ) ) {
43 - $pageId = intval( $row->page_id );
44 - $title = Title::makeTitle( $row->page_namespace, $row->page_title );
45 - $name = $title->getPrefixedText();
46 - $latestTime = $dbw->selectField( 'revision',
47 - 'MAX(rev_timestamp)',
48 - array( 'rev_page' => $pageId ),
49 - $fname );
50 - if( !$latestTime ) {
51 - echo wfWikiID()." $pageId [[$name]] can't find latest rev time?!\n";
52 - continue;
 31+class AttachLatest extends Maintenance {
 32+
 33+ public function __construct() {
 34+ parent::__construct();
 35+ $this->addParam( "fix", "Actually fix the entries, will dry run otherwise" );
 36+ $this->mDescription = "Fix page_latest entries in the page table";
5337 }
 38+
 39+ public function execute() {
 40+ $this->output( "Looking for pages with page_latest set to 0...\n" );
 41+ $dbw = wfGetDB( DB_MASTER );
 42+ $result = $dbw->select( 'page',
 43+ array( 'page_id', 'page_namespace', 'page_title' ),
 44+ array( 'page_latest' => 0 ),
 45+ __METHOD__ );
5446
55 - $revision = Revision::loadFromTimestamp( $dbw, $title, $latestTime );
56 - if( is_null( $revision ) ) {
57 - echo wfWikiID()." $pageId [[$name]] latest time $latestTime, can't find revision id\n";
58 - continue;
 47+ $n = 0;
 48+ while( $row = $dbw->fetchObject( $result ) ) {
 49+ $pageId = intval( $row->page_id );
 50+ $title = Title::makeTitle( $row->page_namespace, $row->page_title );
 51+ $name = $title->getPrefixedText();
 52+ $latestTime = $dbw->selectField( 'revision',
 53+ 'MAX(rev_timestamp)',
 54+ array( 'rev_page' => $pageId ),
 55+ __METHOD__ );
 56+ if( !$latestTime ) {
 57+ $this->output( wfWikiID()." $pageId [[$name]] can't find latest rev time?!\n" );
 58+ continue;
 59+ }
 60+
 61+ $revision = Revision::loadFromTimestamp( $dbw, $title, $latestTime );
 62+ if( is_null( $revision ) ) {
 63+ $this->output( wfWikiID()." $pageId [[$name]] latest time $latestTime, can't find revision id\n" );
 64+ continue;
 65+ }
 66+ $id = $revision->getId();
 67+ $this->output( wfWikiID()." $pageId [[$name]] latest time $latestTime, rev id $id\n" );
 68+ if( $this->hasOption('fix') ) {
 69+ $article = new Article( $title );
 70+ $article->updateRevisionOn( $dbw, $revision );
 71+ }
 72+ $n++;
 73+ }
 74+ $dbw->freeResult( $result );
 75+ $this->output( "Done! Processed $n pages.\n" );
 76+ if( !$this->hasOption('fix') ) {
 77+ $this->output( "This was a dry run; rerun with --fix to update page_latest.\n" );
 78+ }
5979 }
60 - $id = $revision->getId();
61 - echo wfWikiID()." $pageId [[$name]] latest time $latestTime, rev id $id\n";
62 - if( $fixit ) {
63 - $article = new Article( $title );
64 - $article->updateRevisionOn( $dbw, $revision );
65 - }
66 - $n++;
6780 }
68 -$dbw->freeResult( $result );
69 -echo "Done! Processed $n pages.\n";
70 -if( !$fixit ) {
71 - echo "This was a dry run; rerun with --fix to update page_latest.\n";
72 -}
7381
74 -
 82+$maintClass = "AttachLatest";
 83+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/getSlaveServer.php
@@ -5,24 +5,32 @@
66 * @file
77 * @ingroup Maintenance
88 */
 9+
 10+require_once( "Maintenance.php" );
911
10 -require_once( dirname(__FILE__).'/commandLine.inc' );
11 -
12 -if ( $wgAllDBsAreLocalhost ) {
13 - # Can't fool the backup script
14 - print "localhost\n";
15 - exit;
 12+class GetSlaveServer extends Maintenance {
 13+ public function __construct() {
 14+ parent::__construct();
 15+ $this->addParam( "group", "Query group to check specifically" );
 16+ $this->mDescription = "Report the hostname of a slave server";
 17+ }
 18+ public function execute() {
 19+ global $wgAllDBsAreLocalhost;
 20+ if( $wgAllDBsAreLocalhost ) {
 21+ $host = 'localhost';
 22+ } else {
 23+ if( $this->hasOption('group') ) {
 24+ $db = wfGetDB( DB_SLAVE, $this->getOption('group') );
 25+ $host = $db->getServer();
 26+ } else {
 27+ $lb = wfGetLB();
 28+ $i = $lb->getReaderIndex();
 29+ $host = $lb->getServerName( $i );
 30+ }
 31+ }
 32+ $this->output( "$host\n" );
 33+ }
1634 }
1735
18 -if( isset( $options['group'] ) ) {
19 - $db = wfGetDB( DB_SLAVE, $options['group'] );
20 - $host = $db->getServer();
21 -} else {
22 - $lb = wfGetLB();
23 - $i = $lb->getReaderIndex();
24 - $host = $lb->getServerName( $i );
25 -}
26 -
27 -print "$host\n";
28 -
29 -
 36+$maintClass = "GetSlaveServer";
 37+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/changePassword.php
@@ -10,47 +10,32 @@
1111 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
1212 */
1313
14 -$optionsWithArgs = array( 'user', 'password' );
15 -require_once 'commandLine.inc';
 14+require_once( "Maintenance.php" );
1615
17 -$USAGE =
18 - "Usage: php changePassword.php [--user=user --password=password | --help]\n" .
19 - "\toptions:\n" .
20 - "\t\t--help show this message\n" .
21 - "\t\t--user the username to operate on\n" .
22 - "\t\t--password the password to use\n";
23 -
24 -if( in_array( '--help', $argv ) )
25 - wfDie( $USAGE );
26 -
27 -$cp = new ChangePassword( @$options['user'], @$options['password'] );
28 -$cp->main();
29 -
30 -/**
31 - * @ingroup Maintenance
32 - */
33 -class ChangePassword {
34 - var $dbw;
35 - var $user, $password;
36 -
37 - function ChangePassword( $user, $password ) {
38 - global $USAGE;
39 - if( !strlen( $user ) or !strlen( $password ) ) {
40 - wfDie( $USAGE );
 16+class ChangePassword extends Maintenance {
 17+ public function __construct() {
 18+ parent::__construct();
 19+ $this->addParam( "user", "The username to operate on", true, true );
 20+ $this->addParam( "password", "The password to use", true, true );
 21+ $this->mDescription = "Change a user's password."
 22+ }
 23+
 24+ public function execute() {
 25+ if( !$this->hasOption('user') || !$this->hasOption('password') ) {
 26+ $this->error( "Username or password not provided, halting.", true );
4127 }
42 -
43 - $this->user = User::newFromName( $user );
44 - if ( !$this->user->getId() ) {
45 - die ( "No such user: $user\n" );
 28+ $user = User::newFromName( $this->getOption('user') );
 29+ if( !$user->getId() ) {
 30+ $this->error( "No such user: " . $this->getOption('user') . "\n", true );
4631 }
47 -
48 - $this->password = $password;
49 -
50 - $this->dbw = wfGetDB( DB_MASTER );
 32+ try {
 33+ $user->setPassword( $this->getOption('password') );
 34+ $user->saveSettings();
 35+ } catch( PasswordError $pwe ) {
 36+ $this->error( $pwe->getText(), true );
 37+ }
5138 }
52 -
53 - function main() {
54 - $this->user->setPassword( $this->password );
55 - $this->user->saveSettings();
56 - }
5739 }
 40+
 41+$maintClass = "ChangePassword";
 42+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/removeUnusedAccounts.php
@@ -8,63 +8,89 @@
99 * @author Rob Church <robchur@gmail.com>
1010 */
1111
12 -$options = array( 'help', 'delete' );
13 -require_once( 'commandLine.inc' );
14 -require_once( 'removeUnusedAccounts.inc' );
15 -echo( "Remove Unused Accounts\n\n" );
16 -$fname = 'removeUnusedAccounts';
 12+require_once( "Maintenance.php" );
1713
18 -if( isset( $options['help'] ) ) {
19 - showHelp();
20 - exit(1);
21 -}
 14+class RemoveUnusedAccounts extends Maintenance {
 15+ public function __construct() {
 16+ parent::__construct();
 17+ $this->addParam( 'delete', 'Actually delete the account' );
 18+ $this->addParam( 'ignore-groups', 'List of comma-separated groups to exclude', false, true );
 19+ $this->addParam( 'ignore-touched', 'Skip accounts touched in last N days', false, true );
 20+ }
2221
23 -# Do an initial scan for inactive accounts and report the result
24 -echo( "Checking for unused user accounts...\n" );
25 -$del = array();
26 -$dbr = wfGetDB( DB_SLAVE );
27 -$res = $dbr->select( 'user', array( 'user_id', 'user_name', 'user_touched' ), '', $fname );
28 -if( isset( $options['ignore-groups'] ) ) {
29 - $excludedGroups = explode( ',', $options['ignore-groups'] );
30 -} else { $excludedGroups = array(); }
31 -$touchedSeconds = 0;
32 -if( isset( $options['ignore-touched'] ) ) {
33 - $touchedParamError = 0;
34 - if( ctype_digit( $options['ignore-touched'] ) ) {
35 - if( $options['ignore-touched'] <= 0 ) {
36 - $touchedParamError = 1;
 22+ public function execute() {
 23+
 24+ $this->output( "Remove unused accounts\n\n" );
 25+
 26+ # Do an initial scan for inactive accounts and report the result
 27+ $this->output( "Checking for unused user accounts...\n" );
 28+ $del = array();
 29+ $dbr = wfGetDB( DB_SLAVE );
 30+ $res = $dbr->select( 'user', array( 'user_id', 'user_name', 'user_touched' ), '', __METHOD__ );
 31+ if( $this->hasOption('ignore-groups') ) {
 32+ $excludedGroups = explode( ',', $this->getOption('ignore-groups') );
 33+ } else {
 34+ $excludedGroups = array();
3735 }
38 - } else { $touchedParamError = 1; }
39 - if( $touchedParamError == 1 ) {
40 - die( "Please put a valid positive integer on the --ignore-touched parameter.\n" );
41 - } else { $touchedSeconds = 86400 * $options['ignore-touched']; }
42 -}
43 -while( $row = $dbr->fetchObject( $res ) ) {
44 - # Check the account, but ignore it if it's within a $excludedGroups group or if it's touched within the $touchedSeconds seconds.
45 - $instance = User::newFromId( $row->user_id );
46 - if( count( array_intersect( $instance->getEffectiveGroups(), $excludedGroups ) ) == 0
47 - && isInactiveAccount( $row->user_id, true )
48 - && wfTimestamp( TS_UNIX, $row->user_touched ) < wfTimestamp( TS_UNIX, time() - $touchedSeconds )
49 - ) {
50 - # Inactive; print out the name and flag it
51 - $del[] = $row->user_id;
52 - echo( $row->user_name . "\n" );
 36+ $touched = $this->getOption( 'ignore-touched', "1" );
 37+ if( !ctype_digit( $touched ) ) {
 38+ $this->error( "Please put a valid positive integer on the --ignore-touched parameter.\n", true );
 39+ }
 40+ $touchedSeconds = 86400 * $touched;
 41+ while( $row = $dbr->fetchObject( $res ) ) {
 42+ # Check the account, but ignore it if it's within a $excludedGroups group or if it's touched within the $touchedSeconds seconds.
 43+ $instance = User::newFromId( $row->user_id );
 44+ if( count( array_intersect( $instance->getEffectiveGroups(), $excludedGroups ) ) == 0
 45+ && $this->isInactiveAccount( $row->user_id, true )
 46+ && wfTimestamp( TS_UNIX, $row->user_touched ) < wfTimestamp( TS_UNIX, time() - $touchedSeconds )
 47+ ) {
 48+ # Inactive; print out the name and flag it
 49+ $del[] = $row->user_id;
 50+ $this->output( $row->user_name . "\n" );
 51+ }
 52+ }
 53+ $count = count( $del );
 54+ $this->output( "...found {$count}.\n" );
 55+
 56+ # If required, go back and delete each marked account
 57+ if( $count > 0 && $this->hasOption('delete') ) {
 58+ $this->output( "\nDeleting inactive accounts..." );
 59+ $dbw = wfGetDB( DB_MASTER );
 60+ $dbw->delete( 'user', array( 'user_id' => $del ), __METHOD__ );
 61+ $this->output( "done.\n" );
 62+ # Update the site_stats.ss_users field
 63+ $users = $dbw->selectField( 'user', 'COUNT(*)', array(), __METHOD__ );
 64+ $dbw->update( 'site_stats', array( 'ss_users' => $users ), array( 'ss_row_id' => 1 ), __METHOD__ );
 65+ } elseif( $count > 0 ) {
 66+ $this->output( "\nRun the script again with --delete to remove them from the database.\n" );
 67+ }
 68+ $this->output( "\n" );
5369 }
 70+
 71+ /**
 72+ * Could the specified user account be deemed inactive?
 73+ * (No edits, no deleted edits, no log entries, no current/old uploads)
 74+ *
 75+ * @param $id User's ID
 76+ * @param $master Perform checking on the master
 77+ * @return bool
 78+ */
 79+ private function isInactiveAccount( $id, $master = false ) {
 80+ $dbo = wfGetDB( $master ? DB_MASTER : DB_SLAVE );
 81+ $checks = array( 'revision' => 'rev', 'archive' => 'ar', 'logging' => 'log',
 82+ 'image' => 'img', 'oldimage' => 'oi' );
 83+ $count = 0;
 84+
 85+ $dbo->immediateBegin();
 86+ foreach( $checks as $table => $fprefix ) {
 87+ $conds = array( $fprefix . '_user' => $id );
 88+ $count += (int)$dbo->selectField( $table, 'COUNT(*)', $conds, __METHOD__ );
 89+ }
 90+ $dbo->immediateCommit();
 91+
 92+ return $count == 0;
 93+ }
5494 }
55 -$count = count( $del );
56 -echo( "...found {$count}.\n" );
5795
58 -# If required, go back and delete each marked account
59 -if( $count > 0 && isset( $options['delete'] ) ) {
60 - echo( "\nDeleting inactive accounts..." );
61 - $dbw = wfGetDB( DB_MASTER );
62 - $dbw->delete( 'user', array( 'user_id' => $del ), $fname );
63 - echo( "done.\n" );
64 - # Update the site_stats.ss_users field
65 - $users = $dbw->selectField( 'user', 'COUNT(*)', array(), $fname );
66 - $dbw->update( 'site_stats', array( 'ss_users' => $users ), array( 'ss_row_id' => 1 ), $fname );
67 -} else {
68 - if( $count > 0 )
69 - echo( "\nRun the script again with --delete to remove them from the database.\n" );
70 -}
71 -echo( "\n" );
 96+$maintClass = "RemoveUnusedAccounts";
 97+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/populateLogSearch.php
@@ -9,13 +9,88 @@
1010 * @ingroup Maintenance
1111 */
1212
13 -require_once 'commandLine.inc';
14 -require_once 'populateLogSearch.inc';
 13+require_once( "Maintenance.php" );
 14+
 15+class PopulateLogSearch extends Maintenance {
 16+
 17+ const LOG_SEARCH_BATCH_SIZE = 100;
 18+
 19+ public function __construct() {
 20+ parent::__construct();
 21+ $this->mDescription = "Migrate log params to new table and index for searching";
 22+ }
 23+
 24+ public function execute() {
 25+ $db = wfGetDB( DB_MASTER );
 26+ if ( !$db->tableExists( 'log_search' ) ) {
 27+ $this->error( "log_search does not exist\n", true );
 28+ }
 29+ $start = $db->selectField( 'logging', 'MIN(log_id)', false, __FUNCTION__ );
 30+ if( !$start ) {
 31+ $this->output( "Nothing to do.\n" );
 32+ return true;
 33+ }
 34+ $end = $db->selectField( 'logging', 'MAX(log_id)', false, __FUNCTION__ );
1535
16 -$db =& wfGetDB( DB_MASTER );
17 -if ( !$db->tableExists( 'log_search' ) ) {
18 - echo "log_search does not exist\n";
19 - exit( 1 );
 36+ # Do remaining chunk
 37+ $end += self::LOG_SEARCH_BATCH_SIZE - 1;
 38+ $blockStart = $start;
 39+ $blockEnd = $start + self::LOG_SEARCH_BATCH_SIZE - 1;
 40+ while( $blockEnd <= $end ) {
 41+ $this->output( "...doing log_id from $blockStart to $blockEnd\n" );
 42+ $cond = "log_id BETWEEN $blockStart AND $blockEnd";
 43+ $res = $db->select( 'logging', '*', $cond, __FUNCTION__ );
 44+ $batch = array();
 45+ while( $row = $db->fetchObject( $res ) ) {
 46+ // RevisionDelete logs - revisions
 47+ if( LogEventsList::typeAction( $row, array('delete','suppress'), 'revision' ) ) {
 48+ $params = LogPage::extractParams( $row->log_params );
 49+ // Param format: <urlparam> <item CSV> [<ofield> <nfield>]
 50+ if( count($params) >= 2 ) {
 51+ $field = RevisionDeleter::getRelationType($params[0]);
 52+ // B/C, the params may start with a title key
 53+ if( $field == null ) {
 54+ array_shift($params);
 55+ $field = RevisionDeleter::getRelationType($params[0]);
 56+ }
 57+ if( $field == null ) {
 58+ $this->output( "Invalid param type for $row->log_id\n" );
 59+ continue; // skip this row
 60+ }
 61+ $items = explode(',',$params[1]);
 62+ $log = new LogPage( $row->log_type );
 63+ $log->addRelations( $field, $items, $row->log_id );
 64+ }
 65+ // RevisionDelete logs - log events
 66+ } else if( LogEventsList::typeAction( $row, array('delete','suppress'), 'event' ) ) {
 67+ $params = LogPage::extractParams( $row->log_params );
 68+ // Param format: <item CSV> [<ofield> <nfield>]
 69+ if( count($params) >= 1 ) {
 70+ $items = explode(',',$params[0]);
 71+ $log = new LogPage( $row->log_type );
 72+ $log->addRelations( 'log_id', $items, $row->log_id );
 73+ }
 74+ }
 75+ }
 76+ $blockStart += self::LOG_SEARCH_BATCH_SIZE;
 77+ $blockEnd += self::LOG_SEARCH_BATCH_SIZE;
 78+ wfWaitForSlaves( 5 );
 79+ }
 80+ if( $db->insert(
 81+ 'updatelog',
 82+ array( 'ul_key' => 'populate log_search' ),
 83+ __FUNCTION__,
 84+ 'IGNORE'
 85+ )
 86+ ) {
 87+ $this->output( "log_search population complete.\n" );
 88+ return true;
 89+ } else {
 90+ $this->output( "Could not insert log_search population row.\n" );
 91+ return false;
 92+ }
 93+ }
2094 }
2195
22 -migrate_log_params( $db );
 96+$maintClass = "PopulateLogSearch";
 97+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/fetchText.php
@@ -6,34 +6,48 @@
77 * @ingroup Maintenance
88 */
99
10 -require "commandLine.inc";
 10+require_once( "Maintenance.php" );
1111
12 -$db = wfGetDB( DB_SLAVE );
13 -$stdin = fopen( "php://stdin", "rt" );
14 -while( !feof( $stdin ) ) {
15 - $line = fgets( $stdin );
16 - if( $line === false ) {
17 - // We appear to have lost contact...
18 - break;
 12+class FetchText extends Maintenance {
 13+ public function __construct() {
 14+ parent::__construct();
 15+ $this->mDescription = "Fetch the revision text from an old_id";
1916 }
20 - $textId = intval( $line );
21 - $text = doGetText( $db, $textId );
22 - echo strlen( $text ) . "\n";
23 - echo $text;
24 -}
2517
26 -/**
27 - * May throw a database error if, say, the server dies during query.
28 - */
29 -function doGetText( $db, $id ) {
30 - $id = intval( $id );
31 - $row = $db->selectRow( 'text',
32 - array( 'old_text', 'old_flags' ),
33 - array( 'old_id' => $id ),
34 - 'TextPassDumper::getText' );
35 - $text = Revision::getRevisionText( $row );
36 - if( $text === false ) {
37 - return false;
 18+ public function execute() {
 19+ $db = wfGetDB( DB_SLAVE );
 20+ $stdin = $this->getStdin();
 21+ while( !feof( $stdin ) ) {
 22+ $line = fgets( $stdin );
 23+ if( $line === false ) {
 24+ // We appear to have lost contact...
 25+ break;
 26+ }
 27+ $textId = intval( $line );
 28+ $text = $this->doGetText( $db, $textId );
 29+ $this->output( strlen( $text ) . "\n". $text );
 30+ }
3831 }
39 - return $text;
 32+
 33+ /**
 34+ * May throw a database error if, say, the server dies during query.
 35+ * @param $db Database object
 36+ * @param $id int The old_id
 37+ * @return String
 38+ */
 39+ private function doGetText( $db, $id ) {
 40+ $id = intval( $id );
 41+ $row = $db->selectRow( 'text',
 42+ array( 'old_text', 'old_flags' ),
 43+ array( 'old_id' => $id ),
 44+ 'TextPassDumper::getText' );
 45+ $text = Revision::getRevisionText( $row );
 46+ if( $text === false ) {
 47+ return false;
 48+ }
 49+ return $text;
 50+ }
4051 }
 52+
 53+$maintClass = "FetchText";
 54+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/sql.php
@@ -7,38 +7,56 @@
88 * @ingroup Database Maintenance
99 */
1010
11 -require_once( dirname(__FILE__) . '/' . 'commandLine.inc' );
 11+require_once( "Maintenance.php" );
1212
13 -if ( isset( $options['help'] ) ) {
14 - echo "Send SQL queries to a MediaWiki database.\nUsage: php sql.php [<file>]\n";
15 - exit( 1 );
16 -}
 13+class MwSql extends Maintenance {
 14+ public function __construct() {
 15+ parent::__construct();
 16+ $this->mDescription = "Send SQL queries to a MediaWiki database";
 17+ }
1718
18 -if ( isset( $args[0] ) ) {
19 - $fileName = $args[0];
20 - $file = fopen( $fileName, 'r' );
21 - $promptCallback = false;
22 -} else {
23 - $file = STDIN;
24 - $promptObject = new SqlPromptPrinter( "> " );
25 - $promptCallback = $promptObject->cb();
26 -}
 19+ public function execute() {
 20+ if ( $this->hasArg() ) {
 21+ $fileName = $this->getArg();
 22+ $file = fopen( $fileName, 'r' );
 23+ $promptCallback = false;
 24+ } else {
 25+ $file = $this->getStdin();
 26+ $promptObject = new SqlPromptPrinter( "> " );
 27+ $promptCallback = $promptObject->cb();
 28+ }
 29+
 30+ if ( !$file )
 31+ $this->error( "Unable to open input file\n", true );
2732
28 -if ( !$file ) {
29 - echo "Unable to open input file\n";
30 - exit( 1 );
31 -}
 33+ $dbw = wfGetDB( DB_MASTER );
 34+ $error = $dbw->sourceStream( $file, $promptCallback, array( $this, 'sqlPrintResult' ) );
 35+ if ( $error !== true ) {
 36+ $this->error( $error, true );
 37+ } else {
 38+ exit( 0 );
 39+ }
 40+ }
3241
33 -$dbw =& wfGetDB( DB_MASTER );
34 -$error = $dbw->sourceStream( $file, $promptCallback, 'sqlPrintResult' );
35 -if ( $error !== true ) {
36 - echo $error;
37 - exit( 1 );
38 -} else {
39 - exit( 0 );
 42+ /**
 43+ * Print the results, callback for $db->sourceStream()
 44+ * @param $res The results object
 45+ * @param $db Database object
 46+ */
 47+ public function sqlPrintResult( $res, $db ) {
 48+ if ( !$res ) {
 49+ // Do nothing
 50+ } elseif ( is_object( $res ) && $res->numRows() ) {
 51+ while ( $row = $res->fetchObject() ) {
 52+ $this->output( print_r( $row, true ) );
 53+ }
 54+ } else {
 55+ $affected = $db->affectedRows();
 56+ $this->output( "Query OK, $affected row(s) affected\n" );
 57+ }
 58+ }
4059 }
4160
42 -//-----------------------------------------------------------------------------
4361 class SqlPromptPrinter {
4462 function __construct( $prompt ) {
4563 $this->prompt = $prompt;
@@ -53,17 +71,5 @@
5472 }
5573 }
5674
57 -function sqlPrintResult( $res, $db ) {
58 - if ( !$res ) {
59 - // Do nothing
60 - } elseif ( is_object( $res ) && $res->numRows() ) {
61 - while ( $row = $res->fetchObject() ) {
62 - print_r( $row );
63 - }
64 - } else {
65 - $affected = $db->affectedRows();
66 - echo "Query OK, $affected row(s) affected\n";
67 - }
68 -}
69 -
70 -
 75+$maintClass = "MwSql";
 76+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/showStats.php
@@ -14,35 +14,41 @@
1515 * @license GNU General Public License 2.0 or later
1616 */
1717
18 -require_once( 'commandLine.inc' );
 18+require_once( "Maintenance.php" );
1919
20 -#
21 -# Configuration
22 -#
23 -$fields = array(
24 - 'ss_total_views' => 'Total views',
25 - 'ss_total_edits' => 'Total edits',
26 - 'ss_good_articles' => 'Number of articles',
27 - 'ss_total_pages' => 'Total pages',
28 - 'ss_users' => 'Number of users',
29 - 'ss_admins' => 'Number of admins',
30 - 'ss_images' => 'Number of images',
31 -);
32 -
33 -// Get cached stats from slave database
34 -$dbr = wfGetDB( DB_SLAVE );
35 -$fname = 'showStats';
36 -$stats = $dbr->selectRow( 'site_stats', '*', '' );
37 -
38 -// Get maximum size for each column
39 -$max_length_value = $max_length_desc = 0;
40 -foreach( $fields as $field => $desc ) {
41 - $max_length_value = max( $max_length_value, strlen( $stats->$field ) );
42 - $max_length_desc = max( $max_length_desc , strlen( $desc )) ;
 20+class ShowStats extends Maintenance {
 21+ public function __construct() {
 22+ $this->mDescription = "Show the cached statistics";
 23+ }
 24+ public function execute() {
 25+ $fields = array(
 26+ 'ss_total_views' => 'Total views',
 27+ 'ss_total_edits' => 'Total edits',
 28+ 'ss_good_articles' => 'Number of articles',
 29+ 'ss_total_pages' => 'Total pages',
 30+ 'ss_users' => 'Number of users',
 31+ 'ss_admins' => 'Number of admins',
 32+ 'ss_images' => 'Number of images',
 33+ );
 34+
 35+ // Get cached stats from slave database
 36+ $dbr = wfGetDB( DB_SLAVE );
 37+ $stats = $dbr->selectRow( 'site_stats', '*', '', __METHOD__ );
 38+
 39+ // Get maximum size for each column
 40+ $max_length_value = $max_length_desc = 0;
 41+ foreach( $fields as $field => $desc ) {
 42+ $max_length_value = max( $max_length_value, strlen( $stats->$field ) );
 43+ $max_length_desc = max( $max_length_desc , strlen( $desc )) ;
 44+ }
 45+
 46+ // Show them
 47+ foreach( $fields as $field => $desc ) {
 48+ $this->output( sprintf( "%-{$max_length_desc}s: %{$max_length_value}d\n", $desc, $stats->$field ) );
 49+ }
 50+ }
4351 }
4452
45 -// Show them
46 -foreach( $fields as $field => $desc ) {
47 - printf( "%-{$max_length_desc}s: %{$max_length_value}d\n", $desc, $stats->$field );
48 -}
 53+$maintClass = "ShowStats";
 54+require_once( DO_MAINTENANCE );
4955
Index: trunk/phase3/maintenance/nukePage.php
@@ -1,5 +1,4 @@
22 <?php
3 -
43 /**
54 * Erase a page record from the database
65 * Irreversible (can't use standard undelete) and does not update link tables
@@ -9,21 +8,92 @@
109 * @author Rob Church <robchur@gmail.com>
1110 */
1211
13 -require_once( 'commandLine.inc' );
14 -require_once( 'nukePage.inc' );
 12+require_once( "Maintenance.php" );
1513
16 -echo( "Erase Page Record\n\n" );
 14+class NukePage extends Maintenance {
 15+ public function __construct() {
 16+ parent::__construct();
 17+ $this->mDescription = "Remove a page record from the database";
 18+ $this->addParam( 'delete', "Actually delete the page" );
 19+ $this->addArgs( array( 'title' ) );
 20+ }
1721
18 -if( isset( $args[0] ) ) {
19 - NukePage( $args[0], true );
20 -} else {
21 - ShowUsage();
22 -}
 22+ public function execute() {
2323
24 -/** Show script usage information */
25 -function ShowUsage() {
26 - echo( "Remove a page record from the database.\n\n" );
27 - echo( "Usage: php nukePage.php <title>\n\n" );
28 - echo( " <title> : Page title; spaces escaped with underscores\n\n" );
 24+ $name = $this->getArg();
 25+ $delete = $this->getOption( 'delete', false );
 26+
 27+ $dbw = wfGetDB( DB_MASTER );
 28+ $dbw->begin();
 29+
 30+ $tbl_pag = $dbw->tableName( 'page' );
 31+ $tbl_rec = $dbw->tableName( 'recentchanges' );
 32+ $tbl_rev = $dbw->tableName( 'revision' );
 33+
 34+ # Get page ID
 35+ $this->output( "Searching for \"$name\"..." );
 36+ $title = Title::newFromText( $name );
 37+ if( $title ) {
 38+ $id = $title->getArticleID();
 39+ $real = $title->getPrefixedText();
 40+ $isGoodArticle = $title->isContentPage();
 41+ $this->output( "found \"$real\" with ID $id.\n" );
 42+
 43+ # Get corresponding revisions
 44+ $this->output( "Searching for revisions..." );
 45+ $res = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_page = $id" );
 46+ while( $row = $dbw->fetchObject( $res ) ) {
 47+ $revs[] = $row->rev_id;
 48+ }
 49+ $count = count( $revs );
 50+ $this->output( "found $count.\n" );
 51+
 52+ # Delete the page record and associated recent changes entries
 53+ if( $delete ) {
 54+ $this->output( "Deleting page record..." );
 55+ $dbw->query( "DELETE FROM $tbl_pag WHERE page_id = $id" );
 56+ $this->output( "done.\n" );
 57+ $this->output( "Cleaning up recent changes..." );
 58+ $dbw->query( "DELETE FROM $tbl_rec WHERE rc_cur_id = $id" );
 59+ $this->output( "done.\n" );
 60+ }
 61+
 62+ $dbw->commit();
 63+
 64+ # Delete revisions as appropriate
 65+ if( $delete && $count ) {
 66+ $this->output( "Deleting revisions..." );
 67+ $this->deleteRevisions( $revs );
 68+ $this->output( "done.\n" );
 69+ $this->purgeRedundantText( true );
 70+ }
 71+
 72+ # Update stats as appropriate
 73+ if ( $delete ) {
 74+ $this->output( "Updating site stats..." );
 75+ $ga = $isGoodArticle ? -1 : 0; // if it was good, decrement that too
 76+ $stats = new SiteStatsUpdate( 0, -$count, $ga, -1 );
 77+ $stats->doUpdate();
 78+ $this->output( "done.\n" );
 79+ }
 80+ } else {
 81+ $this->output( "not found in database.\n" );
 82+ $dbw->commit();
 83+ }
 84+ }
 85+
 86+ public function deleteRevisions( $ids ) {
 87+ $dbw = wfGetDB( DB_MASTER );
 88+ $dbw->begin();
 89+
 90+ $tbl_rev = $dbw->tableName( 'revision' );
 91+
 92+ $set = implode( ', ', $ids );
 93+ $dbw->query( "DELETE FROM $tbl_rev WHERE rev_id IN ( $set )" );
 94+
 95+ $dbw->commit();
 96+ }
2997 }
3098
 99+$maintClass = "NukePage";
 100+require_once( DO_MAINTENANCE );
\ No newline at end of file
Index: trunk/phase3/maintenance/updateSpecialPages.php
@@ -6,114 +6,114 @@
77 * @file
88 * @ingroup Maintenance
99 */
10 -$options = array('only','help');
 10+
 11+require_once( "Maintenance.php" );
1112
12 -require_once( 'commandLine.inc' );
13 -
14 -require_once( "$IP/includes/SpecialPage.php" );
15 -require_once( "$IP/includes/QueryPage.php" );
16 -
17 -if(@$options['help']) {
18 - print "usage:updateSpecialPages.php [--help] [--only=page]\n";
19 - print " --help : this help message\n";
20 - print " --list : list special pages names\n";
21 - print " --only=page : only update 'page'. Ex: --only=BrokenRedirects\n";
22 - print " --override : update even pages which have had updates disabled\n";
23 - wfDie();
24 -}
25 -
26 -$wgOut->disable();
27 -$dbw = wfGetDB( DB_MASTER );
28 -
29 -foreach( $wgSpecialPageCacheUpdates as $special => $call ) {
30 - if( !is_callable($call) ) {
31 - print "Uncallable function $call!\n";
32 - continue;
 13+class UpdateSpecialPages extends Maintenance {
 14+ public function __construct() {
 15+ parent::__construct();
 16+ $this->addParam( 'list', 'List special page names' );
 17+ $this->addParam( 'only', 'Only update "page". Ex: --only=BrokenRedirects', false, true );
 18+ $this->addParam( 'override', 'Also update pages that have updates disabled' );
3319 }
34 - $t1 = explode( ' ', microtime() );
35 - call_user_func( $call, $dbw );
36 - $t2 = explode( ' ', microtime() );
37 - printf( '%-30s ', $special );
38 - $elapsed = ($t2[0] - $t1[0]) + ($t2[1] - $t1[1]);
39 - $hours = intval( $elapsed / 3600 );
40 - $minutes = intval( $elapsed % 3600 / 60 );
41 - $seconds = $elapsed - $hours * 3600 - $minutes * 60;
42 - if ( $hours ) {
43 - print $hours . 'h ';
44 - }
45 - if ( $minutes ) {
46 - print $minutes . 'm ';
47 - }
48 - printf( "completed in %.2fs\n", $seconds );
49 - # Wait for the slave to catch up
50 - wfWaitForSlaves( 5 );
51 -}
5220
53 -foreach( $wgQueryPages as $page ) {
54 - @list( $class, $special, $limit ) = $page;
 21+ public function execute() {
 22+ global $wgOut;
 23+ $wgOut->disable();
 24+ $dbw = wfGetDB( DB_MASTER );
5525
56 - # --list : just show the name of pages
57 - if( @$options['list'] ) {
58 - print "$special\n";
59 - continue;
60 - }
61 -
62 - if ( !isset( $options['override'] ) && $wgDisableQueryPageUpdate && in_array( $special, $wgDisableQueryPageUpdate ) ) {
63 - printf("%-30s disabled\n", $special);
64 - continue;
65 - }
66 -
67 - $specialObj = SpecialPage::getPage( $special );
68 - if ( !$specialObj ) {
69 - print "No such special page: $special\n";
70 - exit;
71 - }
72 - if ( !class_exists( $class ) ) {
73 - $file = $specialObj->getFile();
74 - require_once( $file );
75 - }
76 - $queryPage = new $class;
77 -
78 - if( !isset($options['only']) or $options['only'] == $queryPage->getName() ) {
79 - printf( '%-30s ', $special );
80 - if ( $queryPage->isExpensive() ) {
 26+ foreach( $wgSpecialPageCacheUpdates as $special => $call ) {
 27+ if( !is_callable($call) ) {
 28+ $this->error( "Uncallable function $call!\n" );
 29+ continue;
 30+ }
8131 $t1 = explode( ' ', microtime() );
82 - # Do the query
83 - $num = $queryPage->recache( $limit === null ? $wgQueryCacheLimit : $limit );
 32+ call_user_func( $call, $dbw );
8433 $t2 = explode( ' ', microtime() );
85 - if ( $num === false ) {
86 - print "FAILED: database error\n";
87 - } else {
88 - print "got $num rows in ";
89 -
90 - $elapsed = ($t2[0] - $t1[0]) + ($t2[1] - $t1[1]);
91 - $hours = intval( $elapsed / 3600 );
92 - $minutes = intval( $elapsed % 3600 / 60 );
93 - $seconds = $elapsed - $hours * 3600 - $minutes * 60;
94 - if ( $hours ) {
95 - print $hours . 'h ';
 34+ $this->output( sprintf( '%-30s ', $special ) );
 35+ $elapsed = ($t2[0] - $t1[0]) + ($t2[1] - $t1[1]);
 36+ $hours = intval( $elapsed / 3600 );
 37+ $minutes = intval( $elapsed % 3600 / 60 );
 38+ $seconds = $elapsed - $hours * 3600 - $minutes * 60;
 39+ if ( $hours ) {
 40+ $this->output( $hours . 'h ' );
 41+ }
 42+ if ( $minutes ) {
 43+ $this->output( $minutes . 'm ' );
 44+ }
 45+ $this->output( sprintf( "completed in %.2fs\n", $seconds ) );
 46+ # Wait for the slave to catch up
 47+ wfWaitForSlaves( 5 );
 48+ }
 49+
 50+ foreach( $wgQueryPages as $page ) {
 51+ @list( $class, $special, $limit ) = $page;
 52+
 53+ # --list : just show the name of pages
 54+ if( $this->hasOption('list') ) {
 55+ $this->output( "$special\n" );
 56+ continue;
 57+ }
 58+
 59+ if ( $this->hasOption('override') && $wgDisableQueryPageUpdate && in_array( $special, $wgDisableQueryPageUpdate ) ) {
 60+ $this->output( sprintf( "%-30s disabled\n", $special ) );
 61+ continue;
 62+ }
 63+
 64+ $specialObj = SpecialPage::getPage( $special );
 65+ if ( !$specialObj ) {
 66+ $this->output( "No such special page: $special\n" );
 67+ exit;
 68+ }
 69+ if ( !class_exists( $class ) ) {
 70+ $file = $specialObj->getFile();
 71+ require_once( $file );
 72+ }
 73+ $queryPage = new $class;
 74+
 75+ if( !$this->hasOption('only') || $this->getOption('only') == $queryPage->getName() ) {
 76+ $this->output( sprintf( '%-30s ', $special ) );
 77+ if ( $queryPage->isExpensive() ) {
 78+ $t1 = explode( ' ', microtime() );
 79+ # Do the query
 80+ $num = $queryPage->recache( $limit === null ? $wgQueryCacheLimit : $limit );
 81+ $t2 = explode( ' ', microtime() );
 82+ if ( $num === false ) {
 83+ $this->output( "FAILED: database error\n" );
 84+ } else {
 85+ $this->output( "got $num rows in " );
 86+
 87+ $elapsed = ($t2[0] - $t1[0]) + ($t2[1] - $t1[1]);
 88+ $hours = intval( $elapsed / 3600 );
 89+ $minutes = intval( $elapsed % 3600 / 60 );
 90+ $seconds = $elapsed - $hours * 3600 - $minutes * 60;
 91+ if ( $hours ) {
 92+ $this->output( $hours . 'h ' );
 93+ }
 94+ if ( $minutes ) {
 95+ $this->output( $minutes . 'm ' );
 96+ }
 97+ $this->output( sprintf( "%.2fs\n", $seconds ) );
9698 }
97 - if ( $minutes ) {
98 - print $minutes . 'm ';
 99+ # Reopen any connections that have closed
 100+ if ( !wfGetLB()->pingAll()) {
 101+ $this->output( "\n" );
 102+ do {
 103+ $this->error( "Connection failed, reconnecting in 10 seconds...\n" );
 104+ sleep(10);
 105+ } while ( !wfGetLB()->pingAll() );
 106+ $this->output( "Reconnected\n\n" );
 107+ } else {
 108+ # Commit the results
 109+ $dbw->immediateCommit();
99110 }
100 - printf( "%.2fs\n", $seconds );
 111+ # Wait for the slave to catch up
 112+ wfWaitForSlaves( 5 );
 113+ } else {
 114+ $this->output( "cheap, skipped\n" );
 115+ }
 116+ }
101117 }
102 - # Reopen any connections that have closed
103 - if ( !wfGetLB()->pingAll()) {
104 - print "\n";
105 - do {
106 - print "Connection failed, reconnecting in 10 seconds...\n";
107 - sleep(10);
108 - } while ( !wfGetLB()->pingAll() );
109 - print "Reconnected\n\n";
110 - } else {
111 - # Commit the results
112 - $dbw->immediateCommit();
113 - }
114 - # Wait for the slave to catch up
115 - wfWaitForSlaves( 5 );
116 - } else {
117 - print "cheap, skipped\n";
118 - }
119118 }
120119 }
 120+
Index: trunk/phase3/maintenance/nukeNS.php
@@ -19,90 +19,80 @@
2020 * based on nukePage by Rob Church
2121 */
2222
23 -require_once( 'commandLine.inc' );
24 -require_once( 'nukePage.inc' );
 23+require_once( "Maintenance.php" );
2524
26 -$ns = NS_MEDIAWIKI;
27 -$delete = false;
 25+class NukeNS extends Maintenance {
 26+ public function __construct() {
 27+ parent::__construct();
 28+ $this->mDescription = "Remove pages with only 1 revision from any namespace";
 29+ $this->addParam( 'delete', "Actually delete the page" );
 30+ $this->addParam( 'ns', 'Namespace to delete from, default NS_MEDIAWIKI', false, true );
 31+ }
2832
29 -if (isset($options['ns']))
30 -{
31 - $ns = $options['ns'];
32 -}
 33+ public function execute() {
 34+ $ns = $this->getOption( 'ns', NS_MEDIAWIKI );
 35+ $delete = $this->getOption( 'delete', false );
 36+ $dbw = wfGetDB( DB_MASTER );
 37+ $dbw->begin();
3338
34 -if (isset( $options['delete'] ) and $options['delete'])
35 -{
36 - $delete = true;
37 -}
 39+ $tbl_pag = $dbw->tableName( 'page' );
 40+ $tbl_rev = $dbw->tableName( 'revision' );
 41+ $res = $dbw->query( "SELECT page_title FROM $tbl_pag WHERE page_namespace = $ns" );
3842
 43+ $n_deleted = 0;
3944
40 -NukeNS( $ns, $delete);
 45+ while( $row = $dbw->fetchObject( $res ) ) {
 46+ //echo "$ns_name:".$row->page_title, "\n";
 47+ $title = Title::newFromText($row->page_title, $ns);
 48+ $id = $title->getArticleID();
4149
42 -function NukeNS($ns_no, $delete) {
 50+ // Get corresponding revisions
 51+ $res2 = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_page = $id" );
 52+ $revs = array();
4353
44 - $dbw = wfGetDB( DB_MASTER );
45 - $dbw->begin();
46 -
47 - $tbl_pag = $dbw->tableName( 'page' );
48 - $tbl_rev = $dbw->tableName( 'revision' );
49 - $res = $dbw->query( "SELECT page_title FROM $tbl_pag WHERE page_namespace = $ns_no" );
 54+ while( $row2 = $dbw->fetchObject( $res2 ) ) {
 55+ $revs[] = $row2->rev_id;
 56+ }
 57+ $count = count( $revs );
5058
51 - $n_deleted = 0;
52 -
53 - while( $row = $dbw->fetchObject( $res ) ) {
54 - //echo "$ns_name:".$row->page_title, "\n";
55 - $title = Title::newFromText($row->page_title, $ns_no);
56 - $id = $title->getArticleID();
 59+ //skip anything that looks modified (i.e. multiple revs)
 60+ if (($count == 1)) {
 61+ #echo $title->getPrefixedText(), "\t", $count, "\n";
 62+ $this->output( "delete: ", $title->getPrefixedText(), "\n" );
5763
58 - // Get corresponding revisions
59 - $res2 = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_page = $id" );
60 - $revs = array();
61 -
62 - while( $row2 = $dbw->fetchObject( $res2 ) ) {
63 - $revs[] = $row2->rev_id;
64 - }
65 - $count = count( $revs );
 64+ //as much as I hate to cut & paste this, it's a little different, and
 65+ //I already have the id & revs
 66+ if( $delete ) {
 67+ $dbw->query( "DELETE FROM $tbl_pag WHERE page_id = $id" );
 68+ $dbw->commit();
 69+ // Delete revisions as appropriate
 70+ $child = $this->spawnChild( 'NukePage', 'NukePage.php' );
 71+ $child->deleteRevisions( $revs );
 72+ $this->purgeRedundantText( true );
 73+ $n_deleted ++;
 74+ }
 75+ } else {
 76+ $this->output( "skip: ", $title->getPrefixedText(), "\n" );
 77+ }
 78+ }
 79+ $dbw->commit();
6680
67 - //skip anything that looks modified (i.e. multiple revs)
68 - if (($count == 1)) {
69 - #echo $title->getPrefixedText(), "\t", $count, "\n";
70 - echo "delete: ", $title->getPrefixedText(), "\n";
71 -
72 - //as much as I hate to cut & paste this, it's a little different, and
73 - //I already have the id & revs
74 -
75 - if( $delete ) {
76 - $dbw->query( "DELETE FROM $tbl_pag WHERE page_id = $id" );
77 - $dbw->commit();
78 - // Delete revisions as appropriate
79 - DeleteRevisions( $revs );
80 - PurgeRedundantText( true );
81 - $n_deleted ++;
82 - }
83 - } else {
84 - echo "skip: ", $title->getPrefixedText(), "\n";
85 - }
86 -
87 -
88 - }
89 - $dbw->commit();
90 -
91 - if ($n_deleted > 0) {
92 - #update statistics - better to decrement existing count, or just count
93 - #the page table?
94 - $pages = $dbw->selectField('site_stats', 'ss_total_pages');
95 - $pages -= $n_deleted;
96 - $dbw->update( 'site_stats',
97 - array('ss_total_pages' => $pages ),
98 - array( 'ss_row_id' => 1),
99 - __METHOD__ );
100 -
101 - }
102 -
103 - if (!$delete) {
104 - echo( "To update the database, run the script with the --delete option.\n" );
105 - }
106 -
 81+ if ($n_deleted > 0) {
 82+ #update statistics - better to decrement existing count, or just count
 83+ #the page table?
 84+ $pages = $dbw->selectField('site_stats', 'ss_total_pages');
 85+ $pages -= $n_deleted;
 86+ $dbw->update( 'site_stats',
 87+ array('ss_total_pages' => $pages ),
 88+ array( 'ss_row_id' => 1),
 89+ __METHOD__ );
 90+ }
 91+
 92+ if (!$delete) {
 93+ $this->output( "To update the database, run the script with the --delete option.\n" );
 94+ }
 95+ }
10796 }
10897
109 -
 98+$maintClass = "NukeNS";
 99+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/README
@@ -10,8 +10,8 @@
1111
1212 Certain scripts will require elevated access to the database. In order to
1313 provide this, first create a MySQL user with "all" permissions on the wiki
14 -database, and then place their username and password in an AdminSettings.php
15 -file in the directory above. See AdminSettings.sample for specifics on this.
 14+database, and then set $wgDBadminuser and $wgDBadminpassword in your
 15+LocalSettings.php
1616
1717 === Brief explanation of files ===
1818
Index: trunk/phase3/maintenance/updaters.inc
@@ -1038,10 +1038,7 @@
10391039 wfOut( "ok.\n" );
10401040 return;
10411041 }
1042 -
1043 - global $IP;
1044 - require_once "$IP/maintenance/initStats.inc";
1045 - wfInitStats();
 1042+ SiteStats::init( false );
10461043 }
10471044
10481045 function do_active_users_init() {
Index: trunk/phase3/maintenance/purgeOldText.php
@@ -1,29 +1,24 @@
22 <?php
3 -
43 /**
54 * Purge old text records from the database
65 *
7 - * @file
86 * @ingroup Maintenance
97 * @author Rob Church <robchur@gmail.com>
108 */
119
12 -$options = array( 'purge', 'help' );
13 -require_once( 'commandLine.inc' );
14 -require_once( 'purgeOldText.inc' );
 10+require_once( "Maintenance.php" );
1511
16 -echo( "Purge Old Text\n\n" );
17 -
18 -if( @$options['help'] ) {
19 - ShowUsage();
20 -} else {
21 - PurgeRedundantText( @$options['purge'] );
 12+class PurgeOldText extends Maintenance {
 13+ public function __construct() {
 14+ parent::__construct();
 15+ $this->mDescription = "Purge old text records from the database";
 16+ $this->addOption( 'purge', 'Performs the deletion' );
 17+ }
 18+
 19+ public function execute() {
 20+ $this->purgeRedundantText( $this->hasOption('purge') );
 21+ }
2222 }
2323
24 -function ShowUsage() {
25 - echo( "Prunes unused text records from the database.\n\n" );
26 - echo( "Usage: php purgeOldText.php [--purge]\n\n" );
27 - echo( "purge : Performs the deletion\n" );
28 - echo( " help : Show this usage information\n" );
29 -}
30 -
 24+$maintClass = "PurgeOldText";
 25+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/Doxyfile
@@ -135,7 +135,7 @@
136136 RECURSIVE = YES
137137 EXCLUDE =
138138 EXCLUDE_SYMLINKS = YES
139 -EXCLUDE_PATTERNS = LocalSettings.php AdminSettings.php
 139+EXCLUDE_PATTERNS = LocalSettings.php
140140 EXAMPLE_PATH =
141141 EXAMPLE_PATTERNS = *
142142 EXAMPLE_RECURSIVE = NO
Index: trunk/phase3/maintenance/populateParentId.php
@@ -1,18 +1,105 @@
22 <?php
3 -
43 /*
54 * Makes the required database updates for rev_parent_id
65 * to be of any use. It can be used for some simple tracking
76 * and to find new page edits by users.
87 */
98
10 -require_once 'commandLine.inc';
11 -require_once 'populateParentId.inc';
12 -
13 -$db =& wfGetDB( DB_MASTER );
14 -if ( !$db->tableExists( 'revision' ) ) {
15 - echo "revision table does not exist\n";
16 - exit( 1 );
 9+require_once( "Maintenance.php" );
 10+
 11+class PopulateParentId extends Maintenance {
 12+
 13+ // Batch size
 14+ const BATCH_SIZE = 200;
 15+
 16+ public function __construct() {
 17+ parent::__construct();
 18+ $this->mDescription = "Populates rev_parent_id";
 19+ }
 20+
 21+ public function execute() {
 22+ $db = wfGetDB( DB_MASTER );
 23+ if ( !$db->tableExists( 'revision' ) ) {
 24+ $this->error( "revision table does not exist\n", true );
 25+ }
 26+ $this->output( "Populating rev_parent_id column\n" );
 27+ $start = $db->selectField( 'revision', 'MIN(rev_id)', false, __FUNCTION__ );
 28+ $end = $db->selectField( 'revision', 'MAX(rev_id)', false, __FUNCTION__ );
 29+ if( is_null( $start ) || is_null( $end ) ){
 30+ $this->output( "...revision table seems to be empty.\n" );
 31+ $db->insert( 'updatelog',
 32+ array( 'ul_key' => 'populate rev_parent_id' ),
 33+ __FUNCTION__,
 34+ 'IGNORE' );
 35+ return;
 36+ }
 37+ # Do remaining chunk
 38+ $end += self::BATCH_SIZE - 1;
 39+ $blockStart = intval( $start );
 40+ $blockEnd = intval( $start ) + self::BATCH_SIZE - 1;
 41+ $count = 0;
 42+ $changed = 0;
 43+ while( $blockEnd <= $end ) {
 44+ $this->output( "...doing rev_id from $blockStart to $blockEnd\n" );
 45+ $cond = "rev_id BETWEEN $blockStart AND $blockEnd";
 46+ $res = $db->select( 'revision',
 47+ array('rev_id','rev_page','rev_timestamp','rev_parent_id'),
 48+ $cond, __FUNCTION__ );
 49+ # Go through and update rev_parent_id from these rows.
 50+ # Assume that the previous revision of the title was
 51+ # the original previous revision of the title when the
 52+ # edit was made...
 53+ foreach( $res as $row ) {
 54+ # First, check rows with the same timestamp other than this one
 55+ # with a smaller rev ID. The highest ID "wins". This avoids loops
 56+ # as timestamp can only decrease and never loops with IDs (from parent to parent)
 57+ $previousID = $db->selectField( 'revision', 'rev_id',
 58+ array( 'rev_page' => $row->rev_page, 'rev_timestamp' => $row->rev_timestamp,
 59+ "rev_id < " . intval( $row->rev_id ) ),
 60+ __FUNCTION__,
 61+ array( 'ORDER BY' => 'rev_id DESC' ) );
 62+ # If there are none, check the the highest ID with a lower timestamp
 63+ if( !$previousID ) {
 64+ # Get the highest older timestamp
 65+ $lastTimestamp = $db->selectField( 'revision', 'rev_timestamp',
 66+ array( 'rev_page' => $row->rev_page, "rev_timestamp < " . $db->addQuotes( $row->rev_timestamp ) ),
 67+ __FUNCTION__,
 68+ array( 'ORDER BY' => 'rev_timestamp DESC' ) );
 69+ # If there is one, let the highest rev ID win
 70+ if( $lastTimestamp ) {
 71+ $previousID = $db->selectField( 'revision', 'rev_id',
 72+ array( 'rev_page' => $row->rev_page, 'rev_timestamp' => $lastTimestamp ),
 73+ __FUNCTION__,
 74+ array( 'ORDER BY' => 'rev_id DESC' ) );
 75+ }
 76+ }
 77+ $previousID = intval($previousID);
 78+ if( $previousID != $row->rev_parent_id )
 79+ $changed++;
 80+ # Update the row...
 81+ $db->update( 'revision',
 82+ array( 'rev_parent_id' => $previousID ),
 83+ array( 'rev_id' => $row->rev_id ),
 84+ __FUNCTION__ );
 85+ $count++;
 86+ }
 87+ $blockStart += self::BATCH_SIZE - 1;
 88+ $blockEnd += self::BATCH_SIZE - 1;
 89+ wfWaitForSlaves( 5 );
 90+ }
 91+ $logged = $db->insert( 'updatelog',
 92+ array( 'ul_key' => 'populate rev_parent_id' ),
 93+ __FUNCTION__,
 94+ 'IGNORE' );
 95+ if( $logged ) {
 96+ $this->output( "rev_parent_id population complete ... {$count} rows [{$changed} changed]\n" );
 97+ return true;
 98+ } else {
 99+ $this->output( "Could not insert rev_parent_id population row.\n" );
 100+ return false;
 101+ }
 102+ }
17103 }
18104
19 -populate_rev_parent_id( $db );
 105+$maintClass = "PopulateParentId";
 106+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/checkBadRedirects.php
@@ -1,30 +1,42 @@
22 <?php
 3+/**
 4+ * CheckBadRedirects - See if pages marked as being redirects
 5+ * really are.
 6+ */
 7+
 8+require_once( "Maintenance.php" );
39
4 -require "commandLine.inc";
 10+class CheckBadRedirects extends Maintenance {
 11+ public function __construct() {
 12+ parent::__construct();
 13+ $this->mDescription = "Look for bad redirects";
 14+ }
515
6 -echo "Fetching redirects...\n";
7 -$dbr = wfGetDB( DB_SLAVE );
8 -$result = $dbr->select(
9 - array( 'page' ),
10 - array( 'page_namespace','page_title', 'page_latest' ),
11 - array( 'page_is_redirect' => 1 ) );
12 -
13 -$count = $result->numRows();
14 -echo "Found $count total redirects.\n";
15 -echo "Looking for bad redirects:\n";
16 -echo "\n";
17 -
18 -foreach( $result as $row ) {
19 - $title = Title::makeTitle( $row->page_namespace, $row->page_title );
20 - $rev = Revision::newFromId( $row->page_latest );
21 - if( $rev ) {
22 - $target = Title::newFromRedirect( $rev->getText() );
23 - if( !$target ) {
24 - echo $title->getPrefixedText();
25 - echo "\n";
 16+ public function execute() {
 17+ $this->output( "Fetching redirects...\n" );
 18+ $dbr = wfGetDB( DB_SLAVE );
 19+ $result = $dbr->select(
 20+ array( 'page' ),
 21+ array( 'page_namespace','page_title', 'page_latest' ),
 22+ array( 'page_is_redirect' => 1 ) );
 23+
 24+ $count = $result->numRows();
 25+ $this->output( "Found $count total redirects.\n" .
 26+ "Looking for bad redirects:\n\n" );
 27+
 28+ foreach( $result as $row ) {
 29+ $title = Title::makeTitle( $row->page_namespace, $row->page_title );
 30+ $rev = Revision::newFromId( $row->page_latest );
 31+ if( $rev ) {
 32+ $target = Title::newFromRedirect( $rev->getText() );
 33+ if( !$target ) {
 34+ $this->output( $title->getPrefixedText() . "\n" );
 35+ }
 36+ }
2637 }
 38+ $this->output( "\ndone.\n" );
2739 }
2840 }
2941
30 -echo "\n";
31 -echo "done.\n";
 42+$maintClass = "CheckBadRedirects";
 43+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/renameDbPrefix.php
@@ -6,62 +6,59 @@
77 * @file
88 * @ingroup Maintenance
99 */
10 -$optionsWithArgs = array( 'old', 'new', 'help' );
 10+
 11+require_once( "Maintenance.php" );
1112
12 -require_once( 'commandLine.inc' );
13 -
14 -if( @$options['help'] || !isset( $options['old'] ) || !isset( $options['new'] ) ) {
15 - print "usage: renameDbPrefix.php [--help] [--old x] [new y]\n";
16 - print " --help : this help message\n";
17 - print " --old x : old db prefix x\n";
18 - print " --old 0 : EMPTY old db prefix x\n";
19 - print " --new y : new db prefix y\n";
20 - print " --new 0 : EMPTY new db prefix\n";
21 - wfDie();
22 -}
23 -
24 -// Allow for no old prefix
25 -if( $options['old'] === '0' ) {
26 - $old = '';
27 -} else {
28 - // Use nice safe, sane, prefixes
29 - preg_match( '/^[a-zA-Z]+_$/', $options['old'], $m );
30 - $old = isset( $m[0] ) ? $m[0] : false;
31 -}
32 -// Allow for no new prefix
33 -if( $options['new'] === '0' ) {
34 - $new = '';
35 -} else {
36 - // Use nice safe, sane, prefixes
37 - preg_match( '/^[a-zA-Z]+_$/', $options['new'], $m );
38 - $new = isset( $m[0] ) ? $m[0] : false;
39 -}
40 -
41 -if( $old === false || $new === false ) {
42 - print "Invalid prefix!\n";
43 - wfDie();
44 -}
45 -if( $old === $new ) {
46 - print "Same prefix. Nothing to rename!\n";
47 - wfDie();
48 -}
49 -
50 -print "Renaming DB prefix for tables of $wgDBname from '$old' to '$new'\n";
51 -$count = 0;
52 -
53 -$dbw = wfGetDB( DB_MASTER );
54 -$res = $dbw->query( "SHOW TABLES LIKE '".$dbw->escapeLike( $old )."%'" );
55 -foreach( $res as $row ) {
56 - // XXX: odd syntax. MySQL outputs an oddly cased "Tables of X"
57 - // sort of message. Best not to try $row->x stuff...
58 - $fields = get_object_vars( $row );
59 - // Silly for loop over one field...
60 - foreach( $fields as $resName => $table ) {
61 - // $old should be regexp safe ([a-zA-Z_])
62 - $newTable = preg_replace( '/^'.$old.'/', $new, $table );
63 - print "Renaming table $table to $newTable\n";
64 - $dbw->query( "RENAME TABLE $table TO $newTable" );
 13+class RenameDbPrefix extends Maintenance {
 14+ public function __construct() {
 15+ parent::__construct();
 16+ $this->addParam( "old", "Old db prefix [0 for none]", true, true );
 17+ $this->addParam( "new", "New db prefix [0 for none]", true, true );
6518 }
66 - $count++;
 19+
 20+ public function execute() {
 21+ // Allow for no old prefix
 22+ if( $this->getOption( 'old', 0 ) === '0' ) {
 23+ $old = '';
 24+ } else {
 25+ // Use nice safe, sane, prefixes
 26+ preg_match( '/^[a-zA-Z]+_$/', $this->getOption('old'), $m );
 27+ $old = isset( $m[0] ) ? $m[0] : false;
 28+ }
 29+ // Allow for no new prefix
 30+ if( $this->getOption( 'new', 0 ) === '0' ) {
 31+ $new = '';
 32+ } else {
 33+ // Use nice safe, sane, prefixes
 34+ preg_match( '/^[a-zA-Z]+_$/', $this->getOption('new'), $m );
 35+ $new = isset( $m[0] ) ? $m[0] : false;
 36+ }
 37+
 38+ if( $old === false || $new === false ) {
 39+ $this->error( "Invalid prefix!\n", true );
 40+ }
 41+ if( $old === $new ) {
 42+ $this->( "Same prefix. Nothing to rename!\n", true );
 43+ }
 44+
 45+ $this->output( "Renaming DB prefix for tables of $wgDBname from '$old' to '$new'\n" );
 46+ $count = 0;
 47+
 48+ $dbw = wfGetDB( DB_MASTER );
 49+ $res = $dbw->query( "SHOW TABLES LIKE '".$dbw->escapeLike( $old )."%'" );
 50+ foreach( $res as $row ) {
 51+ // XXX: odd syntax. MySQL outputs an oddly cased "Tables of X"
 52+ // sort of message. Best not to try $row->x stuff...
 53+ $fields = get_object_vars( $row );
 54+ // Silly for loop over one field...
 55+ foreach( $fields as $resName => $table ) {
 56+ // $old should be regexp safe ([a-zA-Z_])
 57+ $newTable = preg_replace( '/^'.$old.'/', $new, $table );
 58+ $this->output( "Renaming table $table to $newTable\n" );
 59+ $dbw->query( "RENAME TABLE $table TO $newTable" );
 60+ }
 61+ $count++;
 62+ }
 63+ $this->output( "Done! [$count tables]\n" );
 64+ }
6765 }
68 -print "Done! [$count tables]\n";
\ No newline at end of file
Index: trunk/phase3/maintenance/createAndPromote.php
@@ -8,61 +8,53 @@
99 * @author Rob Church <robchur@gmail.com>
1010 */
1111
12 -$options = array( 'help', 'bureaucrat' );
13 -require_once( 'commandLine.inc' );
 12+require_once( "Maintenance.php" );
1413
15 -if( isset( $options['help'] ) ) {
16 - showHelp();
17 - exit( 1 );
18 -}
 14+class CreateAndPromote extends Maintenance {
1915
20 -if( count( $args ) < 2 ) {
21 - echo( "Please provide a username and password for the new account.\n" );
22 - die( 1 );
23 -}
 16+ public function __construct() {
 17+ parent::__construct();
 18+ $this->mDescription = "Create a new user account with administrator rights";
 19+ $this->addParam( "bureaucrat", "Grant the account bureaucrat rights" );
 20+ $this->addArgs( array( "username", "password" ) );
 21+ }
2422
25 -$username = $args[0];
26 -$password = $args[1];
 23+ public function execute() {
 24+ $username = $this->getArg(0);
 25+ $password = $this->getArg(1);
 26+
 27+ $this->output( wfWikiID() . ": Creating and promoting User:{$username}..." );
 28+
 29+ $user = User::newFromName( $username );
 30+ if( !is_object( $user ) ) {
 31+ $this->error( "invalid username.\n", true );
 32+ } elseif( 0 != $user->idForName() ) {
 33+ $this->error( "account exists.\n", true );
 34+ }
2735
28 -echo( wfWikiID() . ": Creating and promoting User:{$username}..." );
 36+ # Try to set the password
 37+ try {
 38+ $user->setPassword( $password );
 39+ } catch( PasswordError $pwe ) {
 40+ $this->error( $pwe->getText(), true );
 41+ }
2942
30 -# Validate username and check it doesn't exist
31 -$user = User::newFromName( $username );
32 -if( !is_object( $user ) ) {
33 - echo( "invalid username.\n" );
34 - die( 1 );
35 -} elseif( 0 != $user->idForName() ) {
36 - echo( "account exists.\n" );
37 - die( 1 );
 43+ # Insert the account into the database
 44+ $user->addToDatabase();
 45+ $user->saveSettings();
 46+
 47+ # Promote user
 48+ $user->addGroup( 'sysop' );
 49+ if( $this->hasOption( 'bureaucrat' ) )
 50+ $user->addGroup( 'bureaucrat' );
 51+
 52+ # Increment site_stats.ss_users
 53+ $ssu = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
 54+ $ssu->doUpdate();
 55+
 56+ $this->output( "done.\n" );
 57+ }
3858 }
3959
40 -# Insert the account into the database
41 -$user->addToDatabase();
42 -$user->setPassword( $password );
43 -$user->saveSettings();
44 -
45 -# Promote user
46 -$user->addGroup( 'sysop' );
47 -if( isset( $option['bureaucrat'] ) )
48 - $user->addGroup( 'bureaucrat' );
49 -
50 -# Increment site_stats.ss_users
51 -$ssu = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
52 -$ssu->doUpdate();
53 -
54 -echo( "done.\n" );
55 -
56 -function showHelp() {
57 - echo( <<<EOT
58 -Create a new user account with administrator rights
59 -
60 -USAGE: php createAndPromote.php [--bureaucrat|--help] <username> <password>
61 -
62 - --bureaucrat
63 - Grant the account bureaucrat rights
64 - --help
65 - Show this help information
66 -
67 -EOT
68 - );
69 -}
\ No newline at end of file
 60+$maintClass = "CreateAndPromote";
 61+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/deleteImageMemcached.php
@@ -3,40 +3,36 @@
44 * This script delete image information from memcached.
55 *
66 * Usage example:
7 - * php deleteImageMemcached.php --until "2005-09-05 00:00:00" --sleep 0 --report 10
 7+ * php deleteImageMemcached.php --until "2005-09-05 00:00:00" --sleep 0
88 *
99 * @file
1010 * @ingroup Maintenance
1111 */
1212
13 -$optionsWithArgs = array( 'until', 'sleep', 'report' );
 13+require_once( "Maintenance.php" );
1414
15 -require_once 'commandLine.inc';
16 -
17 -/**
18 - * @ingroup Maintenance
19 - */
20 -class DeleteImageCache {
21 - var $until, $sleep, $report;
22 -
23 - function DeleteImageCache( $until, $sleep, $report ) {
24 - $this->until = $until;
25 - $this->sleep = $sleep;
26 - $this->report = $report;
 15+class DeleteImageCache extends Maintenance {
 16+ public function __construct() {
 17+ parent::__construct();
 18+ $this->mDescription = "Delete image information from memcached";
 19+ $this->addParam( 'sleep', 'How many seconds to sleep between deletions', true, true );
 20+ $this->addParam( 'until', 'Timestamp to delete all entries prior to', true, true );
2721 }
2822
29 - function main() {
 23+ public function execute() {
3024 global $wgMemc;
31 - $fname = 'DeleteImageCache::main';
3225
 26+ $until = preg_replace( "/[^\d]/", '', $this->getOption('until') );
 27+ $sleep = (int)$this->getOption('sleep') * 1000; // milliseconds
 28+
3329 ini_set( 'display_errors', false );
3430
3531 $dbr = wfGetDB( DB_SLAVE );
3632
3733 $res = $dbr->select( 'image',
3834 array( 'img_name' ),
39 - array( "img_timestamp < {$this->until}" ),
40 - $fname
 35+ array( "img_timestamp < {$until}" ),
 36+ __METHOD__
4137 );
4238
4339 $i = 0;
@@ -44,29 +40,22 @@
4541
4642 while ( $row = $dbr->fetchObject( $res ) ) {
4743 if ($i % $this->report == 0)
48 - printf("%s: %13s done (%s)\n", wfWikiID(), "$i/$total", wfPercent( $i / $total * 100 ));
 44+ $this->output( sprintf("%s: %13s done (%s)\n", wfWikiID(), "$i/$total", wfPercent( $i / $total * 100 ) ) );
4945 $md5 = md5( $row->img_name );
5046 $wgMemc->delete( wfMemcKey( 'Image', $md5 ) );
5147
52 - if ($this->sleep != 0)
53 - usleep( $this->sleep );
 48+ if ($sleep != 0)
 49+ usleep( $sleep );
5450
5551 ++$i;
5652 }
5753 }
5854
59 - function getImageCount() {
60 - $fname = 'DeleteImageCache::getImageCount';
61 -
 55+ private function getImageCount() {
6256 $dbr = wfGetDB( DB_SLAVE );
63 - return $dbr->selectField( 'image', 'COUNT(*)', array(), $fname );
 57+ return $dbr->selectField( 'image', 'COUNT(*)', array(), __METHOD__ );
6458 }
6559 }
6660
67 -$until = preg_replace( "/[^\d]/", '', $options['until'] );
68 -$sleep = (int)$options['sleep'] * 1000; // milliseconds
69 -$report = (int)$options['report'];
70 -
71 -$dic = new DeleteImageCache( $until, $sleep, $report );
72 -$dic->main();
73 -
 61+$maintClass = "DeleteImageCache";
 62+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/benchmarkPurge.php
@@ -6,74 +6,87 @@
77 * @ingroup Maintenance
88 */
99
10 -/** */
11 -require_once( "commandLine.inc" );
 10+require_once( "Maintenance.php" );
1211
13 -/**
14 - * Run a bunch of URLs through SquidUpdate::purge()
15 - * to benchmark Squid response times.
16 - * @param $urls array A bunch of URLs to purge
17 - * @param $trials int How many times to run the test?
18 - */
19 -function benchSquid( $urls, $trials = 1 ) {
20 - $start = wfTime();
21 - for( $i = 0; $i < $trials; $i++) {
22 - SquidUpdate::purge( $urls );
 12+class BenchmarkPurge extends Maintenance {
 13+
 14+ public function __construct() {
 15+ parent::__construct();
 16+ $this->addParams( "count", "How many URLs to feed to Squid for purging", false, true );
 17+ $this->mDescription = "Benchmark the Squid purge functions.";
2318 }
24 - $delta = wfTime() - $start;
25 - $pertrial = $delta / $trials;
26 - $pertitle = $pertrial / count( $urls );
27 - return sprintf( "%4d titles in %6.2fms (%6.2fms each)",
28 - count( $urls ), $pertrial * 1000.0, $pertitle * 1000.0 );
29 -}
30 -
31 -/**
32 - * Get an array of randomUrl()'s.
33 - * @param $length int How many urls to add to the array
34 - */
35 -function randomUrlList( $length ) {
36 - $list = array();
37 - for( $i = 0; $i < $length; $i++ ) {
38 - $list[] = randomUrl();
 19+
 20+ public function execute() {
 21+ global $wgUseSquid;
 22+ if( !$wgUseSquid ) {
 23+ $this->error( "Squid purge benchmark doesn't do much without squid support on.\n". true );
 24+ } else {
 25+ $this->output( "There are " . count( $wgSquidServers ) . " defined squid servers:\n" );
 26+ if( $this->hasOption( 'count' ) ) {
 27+ $lengths = array( intval( $this->getOption('count') ) );
 28+ } else {
 29+ $lengths = array( 1, 10, 100 );
 30+ }
 31+ foreach( $lengths as $length ) {
 32+ $urls = $this->randomUrlList( $length );
 33+ $trial = $this->benchSquid( $urls );
 34+ $this->output( $trial . "\n" );
 35+ }
 36+ }
3937 }
40 - return $list;
41 -}
42 -
43 -/**
44 - * Return a random URL of the wiki. Not necessarily an actual title in the
45 - * database, but at least a URL that looks like one.
46 - */
47 -function randomUrl() {
48 - global $wgServer, $wgArticlePath;
49 - return $wgServer . str_replace( '$1', randomTitle(), $wgArticlePath );
50 -}
51 -
52 -/**
53 - * Create a random title string (not necessarily a Title object).
54 - * For use with randomUrl().
55 - */
56 -function randomTitle() {
57 - $str = '';
58 - $length = mt_rand( 1, 20 );
59 - for( $i = 0; $i < $length; $i++ ) {
60 - $str .= chr( mt_rand( ord('a'), ord('z') ) );
 38+
 39+ /**
 40+ * Run a bunch of URLs through SquidUpdate::purge()
 41+ * to benchmark Squid response times.
 42+ * @param $urls array A bunch of URLs to purge
 43+ * @param $trials int How many times to run the test?
 44+ */
 45+ private function benchSquid( $urls, $trials = 1 ) {
 46+ $start = wfTime();
 47+ for( $i = 0; $i < $trials; $i++) {
 48+ SquidUpdate::purge( $urls );
 49+ }
 50+ $delta = wfTime() - $start;
 51+ $pertrial = $delta / $trials;
 52+ $pertitle = $pertrial / count( $urls );
 53+ return sprintf( "%4d titles in %6.2fms (%6.2fms each)",
 54+ count( $urls ), $pertrial * 1000.0, $pertitle * 1000.0 );
6155 }
62 - return ucfirst( $str );
63 -}
64 -
65 -if( !$wgUseSquid ) {
66 - wfDie( "Squid purge benchmark doesn't do much without squid support on.\n" );
67 -} else {
68 - printf( "There are %d defined squid servers:\n", count( $wgSquidServers ) );
69 - #echo implode( "\n", $wgSquidServers ) . "\n";
70 - if( isset( $options['count'] ) ) {
71 - $lengths = array( intval( $options['count'] ) );
72 - } else {
73 - $lengths = array( 1, 10, 100 );
 56+
 57+ /**
 58+ * Get an array of randomUrl()'s.
 59+ * @param $length int How many urls to add to the array
 60+ */
 61+ private function randomUrlList( $length ) {
 62+ $list = array();
 63+ for( $i = 0; $i < $length; $i++ ) {
 64+ $list[] = $this->randomUrl();
 65+ }
 66+ return $list;
7467 }
75 - foreach( $lengths as $length ) {
76 - $urls = randomUrlList( $length );
77 - $trial = benchSquid( $urls );
78 - print "$trial\n";
 68+
 69+ /**
 70+ * Return a random URL of the wiki. Not necessarily an actual title in the
 71+ * database, but at least a URL that looks like one.
 72+ */
 73+ private function randomUrl() {
 74+ global $wgServer, $wgArticlePath;
 75+ return $wgServer . str_replace( '$1', $this->randomTitle(), $wgArticlePath );
7976 }
 77+
 78+ /**
 79+ * Create a random title string (not necessarily a Title object).
 80+ * For use with randomUrl().
 81+ */
 82+ private function randomTitle() {
 83+ $str = '';
 84+ $length = mt_rand( 1, 20 );
 85+ for( $i = 0; $i < $length; $i++ ) {
 86+ $str .= chr( mt_rand( ord('a'), ord('z') ) );
 87+ }
 88+ return ucfirst( $str );
 89+ }
8090 }
 91+
 92+$maintClass = "BenchmarkPurge";
 93+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/checkAutoLoader.php
@@ -1,29 +1,40 @@
22 <?php
3 -if ( php_sapi_name() != 'cli' ) exit;
 3+/**
 4+ * Check the autoloader
 5+ */
46
5 -$IP = dirname(__FILE__) .'/..';
6 -require( "$IP/includes/AutoLoader.php" );
7 -$files = array_unique( $wgAutoloadLocalClasses );
 7+require_once( "Maintenance.php" );
88
9 -foreach ( $files as $file ) {
10 - if( function_exists( 'parsekit_compile_file' ) ){
11 - $parseInfo = parsekit_compile_file( "$IP/$file" );
12 - $classes = array_keys( $parseInfo['class_table'] );
13 - } else {
14 - $contents = file_get_contents( "$IP/$file" );
15 - $m = array();
16 - preg_match_all( '/\n\s*class\s+([a-zA-Z0-9_]+)/', $contents, $m, PREG_PATTERN_ORDER );
17 - $classes = $m[1];
 9+class CheckAutoLoader extends Maintenance {
 10+ public function __construct() {
 11+ parent::__construct();
 12+ $this->mDescription = "AutoLoader sanity checks";
1813 }
19 - foreach ( $classes as $class ) {
20 - if ( !isset( $wgAutoloadLocalClasses[$class] ) ) {
21 - //printf( "%-50s Unlisted, in %s\n", $class, $file );
22 - echo " '$class' => '$file',\n";
23 - } elseif ( $wgAutoloadLocalClasses[$class] !== $file ) {
24 - echo "$class: Wrong file: found in $file, listed in " . $wgAutoloadLocalClasses[$class] . "\n";
 14+ public function execute() {
 15+ global $wgAutoloadLocalClasses, $IP;
 16+ $files = array_unique( $wgAutoloadLocalClasses );
 17+
 18+ foreach( $files as $file ) {
 19+ if( function_exists( 'parsekit_compile_file' ) ){
 20+ $parseInfo = parsekit_compile_file( "$IP/$file" );
 21+ $classes = array_keys( $parseInfo['class_table'] );
 22+ } else {
 23+ $contents = file_get_contents( "$IP/$file" );
 24+ $m = array();
 25+ preg_match_all( '/\n\s*class\s+([a-zA-Z0-9_]+)/', $contents, $m, PREG_PATTERN_ORDER );
 26+ $classes = $m[1];
 27+ }
 28+ foreach ( $classes as $class ) {
 29+ if ( !isset( $wgAutoloadLocalClasses[$class] ) ) {
 30+ //printf( "%-50s Unlisted, in %s\n", $class, $file );
 31+ $this->output( "\t'$class' => '$file',\n" );
 32+ } elseif ( $wgAutoloadLocalClasses[$class] !== $file ) {
 33+ $this->output( "$class: Wrong file: found in $file, listed in " . $wgAutoloadLocalClasses[$class] . "\n" );
 34+ }
 35+ }
2536 }
2637 }
27 -
2838 }
2939
30 -
 40+$maintClass = "CheckAutoLoader";
 41+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/stats.php
@@ -1,58 +1,73 @@
22 <?php
33 /**
44 * Show statistics from memcached
5 - *
6 - * @file
75 * @ingroup Maintenance
86 */
97
10 -require_once('commandLine.inc');
 8+require_once( "Maintenance.php" );
119
12 -if( get_class( $wgMemc ) == 'FakeMemCachedClient' ) {
13 - die("You are running FakeMemCachedClient, I can not provide any statistics.\n");
 10+class MemcachedStats extends Maintenance {
 11+
 12+ public function __construct() {
 13+ $this->mDescription = "Show statistics from memcached";
 14+ }
 15+
 16+ public function execute() {
 17+ global $wgMemc;
 18+
 19+ // Can't do stats if
 20+ if( get_class( $wgMemc ) == 'FakeMemCachedClient' ) {
 21+ $this->error( "You are running FakeMemCachedClient, I can not provide any statistics.\n", true );
 22+ }
 23+ $session = intval($wgMemc->get(wfMemcKey('stats','request_with_session')));
 24+ $noSession = intval($wgMemc->get(wfMemcKey('stats','request_without_session')));
 25+ $total = $session + $noSession;
 26+ if ( $total == 0 ) {
 27+ $this->error( "You either have no stats or memcached isn't running. Aborting.\n", true );
 28+ }
 29+ $this->output( "Requests\n" );
 30+ $this->output( sprintf( "with session: %-10d %6.2f%%\n", $session, $session/$total*100 );
 31+ $this->output( sprintf( "without session: %-10d %6.2f%%\n", $noSession, $noSession/$total*100 );
 32+ $this->output( sprintf( "total: %-10d %6.2f%%\n", $total, 100 );
 33+
 34+
 35+ $this->output( "\nParser cache\n" );
 36+ $hits = intval($wgMemc->get(wfMemcKey('stats','pcache_hit')));
 37+ $invalid = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_invalid')));
 38+ $expired = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_expired')));
 39+ $absent = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_absent')));
 40+ $stub = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_stub')));
 41+ $total = $hits + $invalid + $expired + $absent + $stub;
 42+ $this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits/$total*100 ) );
 43+ $this->output( sprintf( "invalid: %-10d %6.2f%%\n", $invalid, $invalid/$total*100 ) );
 44+ $this->output( sprintf( "expired: %-10d %6.2f%%\n", $expired, $expired/$total*100 ) );
 45+ $this->output( sprintf( "absent: %-10d %6.2f%%\n", $absent, $absent/$total*100 ) );
 46+ $this->output( sprintf( "stub threshold: %-10d %6.2f%%\n", $stub, $stub/$total*100 ) );
 47+ $this->output( sprintf( "total: %-10d %6.2f%%\n", $total, 100 ) );
 48+
 49+ $hits = intval($wgMemc->get(wfMemcKey('stats','image_cache_hit')));
 50+ $misses = intval($wgMemc->get(wfMemcKey('stats','image_cache_miss')));
 51+ $updates = intval($wgMemc->get(wfMemcKey('stats','image_cache_update')));
 52+ $total = $hits + $misses;
 53+ $this->output("\nImage cache\n");
 54+ $this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits/$total*100 ) );
 55+ $this->output( sprintf( "misses: %-10d %6.2f%%\n", $misses, $misses/$total*100 ) );
 56+ $this->output( sprintf( "updates: %-10d\n", $updates ) );
 57+
 58+ $hits = intval($wgMemc->get(wfMemcKey('stats','diff_cache_hit')));
 59+ $misses = intval($wgMemc->get(wfMemcKey('stats','diff_cache_miss')));
 60+ $uncacheable = intval($wgMemc->get(wfMemcKey('stats','diff_uncacheable')));
 61+ $total = $hits + $misses + $uncacheable;
 62+ $this->output("\nDiff cache\n");
 63+ $this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits/$total*100 );
 64+ $this->output( sprintf( "misses: %-10d %6.2f%%\n", $misses, $misses/$total*100 );
 65+ $this->output( sprintf( "uncacheable: %-10d %6.2f%%\n", $uncacheable, $uncacheable/$total*100 );
 66+ }
1467 }
15 -$session = intval($wgMemc->get(wfMemcKey('stats','request_with_session')));
16 -$noSession = intval($wgMemc->get(wfMemcKey('stats','request_without_session')));
17 -$total = $session + $noSession;
18 -if ( $total == 0 ) {
19 - die("You either have no stats or memcached isn't running. Aborting.\n");
20 -}
21 -print "Requests\n";
22 -printf( "with session: %-10d %6.2f%%\n", $session, $session/$total*100 );
23 -printf( "without session: %-10d %6.2f%%\n", $noSession, $noSession/$total*100 );
24 -printf( "total: %-10d %6.2f%%\n", $total, 100 );
2568
 69+$maintClass = "MemcachedStats";
 70+require_once( DO_MAINTENANCE );
2671
27 -print "\nParser cache\n";
28 -$hits = intval($wgMemc->get(wfMemcKey('stats','pcache_hit')));
29 -$invalid = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_invalid')));
30 -$expired = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_expired')));
31 -$absent = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_absent')));
32 -$stub = intval($wgMemc->get(wfMemcKey('stats','pcache_miss_stub')));
33 -$total = $hits + $invalid + $expired + $absent + $stub;
34 -printf( "hits: %-10d %6.2f%%\n", $hits, $hits/$total*100 );
35 -printf( "invalid: %-10d %6.2f%%\n", $invalid, $invalid/$total*100 );
36 -printf( "expired: %-10d %6.2f%%\n", $expired, $expired/$total*100 );
37 -printf( "absent: %-10d %6.2f%%\n", $absent, $absent/$total*100 );
38 -printf( "stub threshold: %-10d %6.2f%%\n", $stub, $stub/$total*100 );
39 -printf( "total: %-10d %6.2f%%\n", $total, 100 );
4072
41 -$hits = intval($wgMemc->get(wfMemcKey('stats','image_cache_hit')));
42 -$misses = intval($wgMemc->get(wfMemcKey('stats','image_cache_miss')));
43 -$updates = intval($wgMemc->get(wfMemcKey('stats','image_cache_update')));
44 -$total = $hits + $misses;
45 -print("\nImage cache\n");
46 -printf( "hits: %-10d %6.2f%%\n", $hits, $hits/$total*100 );
47 -printf( "misses: %-10d %6.2f%%\n", $misses, $misses/$total*100 );
48 -printf( "updates: %-10d\n", $updates );
4973
50 -$hits = intval($wgMemc->get(wfMemcKey('stats','diff_cache_hit')));
51 -$misses = intval($wgMemc->get(wfMemcKey('stats','diff_cache_miss')));
52 -$uncacheable = intval($wgMemc->get(wfMemcKey('stats','diff_uncacheable')));
53 -$total = $hits + $misses + $uncacheable;
54 -print("\nDiff cache\n");
55 -printf( "hits: %-10d %6.2f%%\n", $hits, $hits/$total*100 );
56 -printf( "misses: %-10d %6.2f%%\n", $misses, $misses/$total*100 );
57 -printf( "uncacheable: %-10d %6.2f%%\n", $uncacheable, $uncacheable/$total*100 );
5874
59 -
Index: trunk/phase3/maintenance/checkUsernames.php
@@ -7,36 +7,33 @@
88 * @ingroup Maintenance
99 */
1010
11 -error_reporting(E_ALL ^ E_NOTICE);
12 -require_once 'commandLine.inc';
1311
14 -class checkUsernames {
15 - var $stderr, $log;
 12+require_once( "Maintenance.php" );
1613
17 - function checkUsernames() {
18 - $this->stderr = fopen( 'php://stderr', 'wt' );
 14+class CheckUsernames extends Maintenance {
 15+
 16+ public function __construct() {
 17+ parent::__construct();
 18+ $this->mDescription = "Verify that database usernames are actually valid";
1919 }
20 - function main() {
21 - $fname = 'checkUsernames::main';
2220
 21+ function execute() {
2322 $dbr = wfGetDB( DB_SLAVE );
2423
2524 $res = $dbr->select( 'user',
2625 array( 'user_id', 'user_name' ),
2726 null,
28 - $fname
 27+ __METHOD__
2928 );
3029
3130 while ( $row = $dbr->fetchObject( $res ) ) {
3231 if ( ! User::isValidUserName( $row->user_name ) ) {
33 - $out = sprintf( "%s: %6d: '%s'\n", wfWikiID(), $row->user_id, $row->user_name );
34 - fwrite( $this->stderr, $out );
 32+ $this->error( sprintf( "%s: %6d: '%s'\n", wfWikiID(), $row->user_id, $row->user_name ) );
3533 wfDebugLog( 'checkUsernames', $out );
3634 }
3735 }
3836 }
3937 }
4038
41 -$cun = new checkUsernames();
42 -$cun->main();
43 -
 39+$maintClass = "CheckUsernames";
 40+require_once( "doMaintenance.php" );
Index: trunk/phase3/maintenance/deleteRevision.php
@@ -6,42 +6,52 @@
77 * @ingroup Maintenance
88 */
99
10 -require_once( 'commandLine.inc' );
 10+require_once( "Maintenance.php" );
1111
12 -$dbw = wfGetDB( DB_MASTER );
 12+class DeleteRevision extends Maintenance {
 13+
 14+ public function __construct() {
 15+ parent::__construct();
 16+ $this->mDescription = "Delete one or more revisions by moving them to the archive table";
 17+ }
 18+
 19+ public function execute() {
 20+ if( count( $this->mArgs ) == 0 ) {
 21+ $this->error( "No revisions specified", true );
 22+ }
1323
14 -if ( count( $args ) == 0 ) {
15 - echo "Usage: php deleteRevision.php <revid> [<revid> ...]\n";
16 - exit(1);
17 -}
18 -
19 -echo "Deleting revision(s) " . implode( ',', $args ) . " from ".wfWikiID()."...\n";
20 -
21 -$affected = 0;
22 -foreach ( $args as $revID ) {
23 - $dbw->insertSelect( 'archive', array( 'page', 'revision' ),
24 - array(
25 - 'ar_namespace' => 'page_namespace',
26 - 'ar_title' => 'page_title',
27 - 'ar_comment' => 'rev_comment',
28 - 'ar_user' => 'rev_user',
29 - 'ar_user_text' => 'rev_user_text',
30 - 'ar_timestamp' => 'rev_timestamp',
31 - 'ar_minor_edit' => 'rev_minor_edit',
32 - 'ar_rev_id' => 'rev_id',
33 - 'ar_text_id' => 'rev_text_id',
34 - ), array(
35 - 'rev_id' => $revID,
36 - 'page_id = rev_page'
37 - ), $fname
38 - );
39 - if ( !$dbw->affectedRows() ) {
40 - echo "Revision $revID not found\n";
41 - } else {
42 - $affected += $dbw->affectedRows();
43 - $dbw->delete( 'revision', array( 'rev_id' => $revID ) );
 24+ $this->output( "Deleting revision(s) " . implode( ',', $this->mArgs ) .
 25+ " from " . wfWikiID() . "...\n" );
 26+ $dbw = wfGetDB( DB_MASTER );
 27+
 28+ $affected = 0;
 29+ foreach ( $this->mArgs as $revID ) {
 30+ $dbw->insertSelect( 'archive', array( 'page', 'revision' ),
 31+ array(
 32+ 'ar_namespace' => 'page_namespace',
 33+ 'ar_title' => 'page_title',
 34+ 'ar_comment' => 'rev_comment',
 35+ 'ar_user' => 'rev_user',
 36+ 'ar_user_text' => 'rev_user_text',
 37+ 'ar_timestamp' => 'rev_timestamp',
 38+ 'ar_minor_edit' => 'rev_minor_edit',
 39+ 'ar_rev_id' => 'rev_id',
 40+ 'ar_text_id' => 'rev_text_id',
 41+ ), array(
 42+ 'rev_id' => $revID,
 43+ 'page_id = rev_page'
 44+ ), __METHOD__
 45+ );
 46+ if ( !$dbw->affectedRows() ) {
 47+ $this->output( "Revision $revID not found\n" );
 48+ } else {
 49+ $affected += $dbw->affectedRows();
 50+ $dbw->delete( 'revision', array( 'rev_id' => $revID ) );
 51+ }
 52+ }
 53+ $this->output( "Deleted $affected revisions\n" );
4454 }
4555 }
4656
47 -print "Deleted $affected revisions\n";
48 -
 57+$maintClass = "DeleteRevision";
 58+require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/initStats.php
@@ -9,23 +9,22 @@
1010 * @author Rob Church <robchur@gmail.com>
1111 * @licence GNU General Public Licence 2.0 or later
1212 */
13 -
14 -$options = array( 'help', 'update', 'noviews' );
15 -require_once( 'commandLine.inc' );
16 -echo( "Refresh Site Statistics\n\n" );
1713
18 -if( isset( $options['help'] ) ) {
19 - showHelp();
20 - exit(1);
21 -}
 14+require_once( "Maintenance.php" );
2215
23 -require "$IP/maintenance/initStats.inc";
24 -wfInitStats( $options );
 16+class InitStats extends Maintenance {
 17+ public function __construct() {
 18+ parent::__construct();
 19+ $this->mDescription = "Re-initialise the site statistics tables";
 20+ $this->addParam( 'update', 'Update the existing statistics (preserves the ss_total_views field)' );
 21+ $this->addParam( 'noviews', "Don't update the page view counter" );
 22+ }
2523
26 -function showHelp() {
27 - echo( "Re-initialise the site statistics tables.\n\n" );
28 - echo( "Usage: php initStats.php [--update|--noviews]\n\n" );
29 - echo( " --update : Update the existing statistics (preserves the ss_total_views field)\n" );
30 - echo( "--noviews : Don't update the page view counter\n\n" );
 24+ public function execute() {
 25+ $this->output( "Refresh Site Statistics\n\n" );
 26+ SiteStats::init( $this->hasOption('update'), $this->hasOption('noviews') );
 27+ }
3128 }
3229
 30+$maintClass = "InitStats";
 31+require_once( DO_MAINTENANCE );
Index: trunk/phase3/UPGRADE
@@ -42,8 +42,7 @@
4343 repository, via a checkout or export operation.
4444
4545 Replace the existing MediaWiki files with the new. You should preserve the
46 -LocalSettings.php file, AdminSettings.php file (if present), and the
47 -"extensions" and "images" directories.
 46+LocalSettings.php file and the "extensions" and "images" directories.
4847
4948 Depending upon your configuration, you may also need to preserve additional
5049 directories, including a custom upload directory ($wgUploadDirectory),
@@ -51,8 +50,8 @@
5251
5352 === Perform the database upgrade ===
5453
55 -You will need an AdminSettings.php file set up in the correct format; see
56 -AdminSettings.sample in the wiki root for more information and examples.
 54+You will need to have $wgDBadminuser and $wgDBadminpass set in your
 55+LocalSettings.php, see there for more info.
5756
5857 From the command line, browse to the "maintenance" directory and run the
5958 update.php script to check and update the schema. This will insert missing
@@ -172,10 +171,10 @@
173172 === Web installer ===
174173
175174 You can use the web-based installer wizard if you first remove the
176 -LocalSettings.php (and AdminSettings.php, if any) files; be sure to
177 -give the installer the same information as you did on the original
178 -install (language/encoding, database name, password, etc). This will
179 -also generate a fresh LocalSettings.php, which you may need to customize.
 175+LocalSettings.php file; be sure to give the installer the same
 176+information as you did on the original install (language/encoding,
 177+database name, password, etc). This will also generate a fresh
 178+LocalSettings.php, which you may need to customize.
180179
181180 You may change some settings during the install, but be very careful!
182181 Changing the encoding in particular will generally leave you with a
@@ -185,8 +184,8 @@
186185
187186 Additionally, as of 1.4.0 you can run an in-place upgrade script from
188187 the command line, keeping your existing LocalSettings.php. This requires
189 -that you create an AdminSettings.php giving an appropriate database user
190 -and password with privileges to modify the database structure.
 188+that you set $wgDBadminuser and $wgDBadminpassword with an appropriate
 189+database user and password with privileges to modify the database structure.
191190
192191 Once the new files are in place, go into the maintenance subdirectory and
193192 run the script:
Index: trunk/phase3/config/index.php
@@ -615,6 +615,7 @@
616616 $conf->RootUser = importPost( "RootUser", "root" );
617617 $conf->RootPW = importPost( "RootPW", "" );
618618 $useRoot = importCheck( 'useroot', false );
 619+ $conf->populateadmin = importCheck( 'populateadmin', false );
619620 $conf->LanguageCode = importPost( "LanguageCode", "en" );
620621 ## MySQL specific:
621622 $conf->DBprefix = importPost( "DBprefix" );
@@ -1527,6 +1528,8 @@
15281529 <label class="column">Superuser account:</label>
15291530 <input type="checkbox" name="useroot" id="useroot" <?php if( $useRoot ) { ?>checked="checked" <?php } ?> />
15301531 &nbsp;<label for="useroot">Use superuser account</label>
 1532+ <input type="checkbox" name="populateadmin" id="populateadmin" <?php if( $conf->populateadmin ) { ?>checked="checked" <?php } ?> />
 1533+ &nbsp;<label for="populateadmin">Set as admin user for maintenance</label>
15311534 </div>
15321535 <div class="config-input"><?php aField( $conf, "RootUser", "Superuser name:", "text" ); ?></div>
15331536 <div class="config-input"><?php aField( $conf, "RootPW", "Superuser password:", "password" ); ?></div>
@@ -1792,6 +1795,11 @@
17931796 # Needs literal string interpolation for the current style path
17941797 $slconf['RightsIcon'] = $conf->RightsIcon;
17951798 }
 1799+
 1800+ if( $conf->populateadmin ) {
 1801+ $slconf['DBadminuser'] = $conf->RootUser;
 1802+ $slconf['DBadminpassword'] = $conf->RootPW;
 1803+ }
17961804
17971805 if( $conf->DBtype == 'mysql' ) {
17981806 $dbsettings =
@@ -1899,6 +1907,10 @@
19001908
19011909 {$dbsettings}
19021910
 1911+## Database admin settings, used for maintenance scripts
 1912+\$wgDBadminuser = \"{$slconf['DBadminuser']}\";
 1913+\$wgDBadminpassword = \"{$slconf['DBadminpassword']}\";
 1914+
19031915 ## Shared memory settings
19041916 \$wgMainCacheType = $cacheType;
19051917 \$wgMemCachedServers = $mcservers;
Property changes on: trunk/phase3
___________________________________________________________________
Modified: svn:mergeinfo
19061918 Merged /trunk/phase3:r52328-52330
19071919 Merged /branches/maintenance-work:r51675-52335

Follow-up revisions

RevisionCommit summaryAuthorDate
r52340Revert r52336 "Merge maintenance-work branch:"...brion02:49, 24 June 2009
r52494(bug 19157) createAndPromote error on bad password...demon16:53, 27 June 2009
r53664* (bug 14201) Set $wgDBadminuser/$wgDBadminpassword during setup...demon00:31, 23 July 2009
r54225Merge maintenance-work branch (now with less errors!):...demon19:35, 2 August 2009
r55612Partial revert of r53664 (bug 14201 Set $wgDBadminuser/$wgDBadminpassword dur...demon18:56, 26 August 2009

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r51676Basic implementation of abstract Maintenance class + docs. TODO: Handle confi...demon02:41, 10 June 2009

Comments

#Comment by Brion VIBBER (talk | contribs)   02:47, 24 June 2009

Seems to break a bunch of stuff; even eval.php throws visible errors. Reverting...

#Comment by Brion VIBBER (talk | contribs)   02:49, 24 June 2009

Reverted in r52340

Status & tagging log