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