r52340 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r52339‎ | r52340 | r52341 >
Date:02:49, 24 June 2009
Author:brion
Status:ok
Tags:
Comment:
Revert r52336 "Merge maintenance-work branch:"
Seems to have broken a bunch of stuff. Don't commit giant non-critical changes that break Setup.php and all maint scripts. Thanks!
Modified paths:
  • /trunk/phase3 (modified) (history)
  • /trunk/phase3/AdminSettings.sample (added) (history)
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/UPGRADE (modified) (history)
  • /trunk/phase3/config/index.php (modified) (history)
  • /trunk/phase3/docs/maintenance.txt (deleted) (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 (deleted) (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 (deleted) (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/maintenance/doMaintenance.php
@@ -1,60 +0,0 @@
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
Index: trunk/phase3/maintenance/Maintenance.php
@@ -1,639 +0,0 @@
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 -
Index: trunk/phase3/maintenance/stats.php
@@ -1,73 +1,58 @@
22 <?php
33 /**
44 * Show statistics from memcached
 5+ *
 6+ * @file
57 * @ingroup Maintenance
68 */
79
8 -require_once( "Maintenance.php" );
 10+require_once('commandLine.inc');
911
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 - }
 12+if( get_class( $wgMemc ) == 'FakeMemCachedClient' ) {
 13+ die("You are running FakeMemCachedClient, I can not provide any statistics.\n");
6714 }
 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 );
6825
69 -$maintClass = "MemcachedStats";
70 -require_once( DO_MAINTENANCE );
7126
 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 );
7240
 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 );
7349
 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 );
7458
 59+
Index: trunk/phase3/maintenance/deleteBatch.php
@@ -1,4 +1,5 @@
22 <?php
 3+
34 /**
45 * Deletes a batch of pages
56 * Usage: php deleteBatch.php [-u <user>] [-r <reason>] [-i <interval>] [listfile]
@@ -12,87 +13,86 @@
1314 * @file
1415 * @ingroup Maintenance
1516 */
16 -
17 -require_once( "Maintenance.php" );
1817
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' ) );
 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;
2861 }
29 -
30 - public function execute() {
31 - global $wgUser;
 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+ }
3271
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 - }
4672
47 - # Setup
48 - if( !$file ) {
49 - $this->error( "Unable to read file, exiting\n", true );
 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... ";
5080 }
51 - $wgUser = User::newFromName( $user );
52 - $dbw = wfGetDB( DB_MASTER );
 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+ }
5391
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 -}
 92+ if ( $interval ) {
 93+ sleep( $interval );
9594 }
 95+ wfWaitForSlaves( 5 );
9696 }
9797
98 -$maintClass = "DeleteBatch";
99 -require_once( DO_MAINTENANCE );
 98+
 99+
Index: trunk/phase3/maintenance/waitForSlave.php
@@ -5,16 +5,11 @@
66 * @ingroup Maintenance
77 */
88
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 - }
 9+require_once( "commandLine.inc" );
 10+if ( isset( $args[0] ) ) {
 11+ wfWaitForSlaves($args[0]);
 12+} else {
 13+ wfWaitForSlaves(10);
1814 }
1915
20 -$maintClass = "WaitForSlave";
21 -require_once( DO_MAINTENANCE );
 16+
Index: trunk/phase3/maintenance/eval.php
@@ -16,66 +16,57 @@
1717 * @ingroup Maintenance
1818 */
1919
20 -require_once( "Maintenance.php" );
 20+$wgUseNormalUser = (bool)getenv('MW_WIKIUSER');
2121
22 -class EvalPrompt extends Maintenance {
 22+$optionsWithArgs = array( 'd' );
2323
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 );
 24+/** */
 25+require_once( "commandLine.inc" );
 26+
 27+if ( isset( $options['d'] ) ) {
 28+ $d = $options['d'];
 29+ if ( $d > 0 ) {
 30+ $wgDebugLogFile = '/dev/stdout';
2931 }
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 - }
 32+ if ( $d > 1 ) {
 33+ $lb = wfGetLB();
 34+ foreach ( $lb->mServers as $i => $server ) {
 35+ $lb->mServers[$i]['flags'] |= DBO_DEBUG;
4836 }
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";
7837 }
 38+ if ( $d > 2 ) {
 39+ $wgDebugFunctionEntry = true;
 40+ }
7941 }
8042
81 -$maintClass = "EvalPrompt";
82 -require_once( DO_MAINTENANCE );
 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+
Index: trunk/phase3/maintenance/renameDbPrefix.php
@@ -6,59 +6,62 @@
77 * @file
88 * @ingroup Maintenance
99 */
10 -
11 -require_once( "Maintenance.php" );
 10+$optionsWithArgs = array( 'old', 'new', 'help' );
1211
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 );
 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" );
1865 }
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 - }
 66+ $count++;
6567 }
 68+print "Done! [$count tables]\n";
\ No newline at end of file
Index: trunk/phase3/maintenance/benchmarkPurge.php
@@ -6,87 +6,74 @@
77 * @ingroup Maintenance
88 */
99
10 -require_once( "Maintenance.php" );
 10+/** */
 11+require_once( "commandLine.inc" );
1112
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.";
 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 );
1823 }
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 - }
 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();
3739 }
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 );
 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') ) );
5561 }
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;
 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 );
6774 }
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 );
 75+ foreach( $lengths as $length ) {
 76+ $urls = randomUrlList( $length );
 77+ $trial = benchSquid( $urls );
 78+ print "$trial\n";
7679 }
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 - }
9080 }
91 -
92 -$maintClass = "BenchmarkPurge";
93 -require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/nukePage.php
@@ -1,4 +1,5 @@
22 <?php
 3+
34 /**
45 * Erase a page record from the database
56 * Irreversible (can't use standard undelete) and does not update link tables
@@ -8,92 +9,21 @@
910 * @author Rob Church <robchur@gmail.com>
1011 */
1112
12 -require_once( "Maintenance.php" );
 13+require_once( 'commandLine.inc' );
 14+require_once( 'nukePage.inc' );
1315
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 - }
 16+echo( "Erase Page Record\n\n" );
2117
22 - public function execute() {
 18+if( isset( $args[0] ) ) {
 19+ NukePage( $args[0], true );
 20+} else {
 21+ ShowUsage();
 22+}
2323
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 - }
 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" );
