r52302 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r52301‎ | r52302 | r52303 >
Date:03:42, 23 June 2009
Author:demon
Status:deferred
Tags:
Comment:
* Port checkBadRedirects, fetchText, getLagTimes, mctest, populateParentId, rebuildFileCache, removeUnusedAccounts
* Use accessor in createAndPromote
Modified paths:
  • /branches/maintenance-work/maintenance/checkBadRedirects.php (modified) (history)
  • /branches/maintenance-work/maintenance/createAndPromote.php (modified) (history)
  • /branches/maintenance-work/maintenance/fetchText.php (modified) (history)
  • /branches/maintenance-work/maintenance/getLagTimes.php (modified) (history)
  • /branches/maintenance-work/maintenance/mctest.php (modified) (history)
  • /branches/maintenance-work/maintenance/populateParentId.inc (deleted) (history)
  • /branches/maintenance-work/maintenance/populateParentId.php (modified) (history)
  • /branches/maintenance-work/maintenance/rebuildFileCache.php (modified) (history)
  • /branches/maintenance-work/maintenance/removeUnusedAccounts.inc (deleted) (history)
  • /branches/maintenance-work/maintenance/removeUnusedAccounts.php (modified) (history)

Diff [purge]

Index: branches/maintenance-work/maintenance/removeUnusedAccounts.inc
@@ -1,46 +0,0 @@
2 -<?php
3 -
4 -/**
5 - * Support functions for the removeUnusedAccounts maintenance script
6 - *
7 - * @file
8 - * @ingroup Maintenance
9 - * @author Rob Church <robchur@gmail.com>
10 - */
11 -
12 -/**
13 - * Could the specified user account be deemed inactive?
14 - * (No edits, no deleted edits, no log entries, no current/old uploads)
15 - *
16 - * @param $id User's ID
17 - * @param $master Perform checking on the master
18 - * @return bool
19 - */
20 -function isInactiveAccount( $id, $master = false ) {
21 - $dbo = wfGetDB( $master ? DB_MASTER : DB_SLAVE );
22 - $fname = 'isInactiveAccount';
23 - $checks = array( 'revision' => 'rev', 'archive' => 'ar', 'logging' => 'log',
24 - 'image' => 'img', 'oldimage' => 'oi' );
25 - $count = 0;
26 -
27 - $dbo->immediateBegin();
28 - foreach( $checks as $table => $fprefix ) {
29 - $conds = array( $fprefix . '_user' => $id );
30 - $count += (int)$dbo->selectField( $table, 'COUNT(*)', $conds, $fname );
31 - }
32 - $dbo->immediateCommit();
33 -
34 - return $count == 0;
35 -}
36 -
37 -/**
38 - * Show help for the maintenance script
39 - */
40 -function showHelp() {
41 - echo( "Delete unused user accounts from the database.\n\n" );
42 - echo( "USAGE: php removeUnusedAccounts.php [--delete]\n\n" );
43 - echo( " --delete : Delete accounts which are discovered to be inactive\n" );
44 - echo( " --ignore-touched=x : Ignore accounts touched within the lasts x days\n" );
45 - echo( " --ignore-groups=x,y : Ignore accounts within these groups\n" );
46 - echo( "\n" );
47 -}
Index: branches/maintenance-work/maintenance/populateParentId.inc
@@ -1,83 +0,0 @@
2 -<?php
3 -
4 -define( 'BATCH_SIZE', 200 );
5 -
6 -function populate_rev_parent_id( $db ) {
7 - wfOut( "Populating rev_parent_id column\n" );
8 - $start = $db->selectField( 'revision', 'MIN(rev_id)', false, __FUNCTION__ );
9 - $end = $db->selectField( 'revision', 'MAX(rev_id)', false, __FUNCTION__ );
10 - if( is_null( $start ) || is_null( $end ) ){
11 - wfOut( "...revision table seems to be empty.\n" );
12 - $db->insert( 'updatelog',
13 - array( 'ul_key' => 'populate rev_parent_id' ),
14 - __FUNCTION__,
15 - 'IGNORE' );
16 - return;
17 - }
18 - # Do remaining chunk
19 - $end += BATCH_SIZE - 1;
20 - $blockStart = intval( $start );
21 - $blockEnd = intval( $start ) + BATCH_SIZE - 1;
22 - $count = 0;
23 - $changed = 0;
24 - while( $blockEnd <= $end ) {
25 - wfOut( "...doing rev_id from $blockStart to $blockEnd\n" );
26 - $cond = "rev_id BETWEEN $blockStart AND $blockEnd";
27 - $res = $db->select( 'revision',
28 - array('rev_id','rev_page','rev_timestamp','rev_parent_id'),
29 - $cond, __FUNCTION__ );
30 - # Go through and update rev_parent_id from these rows.
31 - # Assume that the previous revision of the title was
32 - # the original previous revision of the title when the
33 - # edit was made...
34 - foreach( $res as $row ) {
35 - # First, check rows with the same timestamp other than this one
36 - # with a smaller rev ID. The highest ID "wins". This avoids loops
37 - # as timestamp can only decrease and never loops with IDs (from parent to parent)
38 - $previousID = $db->selectField( 'revision', 'rev_id',
39 - array( 'rev_page' => $row->rev_page, 'rev_timestamp' => $row->rev_timestamp,
40 - "rev_id < " . intval( $row->rev_id ) ),
41 - __FUNCTION__,
42 - array( 'ORDER BY' => 'rev_id DESC' ) );
43 - # If there are none, check the the highest ID with a lower timestamp
44 - if( !$previousID ) {
45 - # Get the highest older timestamp
46 - $lastTimestamp = $db->selectField( 'revision', 'rev_timestamp',
47 - array( 'rev_page' => $row->rev_page, "rev_timestamp < " . $db->addQuotes( $row->rev_timestamp ) ),
48 - __FUNCTION__,
49 - array( 'ORDER BY' => 'rev_timestamp DESC' ) );
50 - # If there is one, let the highest rev ID win
51 - if( $lastTimestamp ) {
52 - $previousID = $db->selectField( 'revision', 'rev_id',
53 - array( 'rev_page' => $row->rev_page, 'rev_timestamp' => $lastTimestamp ),
54 - __FUNCTION__,
55 - array( 'ORDER BY' => 'rev_id DESC' ) );
56 - }
57 - }
58 - $previousID = intval($previousID);
59 - if( $previousID != $row->rev_parent_id )
60 - $changed++;
61 - # Update the row...
62 - $db->update( 'revision',
63 - array( 'rev_parent_id' => $previousID ),
64 - array( 'rev_id' => $row->rev_id ),
65 - __FUNCTION__ );
66 - $count++;
67 - }
68 - $blockStart += BATCH_SIZE - 1;
69 - $blockEnd += BATCH_SIZE - 1;
70 - wfWaitForSlaves( 5 );
71 - }
72 - $logged = $db->insert( 'updatelog',
73 - array( 'ul_key' => 'populate rev_parent_id' ),
74 - __FUNCTION__,
75 - 'IGNORE' );
76 - if( $logged ) {
77 - wfOut( "rev_parent_id population complete ... {$count} rows [{$changed} changed]\n" );
78 - return true;
79 - } else {
80 - wfOut( "Could not insert rev_parent_id population row.\n" );
81 - return false;
82 - }
83 -}
84 -
Index: branches/maintenance-work/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: branches/maintenance-work/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: branches/maintenance-work/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: branches/maintenance-work/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: branches/maintenance-work/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: branches/maintenance-work/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: branches/maintenance-work/maintenance/createAndPromote.php
@@ -20,11 +20,8 @@
2121 }
2222
2323 public function execute() {
24 - if( count( $this->mArgs ) < 2 ) {
25 - $this->error( "Please provide a username and password for the new account.\n", true );
26 - }
27 - $username = $this->mArgs[0];
28 - $password = $this->mArgs[1];
 24+ $username = $this->getArg(0);
 25+ $password = $this->getArg(1);
2926
3027 $this->output( wfWikiID() . ": Creating and promoting User:{$username}..." );
3128
Index: branches/maintenance-work/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 );

Status & tagging log