9729 }
9830
99 -$maintClass = "NukePage";
100 -require_once( DO_MAINTENANCE );
\ No newline at end of file
Index: trunk/phase3/maintenance/updateArticleCount.php
@@ -3,84 +3,38 @@
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
78 * @ingroup Maintenance
89 * @author Rob Church <robchur@gmail.com>
910 */
1011
11 -require_once( "Maintenance.php" );
 12+$options = array( 'update', 'help' );
 13+require_once( 'commandLine.inc' );
 14+require_once( 'updateArticleCount.inc' );
 15+echo( "Update Article Count\n\n" );
1216
13 -class UpdateArticleCount extends Maintenance {
 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+}
1422
15 - // Content namespaces
16 - private $namespaces;
 23+echo( "Counting articles..." );
 24+$counter = new ArticleCounter();
 25+$result = $counter->count();
1726
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;
 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" );
2436 }
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 - }
 37+} else {
 38+ echo( "failed.\n" );
8439 }
 40+echo( "\n" );
8541
86 -$maintClass = "UpdateArticleCount";
87 -require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/checkUsernames.php
@@ -7,33 +7,36 @@
88 * @ingroup Maintenance
99 */
1010
 11+error_reporting(E_ALL ^ E_NOTICE);
 12+require_once 'commandLine.inc';
1113
12 -require_once( "Maintenance.php" );
 14+class checkUsernames {
 15+ var $stderr, $log;
1316
14 -class CheckUsernames extends Maintenance {
15 -
16 - public function __construct() {
17 - parent::__construct();
18 - $this->mDescription = "Verify that database usernames are actually valid";
 17+ function checkUsernames() {
 18+ $this->stderr = fopen( 'php://stderr', 'wt' );
1919 }
 20+ function main() {
 21+ $fname = 'checkUsernames::main';
2022
21 - function execute() {
2223 $dbr = wfGetDB( DB_SLAVE );
2324
2425 $res = $dbr->select( 'user',
2526 array( 'user_id', 'user_name' ),
2627 null,
27 - __METHOD__
 28+ $fname
2829 );
2930
3031 while ( $row = $dbr->fetchObject( $res ) ) {
3132 if ( ! User::isValidUserName( $row->user_name ) ) {
32 - $this->error( sprintf( "%s: %6d: '%s'\n", wfWikiID(), $row->user_id, $row->user_name ) );
 33+ $out = sprintf( "%s: %6d: '%s'\n", wfWikiID(), $row->user_id, $row->user_name );
 34+ fwrite( $this->stderr, $out );
3335 wfDebugLog( 'checkUsernames', $out );
3436 }
3537 }
3638 }
3739 }
3840
39 -$maintClass = "CheckUsernames";
40 -require_once( "doMaintenance.php" );
 41+$cun = new checkUsernames();
 42+$cun->main();
 43+
Index: trunk/phase3/maintenance/checkImages.php
@@ -1,63 +1,51 @@
22 <?php
3 -/**
4 - * Check images to see if they exist, are readable, etc etc
5 - */
6 -require_once( "Maintenance.php" );
73
8 -class CheckImages extends Maintenance {
 4+require( 'commandLine.inc' );
95
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 );
 6+$batchSize = 1000;
 7+$start = '';
 8+$dbr = wfGetDB( DB_SLAVE );
 9+$localRepo = RepoGroup::singleton()->getLocalRepo();
1910
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" );
 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++;
6248 }
63 -}
6449
 50+} while ( $res->numRows() );
 51+
 52+echo "Good images: $numGood/$numImages\n";
Index: trunk/phase3/maintenance/clear_stats.php
@@ -6,34 +6,33 @@
77 * @ingroup Maintenance
88 */
99
10 -require_once( 'Maintenance.php' );
 10+require_once('commandLine.inc');
1111
12 -class clear_stats extends Maintenance {
 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+}
1327
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 - }
 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);
3738 }
3839
39 -$maintClass = "clear_stats";
40 -require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/updaters.inc
@@ -1038,7 +1038,10 @@
10391039 wfOut( "ok.\n" );
10401040 return;
10411041 }
1042 - SiteStats::init( false );
 1042+
 1043+ global $IP;
 1044+ require_once "$IP/maintenance/initStats.inc";
 1045+ wfInitStats();
10431046 }
10441047
10451048 function do_active_users_init() {
Index: trunk/phase3/maintenance/populateLogSearch.php
@@ -9,88 +9,13 @@
1010 * @ingroup Maintenance
1111 */
1212
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__ );
 13+require_once 'commandLine.inc';
 14+require_once 'populateLogSearch.inc';
3515
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 - }
 16+$db =& wfGetDB( DB_MASTER );
 17+if ( !$db->tableExists( 'log_search' ) ) {
 18+ echo "log_search does not exist\n";
 19+ exit( 1 );
9420 }
9521
96 -$maintClass = "PopulateLogSearch";
97 -require_once( DO_MAINTENANCE );
 22+migrate_log_params( $db );
Index: trunk/phase3/maintenance/deleteRevision.php
@@ -6,52 +6,42 @@
77 * @ingroup Maintenance
88 */
99
10 -require_once( "Maintenance.php" );
 10+require_once( 'commandLine.inc' );
1111
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 - }
 12+$dbw = wfGetDB( DB_MASTER );
2313
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" );
 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 ) );
5444 }
5545 }
5646
57 -$maintClass = "DeleteRevision";
58 -require_once( DO_MAINTENANCE );
 47+print "Deleted $affected revisions\n";
 48+
Index: trunk/phase3/maintenance/checkAutoLoader.php
@@ -1,40 +1,29 @@
22 <?php
3 -/**
4 - * Check the autoloader
5 - */
 3+if ( php_sapi_name() != 'cli' ) exit;
64
7 -require_once( "Maintenance.php" );
 5+$IP = dirname(__FILE__) .'/..';
 6+require( "$IP/includes/AutoLoader.php" );
 7+$files = array_unique( $wgAutoloadLocalClasses );
88
9 -class CheckAutoLoader extends Maintenance {
10 - public function __construct() {
11 - parent::__construct();
12 - $this->mDescription = "AutoLoader sanity checks";
 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];
1318 }
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 - }
 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";
3625 }
3726 }
 27+
3828 }
3929
40 -$maintClass = "CheckAutoLoader";
41 -require_once( DO_MAINTENANCE );
 30+
Index: trunk/phase3/maintenance/clear_interwiki_cache.php
@@ -3,36 +3,25 @@
44 * This script is used to clear the interwiki links for ALL languages in
55 * memcached.
66 *
 7+ * @file
78 * @ingroup Maintenance
89 */
910
10 -require_once( "Maintenance.php" );
 11+/** */
 12+require_once('commandLine.inc');
1113
12 -class ClearInterwikiCache extends Maintenance {
 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+}
1320
14 - public function __construct() {
15 - parent::__construct();
16 - $this->mDescription = "Clear all interwiki links for all languages from the cache";
 21+foreach ( $wgLocalDatabases as $db ) {
 22+ print "$db ";
 23+ foreach ( $prefixes as $prefix ) {
 24+ $wgMemc->delete("$db:interwiki:$prefix");
1725 }
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 - }
3626 }
 27+print "\n";
3728
38 -$maintClass = "ClearInterwikiCache";
39 -require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/fetchText.php
@@ -6,48 +6,34 @@
77 * @ingroup Maintenance
88 */
99
10 -require_once( "Maintenance.php" );
 10+require "commandLine.inc";
1111
12 -class FetchText extends Maintenance {
13 - public function __construct() {
14 - parent::__construct();
15 - $this->mDescription = "Fetch the revision text from an old_id";
 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;
1619 }
 20+ $textId = intval( $line );
 21+ $text = doGetText( $db, $textId );
 22+ echo strlen( $text ) . "\n";
 23+ echo $text;
 24+}
1725
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 - }
 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;
3138 }
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 - }
 39+ return $text;
5140 }
52 -
53 -$maintClass = "FetchText";
54 -require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/showStats.php
@@ -14,41 +14,35 @@
1515 * @license GNU General Public License 2.0 or later
1616 */
1717
18 -require_once( "Maintenance.php" );
 18+require_once( 'commandLine.inc' );
1919
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 - }
 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 )) ;
5143 }
5244
53 -$maintClass = "ShowStats";
54 -require_once( DO_MAINTENANCE );
 45+// Show them
 46+foreach( $fields as $field => $desc ) {
 47+ printf( "%-{$max_length_desc}s: %{$max_length_value}d\n", $desc, $stats->$field );
 48+}
5549
Index: trunk/phase3/maintenance/populateParentId.php
@@ -1,105 +1,18 @@
22 <?php
 3+
34 /*
45 * Makes the required database updates for rev_parent_id
56 * to be of any use. It can be used for some simple tracking
67 * and to find new page edits by users.
78 */
89
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 - }
 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 );
10317 }
10418
105 -$maintClass = "PopulateParentId";
106 -require_once( DO_MAINTENANCE );
 19+populate_rev_parent_id( $db );
Index: trunk/phase3/maintenance/createAndPromote.php
@@ -8,53 +8,61 @@
99 * @author Rob Church <robchur@gmail.com>
1010 */
1111
12 -require_once( "Maintenance.php" );
 12+$options = array( 'help', 'bureaucrat' );
 13+require_once( 'commandLine.inc' );
1314
14 -class CreateAndPromote extends Maintenance {
 15+if( isset( $options['help'] ) ) {
 16+ showHelp();
 17+ exit( 1 );
 18+}
1519
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 - }
 20+if( count( $args ) < 2 ) {
 21+ echo( "Please provide a username and password for the new account.\n" );
 22+ die( 1 );
 23+}
2224
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 - }
 25+$username = $args[0];
 26+$password = $args[1];
3527
36 - # Try to set the password
37 - try {
38 - $user->setPassword( $password );
39 - } catch( PasswordError $pwe ) {
40 - $this->error( $pwe->getText(), true );
41 - }
 28+echo( wfWikiID() . ": Creating and promoting User:{$username}..." );
4229
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 - }
 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 );
5838 }
5939
60 -$maintClass = "CreateAndPromote";
61 -require_once( DO_MAINTENANCE );
 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
Index: trunk/phase3/maintenance/deleteDefaultMessages.php
@@ -1,53 +1,48 @@
22 <?php
 3+
34 /**
45 * Deletes all pages in the MediaWiki namespace which were last edited by
56 * "MediaWiki default".
67 *
 8+ * @file
79 * @ingroup Maintenance
810 */
911
10 -require_once( "Maintenance.php" );
 12+if ( !defined( 'MEDIAWIKI' ) ) {
 13+ require_once( 'commandLine.inc' );
 14+ deleteDefaultMessages();
 15+}
1116
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 - }
 17+function deleteDefaultMessages() {
 18+ $user = 'MediaWiki default';
 19+ $reason = 'No longer required';
1820
19 - public function execute() {
20 - $user = 'MediaWiki default';
21 - $reason = 'No longer required';
 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+ );
2234
23 - global $wgUser;
24 - $wgUser = User::newFromName( $user );
25 - $wgUser->addGroup( 'bot' );
 35+ $dbw = wfGetDB( DB_MASTER );
2636
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();
 37+ while ( $row = $dbr->fetchObject( $res ) ) {
 38+ if ( function_exists( 'wfWaitForSlaves' ) ) {
 39+ wfWaitForSlaves( 5 );
4940 }
 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();
5047 }
5148 }
5249
53 -$maintClass = "DeleteDefaultMessages";
54 -require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/updateSpecialPages.php
@@ -6,114 +6,114 @@
77 * @file
88 * @ingroup Maintenance
99 */
10 -
11 -require_once( "Maintenance.php" );
 10+$options = array('only','help');
1211
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' );
 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;
1933 }
 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+}
2052
21 - public function execute() {
22 - global $wgOut;
23 - $wgOut->disable();
24 - $dbw = wfGetDB( DB_MASTER );
 53+foreach( $wgQueryPages as $page ) {
 54+ @list( $class, $special, $limit ) = $page;
2555
26 - foreach( $wgSpecialPageCacheUpdates as $special => $call ) {
27 - if( !is_callable($call) ) {
28 - $this->error( "Uncallable function $call!\n" );
29 - continue;
30 - }
 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() ) {
3181 $t1 = explode( ' ', microtime() );
32 - call_user_func( $call, $dbw );
 82+ # Do the query
 83+ $num = $queryPage->recache( $limit === null ? $wgQueryCacheLimit : $limit );
3384 $t2 = explode( ' ', microtime() );
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 ) );
 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 ';
9896 }
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();
 97+ if ( $minutes ) {
 98+ print $minutes . 'm ';
11099 }
111 - # Wait for the slave to catch up
112 - wfWaitForSlaves( 5 );
113 - } else {
114 - $this->output( "cheap, skipped\n" );
115 - }
116 - }
 100+ printf( "%.2fs\n", $seconds );
117101 }
 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+ }
118119 }
119120 }
120 -
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 set $wgDBadminuser and $wgDBadminpassword in your
15 -LocalSettings.php
 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.
1616
1717 === Brief explanation of files ===
1818
Index: trunk/phase3/maintenance/attachLatest.php
@@ -25,58 +25,49 @@
2626 * @ingroup Maintenance
2727 */
2828
29 -require_once( "Maintenance.php" );
 29+require_once( 'commandLine.inc' );
3030
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";
 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;
3753 }
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__ );
4654
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 - }
 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;
7959 }
 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++;
8067 }
 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+}
8173
82 -$maintClass = "AttachLatest";
83 -require_once( DO_MAINTENANCE );
 74+
Index: trunk/phase3/maintenance/checkBadRedirects.php
@@ -1,42 +1,30 @@
22 <?php
3 -/**
4 - * CheckBadRedirects - See if pages marked as being redirects
5 - * really are.
6 - */
7 -
8 -require_once( "Maintenance.php" );
93
10 -class CheckBadRedirects extends Maintenance {
11 - public function __construct() {
12 - parent::__construct();
13 - $this->mDescription = "Look for bad redirects";
14 - }
 4+require "commandLine.inc";
155
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 - }
 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";
3726 }
38 - $this->output( "\ndone.\n" );
3927 }
4028 }
4129
42 -$maintClass = "CheckBadRedirects";
43 -require_once( DO_MAINTENANCE );
 30+echo "\n";
 31+echo "done.\n";
Index: trunk/phase3/maintenance/showJobs.php
@@ -9,19 +9,10 @@
1010 * @author Tim Starling
1111 * @author Ashar Voultoiz
1212 */
13 -
14 -require_once( "Maintenance.php" );
 13+require_once( 'commandLine.inc' );
1514
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 -}
 15+$dbw = wfGetDB( DB_MASTER );
 16+$count = $dbw->selectField( 'job', 'count(*)', '', 'runJobs.php' );
 17+print $count."\n";
2618
27 -$maintClass = "ShowJobs";
28 -require_once( DO_MAINTENANCE );
 19+
Index: trunk/phase3/maintenance/mctest.php
@@ -8,63 +8,58 @@
99 * @ingroup Maintenance
1010 */
1111
12 -require_once( "Maintenance.php" );
 12+$optionsWithArgs = array( 'i' );
1313
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 - }
 14+require_once('commandLine.inc');
2215
23 - public function execute() {
24 - global $wgMemCachedServers;
 16+function microtime_float()
 17+{
 18+ list($usec, $sec) = explode(" ", microtime());
 19+ return ((float)$usec + (float)$sec);
 20+}
2521
26 - $iterations = $this->getOption( 'i', 100 );
27 - if( $this->hasArg() )
28 - $wgMemCachedServers = array( $this->getArg() );
2922
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" );
 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++;
5745 }
5846 }
5947
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);
 48+ for ( $i=1; $i<=$iterations; $i++ ) {
 49+ if ( !is_null( $mcc->incr( "test$i", $i ) ) ) {
 50+ $incr++;
 51+ }
6752 }
 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";
6863 }
6964
70 -$maintClass = "mcTest";
71 -require_once( DO_MAINTENANCE );
 65+
 66+
Index: trunk/phase3/maintenance/sql.php
@@ -7,56 +7,38 @@
88 * @ingroup Database Maintenance
99 */
1010
11 -require_once( "Maintenance.php" );
 11+require_once( dirname(__FILE__) . '/' . 'commandLine.inc' );
1212
13 -class MwSql extends Maintenance {
14 - public function __construct() {
15 - parent::__construct();
16 - $this->mDescription = "Send SQL queries to a MediaWiki database";
17 - }
 13+if ( isset( $options['help'] ) ) {
 14+ echo "Send SQL queries to a MediaWiki database.\nUsage: php sql.php [<file>]\n";
 15+ exit( 1 );
 16+}
1817
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 );
 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+}
3227
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 - }
 28+if ( !$file ) {
 29+ echo "Unable to open input file\n";
 30+ exit( 1 );
 31+}
4132
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 - }
 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 );
5940 }
6041
 42+//-----------------------------------------------------------------------------
6143 class SqlPromptPrinter {
6244 function __construct( $prompt ) {
6345 $this->prompt = $prompt;
@@ -71,5 +53,17 @@
7254 }
7355 }
7456
75 -$maintClass = "MwSql";
76 -require_once( DO_MAINTENANCE );
 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+
Index: trunk/phase3/maintenance/reassignEdits.php
@@ -1,153 +1,56 @@
22 <?php
 3+
34 /**
45 * Reassign edits from a user or IP address to another user
56 *
 7+ * @file
68 * @ingroup Maintenance
79 * @author Rob Church <robchur@gmail.com>
810 * @licence GNU General Public Licence 2.0 or later
911 */
1012
11 -require_once( "Maintenance.php" );
 13+$options = array( 'force', 'norc', 'quiet', 'report' );
 14+require_once( 'commandLine.inc' );
 15+require_once( 'reassignEdits.inc' );
1216
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 - }
 17+# Set silent mode; --report overrides --quiet
 18+if( !@$options['report'] && @$options['quiet'] )
 19+ setSilent();
2220
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 - }
 21+out( "Reassign Edits\n\n" );
4322
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();
 23+if( @$args[0] && @$args[1] ) {
5624
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 - }
 25+ # Set up the users involved
 26+ $from =& initialiseUser( $args[0] );
 27+ $to =& initialiseUser( $args[1] );
8028
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;
 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" );
10340 }
10441
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 - }
 42+} else {
 43+ ShowUsage();
 44+}
14845
149 -
 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" );
15056 }
15157
152 -$maintClass = "ReassignEdits";
153 -require_once( DO_MAINTENANCE );
154 -
Index: trunk/phase3/maintenance/removeUnusedAccounts.php
@@ -8,89 +8,63 @@
99 * @author Rob Church <robchur@gmail.com>
1010 */
1111
12 -require_once( "Maintenance.php" );
 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';
1317
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 - }
 18+if( isset( $options['help'] ) ) {
 19+ showHelp();
 20+ exit(1);
 21+}
2122
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();
 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;
3537 }
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" );
 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" );
6953 }
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 - }
9454 }
 55+$count = count( $del );
 56+echo( "...found {$count}.\n" );
9557
96 -$maintClass = "RemoveUnusedAccounts";
97 -require_once( DO_MAINTENANCE );
 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" );
Index: trunk/phase3/maintenance/initStats.php
@@ -9,22 +9,23 @@
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" );
1317
14 -require_once( "Maintenance.php" );
 18+if( isset( $options['help'] ) ) {
 19+ showHelp();
 20+ exit(1);
 21+}
1522
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 - }
 23+require "$IP/maintenance/initStats.inc";
 24+wfInitStats( $options );
2325
24 - public function execute() {
25 - $this->output( "Refresh Site Statistics\n\n" );
26 - SiteStats::init( $this->hasOption('update'), $this->hasOption('noviews') );
27 - }
 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" );
2831 }
2932
30 -$maintClass = "InitStats";
31 -require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/runJobs.php
@@ -10,77 +10,71 @@
1111 * @ingroup Maintenance
1212 */
1313
14 -require_once( "Maintenance.php" );
 14+$optionsWithArgs = array( 'maxjobs', 'type', 'procs' );
 15+$wgUseNormalUser = true;
 16+require_once( 'commandLine.inc' );
1517
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;
 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 );
2523 }
 24+ $fc = new ForkController( $procs );
 25+ if ( $fc->start( $procs ) != 'child' ) {
 26+ exit( 0 );
 27+ }
 28+}
2629
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);
 30+if ( isset( $options['maxjobs'] ) ) {
 31+ $maxJobs = $options['maxjobs'];
 32+} else {
 33+ $maxJobs = 10000;
 34+}
4735
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 - }
 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" );
7368 }
 69+ if ( $maxJobs && ++$n > $maxJobs ) {
 70+ break 2;
 71+ }
7472 }
 73+}
7574
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 - }
 75+
 76+function runJobsLog( $msg ) {
 77+ print wfTimestamp( TS_DB ) . " $msg\n";
 78+ wfDebugLog( 'runJobs', $msg );
8479 }
8580
86 -$maintClass = "RunJobs";
87 -require_once( DO_MAINTENANCE );
 81+
Index: trunk/phase3/maintenance/updateSearchIndex.php
@@ -5,159 +5,58 @@
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.WIKI_ID.pos by default
10 - * LOCKTIME is how long the searchindex and revision tables will be locked for
 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
1111 * -q means quiet
1212 *
 13+ * @file
1314 * @ingroup Maintenance
1415 */
15 -
16 -require_once( "Maintenance.php" );
1716
18 -class UpdateSearchIndex extends Maintenance {
 17+/** */
 18+$optionsWithArgs = array( 's', 'e', 'p' );
1919
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 - }
 20+require_once( 'commandLine.inc' );
 21+require_once( 'updateSearchIndex.inc' );
2822
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;
 23+if ( isset( $options['p'] ) ) {
 24+ $posFile = $options['p'];
 25+} else {
 26+ $posFile = 'searchUpdate.' . wfWikiId() . '.pos';
 27+}
5528
56 - $wgDisableSearchUpdate = false;
 29+if ( isset( $options['e'] ) ) {
 30+ $end = $options['e'];
 31+} else {
 32+ $end = wfTimestampNow();
 33+}
5734
58 - $dbw = wfGetDB( DB_MASTER );
59 - $recentchanges = $dbw->tableName( 'recentchanges' );
 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+}
6048
61 - $this->output( "Updating searchindex between $start and $end\n" );
 49+if ( isset( $options['l'] ) ) {
 50+ $lockTime = $options['l'];
 51+} else {
 52+ $lockTime = 20;
 53+}
6254
63 - # Select entries from recentchanges which are on top and between the specified times
64 - $start = $dbw->strencode( $start );
65 - $end = $dbw->strencode( $end );
 55+$quiet = (bool)(@$options['q']);
6656
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__ );
 57+updateSearchIndex( $start, $end, $lockTime, $quiet );
7358
 59+$file = fopen( $posFile, 'w' );
 60+fwrite( $file, $end );
 61+fclose( $file );
7462
75 - # Lock searchindex
76 - if ( $maxLockTime ) {
77 - $this->output( " --- Waiting for lock ---" );
78 - $this->lockSearchindex( $dbw );
79 - $lockTime = time();
80 - $this->output( "\n" );
81 - }
8263
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/moveBatch.php
@@ -1,7 +1,9 @@
22 <?php
 3+
34 /**
45 * Maintenance script to move a batch of pages
56 *
 7+ * @file
68 * @ingroup Maintenance
79 * @author Tim Starling
810 *
@@ -18,77 +20,77 @@
1921 * e.g. immobile_namespace for namespaces which can't be moved
2022 */
2123
22 -require_once( "Maintenance.php" );
 24+$oldCwd = getcwd();
 25+$optionsWithArgs = array( 'u', 'r', 'i' );
 26+require_once( 'commandLine.inc' );
2327
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' ) );
 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;
3267 }
33 -
34 - public function execute() {
35 - global $wgUser;
 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+ }
3679
37 - # Change to current working directory
38 - $oldCwd = getcwd();
39 - chdir( $oldCwd );
4080
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 - }
 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";
5089
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 - }
 90+ if ( $interval ) {
 91+ sleep( $interval );
9192 }
 93+ wfWaitForSlaves( 5 );
9294 }
9395
94 -$maintClass = "MoveBatch";
95 -require_once( DO_MAINTENANCE );
 96+
 97+
Index: trunk/phase3/maintenance/refreshImageCount.php
@@ -7,34 +7,23 @@
88 * @ingroup Maintenance
99 */
1010
11 -require_once( "Maintenance.php" );
 11+require_once( "commandLine.inc" );
1212
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 );
 13+$dbw = wfGetDB( DB_MASTER );
2114
22 - // Load the current value from the master
23 - $count = $dbw->selectField( 'site_stats', 'ss_images' );
 15+// Load the current value from the master
 16+$count = $dbw->selectField( 'site_stats', 'ss_images' );
2417
25 - $this->output( wfWikiID() . ": forcing ss_images to $count\n" );
 18+echo wfWikiID().": forcing ss_images to $count\n";
2619
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 -}
 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 ) );
3824
39 -$maintClass = "RefreshImageCount";
40 -require_once( DO_MAINTENANCE );
 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 ) );
4129
 30+
Index: trunk/phase3/maintenance/purgeOldText.php
@@ -1,24 +1,29 @@
22 <?php
 3+
34 /**
45 * Purge old text records from the database
56 *
 7+ * @file
68 * @ingroup Maintenance
79 * @author Rob Church <robchur@gmail.com>
810 */
911
10 -require_once( "Maintenance.php" );
 12+$options = array( 'purge', 'help' );
 13+require_once( 'commandLine.inc' );
 14+require_once( 'purgeOldText.inc' );
1115
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 - }
 16+echo( "Purge Old Text\n\n" );
 17+
 18+if( @$options['help'] ) {
 19+ ShowUsage();
 20+} else {
 21+ PurgeRedundantText( @$options['purge'] );
2222 }
2323
24 -$maintClass = "PurgeOldText";
25 -require_once( DO_MAINTENANCE );
 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+
Index: trunk/phase3/maintenance/Doxyfile
@@ -135,7 +135,7 @@
136136 RECURSIVE = YES
137137 EXCLUDE =
138138 EXCLUDE_SYMLINKS = YES
139 -EXCLUDE_PATTERNS = LocalSettings.php
 139+EXCLUDE_PATTERNS = LocalSettings.php AdminSettings.php
140140 EXAMPLE_PATH =
141141 EXAMPLE_PATTERNS = *
142142 EXAMPLE_RECURSIVE = NO
Index: trunk/phase3/maintenance/getSlaveServer.php
@@ -5,32 +5,24 @@
66 * @file
77 * @ingroup Maintenance
88 */
9 -
10 -require_once( "Maintenance.php" );
119
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 - }
 10+require_once( dirname(__FILE__).'/commandLine.inc' );
 11+
 12+if ( $wgAllDBsAreLocalhost ) {
 13+ # Can't fool the backup script
 14+ print "localhost\n";
 15+ exit;
3416 }
3517
36 -$maintClass = "GetSlaveServer";
37 -require_once( DO_MAINTENANCE );
 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+
Index: trunk/phase3/maintenance/renderDump.php
@@ -27,61 +27,42 @@
2828 * @file
2929 * @ingroup Maintenance
3030 */
31 -
32 -require_once( "Maintenance.php" );
3331
34 -class DumpRenderer extends Maintenance {
 32+$optionsWithArgs = array( 'report' );
3533
36 - private $count = 0;
37 - private $outputDirectory, $startTime;
 34+require_once( 'commandLine.inc' );
3835
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 );
 36+class DumpRenderer {
 37+ function __construct( $dir ) {
 38+ $this->stderr = fopen( "php://stderr", "wt" );
 39+ $this->outputDirectory = $dir;
 40+ $this->count = 0;
4341 }
4442
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 ) {
 43+ function handleRevision( $rev ) {
6344 $title = $rev->getTitle();
6445 if (!$title) {
65 - $this->error( "Got bogus revision with null title!" );
 46+ fprintf( $this->stderr, "Got bogus revision with null title!" );
6647 return;
6748 }
6849 $display = $title->getPrefixedText();
69 -
 50+
7051 $this->count++;
71 -
 52+
7253 $sanitized = rawurlencode( $display );
7354 $filename = sprintf( "%s/wiki-%07d-%s.html",
7455 $this->outputDirectory,
7556 $this->count,
7657 $sanitized );
77 - $this->output( sprintf( $this->stderr, "%s\n", $filename, $display ) );
78 -
79 - // fixme (what?)
 58+ fprintf( $this->stderr, "%s\n", $filename, $display );
 59+
 60+ // fixme
8061 $user = new User();
8162 $parser = new Parser();
8263 $options = ParserOptions::newFromUser( $user );
83 -
 64+
8465 $output = $parser->parse( $rev->getText(), $title, $options );
85 -
 66+
8667 file_put_contents( $filename,
8768 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" " .
8869 "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" .
@@ -95,7 +76,27 @@
9677 "</body>\n" .
9778 "</html>" );
9879 }
 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+ }
9993 }
10094
101 -$maintClass = "DumpRenderer";
102 -require_once( DO_MAINTENANCE );
 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+
Index: trunk/phase3/maintenance/changePassword.php
@@ -10,32 +10,47 @@
1111 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
1212 */
1313
14 -require_once( "Maintenance.php" );
 14+$optionsWithArgs = array( 'user', 'password' );
 15+require_once 'commandLine.inc';
1516
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 );
 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 );
2741 }
28 - $user = User::newFromName( $this->getOption('user') );
29 - if( !$user->getId() ) {
30 - $this->error( "No such user: " . $this->getOption('user') . "\n", true );
 42+
 43+ $this->user = User::newFromName( $user );
 44+ if ( !$this->user->getId() ) {
 45+ die ( "No such user: $user\n" );
3146 }
32 - try {
33 - $user->setPassword( $this->getOption('password') );
34 - $user->saveSettings();
35 - } catch( PasswordError $pwe ) {
36 - $this->error( $pwe->getText(), true );
37 - }
 47+
 48+ $this->password = $password;
 49+
 50+ $this->dbw = wfGetDB( DB_MASTER );
3851 }
 52+
 53+ function main() {
 54+ $this->user->setPassword( $this->password );
 55+ $this->user->saveSettings();
 56+ }
3957 }
40 -
41 -$maintClass = "ChangePassword";
42 -require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/deleteImageMemcached.php
@@ -3,36 +3,40 @@
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
 7+ * php deleteImageMemcached.php --until "2005-09-05 00:00:00" --sleep 0 --report 10
88 *
99 * @file
1010 * @ingroup Maintenance
1111 */
1212
13 -require_once( "Maintenance.php" );
 13+$optionsWithArgs = array( 'until', 'sleep', 'report' );
1414
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 );
 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;
2127 }
2228
23 - public function execute() {
 29+ function main() {
2430 global $wgMemc;
 31+ $fname = 'DeleteImageCache::main';
2532
26 - $until = preg_replace( "/[^\d]/", '', $this->getOption('until') );
27 - $sleep = (int)$this->getOption('sleep') * 1000; // milliseconds
28 -
2933 ini_set( 'display_errors', false );
3034
3135 $dbr = wfGetDB( DB_SLAVE );
3236
3337 $res = $dbr->select( 'image',
3438 array( 'img_name' ),
35 - array( "img_timestamp < {$until}" ),
36 - __METHOD__
 39+ array( "img_timestamp < {$this->until}" ),
 40+ $fname
3741 );
3842
3943 $i = 0;
@@ -40,22 +44,29 @@
4145
4246 while ( $row = $dbr->fetchObject( $res ) ) {
4347 if ($i % $this->report == 0)
44 - $this->output( sprintf("%s: %13s done (%s)\n", wfWikiID(), "$i/$total", wfPercent( $i / $total * 100 ) ) );
 48+ printf("%s: %13s done (%s)\n", wfWikiID(), "$i/$total", wfPercent( $i / $total * 100 ));
4549 $md5 = md5( $row->img_name );
4650 $wgMemc->delete( wfMemcKey( 'Image', $md5 ) );
4751
48 - if ($sleep != 0)
49 - usleep( $sleep );
 52+ if ($this->sleep != 0)
 53+ usleep( $this->sleep );
5054
5155 ++$i;
5256 }
5357 }
5458
55 - private function getImageCount() {
 59+ function getImageCount() {
 60+ $fname = 'DeleteImageCache::getImageCount';
 61+
5662 $dbr = wfGetDB( DB_SLAVE );
57 - return $dbr->selectField( 'image', 'COUNT(*)', array(), __METHOD__ );
 63+ return $dbr->selectField( 'image', 'COUNT(*)', array(), $fname );
5864 }
5965 }
6066
61 -$maintClass = "DeleteImageCache";
62 -require_once( DO_MAINTENANCE );
 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+
Index: trunk/phase3/maintenance/nukeNS.php
@@ -19,80 +19,90 @@
2020 * based on nukePage by Rob Church
2121 */
2222
23 -require_once( "Maintenance.php" );
 23+require_once( 'commandLine.inc' );
 24+require_once( 'nukePage.inc' );
2425
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 - }
 26+$ns = NS_MEDIAWIKI;
 27+$delete = false;
3228
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();
 29+if (isset($options['ns']))
 30+{
 31+ $ns = $options['ns'];
 32+}
3833
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" );
 34+if (isset( $options['delete'] ) and $options['delete'])
 35+{
 36+ $delete = true;
 37+}
4238
43 - $n_deleted = 0;
4439
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();
 40+NukeNS( $ns, $delete);
4941
50 - // Get corresponding revisions
51 - $res2 = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_page = $id" );
52 - $revs = array();
 42+function NukeNS($ns_no, $delete) {
5343
54 - while( $row2 = $dbw->fetchObject( $res2 ) ) {
55 - $revs[] = $row2->rev_id;
56 - }
57 - $count = count( $revs );
 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" );
5850
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" );
 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();
6357
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();
 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 );
8066
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 - }
 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+
96107 }
97108
98 -$maintClass = "NukeNS";
99 -require_once( DO_MAINTENANCE );
 109+
Index: trunk/phase3/maintenance/undelete.php
@@ -6,35 +6,34 @@
77 * @ingroup Maintenance
88 */
99
10 -require_once( "Maintenance.php" );
 10+$usage = <<<EOT
 11+Undelete a page
 12+Usage: php undelete.php [-u <user>] [-r <reason>] <pagename>
1113
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 - }
 14+EOT;
2015
21 - public function execute() {
22 - global $wgUser;
 16+$optionsWithArgs = array( 'u', 'r' );
 17+require_once( 'commandLine.inc' );
2318
24 - $user = $this->getOption( 'u', 'Command line script' );
25 - $reason = $this->getOption( 'r', '' );
26 - $pageName = $this->getArg();
 19+$user = 'Command line script';
 20+$reason = '';
2721
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 - }
 22+if ( isset( $options['u'] ) ) {
 23+ $user = $options['u'];
3824 }
 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 -$maintClass = "Undelete";
41 -require_once( DO_MAINTENANCE );
 40+
Index: trunk/phase3/maintenance/getLagTimes.php
@@ -4,37 +4,26 @@
55 * @ingroup Maintenance
66 */
77
8 -require_once( "Maintenance.php" );
 8+require 'commandLine.inc';
99
10 -class GetLagTimes extends Maintenance {
11 - public function __construct() {
12 - parent::__construct();
13 - $this->mDescription = "Dump replication lag times";
14 - }
 10+$lb = wfGetLB();
1511
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" );
 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 );
2222 } else {
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 - }
 23+ $ip = gethostbyname( $host );
3624 }
 25+ $starLen = min( intval( $lag ), 40 );
 26+ $stars = str_repeat( '*', $starLen );
 27+ printf( "%10s %20s %3d %s\n", $ip, $host, $lag, $stars );
3728 }
3829 }
3930
40 -$maintClass = "GetLagTimes";
41 -require_once( DO_MAINTENANCE );
Index: trunk/phase3/maintenance/rebuildFileCache.php
@@ -6,96 +6,87 @@
77 * @ingroup Maintenance
88 */
99
10 -require_once( "Maintenance.php" );
 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
1117
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 - }
 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";
1822
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 );
 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?
2354 }
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" );
 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;
8462 } else {
85 - $this->output( "Page {$row->page_id} not cacheable\n" );
 63+ echo "Page {$row->page_id} already cached\n";
 64+ continue; // done already!
8665 }
87 - $dbw->commit(); // commit any changes
8866 }
89 - $blockStart += $BATCH_SIZE;
90 - $blockEnd += $BATCH_SIZE;
91 - wfWaitForSlaves( 5 );
 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";
9280 }
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);
 81+ $dbw->commit(); // commit any changes
10082 }
 83+ $blockStart += $BATCH_SIZE;
 84+ $blockEnd += $BATCH_SIZE;
 85+ wfWaitForSlaves( 5 );
10186 }
102 -require_once( "commandLine.inc" );
 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);
Index: trunk/phase3/maintenance/nextJobDB.php
@@ -6,71 +6,55 @@
77 * @ingroup Maintenance
88 */
99
10 -require_once( "Maintenance.php" );
 10+$options = array( 'type' );
1111
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 );
 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;
1730 }
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 )
 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)";
3046 }
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)] );
 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];
3451 }
3552 }
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 - }
 53+
 54+ $wgMemc->set( $mckey, $pendingDBs, 300 );
7455 }
7556
76 -$maintClass = "nextJobDb";
77 -require_once( DO_MAINTENANCE );
 57+if ( $pendingDBs ) {
 58+ echo $pendingDBs[mt_rand(0, count( $pendingDBs ) - 1)];
 59+}
 60+
 61+
Index: trunk/phase3/UPGRADE
@@ -42,7 +42,8 @@
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 and the "extensions" and "images" directories.
 46+LocalSettings.php file, AdminSettings.php file (if present), and the
 47+"extensions" and "images" directories.
4748
4849 Depending upon your configuration, you may also need to preserve additional
4950 directories, including a custom upload directory ($wgUploadDirectory),
@@ -50,8 +51,8 @@
5152
5253 === Perform the database upgrade ===
5354
54 -You will need to have $wgDBadminuser and $wgDBadminpass set in your
55 -LocalSettings.php, see there for more info.
 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.
5657
5758 From the command line, browse to the "maintenance" directory and run the
5859 update.php script to check and update the schema. This will insert missing
@@ -171,10 +172,10 @@
172173 === Web installer ===
173174
174175 You can use the web-based installer wizard if you first remove the
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.
 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.
179180
180181 You may change some settings during the install, but be very careful!
181182 Changing the encoding in particular will generally leave you with a
@@ -184,8 +185,8 @@
185186
186187 Additionally, as of 1.4.0 you can run an in-place upgrade script from
187188 the command line, keeping your existing LocalSettings.php. This requires
188 -that you set $wgDBadminuser and $wgDBadminpassword with an appropriate
189 -database user and password with privileges to modify the database structure.
 189+that you create an AdminSettings.php giving an appropriate database user
 190+and password with privileges to modify the database structure.
190191
191192 Once the new files are in place, go into the maintenance subdirectory and
192193 run the script:
Index: trunk/phase3/docs/maintenance.txt
@@ -1,54 +0,0 @@
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 -
23 -3. HOW TO WRITE YOUR OWN
24 -Make a file in the maintenance directory called myScript.php or something.
25 -In it, write the following:
26 -
27 -==BEGIN==
28 -
29 -<?php
30 -
31 -require_once( "Maintenance.php" );
32 -
33 -class DemoMaint extends Maintenance {
34 -
35 - public function __construct() {
36 - parent::__construct();
37 - }
38 -
39 - protected function execute() {
40 - }
41 -}
42 -
43 -$maintClass = "DemoMaint";
44 -require_once( DO_MAINTENANCE );
45 -
46 -==END==
47 -
48 -That's it. In the execute() method, you have access to all of the normal
49 -MediaWiki functions, so you can get a DB connection, use the cache, etc.
50 -For full docs on the Maintenance class, see the auto-generated docs at
51 -http://svn.wikimedia.org/doc/classMaintenance.html
\ No newline at end of file
Index: trunk/phase3/docs/scripts.txt
@@ -35,9 +35,10 @@
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 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.
 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.
4243
4344 redirect.php
4445 Script that only redirect to the article passed in the wpDropdown parameter
Index: trunk/phase3/includes/SiteStats.php
@@ -49,8 +49,11 @@
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+
5356 ob_start();
54 - self::init( false );
 57+ wfInitStats();
5558 ob_end_clean();
5659
5760 $row = self::doLoad( wfGetDB( DB_MASTER ) );
@@ -174,63 +177,6 @@
175178 }
176179 return true;
177180 }
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 - }
235181 }
236182
237183
Index: trunk/phase3/AdminSettings.sample
@@ -0,0 +1,32 @@
 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;
Property changes on: trunk/phase3/AdminSettings.sample
___________________________________________________________________
Added: svn:eol-style
134 + native
Added: svn:keywords
235 + Author Date Id Revision
Index: trunk/phase3/profileinfo.php
@@ -4,6 +4,7 @@
55 $wgEnableProfileInfo = $wgProfileToDatabase = false;
66
77 require_once( './includes/WebStart.php' );
 8+@include_once( './AdminSettings.php' );
89
910 ?>
1011 <!--
Index: trunk/phase3/t/Search.inc
@@ -7,10 +7,11 @@
88 require 'includes/Defines.php';
99 require 'includes/ProfilerStub.php';
1010 require 'LocalSettings.php';
 11+require 'AdminSettings.php';
1112 require 'includes/Setup.php';
1213
1314 function buildTestDatabase( $tables ) {
14 - global $wgDBprefix, $wgDBserver, $wgDBname, $wgDBtype;
 15+ global $wgDBprefix, $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname, $wgDBtype;
1516 $oldPrefix = $wgDBprefix;
1617 $wgDBprefix = 'parsertest';
1718
Index: trunk/phase3/config/index.php
@@ -615,7 +615,6 @@
616616 $conf->RootUser = importPost( "RootUser", "root" );
617617 $conf->RootPW = importPost( "RootPW", "" );
618618 $useRoot = importCheck( 'useroot', false );
619 - $conf->populateadmin = importCheck( 'populateadmin', false );
620619 $conf->LanguageCode = importPost( "LanguageCode", "en" );
621620 ## MySQL specific:
622621 $conf->DBprefix = importPost( "DBprefix" );
@@ -1528,8 +1527,6 @@
15291528 <label class="column">Superuser account:</label>
15301529 <input type="checkbox" name="useroot" id="useroot" <?php if( $useRoot ) { ?>checked="checked" <?php } ?> />
15311530 &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>
15341531 </div>
15351532 <div class="config-input"><?php aField( $conf, "RootUser", "Superuser name:", "text" ); ?></div>
15361533 <div class="config-input"><?php aField( $conf, "RootPW", "Superuser password:", "password" ); ?></div>
@@ -1795,11 +1792,6 @@
17961793 # Needs literal string interpolation for the current style path
17971794 $slconf['RightsIcon'] = $conf->RightsIcon;
17981795 }
1799 -
1800 - if( $conf->populateadmin ) {
1801 - $slconf['DBadminuser'] = $conf->RootUser;
1802 - $slconf['DBadminpassword'] = $conf->RootPW;
1803 - }
18041796
18051797 if( $conf->DBtype == 'mysql' ) {
18061798 $dbsettings =
@@ -1907,10 +1899,6 @@
19081900
19091901 {$dbsettings}
19101902
1911 -## Database admin settings, used for maintenance scripts
1912 -\$wgDBadminuser = \"{$slconf['DBadminuser']}\";
1913 -\$wgDBadminpassword = \"{$slconf['DBadminpassword']}\";
1914 -
19151903 ## Shared memory settings
19161904 \$wgMainCacheType = $cacheType;
19171905 \$wgMemCachedServers = $mcservers;
Index: trunk/phase3/RELEASE-NOTES
@@ -41,7 +41,6 @@
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
4645
4746 === New features in 1.16 ===
4847
@@ -92,11 +91,8 @@
9392 stripped from them.
9493 * Added a PHP port of CDB (constant database), for improved local caching when
9594 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
10095
 96+
10197 === Bug fixes in 1.16 ===
10298
10399 * (bug 18031) Make namespace selector on Special:Export remember the previous
@@ -204,8 +200,6 @@
205201 * (bug 19294) Always show Sp-contributions-footer(-anon)
206202 * Attempts to restrict reading of pages while anonymous viewing is allowed
207203 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
210204 * (bug 8445) Multiple-character search terms are now handled properly for Chinese
211205
212206 == API changes in 1.16 ==
Property changes on: trunk/phase3
___________________________________________________________________
Modified: svn:mergeinfo
213207 Reverse-merged /trunk/phase3:r52328-52330
214208 Reverse-merged /branches/maintenance-work:r51675-52335

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r52336Merge maintenance-work branch:...demon02:02, 24 June 2009

Status & tagging log