summaryrefslogtreecommitdiff
path: root/maintenance
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2010-07-28 11:52:48 +0200
committerPierre Schmitz <pierre@archlinux.de>2010-07-28 11:52:48 +0200
commit222b01f5169f1c7e69762e0e8904c24f78f71882 (patch)
tree8e932e12546bb991357ec48eb1638d1770be7a35 /maintenance
parent00ab76a6b686e98a914afc1975812d2b1aaa7016 (diff)
update to MediaWiki 1.16.0
Diffstat (limited to 'maintenance')
-rw-r--r--maintenance/7zip.inc69
-rw-r--r--maintenance/Doxyfile2
-rw-r--r--maintenance/FiveUpgrade.inc2
-rw-r--r--maintenance/Maintenance.php860
-rw-r--r--maintenance/README6
-rw-r--r--maintenance/addwiki.php412
-rw-r--r--maintenance/apache-ampersand.diff53
-rw-r--r--maintenance/archives/patch-change_tag-indexes.sql21
-rw-r--r--maintenance/archives/patch-eu_local_id.sql3
-rw-r--r--maintenance/archives/patch-external_user.sql9
-rw-r--r--maintenance/archives/patch-filearchive-user-index.sql (renamed from maintenance/archives/patch-filearhive-user-index.sql)0
-rw-r--r--maintenance/archives/patch-job.sql18
-rw-r--r--maintenance/archives/patch-l10n_cache.sql8
-rw-r--r--maintenance/archives/patch-log_search-rename-index.sql7
-rw-r--r--maintenance/archives/patch-log_search.sql10
-rw-r--r--maintenance/archives/patch-log_user_text.sql5
-rw-r--r--maintenance/archives/patch-mime_minor_length.sql10
-rw-r--r--maintenance/archives/patch-rd_interwiki.sql6
-rw-r--r--maintenance/archives/patch-tc-timestamp.sql4
-rw-r--r--maintenance/archives/patch-transcache.sql2
-rw-r--r--maintenance/archives/patch-user_properties.sql22
-rw-r--r--maintenance/archives/populateSha1.php59
-rw-r--r--maintenance/archives/rebuildRecentchanges.inc123
-rw-r--r--maintenance/archives/upgradeWatchlist.php67
-rw-r--r--maintenance/attachLatest.php96
-rw-r--r--maintenance/attribute.php106
-rw-r--r--maintenance/backup.inc18
-rw-r--r--maintenance/benchmarkPurge.php157
-rw-r--r--maintenance/changePassword.php78
-rw-r--r--maintenance/checkAutoLoader.php70
-rw-r--r--maintenance/checkBadRedirects.php75
-rw-r--r--maintenance/checkImages.php121
-rw-r--r--maintenance/checkSyntax.php296
-rw-r--r--maintenance/checkUsernames.php44
-rw-r--r--maintenance/cleanupCaps.php57
-rw-r--r--maintenance/cleanupImages.php88
-rw-r--r--maintenance/cleanupSpam.php211
-rw-r--r--maintenance/cleanupTable.inc161
-rw-r--r--maintenance/cleanupTitles.php67
-rw-r--r--maintenance/cleanupWatchlist.php60
-rw-r--r--maintenance/clear_interwiki_cache.php56
-rw-r--r--maintenance/clear_stats.php71
-rw-r--r--maintenance/commandLine.inc269
-rw-r--r--maintenance/convertLinks.inc6
-rw-r--r--maintenance/convertLinks.php239
-rw-r--r--maintenance/convertUserOptions.php72
-rw-r--r--maintenance/counter.php12
-rw-r--r--maintenance/createAndPromote.php111
-rw-r--r--maintenance/deleteArchivedFiles.inc56
-rw-r--r--maintenance/deleteArchivedFiles.php85
-rw-r--r--maintenance/deleteArchivedRevisions.inc34
-rw-r--r--maintenance/deleteArchivedRevisions.php71
-rw-r--r--maintenance/deleteBatch.php172
-rw-r--r--maintenance/deleteDefaultMessages.php90
-rw-r--r--maintenance/deleteImageMemcached.php73
-rw-r--r--maintenance/deleteOldRevisions.inc68
-rw-r--r--maintenance/deleteOldRevisions.php102
-rw-r--r--maintenance/deleteOrphanedRevisions.inc.php32
-rw-r--r--maintenance/deleteOrphanedRevisions.php118
-rw-r--r--maintenance/deleteRevision.php104
-rw-r--r--maintenance/deleteSelfExternals.php54
-rw-r--r--maintenance/doMaintenance.php102
-rw-r--r--maintenance/dumpBackup.php20
-rw-r--r--maintenance/dumpInterwiki.inc3
-rw-r--r--maintenance/dumpInterwiki.php7
-rw-r--r--maintenance/dumpLinks.php60
-rw-r--r--maintenance/dumpSisterSites.php47
-rw-r--r--maintenance/dumpTextPass.php80
-rw-r--r--maintenance/dumpUploads.php87
-rw-r--r--maintenance/edit.php144
-rw-r--r--maintenance/fetchInterwiki.pl102
-rw-r--r--maintenance/fetchText.php82
-rw-r--r--maintenance/findhooks.php285
-rw-r--r--maintenance/fixSlaveDesync.php335
-rw-r--r--maintenance/fixTimestamps.php200
-rw-r--r--maintenance/fixUserRegistration.php61
-rw-r--r--maintenance/fuzz-tester.php8
-rw-r--r--maintenance/gearman/gearman.inc2
-rw-r--r--maintenance/gearman/gearmanWorker.php2
-rw-r--r--maintenance/generateSitemap.php158
-rw-r--r--maintenance/getLagTimes.php59
-rw-r--r--maintenance/getSlaveServer.php60
-rw-r--r--maintenance/getText.php58
-rw-r--r--maintenance/httpSessionDownload.php57
-rw-r--r--maintenance/ibm_db2/README40
-rw-r--r--maintenance/ibm_db2/tables.sql551
-rw-r--r--maintenance/importDump.php13
-rw-r--r--maintenance/importImages.inc (renamed from maintenance/importImages.inc.php)30
-rw-r--r--maintenance/importImages.php168
-rw-r--r--maintenance/importLogs.inc144
-rw-r--r--maintenance/importLogs.php27
-rw-r--r--maintenance/importTextFile.php4
-rw-r--r--maintenance/importUseModWiki.php16
-rw-r--r--maintenance/initEditCount.php161
-rw-r--r--maintenance/initStats.inc57
-rw-r--r--maintenance/initStats.php87
-rw-r--r--maintenance/install-utils.inc219
-rw-r--r--maintenance/installExtension.php2
-rw-r--r--maintenance/interwiki.sql18
-rw-r--r--maintenance/lag.php51
-rw-r--r--maintenance/language/StatOutputs.php14
-rw-r--r--maintenance/language/alltrans.php40
-rw-r--r--maintenance/language/checkDupeMessages.php118
-rw-r--r--maintenance/language/checkExtensions.php4
-rw-r--r--maintenance/language/checkLanguage.inc39
-rw-r--r--maintenance/language/countMessages.php87
-rw-r--r--maintenance/language/date-formats.php102
-rw-r--r--maintenance/language/diffLanguage.php6
-rw-r--r--maintenance/language/digit2html.php68
-rw-r--r--maintenance/language/dumpMessages.php48
-rw-r--r--maintenance/language/generateNormalizerData.php137
-rw-r--r--maintenance/language/lang2po.php225
-rw-r--r--maintenance/language/langmemusage.php58
-rw-r--r--maintenance/language/languages.inc18
-rw-r--r--maintenance/language/makeMessageDB.php45
-rw-r--r--maintenance/language/messageTypes.inc25
-rw-r--r--maintenance/language/messages.inc520
-rw-r--r--maintenance/language/rebuildLanguage.php55
-rw-r--r--maintenance/language/transstat.php71
-rw-r--r--maintenance/language/writeMessagesArray.inc25
-rw-r--r--maintenance/mcc.php84
-rw-r--r--maintenance/mctest.php112
-rw-r--r--maintenance/mergeMessageFileList.php72
-rw-r--r--maintenance/migrateUserGroup.php70
-rw-r--r--maintenance/minify.php111
-rw-r--r--maintenance/moveBatch.php157
-rw-r--r--maintenance/mwdocgen.php15
-rw-r--r--maintenance/namespace2sql.php18
-rw-r--r--maintenance/namespaceDupes.php244
-rw-r--r--maintenance/nextJobDB.php127
-rw-r--r--maintenance/nukeNS.php158
-rw-r--r--maintenance/nukePage.inc91
-rw-r--r--maintenance/nukePage.php114
-rw-r--r--maintenance/ora/patch_seq_names_pre1.16.sql8
-rw-r--r--maintenance/ora/tables.sql1129
-rw-r--r--maintenance/ora/user.sql16
-rw-r--r--maintenance/orphans.php371
-rw-r--r--maintenance/ourusers.php1
-rw-r--r--maintenance/parserTests.inc731
-rw-r--r--maintenance/parserTests.php15
-rw-r--r--maintenance/parserTests.txt972
-rw-r--r--maintenance/patchSql.php69
-rw-r--r--maintenance/populateCategory.php128
-rw-r--r--maintenance/populateLogSearch.inc80
-rw-r--r--maintenance/populateLogSearch.php153
-rw-r--r--maintenance/populateLogUsertext.php82
-rw-r--r--maintenance/populateParentId.php119
-rw-r--r--maintenance/populateSha1.php101
-rw-r--r--maintenance/postgres/archives/patch-l10n_cache.sql8
-rw-r--r--maintenance/postgres/archives/patch-log_search.sql9
-rw-r--r--maintenance/postgres/archives/patch-update_sequences.sql20
-rw-r--r--maintenance/postgres/archives/patch-user_properties.sql8
-rw-r--r--maintenance/postgres/compare_schemas.pl15
-rw-r--r--maintenance/postgres/mediawiki_mysql2postgres.pl6
-rw-r--r--maintenance/postgres/tables.sql71
-rw-r--r--maintenance/preprocessorFuzzTest.php5
-rw-r--r--maintenance/protect.php68
-rw-r--r--maintenance/purgeList.php74
-rw-r--r--maintenance/purgeOldText.inc4
-rw-r--r--maintenance/purgeOldText.php46
-rw-r--r--maintenance/reassignEdits.inc.php143
-rw-r--r--maintenance/reassignEdits.php197
-rw-r--r--maintenance/rebuildFileCache.php179
-rw-r--r--maintenance/rebuildImages.php8
-rw-r--r--maintenance/rebuildInterwiki.inc1
-rw-r--r--maintenance/rebuildInterwiki.php3
-rw-r--r--maintenance/rebuildLocalisationCache.php133
-rw-r--r--maintenance/rebuildall.php78
-rw-r--r--maintenance/rebuildmessages.php56
-rw-r--r--maintenance/rebuildrecentchanges.inc246
-rw-r--r--maintenance/rebuildrecentchanges.php292
-rw-r--r--maintenance/rebuildtextindex.inc66
-rw-r--r--maintenance/rebuildtextindex.php132
-rw-r--r--maintenance/refreshImageCount.php65
-rw-r--r--maintenance/refreshLinks.inc202
-rw-r--r--maintenance/refreshLinks.php313
-rw-r--r--maintenance/removeUnusedAccounts.inc46
-rw-r--r--maintenance/removeUnusedAccounts.php150
-rw-r--r--maintenance/renameDbPrefix.php129
-rw-r--r--maintenance/renamewiki.php115
-rw-r--r--maintenance/renderDump.php75
-rw-r--r--maintenance/rollbackEdits.php97
-rw-r--r--maintenance/runBatchedQuery.php60
-rw-r--r--maintenance/runJobs.php141
-rw-r--r--maintenance/showJobs.php49
-rw-r--r--maintenance/showStats.php78
-rw-r--r--maintenance/sql.php108
-rw-r--r--maintenance/sqlite.php113
-rw-r--r--maintenance/sqlite/archives/initial-indexes.sql39
-rw-r--r--maintenance/sqlite/archives/patch-log_user_text.sql5
-rw-r--r--maintenance/sqlite/archives/patch-rd_interwiki.sql5
-rw-r--r--maintenance/sqlite/archives/patch-tc-timestamp.sql3
-rw-r--r--maintenance/sqlite/archives/searchindex-fts3.sql18
-rw-r--r--maintenance/sqlite/archives/searchindex-no-fts.sql25
-rw-r--r--maintenance/stats.php127
-rw-r--r--maintenance/storage/compressOld.inc6
-rw-r--r--maintenance/storage/compressOld.php2
-rw-r--r--maintenance/storage/dumpRev.php111
-rw-r--r--maintenance/storage/fixBug20757.php314
-rwxr-xr-xmaintenance/storage/make-blobs11
-rw-r--r--maintenance/storage/moveToExternal.php2
-rw-r--r--maintenance/storage/orphanStats.php43
-rw-r--r--maintenance/storage/recompressTracked.php57
-rw-r--r--maintenance/storage/resolveStubs.php10
-rw-r--r--maintenance/storage/storageTypeStats.php98
-rw-r--r--maintenance/storage/trackBlobs.php28
-rw-r--r--maintenance/tables.sql99
-rw-r--r--maintenance/testRunner.ora.sql37
-rw-r--r--maintenance/tests/.svnignore6
-rw-r--r--maintenance/tests/ApiSetup.php39
-rw-r--r--maintenance/tests/ApiTest.php164
-rw-r--r--maintenance/tests/CdbTest.php79
-rw-r--r--maintenance/tests/DatabaseSqliteTest.php57
-rw-r--r--maintenance/tests/DatabaseTest.php92
-rw-r--r--maintenance/tests/GlobalTest.php212
-rw-r--r--maintenance/tests/HttpTest.php567
-rw-r--r--maintenance/tests/IPTest.php52
-rw-r--r--maintenance/tests/ImageFunctionsTest.php48
-rw-r--r--maintenance/tests/LanguageConverterTest.php148
-rw-r--r--maintenance/tests/LicensesTest.php17
-rw-r--r--maintenance/tests/LocalFileTest.php97
-rw-r--r--maintenance/tests/Makefile23
-rw-r--r--maintenance/tests/MediaWikiParserTest.php283
-rw-r--r--maintenance/tests/MediaWiki_Setup.php28
-rw-r--r--maintenance/tests/README24
-rw-r--r--maintenance/tests/RevisionTest.php114
-rw-r--r--maintenance/tests/SanitizerTest.php73
-rw-r--r--maintenance/tests/SearchEngineTest.php138
-rw-r--r--maintenance/tests/SearchMySQLTest.php26
-rw-r--r--maintenance/tests/SearchUpdateTest.php103
-rw-r--r--maintenance/tests/SiteConfigurationTest.php311
-rw-r--r--maintenance/tests/TimeAdjustTest.php40
-rw-r--r--maintenance/tests/TitleTest.php17
-rw-r--r--maintenance/tests/XmlTest.php115
-rw-r--r--maintenance/tests/bootstrap.php15
-rw-r--r--maintenance/tests/phpunit.xml17
-rw-r--r--maintenance/tests/test-prefetch-current.xml75
-rw-r--r--maintenance/tests/test-prefetch-previous.xml57
-rw-r--r--maintenance/tests/test-prefetch-stub.xml75
-rw-r--r--maintenance/undelete.php51
-rw-r--r--maintenance/update.php56
-rw-r--r--maintenance/updateArticleCount.inc.php61
-rw-r--r--maintenance/updateArticleCount.php113
-rw-r--r--maintenance/updateRestrictions.php160
-rw-r--r--maintenance/updateSearchIndex.inc115
-rw-r--r--maintenance/updateSearchIndex.php186
-rw-r--r--maintenance/updateSpecialPages.php215
-rw-r--r--maintenance/updaters.inc532
-rw-r--r--maintenance/upgrade1_5.php2
-rw-r--r--maintenance/userOptions.inc10
-rw-r--r--maintenance/users.sql6
-rw-r--r--maintenance/waitForSlave.php35
-rw-r--r--maintenance/wikipedia-interwiki.sql7
253 files changed, 17566 insertions, 7782 deletions
diff --git a/maintenance/7zip.inc b/maintenance/7zip.inc
new file mode 100644
index 00000000..617083bf
--- /dev/null
+++ b/maintenance/7zip.inc
@@ -0,0 +1,69 @@
+<?php
+/**
+ * Stream wrapper around 7za filter program.
+ * Required since we can't pass an open file resource to XMLReader->open()
+ * which is used for the text prefetch.
+ *
+ * @ingroup Maintenance
+ */
+class SevenZipStream {
+ var $stream;
+
+ private function stripPath( $path ) {
+ $prefix = 'mediawiki.compress.7z://';
+ return substr( $path, strlen( $prefix ) );
+ }
+
+ function stream_open( $path, $mode, $options, &$opened_path ) {
+ if( $mode[0] == 'r' ) {
+ $options = 'e -bd -so';
+ } elseif( $mode[0] == 'w' ) {
+ $options = 'a -bd -si';
+ } else {
+ return false;
+ }
+ $arg = wfEscapeShellArg( $this->stripPath( $path ) );
+ $command = "7za $options $arg";
+ if( !wfIsWindows() ) {
+ // Suppress the stupid messages on stderr
+ $command .= ' 2>/dev/null';
+ }
+ $this->stream = popen( $command, $mode );
+ return ($this->stream !== false);
+ }
+
+ function url_stat( $path, $flags ) {
+ return stat( $this->stripPath( $path ) );
+ }
+
+ // This is all so lame; there should be a default class we can extend
+
+ function stream_close() {
+ return fclose( $this->stream );
+ }
+
+ function stream_flush() {
+ return fflush( $this->stream );
+ }
+
+ function stream_read( $count ) {
+ return fread( $this->stream, $count );
+ }
+
+ function stream_write( $data ) {
+ return fwrite( $this->stream, $data );
+ }
+
+ function stream_tell() {
+ return ftell( $this->stream );
+ }
+
+ function stream_eof() {
+ return feof( $this->stream );
+ }
+
+ function stream_seek( $offset, $whence ) {
+ return fseek( $this->stream, $offset, $whence );
+ }
+}
+stream_wrapper_register( 'mediawiki.compress.7z', 'SevenZipStream' ); \ No newline at end of file
diff --git a/maintenance/Doxyfile b/maintenance/Doxyfile
index cdc748d8..db737bff 100644
--- a/maintenance/Doxyfile
+++ b/maintenance/Doxyfile
@@ -135,7 +135,7 @@ FILE_PATTERNS = *.c \
RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS = YES
-EXCLUDE_PATTERNS = LocalSettings.php AdminSettings.php
+EXCLUDE_PATTERNS = LocalSettings.php AdminSettings.php .svn {{EXCLUDE}}
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
EXAMPLE_RECURSIVE = NO
diff --git a/maintenance/FiveUpgrade.inc b/maintenance/FiveUpgrade.inc
index b041a436..be0112e9 100644
--- a/maintenance/FiveUpgrade.inc
+++ b/maintenance/FiveUpgrade.inc
@@ -526,7 +526,7 @@ class FiveUpgrade {
} else {
global $IP;
$this->log( 'adding iw_trans...' );
- dbsource( $IP . '/maintenance/archives/patch-interwiki-trans.sql', $this->dbw );
+ $this->dbw->sourceFile( $IP . '/maintenance/archives/patch-interwiki-trans.sql' );
$this->log( 'added iw_trans.' );
}
diff --git a/maintenance/Maintenance.php b/maintenance/Maintenance.php
new file mode 100644
index 00000000..277278c0
--- /dev/null
+++ b/maintenance/Maintenance.php
@@ -0,0 +1,860 @@
+<?php
+/**
+ * @file
+ * @ingroup Maintenance
+ * @defgroup Maintenance Maintenance
+ */
+
+// Define this so scripts can easily find doMaintenance.php
+define( 'DO_MAINTENANCE', dirname( __FILE__ ) . '/doMaintenance.php' );
+$maintClass = false;
+
+// Make sure we're on PHP5 or better
+if( version_compare( PHP_VERSION, '5.0.0' ) < 0 ) {
+ echo( "Sorry! This version of MediaWiki requires PHP 5; you are running " .
+ PHP_VERSION . ".\n\n" .
+ "If you are sure you already have PHP 5 installed, it may be installed\n" .
+ "in a different path from PHP 4. Check with your system administrator.\n" );
+ die();
+}
+
+/**
+ * Abstract maintenance class for quickly writing and churning out
+ * maintenance scripts with minimal effort. All that _must_ be defined
+ * is the execute() method. See docs/maintenance.txt for more info
+ * and a quick demo of how to use it.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @author Chad Horohoe <chad@anyonecanedit.org>
+ * @since 1.16
+ * @ingroup Maintenance
+ */
+abstract class Maintenance {
+
+ /**
+ * Constants for DB access type
+ * @see Maintenance::getDbType()
+ */
+ const DB_NONE = 0;
+ const DB_STD = 1;
+ const DB_ADMIN = 2;
+
+ // Const for getStdin()
+ const STDIN_ALL = 'all';
+
+ // This is the desired params
+ protected $mParams = array();
+
+ // Array of desired args
+ protected $mArgList = array();
+
+ // This is the list of options that were actually passed
+ protected $mOptions = array();
+
+ // This is the list of arguments that were actually passed
+ protected $mArgs = array();
+
+ // Name of the script currently running
+ protected $mSelf;
+
+ // Special vars for params that are always used
+ protected $mQuiet = false;
+ protected $mDbUser, $mDbPass;
+
+ // A description of the script, children should change this
+ protected $mDescription = '';
+
+ // Have we already loaded our user input?
+ protected $mInputLoaded = false;
+
+ // Batch size. If a script supports this, they should set
+ // a default with setBatchSize()
+ protected $mBatchSize = null;
+
+ /**
+ * List of all the core maintenance scripts. This is added
+ * to scripts added by extensions in $wgMaintenanceScripts
+ * and returned by getMaintenanceScripts()
+ */
+ protected static $mCoreScripts = null;
+
+ /**
+ * Default constructor. Children should call this if implementing
+ * their own constructors
+ */
+ public function __construct() {
+ $this->addDefaultParams();
+ register_shutdown_function( array( $this, 'outputChanneled' ), false );
+ }
+
+ /**
+ * Do the actual work. All child classes will need to implement this
+ */
+ abstract public function execute();
+
+ /**
+ * Add a parameter to the script. Will be displayed on --help
+ * with the associated description
+ *
+ * @param $name String The name of the param (help, version, etc)
+ * @param $description String The description of the param to show on --help
+ * @param $required boolean Is the param required?
+ * @param $withArg Boolean Is an argument required with this option?
+ */
+ protected function addOption( $name, $description, $required = false, $withArg = false ) {
+ $this->mParams[$name] = array( 'desc' => $description, 'require' => $required, 'withArg' => $withArg );
+ }
+
+ /**
+ * Checks to see if a particular param exists.
+ * @param $name String The name of the param
+ * @return boolean
+ */
+ protected function hasOption( $name ) {
+ return isset( $this->mOptions[$name] );
+ }
+
+ /**
+ * Get an option, or return the default
+ * @param $name String The name of the param
+ * @param $default mixed Anything you want, default null
+ * @return mixed
+ */
+ protected function getOption( $name, $default = null ) {
+ if( $this->hasOption( $name ) ) {
+ return $this->mOptions[$name];
+ } else {
+ // Set it so we don't have to provide the default again
+ $this->mOptions[$name] = $default;
+ return $this->mOptions[$name];
+ }
+ }
+
+ /**
+ * Add some args that are needed
+ * @param $arg String Name of the arg, like 'start'
+ * @param $description String Short description of the arg
+ * @param $required Boolean Is this required?
+ */
+ protected function addArg( $arg, $description, $required = true ) {
+ $this->mArgList[] = array(
+ 'name' => $arg,
+ 'desc' => $description,
+ 'require' => $required
+ );
+ }
+
+ /**
+ * Does a given argument exist?
+ * @param $argId int The integer value (from zero) for the arg
+ * @return boolean
+ */
+ protected function hasArg( $argId = 0 ) {
+ return isset( $this->mArgs[$argId] );
+ }
+
+ /**
+ * Get an argument.
+ * @param $argId int The integer value (from zero) for the arg
+ * @param $default mixed The default if it doesn't exist
+ * @return mixed
+ */
+ protected function getArg( $argId = 0, $default = null ) {
+ return $this->hasArg( $argId ) ? $this->mArgs[$argId] : $default;
+ }
+
+ /**
+ * Set the batch size.
+ * @param $s int The number of operations to do in a batch
+ */
+ protected function setBatchSize( $s = 0 ) {
+ $this->mBatchSize = $s;
+ }
+
+ /**
+ * Get the script's name
+ * @return String
+ */
+ public function getName() {
+ return $this->mSelf;
+ }
+
+ /**
+ * Return input from stdin.
+ * @param $length int The number of bytes to read. If null,
+ * just return the handle. Maintenance::STDIN_ALL returns
+ * the full length
+ * @return mixed
+ */
+ protected function getStdin( $len = null ) {
+ if ( $len == Maintenance::STDIN_ALL )
+ return file_get_contents( 'php://stdin' );
+ $f = fopen( 'php://stdin', 'rt' );
+ if( !$len )
+ return $f;
+ $input = fgets( $f, $len );
+ fclose( $f );
+ return rtrim( $input );
+ }
+
+ /**
+ * Throw some output to the user. Scripts can call this with no fears,
+ * as we handle all --quiet stuff here
+ * @param $out String The text to show to the user
+ * @param $channel Mixed Unique identifier for the channel. See function outputChanneled.
+ */
+ protected function output( $out, $channel = null ) {
+ if( $this->mQuiet ) {
+ return;
+ }
+ if ( $channel === null ) {
+ $f = fopen( 'php://stdout', 'w' );
+ fwrite( $f, $out );
+ fclose( $f );
+ }
+ else {
+ $out = preg_replace( '/\n\z/', '', $out );
+ $this->outputChanneled( $out, $channel );
+ }
+ }
+
+ /**
+ * Throw an error to the user. Doesn't respect --quiet, so don't use
+ * this for non-error output
+ * @param $err String The error to display
+ * @param $die boolean If true, go ahead and die out.
+ */
+ protected function error( $err, $die = false ) {
+ $this->outputChanneled( false );
+ if ( php_sapi_name() == 'cli' ) {
+ fwrite( STDERR, $err . "\n" );
+ } else {
+ $f = fopen( 'php://stderr', 'w' );
+ fwrite( $f, $err . "\n" );
+ fclose( $f );
+ }
+ if( $die ) die();
+ }
+
+ private $atLineStart = true;
+ private $lastChannel = null;
+
+ /**
+ * Message outputter with channeled message support. Messages on the
+ * same channel are concatenated, but any intervening messages in another
+ * channel start a new line.
+ * @param $msg String The message without trailing newline
+ * @param $channel Channel identifier or null for no channel. Channel comparison uses ===.
+ */
+ public function outputChanneled( $msg, $channel = null ) {
+ $handle = fopen( 'php://stdout', 'w' );
+
+ if ( $msg === false ) {
+ // For cleanup
+ if ( !$this->atLineStart ) fwrite( $handle, "\n" );
+ fclose( $handle );
+ return;
+ }
+
+ // End the current line if necessary
+ if ( !$this->atLineStart && $channel !== $this->lastChannel ) {
+ fwrite( $handle, "\n" );
+ }
+
+ fwrite( $handle, $msg );
+
+ $this->atLineStart = false;
+ if ( $channel === null ) {
+ // For unchanneled messages, output trailing newline immediately
+ fwrite( $handle, "\n" );
+ $this->atLineStart = true;
+ }
+ $this->lastChannel = $channel;
+
+ // Cleanup handle
+ fclose( $handle );
+ }
+
+ /**
+ * Does the script need different DB access? By default, we give Maintenance
+ * scripts normal rights to the DB. Sometimes, a script needs admin rights
+ * access for a reason and sometimes they want no access. Subclasses should
+ * override and return one of the following values, as needed:
+ * Maintenance::DB_NONE - For no DB access at all
+ * Maintenance::DB_STD - For normal DB access, default
+ * Maintenance::DB_ADMIN - For admin DB access
+ * @return int
+ */
+ public function getDbType() {
+ return Maintenance::DB_STD;
+ }
+
+ /**
+ * Add the default parameters to the scripts
+ */
+ protected function addDefaultParams() {
+ $this->addOption( 'help', "Display this help message" );
+ $this->addOption( 'quiet', "Whether to supress non-error output" );
+ $this->addOption( 'conf', "Location of LocalSettings.php, if not default", false, true );
+ $this->addOption( 'wiki', "For specifying the wiki ID", false, true );
+ $this->addOption( 'globals', "Output globals at the end of processing for debugging" );
+ // If we support a DB, show the options
+ if( $this->getDbType() > 0 ) {
+ $this->addOption( 'dbuser', "The DB user to use for this script", false, true );
+ $this->addOption( 'dbpass', "The password to use for this script", false, true );
+ }
+ // If we support $mBatchSize, show the option
+ if( $this->mBatchSize ) {
+ $this->addOption( 'batch-size', 'Run this many operations ' .
+ 'per batch, default: ' . $this->mBatchSize , false, true );
+ }
+ }
+
+ /**
+ * Run a child maintenance script. Pass all of the current arguments
+ * to it.
+ * @param $maintClass String A name of a child maintenance class
+ * @param $classFile String Full path of where the child is
+ * @return Maintenance child
+ */
+ protected function runChild( $maintClass, $classFile = null ) {
+ // If we haven't already specified, kill setup procedures
+ // for child scripts, we've already got a sane environment
+ self::disableSetup();
+
+ // Make sure the class is loaded first
+ if( !class_exists( $maintClass ) ) {
+ if( $classFile ) {
+ require_once( $classFile );
+ }
+ if( !class_exists( $maintClass ) ) {
+ $this->error( "Cannot spawn child: $maintClass" );
+ }
+ }
+
+ $child = new $maintClass();
+ $child->loadParamsAndArgs( $this->mSelf, $this->mOptions, $this->mArgs );
+ return $child;
+ }
+
+ /**
+ * Disable Setup.php mostly
+ */
+ protected static function disableSetup() {
+ if( !defined( 'MW_NO_SETUP' ) )
+ define( 'MW_NO_SETUP', true );
+ }
+
+ /**
+ * Do some sanity checking and basic setup
+ */
+ public function setup() {
+ global $IP, $wgCommandLineMode, $wgRequestTime;
+
+ # Abort if called from a web server
+ if ( isset( $_SERVER ) && array_key_exists( 'REQUEST_METHOD', $_SERVER ) ) {
+ $this->error( "This script must be run from the command line", true );
+ }
+
+ # Make sure we can handle script parameters
+ if( !ini_get( 'register_argc_argv' ) ) {
+ $this->error( "Cannot get command line arguments, register_argc_argv is set to false", true );
+ }
+
+ if( version_compare( phpversion(), '5.2.4' ) >= 0 ) {
+ // Send PHP warnings and errors to stderr instead of stdout.
+ // This aids in diagnosing problems, while keeping messages
+ // out of redirected output.
+ if( ini_get( 'display_errors' ) ) {
+ ini_set( 'display_errors', 'stderr' );
+ }
+
+ // Don't touch the setting on earlier versions of PHP,
+ // as setting it would disable output if you'd wanted it.
+
+ // Note that exceptions are also sent to stderr when
+ // command-line mode is on, regardless of PHP version.
+ }
+
+ # Set the memory limit
+ # Note we need to set it again later in cache LocalSettings changed it
+ ini_set( 'memory_limit', $this->memoryLimit() );
+
+ # Set max execution time to 0 (no limit). PHP.net says that
+ # "When running PHP from the command line the default setting is 0."
+ # But sometimes this doesn't seem to be the case.
+ ini_set( 'max_execution_time', 0 );
+
+ $wgRequestTime = microtime( true );
+
+ # Define us as being in MediaWiki
+ define( 'MEDIAWIKI', true );
+
+ # Setup $IP, using MW_INSTALL_PATH if it exists
+ $IP = strval( getenv( 'MW_INSTALL_PATH' ) ) !== ''
+ ? getenv( 'MW_INSTALL_PATH' )
+ : realpath( dirname( __FILE__ ) . '/..' );
+
+ $wgCommandLineMode = true;
+ # Turn off output buffering if it's on
+ @ob_end_flush();
+
+ $this->loadParamsAndArgs();
+ $this->maybeHelp();
+ $this->validateParamsAndArgs();
+ }
+
+ /**
+ * Normally we disable the memory_limit when running admin scripts.
+ * Some scripts may wish to actually set a limit, however, to avoid
+ * blowing up unexpectedly.
+ */
+ public function memoryLimit() {
+ return -1;
+ }
+
+ /**
+ * Clear all params and arguments.
+ */
+ public function clearParamsAndArgs() {
+ $this->mOptions = array();
+ $this->mArgs = array();
+ $this->mInputLoaded = false;
+ }
+
+ /**
+ * Process command line arguments
+ * $mOptions becomes an array with keys set to the option names
+ * $mArgs becomes a zero-based array containing the non-option arguments
+ *
+ * @param $self String The name of the script, if any
+ * @param $opts Array An array of options, in form of key=>value
+ * @param $args Array An array of command line arguments
+ */
+ public function loadParamsAndArgs( $self = null, $opts = null, $args = null ) {
+ # If we were given opts or args, set those and return early
+ if( $self ) {
+ $this->mSelf = $self;
+ $this->mInputLoaded = true;
+ }
+ if( $opts ) {
+ $this->mOptions = $opts;
+ $this->mInputLoaded = true;
+ }
+ if( $args ) {
+ $this->mArgs = $args;
+ $this->mInputLoaded = true;
+ }
+
+ # If we've already loaded input (either by user values or from $argv)
+ # skip on loading it again. The array_shift() will corrupt values if
+ # it's run again and again
+ if( $this->mInputLoaded ) {
+ $this->loadSpecialVars();
+ return;
+ }
+
+ global $argv;
+ $this->mSelf = array_shift( $argv );
+
+ $options = array();
+ $args = array();
+
+ # Parse arguments
+ for( $arg = reset( $argv ); $arg !== false; $arg = next( $argv ) ) {
+ if ( $arg == '--' ) {
+ # End of options, remainder should be considered arguments
+ $arg = next( $argv );
+ while( $arg !== false ) {
+ $args[] = $arg;
+ $arg = next( $argv );
+ }
+ break;
+ } elseif ( substr( $arg, 0, 2 ) == '--' ) {
+ # Long options
+ $option = substr( $arg, 2 );
+ if ( isset( $this->mParams[$option] ) && $this->mParams[$option]['withArg'] ) {
+ $param = next( $argv );
+ if ( $param === false ) {
+ $this->error( "\nERROR: $option needs a value after it\n" );
+ $this->maybeHelp( true );
+ }
+ $options[$option] = $param;
+ } else {
+ $bits = explode( '=', $option, 2 );
+ if( count( $bits ) > 1 ) {
+ $option = $bits[0];
+ $param = $bits[1];
+ } else {
+ $param = 1;
+ }
+ $options[$option] = $param;
+ }
+ } elseif ( substr( $arg, 0, 1 ) == '-' ) {
+ # Short options
+ for ( $p=1; $p<strlen( $arg ); $p++ ) {
+ $option = $arg{$p};
+ if ( isset( $this->mParams[$option]['withArg'] ) && $this->mParams[$option]['withArg'] ) {
+ $param = next( $argv );
+ if ( $param === false ) {
+ $this->error( "\nERROR: $option needs a value after it\n" );
+ $this->maybeHelp( true );
+ }
+ $options[$option] = $param;
+ } else {
+ $options[$option] = 1;
+ }
+ }
+ } else {
+ $args[] = $arg;
+ }
+ }
+
+ $this->mOptions = $options;
+ $this->mArgs = $args;
+ $this->loadSpecialVars();
+ $this->mInputLoaded = true;
+ }
+
+ /**
+ * Run some validation checks on the params, etc
+ */
+ protected function validateParamsAndArgs() {
+ $die = false;
+ # Check to make sure we've got all the required options
+ foreach( $this->mParams as $opt => $info ) {
+ if( $info['require'] && !$this->hasOption( $opt ) ) {
+ $this->error( "Param $opt required!" );
+ $die = true;
+ }
+ }
+ # Check arg list too
+ foreach( $this->mArgList as $k => $info ) {
+ if( $info['require'] && !$this->hasArg($k) ) {
+ $this->error( "Argument <" . $info['name'] . "> required!" );
+ $die = true;
+ }
+ }
+
+ if( $die ) $this->maybeHelp( true );
+ }
+
+ /**
+ * Handle the special variables that are global to all scripts
+ */
+ protected function loadSpecialVars() {
+ if( $this->hasOption( 'dbuser' ) )
+ $this->mDbUser = $this->getOption( 'dbuser' );
+ if( $this->hasOption( 'dbpass' ) )
+ $this->mDbPass = $this->getOption( 'dbpass' );
+ if( $this->hasOption( 'quiet' ) )
+ $this->mQuiet = true;
+ if( $this->hasOption( 'batch-size' ) )
+ $this->mBatchSize = $this->getOption( 'batch-size' );
+ }
+
+ /**
+ * Maybe show the help.
+ * @param $force boolean Whether to force the help to show, default false
+ */
+ protected function maybeHelp( $force = false ) {
+ $screenWidth = 80; // TODO: Caculate this!
+ $tab = " ";
+ $descWidth = $screenWidth - ( 2 * strlen( $tab ) );
+
+ ksort( $this->mParams );
+ if( $this->hasOption( 'help' ) || $force ) {
+ $this->mQuiet = false;
+
+ if( $this->mDescription ) {
+ $this->output( "\n" . $this->mDescription . "\n" );
+ }
+ $output = "\nUsage: php " . basename( $this->mSelf );
+ if( $this->mParams ) {
+ $output .= " [--" . implode( array_keys( $this->mParams ), "|--" ) . "]";
+ }
+ if( $this->mArgList ) {
+ $output .= " <";
+ foreach( $this->mArgList as $k => $arg ) {
+ $output .= $arg['name'] . ">";
+ if( $k < count( $this->mArgList ) - 1 )
+ $output .= " <";
+ }
+ }
+ $this->output( "$output\n" );
+ foreach( $this->mParams as $par => $info ) {
+ $this->output( wordwrap( "$tab$par : " . $info['desc'], $descWidth,
+ "\n$tab$tab" ) . "\n" );
+ }
+ foreach( $this->mArgList as $info ) {
+ $this->output( wordwrap( "$tab<" . $info['name'] . "> : " .
+ $info['desc'], $descWidth, "\n$tab$tab" ) . "\n" );
+ }
+ die( 1 );
+ }
+ }
+
+ /**
+ * Handle some last-minute setup here.
+ */
+ public function finalSetup() {
+ global $wgCommandLineMode, $wgShowSQLErrors;
+ global $wgTitle, $wgProfiling, $IP, $wgDBadminuser, $wgDBadminpassword;
+ global $wgDBuser, $wgDBpassword, $wgDBservers, $wgLBFactoryConf;
+
+ # Turn off output buffering again, it might have been turned on in the settings files
+ if( ob_get_level() ) {
+ ob_end_flush();
+ }
+ # Same with these
+ $wgCommandLineMode = true;
+
+ # If these were passed, use them
+ if( $this->mDbUser )
+ $wgDBadminuser = $this->mDbUser;
+ if( $this->mDbPass )
+ $wgDBadminpassword = $this->mDbPass;
+
+ if ( $this->getDbType() == self::DB_ADMIN && isset( $wgDBadminuser ) ) {
+ $wgDBuser = $wgDBadminuser;
+ $wgDBpassword = $wgDBadminpassword;
+
+ if( $wgDBservers ) {
+ foreach ( $wgDBservers as $i => $server ) {
+ $wgDBservers[$i]['user'] = $wgDBuser;
+ $wgDBservers[$i]['password'] = $wgDBpassword;
+ }
+ }
+ if( isset( $wgLBFactoryConf['serverTemplate'] ) ) {
+ $wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser;
+ $wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword;
+ }
+ }
+
+ if ( defined( 'MW_CMDLINE_CALLBACK' ) ) {
+ $fn = MW_CMDLINE_CALLBACK;
+ $fn();
+ }
+
+ $wgShowSQLErrors = true;
+ @set_time_limit( 0 );
+ ini_set( 'memory_limit', $this->memoryLimit() );
+
+ $wgProfiling = false; // only for Profiler.php mode; avoids OOM errors
+ }
+
+ /**
+ * Potentially debug globals. Originally a feature only
+ * for refreshLinks
+ */
+ public function globals() {
+ if( $this->hasOption( 'globals' ) ) {
+ print_r( $GLOBALS );
+ }
+ }
+
+ /**
+ * Do setup specific to WMF
+ */
+ public function loadWikimediaSettings() {
+ global $IP, $wgNoDBParam, $wgUseNormalUser, $wgConf, $site, $lang;
+
+ if ( empty( $wgNoDBParam ) ) {
+ # Check if we were passed a db name
+ if ( isset( $this->mOptions['wiki'] ) ) {
+ $db = $this->mOptions['wiki'];
+ } else {
+ $db = array_shift( $this->mArgs );
+ }
+ list( $site, $lang ) = $wgConf->siteFromDB( $db );
+
+ # If not, work out the language and site the old way
+ if ( is_null( $site ) || is_null( $lang ) ) {
+ if ( !$db ) {
+ $lang = 'aa';
+ } else {
+ $lang = $db;
+ }
+ if ( isset( $this->mArgs[0] ) ) {
+ $site = array_shift( $this->mArgs );
+ } else {
+ $site = 'wikipedia';
+ }
+ }
+ } else {
+ $lang = 'aa';
+ $site = 'wikipedia';
+ }
+
+ # This is for the IRC scripts, which now run as the apache user
+ # The apache user doesn't have access to the wikiadmin_pass command
+ if ( $_ENV['USER'] == 'apache' ) {
+ #if ( posix_geteuid() == 48 ) {
+ $wgUseNormalUser = true;
+ }
+
+ putenv( 'wikilang=' . $lang );
+
+ $DP = $IP;
+ ini_set( 'include_path', ".:$IP:$IP/includes:$IP/languages:$IP/maintenance" );
+
+ if ( $lang == 'test' && $site == 'wikipedia' ) {
+ define( 'TESTWIKI', 1 );
+ }
+ }
+
+ /**
+ * Generic setup for most installs. Returns the location of LocalSettings
+ * @return String
+ */
+ public function loadSettings() {
+ global $wgWikiFarm, $wgCommandLineMode, $IP, $DP;
+
+ $wgWikiFarm = false;
+ if ( isset( $this->mOptions['conf'] ) ) {
+ $settingsFile = $this->mOptions['conf'];
+ } else {
+ $settingsFile = "$IP/LocalSettings.php";
+ }
+ if ( isset( $this->mOptions['wiki'] ) ) {
+ $bits = explode( '-', $this->mOptions['wiki'] );
+ if ( count( $bits ) == 1 ) {
+ $bits[] = '';
+ }
+ define( 'MW_DB', $bits[0] );
+ define( 'MW_PREFIX', $bits[1] );
+ }
+
+ if ( !is_readable( $settingsFile ) ) {
+ $this->error( "A copy of your installation's LocalSettings.php\n" .
+ "must exist and be readable in the source directory.", true );
+ }
+ $wgCommandLineMode = true;
+ $DP = $IP;
+ return $settingsFile;
+ }
+
+ /**
+ * Support function for cleaning up redundant text records
+ * @param $delete boolean Whether or not to actually delete the records
+ * @author Rob Church <robchur@gmail.com>
+ */
+ protected function purgeRedundantText( $delete = true ) {
+ # Data should come off the master, wrapped in a transaction
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin();
+
+ $tbl_arc = $dbw->tableName( 'archive' );
+ $tbl_rev = $dbw->tableName( 'revision' );
+ $tbl_txt = $dbw->tableName( 'text' );
+
+ # Get "active" text records from the revisions table
+ $this->output( "Searching for active text records in revisions table..." );
+ $res = $dbw->query( "SELECT DISTINCT rev_text_id FROM $tbl_rev" );
+ foreach( $res as $row ) {
+ $cur[] = $row->rev_text_id;
+ }
+ $this->output( "done.\n" );
+
+ # Get "active" text records from the archive table
+ $this->output( "Searching for active text records in archive table..." );
+ $res = $dbw->query( "SELECT DISTINCT ar_text_id FROM $tbl_arc" );
+ foreach( $res as $row ) {
+ $cur[] = $row->ar_text_id;
+ }
+ $this->output( "done.\n" );
+
+ # Get the IDs of all text records not in these sets
+ $this->output( "Searching for inactive text records..." );
+ $set = implode( ', ', $cur );
+ $res = $dbw->query( "SELECT old_id FROM $tbl_txt WHERE old_id NOT IN ( $set )" );
+ $old = array();
+ foreach( $res as $row ) {
+ $old[] = $row->old_id;
+ }
+ $this->output( "done.\n" );
+
+ # Inform the user of what we're going to do
+ $count = count( $old );
+ $this->output( "$count inactive items found.\n" );
+
+ # Delete as appropriate
+ if( $delete && $count ) {
+ $this->output( "Deleting..." );
+ $set = implode( ', ', $old );
+ $dbw->query( "DELETE FROM $tbl_txt WHERE old_id IN ( $set )" );
+ $this->output( "done.\n" );
+ }
+
+ # Done
+ $dbw->commit();
+ }
+
+ /**
+ * Get the maintenance directory.
+ */
+ protected function getDir() {
+ return dirname( __FILE__ );
+ }
+
+ /**
+ * Get the list of available maintenance scripts. Note
+ * that if you call this _before_ calling doMaintenance
+ * you won't have any extensions in it yet
+ * @return array
+ */
+ public static function getMaintenanceScripts() {
+ global $wgMaintenanceScripts;
+ return $wgMaintenanceScripts + self::getCoreScripts();
+ }
+
+ /**
+ * Return all of the core maintenance scripts
+ * @return array
+ */
+ protected static function getCoreScripts() {
+ if( !self::$mCoreScripts ) {
+ self::disableSetup();
+ $paths = array(
+ dirname( __FILE__ ),
+ dirname( __FILE__ ) . '/gearman',
+ dirname( __FILE__ ) . '/language',
+ dirname( __FILE__ ) . '/storage',
+ );
+ self::$mCoreScripts = array();
+ foreach( $paths as $p ) {
+ $handle = opendir( $p );
+ while( ( $file = readdir( $handle ) ) !== false ) {
+ if( $file == 'Maintenance.php' )
+ continue;
+ $file = $p . '/' . $file;
+ if( is_dir( $file ) || !strpos( $file, '.php' ) ||
+ ( strpos( file_get_contents( $file ), '$maintClass' ) === false ) ) {
+ continue;
+ }
+ require( $file );
+ $vars = get_defined_vars();
+ if( array_key_exists( 'maintClass', $vars ) ) {
+ self::$mCoreScripts[$vars['maintClass']] = $file;
+ }
+ }
+ closedir( $handle );
+ }
+ }
+ return self::$mCoreScripts;
+ }
+}
diff --git a/maintenance/README b/maintenance/README
index e2215c1d..d6e76917 100644
--- a/maintenance/README
+++ b/maintenance/README
@@ -10,8 +10,8 @@ proper installation.
Certain scripts will require elevated access to the database. In order to
provide this, first create a MySQL user with "all" permissions on the wiki
-database, and then place their username and password in an AdminSettings.php
-file in the directory above. See AdminSettings.sample for specifics on this.
+database, and then set $wgDBadminuser and $wgDBadminpassword in your
+LocalSettings.php
=== Brief explanation of files ===
@@ -94,7 +94,7 @@ installations.
Immediately complete all jobs in the job queue
stats.php
- Show all statistics stored in memcached
+ Show all statistics stored in the cache
undelete.php
Undelete all revisions of a page
diff --git a/maintenance/addwiki.php b/maintenance/addwiki.php
index ebe52f2e..0cb4d74a 100644
--- a/maintenance/addwiki.php
+++ b/maintenance/addwiki.php
@@ -1,104 +1,163 @@
<?php
/**
+ * @defgroup Wikimedia Wikimedia
+ */
+
+/**
* Add a new wiki
* Wikimedia specific!
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @file
* @ingroup Maintenance
+ * @ingroup Wikimedia
*/
-$wgNoDBParam = true;
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-require_once( "commandLine.inc" );
-require_once( "rebuildInterwiki.inc" );
-require_once( "languages/Names.php" );
-if ( count( $args ) != 3 ) {
- wfDie( "Usage: php addwiki.php <language> <site> <dbname>\nThe site for Wikipedia is 'wikipedia'.\n" );
-}
+class AddWiki extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Add a new wiki to the family. Wikimedia specific!";
+ $this->addArg( 'language', 'Language code of new site' );
+ $this->addArg( 'site', 'Type of site' );
+ $this->addArg( 'dbname', 'Name of database to create' );
+ }
-addWiki( $args[0], $args[1], $args[2] );
+ public function getDbType() {
+ return Maintenance::DB_ADMIN;
+ }
-# -----------------------------------------------------------------
+ public function execute() {
+ global $IP, $wgLanguageNames, $wgDefaultExternalStore, $wgNoDBParam;
-function addWiki( $lang, $site, $dbName )
-{
- global $IP, $wgLanguageNames, $wgDefaultExternalStore;
+ $wgNoDBParam = true;
+ $lang = $this->getArg(0);
+ $site = $this->getArg(1);
+ $dbName = $this->getArg(2);
- if ( !isset( $wgLanguageNames[$lang] ) ) {
- print "Language $lang not found in \$wgLanguageNames\n";
- return;
- }
- $name = $wgLanguageNames[$lang];
+ if ( !isset( $wgLanguageNames[$lang] ) ) {
+ $this->error( "Language $lang not found in \$wgLanguageNames", true );
+ }
+ $name = $wgLanguageNames[$lang];
- $dbw = wfGetDB( DB_MASTER );
- $common = "/home/wikipedia/common";
- $maintenance = "$IP/maintenance";
+ $dbw = wfGetDB( DB_MASTER );
+ $common = "/home/wikipedia/common";
- print "Creating database $dbName for $lang.$site ($name)\n";
-
- # Set up the database
- $dbw->query( "SET table_type=Innodb" );
- $dbw->query( "CREATE DATABASE $dbName" );
- $dbw->selectDB( $dbName );
-
- print "Initialising tables\n";
- dbsource( "$maintenance/tables.sql", $dbw );
- dbsource( "$IP/extensions/OAI/update_table.sql", $dbw );
- dbsource( "$IP/extensions/AntiSpoof/sql/patch-antispoof.mysql.sql", $dbw );
- dbsource( "$IP/extensions/CheckUser/cu_changes.sql", $dbw );
- dbsource( "$IP/extensions/CheckUser/cu_log.sql", $dbw );
- dbsource( "$IP/extensions/TitleKey/titlekey.sql", $dbw );
- dbsource( "$IP/extensions/Oversight/hidden.sql", $dbw );
- dbsource( "$IP/extensions/GlobalBlocking/localdb_patches/setup-global_block_whitelist.sql", $dbw );
-
- $dbw->query( "INSERT INTO site_stats(ss_row_id) VALUES (1)" );
-
- # Initialise external storage
- if ( is_array( $wgDefaultExternalStore ) ) {
- $stores = $wgDefaultExternalStore;
- } elseif ( $stores ) {
- $stores = array( $wgDefaultExternalStore );
- } else {
- $stores = array();
- }
- if ( count( $stores ) ) {
- require_once( 'ExternalStoreDB.php' );
- print "Initialising external storage $store...\n";
- global $wgDBuser, $wgDBpassword, $wgExternalServers;
- foreach ( $stores as $storeURL ) {
- $m = array();
- if ( !preg_match( '!^DB://(.*)$!', $storeURL, $m ) ) {
- continue;
+ $this->output( "Creating database $dbName for $lang.$site ($name)\n" );
+
+ # Set up the database
+ $dbw->query( "SET table_type=Innodb" );
+ $dbw->query( "CREATE DATABASE $dbName" );
+ $dbw->selectDB( $dbName );
+
+ $this->output( "Initialising tables\n" );
+ $dbw->sourceFile( $this->getDir() . '/tables.sql' );
+ $dbw->sourceFile( "$IP/extensions/OAI/update_table.sql" );
+ $dbw->sourceFile( "$IP/extensions/AntiSpoof/sql/patch-antispoof.mysql.sql" );
+ $dbw->sourceFile( "$IP/extensions/CheckUser/cu_changes.sql" );
+ $dbw->sourceFile( "$IP/extensions/CheckUser/cu_log.sql" );
+ $dbw->sourceFile( "$IP/extensions/TitleKey/titlekey.sql" );
+ $dbw->sourceFile( "$IP/extensions/Oversight/hidden.sql" );
+ $dbw->sourceFile( "$IP/extensions/GlobalBlocking/localdb_patches/setup-global_block_whitelist.sql" );
+ $dbw->sourceFile( "$IP/extensions/AbuseFilter/abusefilter.tables.sql" );
+ $dbw->sourceFile( "$IP/extensions/UsabilityInitiative/PrefStats/PrefStats.sql" );
+ $dbw->sourceFile( "$IP/extensions/ProofreadPage/ProofreadPage.sql" );
+ $dbw->sourceFile( "$IP/extensions/UsabilityInitiative/ClickTracking/ClickTrackingEvents.sql" );
+ $dbw->sourceFile( "$IP/extensions/UsabilityInitiative/ClickTracking/ClickTracking.sql" );
+ $dbw->sourceFile( "$IP/extensions/UsabilityInitiative/UserDailyContribs/UserDailyContribs.sql" );
+
+ $dbw->query( "INSERT INTO site_stats(ss_row_id) VALUES (1)" );
+
+ # Initialise external storage
+ if ( is_array( $wgDefaultExternalStore ) ) {
+ $stores = $wgDefaultExternalStore;
+ } elseif ( $stores ) {
+ $stores = array( $wgDefaultExternalStore );
+ } else {
+ $stores = array();
+ }
+ if ( count( $stores ) ) {
+ global $wgDBuser, $wgDBpassword, $wgExternalServers;
+ foreach ( $stores as $storeURL ) {
+ $m = array();
+ if ( !preg_match( '!^DB://(.*)$!', $storeURL, $m ) ) {
+ continue;
+ }
+
+ $cluster = $m[1];
+ $this->output( "Initialising external storage $cluster...\n" );
+
+ # Hack
+ $wgExternalServers[$cluster][0]['user'] = $wgDBuser;
+ $wgExternalServers[$cluster][0]['password'] = $wgDBpassword;
+
+ $store = new ExternalStoreDB;
+ $extdb = $store->getMaster( $cluster );
+ $extdb->query( "SET table_type=InnoDB" );
+ $extdb->query( "CREATE DATABASE $dbName" );
+ $extdb->selectDB( $dbName );
+
+ # Hack x2
+ $blobsTable = $store->getTable( $extdb );
+ $sedCmd = "sed s/blobs\\\\\\>/$blobsTable/ " . $this->getDir() . "/storage/blobs.sql";
+ $blobsFile = popen( $sedCmd, 'r' );
+ $extdb->sourceStream( $blobsFile );
+ pclose( $blobsFile );
+ $extdb->commit();
}
-
- $cluster = $m[1];
-
- # Hack
- $wgExternalServers[$cluster][0]['user'] = $wgDBuser;
- $wgExternalServers[$cluster][0]['password'] = $wgDBpassword;
-
- $store = new ExternalStoreDB;
- $extdb =& $store->getMaster( $cluster );
- $extdb->query( "SET table_type=InnoDB" );
- $extdb->query( "CREATE DATABASE $dbName" );
- $extdb->selectDB( $dbName );
- dbsource( "$maintenance/storage/blobs.sql", $extdb );
- $extdb->immediateCommit();
}
- }
- global $wgTitle, $wgArticle;
- $wgTitle = Title::newFromText( wfMsgWeirdKey( "mainpage/$lang" ) );
- print "Writing main page to " . $wgTitle->getPrefixedDBkey() . "\n";
- $wgArticle = new Article( $wgTitle );
- $ucsite = ucfirst( $site );
+ global $wgTitle, $wgArticle;
+ $wgTitle = Title::newFromText( wfMsgWeirdKey( "mainpage/$lang" ) );
+ $this->output( "Writing main page to " . $wgTitle->getPrefixedDBkey() . "\n" );
+ $wgArticle = new Article( $wgTitle );
+ $ucsite = ucfirst( $site );
+
+ $wgArticle->insertNewArticle( $this->getFirstArticle( $ucsite, $name ), '', false, false );
+
+ $this->output( "Adding to dblists\n" );
+
+ # Add to dblist
+ $file = fopen( "$common/all.dblist", "a" );
+ fwrite( $file, "$dbName\n" );
+ fclose( $file );
+
+ # Update the sublists
+ shell_exec("cd $common && ./refresh-dblist");
+
+ #print "Constructing interwiki SQL\n";
+ # Rebuild interwiki tables
+ #passthru( '/home/wikipedia/conf/interwiki/update' );
- $wgArticle->insertNewArticle( <<<EOT
+ $this->output( "Script ended. You still have to:
+ * Add any required settings in InitialiseSettings.php
+ * Run sync-common-all
+ * Run /home/wikipedia/conf/interwiki/update
+ " );
+ }
+
+ private function getFirstArticle( $ucsite, $name ) {
+ return <<<EOT
==This subdomain is reserved for the creation of a [[wikimedia:Our projects|$ucsite]] in '''[[w:en:{$name}|{$name}]]''' language==
* Please '''do not start editing''' this new site. This site has a test project on the [[incubator:|Wikimedia Incubator]] (or on the [[betawikiversity:|BetaWikiversity]] or on the [[oldwikisource:|Old Wikisource]]) and it will be imported to here.
-* If you would like to help translating the interface to this language, please do not translate here, but go to [[betawiki:|Betawiki]], a special wiki for translating the interface. That way everyone can use it on every wiki using the [[mw:|same software]].
+* If you would like to help translating the interface to this language, please do not translate here, but go to [[translatewiki:|translatewiki]], a special wiki for translating the interface. That way everyone can use it on every wiki using the [[mw:|same software]].
* For information about how to edit and for other general help, see [[m:Help:Contents|Help on Wikimedia's Meta-Wiki]] or [[mw:Help:Contents|Help on MediaWiki.org]].
@@ -116,135 +175,280 @@ function addWiki( $lang, $site, $dbName )
See Wikimedia's [[m:|Meta-Wiki]] for the coordination of these projects.
[[aa:]]
+[[ab:]]
+[[ace:]]
[[af:]]
+[[ak:]]
[[als:]]
+[[am:]]
+[[an:]]
+[[ang:]]
[[ar:]]
-[[de:]]
-[[en:]]
+[[arc:]]
+[[arz:]]
[[as:]]
[[ast:]]
+[[av:]]
[[ay:]]
[[az:]]
+[[ba:]]
+[[bar:]]
+[[bat-smg:]]
[[bcl:]]
[[be:]]
+[[be-x-old:]]
[[bg:]]
+[[bh:]]
+[[bi:]]
+[[bm:]]
[[bn:]]
[[bo:]]
+[[bpy:]]
+[[br:]]
[[bs:]]
-[[cs:]]
+[[bug:]]
+[[bxr:]]
+[[ca:]]
+[[cbk-zam:]]
+[[cdo:]]
+[[ce:]]
+[[ceb:]]
+[[ch:]]
+[[cho:]]
+[[chr:]]
+[[chy:]]
+[[ckb:]]
[[co:]]
+[[cr:]]
+[[crh:]]
[[cs:]]
+[[csb:]]
+[[cu:]]
+[[cv:]]
[[cy:]]
[[da:]]
+[[de:]]
+[[diq:]]
+[[dk:]]
+[[dsb:]]
+[[dv:]]
+[[dz:]]
+[[ee:]]
[[el:]]
+[[eml:]]
+[[en:]]
[[eo:]]
[[es:]]
[[et:]]
[[eu:]]
+[[ext:]]
[[fa:]]
+[[ff:]]
[[fi:]]
+[[fiu-vro:]]
+[[fj:]]
+[[fo:]]
[[fr:]]
+[[frp:]]
+[[fur:]]
[[fy:]]
[[ga:]]
+[[gan:]]
+[[gd:]]
[[gl:]]
+[[glk:]]
[[gn:]]
+[[got:]]
[[gu:]]
+[[gv:]]
+[[ha:]]
+[[hak:]]
+[[haw:]]
[[he:]]
[[hi:]]
+[[hif:]]
+[[ho:]]
[[hr:]]
[[hsb:]]
+[[ht:]]
+[[hu:]]
[[hy:]]
+[[hz:]]
[[ia:]]
[[id:]]
+[[ie:]]
+[[ig:]]
+[[ii:]]
+[[ik:]]
+[[ilo:]]
+[[io:]]
[[is:]]
[[it:]]
+[[iu:]]
[[ja:]]
+[[jbo:]]
+[[jv:]]
[[ka:]]
+[[kaa:]]
+[[kab:]]
+[[kg:]]
+[[ki:]]
+[[kj:]]
[[kk:]]
+[[kl:]]
[[km:]]
[[kn:]]
[[ko:]]
+[[kr:]]
[[ks:]]
+[[ksh:]]
[[ku:]]
+[[kv:]]
+[[kw:]]
[[ky:]]
[[la:]]
+[[lad:]]
+[[lb:]]
+[[lbe:]]
+[[lg:]]
+[[li:]]
+[[lij:]]
+[[lmo:]]
[[ln:]]
[[lo:]]
[[lt:]]
[[lv:]]
-[[hu:]]
+[[map-bms:]]
+[[mdf:]]
+[[mg:]]
+[[mh:]]
+[[mhr:]]
[[mi:]]
[[mk:]]
[[ml:]]
[[mn:]]
+[[mo:]]
[[mr:]]
[[ms:]]
[[mt:]]
+[[mus:]]
+[[mwl:]]
[[my:]]
+[[myv:]]
+[[mzn:]]
[[na:]]
-[[nah:]]
+[[nan:]]
+[[nap:]]
[[nds:]]
+[[nds-nl:]]
[[ne:]]
+[[new:]]
+[[ng:]]
[[nl:]]
+[[nn:]]
[[no:]]
+[[nov:]]
+[[nrm:]]
+[[nv:]]
+[[ny:]]
[[oc:]]
[[om:]]
+[[or:]]
+[[os:]]
[[pa:]]
+[[pag:]]
+[[pam:]]
+[[pap:]]
+[[pdc:]]
+[[pi:]]
+[[pih:]]
[[pl:]]
+[[pms:]]
+[[pnt:]]
+[[pnb:]]
[[ps:]]
[[pt:]]
[[qu:]]
+[[rm:]]
+[[rmy:]]
+[[rn:]]
[[ro:]]
+[[roa-rup:]]
+[[roa-tara:]]
[[ru:]]
+[[rw:]]
[[sa:]]
+[[sah:]]
+[[sc:]]
+[[scn:]]
+[[sco:]]
+[[sd:]]
+[[se:]]
+[[sg:]]
+[[sh:]]
[[si:]]
+[[simple:]]
[[sk:]]
[[sl:]]
+[[sm:]]
+[[sn:]]
+[[so:]]
[[sq:]]
[[sr:]]
+[[srn:]]
+[[ss:]]
+[[st:]]
+[[stq:]]
+[[su:]]
[[sv:]]
[[sw:]]
+[[szl:]]
[[ta:]]
[[te:]]
+[[tet:]]
[[tg:]]
[[th:]]
+[[ti:]]
[[tk:]]
[[tl:]]
+[[tn:]]
+[[to:]]
+[[tpi:]]
[[tr:]]
+[[ts:]]
[[tt:]]
+[[tum:]]
+[[tw:]]
+[[ty:]]
+[[udm:]]
[[ug:]]
[[uk:]]
[[ur:]]
[[uz:]]
+[[ve:]]
+[[vec:]]
[[vi:]]
+[[vls:]]
[[vo:]]
+[[wa:]]
+[[war:]]
+[[wo:]]
+[[wuu:]]
+[[xal:]]
[[xh:]]
+[[yi:]]
[[yo:]]
[[za:]]
+[[zea:]]
[[zh:]]
+[[zh-classical:]]
+[[zh-min-nan:]]
+[[zh-yue:]]
[[zu:]]
-EOT
-, '', false, false );
-
- print "Adding to dblists\n";
-
- # Add to dblist
- $file = fopen( "$common/all.dblist", "a" );
- fwrite( $file, "$dbName\n" );
- fclose( $file );
-
- # Update the sublists
- shell_exec("cd $common && ./refresh-dblist");
-
- #print "Constructing interwiki SQL\n";
- # Rebuild interwiki tables
- #passthru( '/home/wikipedia/conf/interwiki/update' );
-
- print "Script ended. You still have to:
-* Add any required settings in InitialiseSettings.php
-* Run sync-common-all
-* Run /home/wikipedia/conf/interwiki/update
-";
+EOT;
+ }
}
+$maintClass = "AddWiki";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/apache-ampersand.diff b/maintenance/apache-ampersand.diff
deleted file mode 100644
index f281ce15..00000000
--- a/maintenance/apache-ampersand.diff
+++ /dev/null
@@ -1,53 +0,0 @@
---- orig/apache_1.3.26/src/modules/standard/mod_rewrite.h Wed Mar 13 13:05:34 2002
-+++ apache_1.3.26/src/modules/standard/mod_rewrite.h Tue Oct 15 14:07:21 2002
-@@ -447,6 +447,7 @@
- static char *rewrite_mapfunc_toupper(request_rec *r, char *key);
- static char *rewrite_mapfunc_tolower(request_rec *r, char *key);
- static char *rewrite_mapfunc_escape(request_rec *r, char *key);
-+static char *rewrite_mapfunc_ampescape(request_rec *r, char *key);
- static char *rewrite_mapfunc_unescape(request_rec *r, char *key);
- static char *select_random_value_part(request_rec *r, char *value);
- static void rewrite_rand_init(void);
---- orig/apache_1.3.26/src/modules/standard/mod_rewrite.c Wed May 29 10:39:23 2002
-+++ apache_1.3.26/src/modules/standard/mod_rewrite.c Tue Oct 15 14:07:49 2002
-@@ -502,6 +502,9 @@
- else if (strcmp(a2+4, "unescape") == 0) {
- new->func = rewrite_mapfunc_unescape;
- }
-+ else if (strcmp(a2+4, "ampescape") == 0) {
-+ new->func = rewrite_mapfunc_ampescape;
-+ }
- else if (sconf->state == ENGINE_ENABLED) {
- return ap_pstrcat(cmd->pool, "RewriteMap: internal map not found:",
- a2+4, NULL);
-@@ -2982,6 +2985,30 @@
-
- value = ap_escape_uri(r->pool, key);
- return value;
-+}
-+
-+static char *rewrite_mapfunc_ampescape(request_rec *r, char *key)
-+{
-+ /* We only need to escape the ampersand */
-+ char *copy = ap_palloc(r->pool, 3 * strlen(key) + 3);
-+ const unsigned char *s = (const unsigned char *)key;
-+ unsigned char *d = (unsigned char *)copy;
-+ unsigned c;
-+
-+ while ((c = *s)) {
-+ if (c == '&') {
-+ *d++ = '%';
-+ *d++ = '2';
-+ *d++ = '6';
-+ }
-+ else {
-+ *d++ = c;
-+ }
-+ ++s;
-+ }
-+ *d = '\0';
-+
-+ return copy;
- }
-
- static char *rewrite_mapfunc_unescape(request_rec *r, char *key)
diff --git a/maintenance/archives/patch-change_tag-indexes.sql b/maintenance/archives/patch-change_tag-indexes.sql
new file mode 100644
index 00000000..9f8d8f80
--- /dev/null
+++ b/maintenance/archives/patch-change_tag-indexes.sql
@@ -0,0 +1,21 @@
+--
+-- Rename indexes on change_tag from implicit to explicit names
+--
+
+DROP INDEX ct_rc_id ON /*_*/change_tag;
+DROP INDEX ct_log_id ON /*_*/change_tag;
+DROP INDEX ct_rev_id ON /*_*/change_tag;
+DROP INDEX ct_tag ON /*_*/change_tag;
+
+DROP INDEX ts_rc_id ON /*_*/tag_summary;
+DROP INDEX ts_log_id ON /*_*/tag_summary;
+DROP INDEX ts_rev_id ON /*_*/tag_summary;
+
+CREATE UNIQUE INDEX /*i*/change_tag_rc_tag ON /*_*/change_tag (ct_rc_id,ct_tag);
+CREATE UNIQUE INDEX /*i*/change_tag_log_tag ON /*_*/change_tag (ct_log_id,ct_tag);
+CREATE UNIQUE INDEX /*i*/change_tag_rev_tag ON /*_*/change_tag (ct_rev_id,ct_tag);
+CREATE INDEX /*i*/change_tag_tag_id ON /*_*/change_tag (ct_tag,ct_rc_id,ct_rev_id,ct_log_id);
+
+CREATE UNIQUE INDEX /*i*/tag_summary_rc_id ON /*_*/tag_summary (ts_rc_id);
+CREATE UNIQUE INDEX /*i*/tag_summary_log_id ON /*_*/tag_summary (ts_log_id);
+CREATE UNIQUE INDEX /*i*/tag_summary_rev_id ON /*_*/tag_summary (ts_rev_id);
diff --git a/maintenance/archives/patch-eu_local_id.sql b/maintenance/archives/patch-eu_local_id.sql
new file mode 100644
index 00000000..bb59d067
--- /dev/null
+++ b/maintenance/archives/patch-eu_local_id.sql
@@ -0,0 +1,3 @@
+ALTER TABLE /*_*/external_user
+CHANGE COLUMN eu_wiki_id
+eu_local_id int unsigned NOT NULL;
diff --git a/maintenance/archives/patch-external_user.sql b/maintenance/archives/patch-external_user.sql
new file mode 100644
index 00000000..176b46d4
--- /dev/null
+++ b/maintenance/archives/patch-external_user.sql
@@ -0,0 +1,9 @@
+CREATE TABLE /*_*/external_user (
+ -- Foreign key to user_id
+ eu_local_id int unsigned NOT NULL PRIMARY KEY,
+
+ -- Some opaque identifier provided by the external database
+ eu_external_id varchar(255) binary NOT NULL
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/eu_external_id ON /*_*/external_user (eu_external_id);
diff --git a/maintenance/archives/patch-filearhive-user-index.sql b/maintenance/archives/patch-filearchive-user-index.sql
index c79000ad..c79000ad 100644
--- a/maintenance/archives/patch-filearhive-user-index.sql
+++ b/maintenance/archives/patch-filearchive-user-index.sql
diff --git a/maintenance/archives/patch-job.sql b/maintenance/archives/patch-job.sql
index a1f9094f..c9199efb 100644
--- a/maintenance/archives/patch-job.sql
+++ b/maintenance/archives/patch-job.sql
@@ -1,9 +1,9 @@
-
-- Jobs performed by parallel apache threads or a command-line daemon
-CREATE TABLE /*$wgDBprefix*/job (
- job_id int unsigned NOT NULL auto_increment,
+CREATE TABLE /*_*/job (
+ job_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
- -- Command name, currently only refreshLinks is defined
+ -- Command name
+ -- Limited to 60 to prevent key length overflow
job_cmd varbinary(60) NOT NULL default '',
-- Namespace and title to act on
@@ -12,9 +12,9 @@ CREATE TABLE /*$wgDBprefix*/job (
job_title varchar(255) binary NOT NULL,
-- Any other parameters to the command
- -- Presently unused, format undefined
- job_params blob NOT NULL,
-
- PRIMARY KEY job_id (job_id),
- KEY (job_cmd, job_namespace, job_title)
+ -- Stored as a PHP serialized array, or an empty string if there are no parameters
+ job_params blob NOT NULL
) /*$wgDBTableOptions*/;
+
+CREATE INDEX /*i*/job_cmd ON /*_*/job (job_cmd, job_namespace, job_title, job_params(128));
+
diff --git a/maintenance/archives/patch-l10n_cache.sql b/maintenance/archives/patch-l10n_cache.sql
new file mode 100644
index 00000000..4c865d71
--- /dev/null
+++ b/maintenance/archives/patch-l10n_cache.sql
@@ -0,0 +1,8 @@
+-- Table for storing localisation data
+CREATE TABLE /*_*/l10n_cache (
+ lc_lang varbinary(32) NOT NULL,
+ lc_key varchar(255) NOT NULL,
+ lc_value mediumblob NOT NULL
+) /*$wgDBTableOptions*/;
+CREATE INDEX /*i*/lc_lang_key ON /*_*/l10n_cache (lc_lang, lc_key);
+
diff --git a/maintenance/archives/patch-log_search-rename-index.sql b/maintenance/archives/patch-log_search-rename-index.sql
new file mode 100644
index 00000000..41e051d8
--- /dev/null
+++ b/maintenance/archives/patch-log_search-rename-index.sql
@@ -0,0 +1,7 @@
+-- Rename the primary unique index from PRIMARY to ls_field_val
+-- This is for MySQL only and is necessary only for databases which were updated
+-- between MW 1.16 development revisions r50567 and r51465.
+ALTER TABLE /*_*/log_search
+ DROP PRIMARY KEY,
+ ADD UNIQUE INDEX ls_field_val (ls_field,ls_value,ls_log_id);
+
diff --git a/maintenance/archives/patch-log_search.sql b/maintenance/archives/patch-log_search.sql
new file mode 100644
index 00000000..8d92030b
--- /dev/null
+++ b/maintenance/archives/patch-log_search.sql
@@ -0,0 +1,10 @@
+CREATE TABLE /*_*/log_search (
+ -- The type of ID (rev ID, log ID, rev timestamp, username)
+ ls_field varbinary(32) NOT NULL,
+ -- The value of the ID
+ ls_value varchar(255) NOT NULL,
+ -- Key to log_id
+ ls_log_id int unsigned NOT NULL default 0
+) /*$wgDBTableOptions*/;
+CREATE UNIQUE INDEX /*i*/ls_field_val ON /*_*/log_search (ls_field,ls_value,ls_log_id);
+CREATE INDEX /*i*/ls_log_id ON /*_*/log_search (ls_log_id);
diff --git a/maintenance/archives/patch-log_user_text.sql b/maintenance/archives/patch-log_user_text.sql
index dffbe8be..9a783d87 100644
--- a/maintenance/archives/patch-log_user_text.sql
+++ b/maintenance/archives/patch-log_user_text.sql
@@ -1,7 +1,8 @@
ALTER TABLE /*$wgDBprefix*/logging
ADD log_user_text varchar(255) binary NOT NULL default '',
- ADD log_target_id int unsigned NULL,
+ ADD log_page int unsigned NULL,
CHANGE log_type log_type varbinary(32) NOT NULL,
CHANGE log_action log_action varbinary(32) NOT NULL;
-CREATE INDEX /*i*/user_type_time ON /*_*/logging (log_user, log_type, log_timestamp);
+CREATE INDEX /*i*/log_user_type_time ON /*_*/logging (log_user, log_type, log_timestamp);
+CREATE INDEX /*i*/log_page_id_time ON /*_*/logging (log_page,log_timestamp);
diff --git a/maintenance/archives/patch-mime_minor_length.sql b/maintenance/archives/patch-mime_minor_length.sql
new file mode 100644
index 00000000..3a3c5c4f
--- /dev/null
+++ b/maintenance/archives/patch-mime_minor_length.sql
@@ -0,0 +1,10 @@
+ALTER TABLE /*_*/filearchive
+ MODIFY COLUMN fa_minor_mime varbinary(100) default "unknown";
+
+ALTER TABLE /*_*/image
+ MODIFY COLUMN img_minor_mime varbinary(100) NOT NULL default "unknown";
+
+ALTER TABLE /*_*/oldimage
+ MODIFY COLUMN oi_minor_mime varbinary(100) NOT NULL default "unknown";
+
+INSERT INTO /*_*/updatelog VALUES ('mime_minor_length'); \ No newline at end of file
diff --git a/maintenance/archives/patch-rd_interwiki.sql b/maintenance/archives/patch-rd_interwiki.sql
new file mode 100644
index 00000000..a12f1a7d
--- /dev/null
+++ b/maintenance/archives/patch-rd_interwiki.sql
@@ -0,0 +1,6 @@
+-- Add interwiki and fragment columns to redirect table
+
+ALTER TABLE /*$wgDBprefix*/redirect
+ ADD rd_interwiki varchar(32) default NULL,
+ ADD rd_fragment varchar(255) binary default NULL;
+
diff --git a/maintenance/archives/patch-tc-timestamp.sql b/maintenance/archives/patch-tc-timestamp.sql
new file mode 100644
index 00000000..4d90cf34
--- /dev/null
+++ b/maintenance/archives/patch-tc-timestamp.sql
@@ -0,0 +1,4 @@
+ALTER TABLE /*_*/transcache MODIFY tc_time binary(14);
+UPDATE /*_*/transcache SET tc_time = DATE_FORMAT(FROM_UNIXTIME(tc_time), "%Y%c%d%H%i%s");
+
+INSERT INTO /*_*/updatelog VALUES ('convert transcache field');
diff --git a/maintenance/archives/patch-transcache.sql b/maintenance/archives/patch-transcache.sql
index d0731697..70870efa 100644
--- a/maintenance/archives/patch-transcache.sql
+++ b/maintenance/archives/patch-transcache.sql
@@ -1,7 +1,7 @@
CREATE TABLE /*$wgDBprefix*/transcache (
tc_url varbinary(255) NOT NULL,
tc_contents TEXT,
- tc_time INT NOT NULL,
+ tc_time binary(14) NOT NULL,
UNIQUE INDEX tc_url_idx(tc_url)
) /*$wgDBTableOptions*/;
diff --git a/maintenance/archives/patch-user_properties.sql b/maintenance/archives/patch-user_properties.sql
new file mode 100644
index 00000000..e30e00dc
--- /dev/null
+++ b/maintenance/archives/patch-user_properties.sql
@@ -0,0 +1,22 @@
+--
+-- User preferences and perhaps other fun stuff. :)
+-- Replaces the old user.user_options blob, with a couple nice properties:
+--
+-- 1) We only store non-default settings, so changes to the defauls
+-- are now reflected for everybody, not just new accounts.
+-- 2) We can more easily do bulk lookups, statistics, or modifications of
+-- saved options since it's a sane table structure.
+--
+CREATE TABLE /*_*/user_properties(
+ -- Foreign key to user.user_id
+ up_user int not null,
+
+ -- Name of the option being saved. This is indexed for bulk lookup.
+ up_property varbinary(32) not null,
+
+ -- Property value as a string.
+ up_value blob
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/user_properties_user_property on /*_*/user_properties (up_user,up_property);
+CREATE INDEX /*i*/user_properties_property on /*_*/user_properties (up_property);
diff --git a/maintenance/archives/populateSha1.php b/maintenance/archives/populateSha1.php
deleted file mode 100644
index 487d3bad..00000000
--- a/maintenance/archives/populateSha1.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-/**
- * Optional upgrade script to populate the img_sha1 field
- *
- * @file
- * @ingroup MaintenanceArchive
- */
-
-$optionsWithArgs = array( 'method' );
-require_once( dirname(__FILE__).'/../commandLine.inc' );
-$method = isset( $options['method'] ) ? $options['method'] : 'normal';
-
-$t = -microtime( true );
-$fname = 'populateSha1.php';
-$dbw = wfGetDB( DB_MASTER );
-$res = $dbw->select( 'image', array( 'img_name' ), array( 'img_sha1' => '' ), $fname );
-$imageTable = $dbw->tableName( 'image' );
-$oldimageTable = $dbw->tableName( 'oldimage' );
-$batch = array();
-
-$cmd = 'mysql -u' . wfEscapeShellArg( $wgDBuser ) .
- ' -h' . wfEscapeShellArg( $wgDBserver ) .
- ' -p' . wfEscapeShellArg( $wgDBpassword, $wgDBname );
-if ( $method == 'pipe' ) {
- echo "Using pipe method\n";
- $pipe = popen( $cmd, 'w' );
-}
-
-$numRows = $res->numRows();
-$i = 0;
-foreach ( $res as $row ) {
- if ( $i % 100 == 0 ) {
- printf( "Done %d of %d, %5.3f%% \r", $i, $numRows, $i / $numRows * 100 );
- wfWaitForSlaves( 5 );
- }
- $file = wfLocalFile( $row->img_name );
- if ( !$file ) {
- continue;
- }
- $sha1 = File::sha1Base36( $file->getPath() );
- if ( strval( $sha1 ) !== '' ) {
- $sql = "UPDATE $imageTable SET img_sha1=" . $dbw->addQuotes( $sha1 ) .
- " WHERE img_name=" . $dbw->addQuotes( $row->img_name );
- if ( $method == 'pipe' ) {
- fwrite( $pipe, "$sql;\n" );
- } else {
- $dbw->query( $sql, $fname );
- }
- }
- $i++;
-}
-if ( $method == 'pipe' ) {
- fflush( $pipe );
- pclose( $pipe );
-}
-$t += microtime( true );
-printf( "\nDone %d files in %.1f seconds\n", $numRows, $t );
-
-?>
diff --git a/maintenance/archives/rebuildRecentchanges.inc b/maintenance/archives/rebuildRecentchanges.inc
deleted file mode 100644
index 65ba560c..00000000
--- a/maintenance/archives/rebuildRecentchanges.inc
+++ /dev/null
@@ -1,123 +0,0 @@
-<?php
-/**
- * Rebuild recent changes table
- *
- * @file
- * @deprecated
- * @ingroup MaintenanceArchive
- * @defgroup MaintenanceArchive MaintenanceArchive
- */
-
-/** */
-function rebuildRecentChangesTable()
-{
- $sql = "DROP TABLE IF EXISTS recentchanges";
- wfQuery( $sql );
-
- $sql = "CREATE TABLE recentchanges (
- rc_timestamp varchar(14) binary NOT NULL default '',
- rc_cur_time varchar(14) binary NOT NULL default '',
- rc_user int(10) unsigned NOT NULL default '0',
- rc_user_text varchar(255) binary NOT NULL default '',
- rc_namespace tinyint(3) unsigned NOT NULL default '0',
- rc_title varchar(255) binary NOT NULL default '',
- rc_comment varchar(255) binary NOT NULL default '',
- rc_minor tinyint(3) unsigned NOT NULL default '0',
- rc_new tinyint(3) unsigned NOT NULL default '0',
- rc_cur_id int(10) unsigned NOT NULL default '0',
- rc_this_oldid int(10) unsigned NOT NULL default '0',
- rc_last_oldid int(10) unsigned NOT NULL default '0',
- INDEX rc_cur_id (rc_cur_id),
- INDEX rc_cur_time (rc_cur_time),
- INDEX rc_timestamp (rc_timestamp),
- INDEX rc_namespace (rc_namespace),
- INDEX rc_title (rc_title)
-) ENGINE=MyISAM PACK_KEYS=1;";
- wfQuery( $sql );
-
- print( "Loading from CUR table...\n" );
-
- $sql = "INSERT INTO recentchanges (rc_timestamp,rc_cur_time,rc_user," .
- "rc_user_text,rc_namespace,rc_title,rc_comment,rc_minor,rc_new," .
- "rc_cur_id,rc_this_oldid,rc_last_oldid) SELECT cur_timestamp," .
- "cur_timestamp,cur_user,cur_user_text,cur_namespace,cur_title," .
- "cur_comment,cur_minor_edit,cur_is_new,cur_id,0,0 FROM cur " .
- "ORDER BY cur_timestamp DESC LIMIT 5000";
- wfQuery( $sql );
-
- print( "Loading from OLD table...\n" );
-
- $sql = "INSERT INTO recentchanges (rc_timestamp,rc_cur_time,rc_user," .
- "rc_user_text,rc_namespace,rc_title,rc_comment,rc_minor,rc_new," .
- "rc_cur_id,rc_this_oldid,rc_last_oldid) SELECT old_timestamp,''," .
- "old_user,old_user_text,old_namespace,old_title,old_comment," .
- "old_minor_edit,0,0,old_id,0 FROM old ORDER BY old_timestamp " .
- "DESC LIMIT 5000";
- wfQuery( $sql );
-
- $sql = "SELECT rc_timestamp FROM recentchanges " .
- "ORDER BY rc_timestamp DESC LIMIT 5000,1";
- $res = wfQuery( $sql );
- $obj = wfFetchObject( $res );
- $ts = $obj->rc_timestamp;
-
- $sql = "DELETE FROM recentchanges WHERE rc_timestamp < '{$ts}'";
- wfQuery( $sql );
-
- rebuildRecentChangesTablePass2();
-}
-
-function rebuildRecentChangesTablePass2()
-{
- $ns = $id = $count = 0;
- $title = $ct = "";
-
- print( "Updating links...\n" );
-
- $sql = "SELECT rc_namespace,rc_title,rc_timestamp FROM recentchanges " .
- "ORDER BY rc_namespace,rc_title,rc_timestamp DESC";
- $res = wfQuery( $sql );
-
- while ( $obj = wfFetchObject( $res ) ) {
- if ( ! ( $ns == $obj->rc_namespace &&
- 0 == strcmp( $title, wfStrencode( $obj->rc_title ) ) ) ) {
-
- $ns = $obj->rc_namespace;
- $title = wfStrencode( $obj->rc_title );
-
- $sql = "SELECT cur_id,cur_timestamp FROM cur WHERE " .
- "cur_namespace={$ns} AND cur_title='{$title}'";
- $res2 = wfQuery( $sql );
- $obj2 = wfFetchObject( $res2 );
-
- $id = $obj2->cur_id;
- $ct = $obj2->cur_timestamp;
- }
- $sql = "SELECT old_id FROM old WHERE old_namespace={$ns} " .
- "AND old_title='{$title}' AND old_timestamp < '" .
- "{$obj->rc_timestamp}' ORDER BY old_timestamp DESC LIMIT 1";
- $res2 = wfQuery( $sql );
-
- if ( 0 != wfNumRows( $res2 ) ) {
- $obj2 = wfFetchObject( $res2 );
-
- $sql = "UPDATE recentchanges SET rc_cur_id={$id},rc_cur_time=" .
- "'{$ct}',rc_last_oldid={$obj2->old_id} WHERE " .
- "rc_namespace={$ns} AND rc_title='{$title}' AND " .
- "rc_timestamp='{$obj->rc_timestamp}'";
- wfQuery( $sql );
- } else {
- $sql = "UPDATE recentchanges SET rc_cur_id={$id},rc_cur_time=" .
- "'{$ct}' WHERE rc_namespace={$ns} AND rc_title='{$title}' " .
- "AND rc_timestamp='{$obj->rc_timestamp}'";
- wfQuery( $sql );
- }
-
- if ( 0 == ( ++$count % 500 ) ) {
- printf( "%d records processed.\n", $count );
- }
- }
-}
-
-
-?>
diff --git a/maintenance/archives/upgradeWatchlist.php b/maintenance/archives/upgradeWatchlist.php
deleted file mode 100644
index 9788aa56..00000000
--- a/maintenance/archives/upgradeWatchlist.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-/**
- * @file
- * @deprecated
- * @ingroup MaintenanceArchive
- */
-
-/** */
-print "This script is obsolete!";
-print "It is retained in the source here in case some of its
-code might be useful for ad-hoc conversion tasks, but it is
-not maintained and probably won't even work as is.";
-exit();
-
-# Convert watchlists to new format
-
-global $IP;
-require_once( "../LocalSettings.php" );
-require_once( "$IP/Setup.php" );
-
-$wgTitle = Title::newFromText( "Rebuild links script" );
-set_time_limit(0);
-
-$wgDBuser = "wikiadmin";
-$wgDBpassword = $wgDBadminpassword;
-
-$sql = "DROP TABLE IF EXISTS watchlist";
-wfQuery( $sql, DB_MASTER );
-$sql = "CREATE TABLE watchlist (
- wl_user int(5) unsigned NOT NULL,
- wl_page int(8) unsigned NOT NULL,
- UNIQUE KEY (wl_user, wl_page)
-) ENGINE=MyISAM PACK_KEYS=1";
-wfQuery( $sql, DB_MASTER );
-
-$lc = new LinkCache;
-
-# Now, convert!
-$sql = "SELECT user_id,user_watch FROM user";
-$res = wfQuery( $sql, DB_SLAVE );
-$nu = wfNumRows( $res );
-$sql = "INSERT into watchlist (wl_user,wl_page) VALUES ";
-$i = $n = 0;
-while( $row = wfFetchObject( $res ) ) {
- $list = explode( "\n", $row->user_watch );
- $bits = array();
- foreach( $list as $title ) {
- if( $id = $lc->addLink( $title ) and ! $bits[$id]++) {
- $sql .= ($i++ ? "," : "") . "({$row->user_id},{$id})";
- }
- }
- if( ($n++ % 100) == 0 ) echo "$n of $nu users done...\n";
-}
-echo "$n users done.\n";
-if( $i ) {
- wfQuery( $sql, DB_MASTER );
-}
-
-
-# Add index
-# is this necessary?
-$sql = "ALTER TABLE watchlist
- ADD INDEX wl_user (wl_user),
- ADD INDEX wl_page (wl_page)";
-#wfQuery( $sql, DB_MASTER );
-
-
diff --git a/maintenance/attachLatest.php b/maintenance/attachLatest.php
index 8d680afa..67f3088b 100644
--- a/maintenance/attachLatest.php
+++ b/maintenance/attachLatest.php
@@ -1,8 +1,8 @@
<?php
-// quick hackjob to fix damages imports on wikisource
-// page records have page_latest wrong
-
/**
+ * quick hackjob to fix damages imports on wikisource
+ * page records have page_latest wrong
+ *
* Copyright (C) 2005 Brion Vibber <brion@pobox.com>
* http://www.mediawiki.org/
*
@@ -21,53 +21,61 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @file
* @ingroup Maintenance
*/
-require_once( 'commandLine.inc' );
-
-$fixit = isset( $options['fix'] );
-$fname = 'attachLatest';
-
-echo "Looking for pages with page_latest set to 0...\n";
-$dbw = wfGetDB( DB_MASTER );
-$result = $dbw->select( 'page',
- array( 'page_id', 'page_namespace', 'page_title' ),
- array( 'page_latest' => 0 ),
- $fname );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-$n = 0;
-while( $row = $dbw->fetchObject( $result ) ) {
- $pageId = intval( $row->page_id );
- $title = Title::makeTitle( $row->page_namespace, $row->page_title );
- $name = $title->getPrefixedText();
- $latestTime = $dbw->selectField( 'revision',
- 'MAX(rev_timestamp)',
- array( 'rev_page' => $pageId ),
- $fname );
- if( !$latestTime ) {
- echo wfWikiID()." $pageId [[$name]] can't find latest rev time?!\n";
- continue;
+class AttachLatest extends Maintenance {
+
+ public function __construct() {
+ parent::__construct();
+ $this->addOption( "fix", "Actually fix the entries, will dry run otherwise" );
+ $this->mDescription = "Fix page_latest entries in the page table";
}
+
+ public function execute() {
+ $this->output( "Looking for pages with page_latest set to 0...\n" );
+ $dbw = wfGetDB( DB_MASTER );
+ $result = $dbw->select( 'page',
+ array( 'page_id', 'page_namespace', 'page_title' ),
+ array( 'page_latest' => 0 ),
+ __METHOD__ );
- $revision = Revision::loadFromTimestamp( $dbw, $title, $latestTime );
- if( is_null( $revision ) ) {
- echo wfWikiID()." $pageId [[$name]] latest time $latestTime, can't find revision id\n";
- continue;
+ $n = 0;
+ foreach( $result as $row ) {
+ $pageId = intval( $row->page_id );
+ $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+ $name = $title->getPrefixedText();
+ $latestTime = $dbw->selectField( 'revision',
+ 'MAX(rev_timestamp)',
+ array( 'rev_page' => $pageId ),
+ __METHOD__ );
+ if( !$latestTime ) {
+ $this->output( wfWikiID()." $pageId [[$name]] can't find latest rev time?!\n" );
+ continue;
+ }
+
+ $revision = Revision::loadFromTimestamp( $dbw, $title, $latestTime );
+ if( is_null( $revision ) ) {
+ $this->output( wfWikiID()." $pageId [[$name]] latest time $latestTime, can't find revision id\n" );
+ continue;
+ }
+ $id = $revision->getId();
+ $this->output( wfWikiID()." $pageId [[$name]] latest time $latestTime, rev id $id\n" );
+ if( $this->hasOption('fix') ) {
+ $article = new Article( $title );
+ $article->updateRevisionOn( $dbw, $revision );
+ }
+ $n++;
+ }
+ $dbw->freeResult( $result );
+ $this->output( "Done! Processed $n pages.\n" );
+ if( !$this->hasOption('fix') ) {
+ $this->output( "This was a dry run; rerun with --fix to update page_latest.\n" );
+ }
}
- $id = $revision->getId();
- echo wfWikiID()." $pageId [[$name]] latest time $latestTime, rev id $id\n";
- if( $fixit ) {
- $article = new Article( $title );
- $article->updateRevisionOn( $dbw, $revision );
- }
- $n++;
-}
-$dbw->freeResult( $result );
-echo "Done! Processed $n pages.\n";
-if( !$fixit ) {
- echo "This was a dry run; rerun with --fix to update page_latest.\n";
}
-
+$maintClass = "AttachLatest";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/attribute.php b/maintenance/attribute.php
deleted file mode 100644
index 63f19435..00000000
--- a/maintenance/attribute.php
+++ /dev/null
@@ -1,106 +0,0 @@
-<?php
-/**
- * Script for re-attributing edits
- * Use reassingEdits.php
- *
- * @file
- * @ingroup Maintenance
- */
-
-/** */
-require_once( "commandLine.inc" );
-
-# Parameters
-if ( count( $args ) < 2 ) {
- print "Not enough parameters\n";
- if ( $wgWikiFarm ) {
- print "Usage: php attribute.php <language> <site> <source> <destination>\n";
- } else {
- print "Usage: php attribute.php <source> <destination>\n";
- }
- exit;
-}
-
-$source = $args[0];
-$dest = $args[1];
-
-$dbr = wfGetDB( DB_SLAVE );
-extract( $dbr->tableNames( 'page', 'revision','user' ));
-$eSource = $dbr->strencode( $source );
-$eDest = $dbr->strencode( $dest );
-
-# Get user id
-$res = $dbr->query( "SELECT user_id FROM $user WHERE user_name='$eDest'" );
-$row = $dbr->fetchObject( $res );
-if ( !$row ) {
- print "Warning: the target name \"$dest\" does not exist";
- $uid = 0;
-} else {
- $uid = $row->user_id;
-}
-
-# Initialise files
-$logfile = fopen( "attribute.log", "a" );
-$sqlfile = fopen( "attribute.sql", "a" );
-
-fwrite( $logfile, "* $source &rarr; $dest\n" );
-
-fwrite( $sqlfile,
-"-- Changing attribution SQL file
--- Generated with attribute.php
--- $source -> $dest ($uid)
-");
-
-$omitTitle = "Wikipedia:Changing_attribution_for_an_edit";
-
-# Get revisions
-print "\nPage revisions\n\n";
-
-$res = $dbr->query( "SELECT page_namespace, page_title, rev_id, rev_timestamp
-FROM $revision,$page
-WHERE rev_user_text='$eSource' and rev_page=page_id" );
-$row = $dbr->fetchObject( $res );
-
-if ( $row ) {
-/*
- if ( $row->old_title=='Votes_for_deletion' && $row->old_namespace == 4 ) {
- # We don't have that long
- break;
- }
-*/
- fwrite( $logfile, "**Revision IDs: " );
- fwrite( $sqlfile, "UPDATE $revision SET rev_user=$uid, rev_user_text='$eDest' WHERE rev_id IN (\n" );
-
- for ( $first=true; $row; $row = $dbr->fetchObject( $res ) ) {
- $title = Title::makeTitle( $row->page_namespace, $row->page_title );
- $fullTitle = $title->getPrefixedDbKey();
- if ( $fullTitle == $omitTitle ) {
- continue;
- }
-
- print "$fullTitle\n";
- $url = $title->getFullUrl( "oldid={$row->rev_id}" );
-
- # Output
- fwrite( $sqlfile, " " );
- if ( $first ) {
- $first = false;
- } else {
- fwrite( $sqlfile, ", " );
- fwrite( $logfile, ", " );
- }
-
- fwrite( $sqlfile, "{$row->rev_id} -- $url\n" );
- fwrite( $logfile, "[$url {$row->rev_id}]" );
-
- }
- fwrite( $sqlfile, ");\n" );
- fwrite( $logfile, "\n" );
-}
-
-print "\n";
-
-fclose( $sqlfile );
-fclose( $logfile );
-
-
diff --git a/maintenance/backup.inc b/maintenance/backup.inc
index e2e5363e..30bd0d88 100644
--- a/maintenance/backup.inc
+++ b/maintenance/backup.inc
@@ -232,13 +232,13 @@ class BackupDumper {
$this->startTime = wfTime();
}
+ /**
+ * @fixme the --server parameter is currently not respected, as it doesn't seem
+ * terribly easy to ask the load balancer for a particular connection by name.
+ */
function backupDb() {
- global $wgDBadminuser, $wgDBadminpassword;
- global $wgDBname, $wgDebugDumpSql, $wgDBtype;
- $flags = ($wgDebugDumpSql ? DBO_DEBUG : 0) | DBO_DEFAULT; // god-damn hack
-
- $class = 'Database' . ucfirst($wgDBtype);
- $db = new $class( $this->backupServer(), $wgDBadminuser, $wgDBadminpassword, $wgDBname, false, $flags );
+ $this->lb = wfGetLBFactory()->newMainLB();
+ $db = $this->lb->getConnection( DB_SLAVE, 'backup' );
// Discourage the server from disconnecting us if it takes a long time
// to read out the big ol' batch query.
@@ -246,6 +246,12 @@ class BackupDumper {
return $db;
}
+
+ function __destruct() {
+ if( isset( $this->lb ) ) {
+ $this->lb->closeAll();
+ }
+ }
function backupServer() {
global $wgDBserver;
diff --git a/maintenance/benchmarkPurge.php b/maintenance/benchmarkPurge.php
index 796e1da2..d167cf92 100644
--- a/maintenance/benchmarkPurge.php
+++ b/maintenance/benchmarkPurge.php
@@ -2,78 +2,105 @@
/**
* Squid purge benchmark script
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-/** */
-require_once( "commandLine.inc" );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-/**
- * Run a bunch of URLs through SquidUpdate::purge()
- * to benchmark Squid response times.
- * @param $urls array A bunch of URLs to purge
- * @param $trials int How many times to run the test?
- */
-function benchSquid( $urls, $trials = 1 ) {
- $start = wfTime();
- for( $i = 0; $i < $trials; $i++) {
- SquidUpdate::purge( $urls );
+class BenchmarkPurge extends Maintenance {
+
+ public function __construct() {
+ parent::__construct();
+ $this->addOption( "count", "How many URLs to feed to Squid for purging", false, true );
+ $this->mDescription = "Benchmark the Squid purge functions.";
}
- $delta = wfTime() - $start;
- $pertrial = $delta / $trials;
- $pertitle = $pertrial / count( $urls );
- return sprintf( "%4d titles in %6.2fms (%6.2fms each)",
- count( $urls ), $pertrial * 1000.0, $pertitle * 1000.0 );
-}
-
-/**
- * Get an array of randomUrl()'s.
- * @param $length int How many urls to add to the array
- */
-function randomUrlList( $length ) {
- $list = array();
- for( $i = 0; $i < $length; $i++ ) {
- $list[] = randomUrl();
+
+ public function execute() {
+ global $wgUseSquid;
+ if( !$wgUseSquid ) {
+ $this->error( "Squid purge benchmark doesn't do much without squid support on.". true );
+ } else {
+ $this->output( "There are " . count( $wgSquidServers ) . " defined squid servers:\n" );
+ if( $this->hasOption( 'count' ) ) {
+ $lengths = array( intval( $this->getOption('count') ) );
+ } else {
+ $lengths = array( 1, 10, 100 );
+ }
+ foreach( $lengths as $length ) {
+ $urls = $this->randomUrlList( $length );
+ $trial = $this->benchSquid( $urls );
+ $this->output( $trial . "\n" );
+ }
+ }
}
- return $list;
-}
-
-/**
- * Return a random URL of the wiki. Not necessarily an actual title in the
- * database, but at least a URL that looks like one.
- */
-function randomUrl() {
- global $wgServer, $wgArticlePath;
- return $wgServer . str_replace( '$1', randomTitle(), $wgArticlePath );
-}
-
-/**
- * Create a random title string (not necessarily a Title object).
- * For use with randomUrl().
- */
-function randomTitle() {
- $str = '';
- $length = mt_rand( 1, 20 );
- for( $i = 0; $i < $length; $i++ ) {
- $str .= chr( mt_rand( ord('a'), ord('z') ) );
+
+ /**
+ * Run a bunch of URLs through SquidUpdate::purge()
+ * to benchmark Squid response times.
+ * @param $urls array A bunch of URLs to purge
+ * @param $trials int How many times to run the test?
+ */
+ private function benchSquid( $urls, $trials = 1 ) {
+ $start = wfTime();
+ for( $i = 0; $i < $trials; $i++) {
+ SquidUpdate::purge( $urls );
+ }
+ $delta = wfTime() - $start;
+ $pertrial = $delta / $trials;
+ $pertitle = $pertrial / count( $urls );
+ return sprintf( "%4d titles in %6.2fms (%6.2fms each)",
+ count( $urls ), $pertrial * 1000.0, $pertitle * 1000.0 );
}
- return ucfirst( $str );
-}
-
-if( !$wgUseSquid ) {
- wfDie( "Squid purge benchmark doesn't do much without squid support on.\n" );
-} else {
- printf( "There are %d defined squid servers:\n", count( $wgSquidServers ) );
- #echo implode( "\n", $wgSquidServers ) . "\n";
- if( isset( $options['count'] ) ) {
- $lengths = array( intval( $options['count'] ) );
- } else {
- $lengths = array( 1, 10, 100 );
+
+ /**
+ * Get an array of randomUrl()'s.
+ * @param $length int How many urls to add to the array
+ */
+ private function randomUrlList( $length ) {
+ $list = array();
+ for( $i = 0; $i < $length; $i++ ) {
+ $list[] = $this->randomUrl();
+ }
+ return $list;
}
- foreach( $lengths as $length ) {
- $urls = randomUrlList( $length );
- $trial = benchSquid( $urls );
- print "$trial\n";
+
+ /**
+ * Return a random URL of the wiki. Not necessarily an actual title in the
+ * database, but at least a URL that looks like one.
+ */
+ private function randomUrl() {
+ global $wgServer, $wgArticlePath;
+ return $wgServer . str_replace( '$1', $this->randomTitle(), $wgArticlePath );
+ }
+
+ /**
+ * Create a random title string (not necessarily a Title object).
+ * For use with randomUrl().
+ */
+ private function randomTitle() {
+ $str = '';
+ $length = mt_rand( 1, 20 );
+ for( $i = 0; $i < $length; $i++ ) {
+ $str .= chr( mt_rand( ord('a'), ord('z') ) );
+ }
+ return ucfirst( $str );
}
}
+
+$maintClass = "BenchmarkPurge";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/changePassword.php b/maintenance/changePassword.php
index 0fe8c0be..fbc3fa76 100644
--- a/maintenance/changePassword.php
+++ b/maintenance/changePassword.php
@@ -2,55 +2,51 @@
/**
* Change the password of a given user
*
- * @file
- * @ingroup Maintenance
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
*
* @author Ævar Arnfjörð Bjarmason <avarab@gmail.com>
* @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
- */
-
-$optionsWithArgs = array( 'user', 'password' );
-require_once 'commandLine.inc';
-
-$USAGE =
- "Usage: php changePassword.php [--user=user --password=password | --help]\n" .
- "\toptions:\n" .
- "\t\t--help show this message\n" .
- "\t\t--user the username to operate on\n" .
- "\t\t--password the password to use\n";
-
-if( in_array( '--help', $argv ) )
- wfDie( $USAGE );
-
-$cp = new ChangePassword( @$options['user'], @$options['password'] );
-$cp->main();
-
-/**
* @ingroup Maintenance
*/
-class ChangePassword {
- var $dbw;
- var $user, $password;
-
- function ChangePassword( $user, $password ) {
- global $USAGE;
- if( !strlen( $user ) or !strlen( $password ) ) {
- wfDie( $USAGE );
- }
-
- $this->user = User::newFromName( $user );
- if ( !$this->user->getId() ) {
- die ( "No such user: $user\n" );
- }
- $this->password = $password;
+require_once( dirname(__FILE__) . '/Maintenance.php' );
- $this->dbw = wfGetDB( DB_MASTER );
+class ChangePassword extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->addOption( "user", "The username to operate on", true, true );
+ $this->addOption( "password", "The password to use", true, true );
+ $this->mDescription = "Change a user's password";
}
-
- function main() {
- $this->user->setPassword( $this->password );
- $this->user->saveSettings();
+
+ public function execute() {
+ $user = User::newFromName( $this->getOption('user') );
+ if( !$user->getId() ) {
+ $this->error( "No such user: " . $this->getOption('user'), true );
+ }
+ try {
+ $user->setPassword( $this->getOption('password') );
+ $user->saveSettings();
+ $this->output( "Password set for " . $user->getName() . "\n" );
+ } catch( PasswordError $pwe ) {
+ $this->error( $pwe->getText(), true );
+ }
}
}
+
+$maintClass = "ChangePassword";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/checkAutoLoader.php b/maintenance/checkAutoLoader.php
index 554395ca..9c8f29e3 100644
--- a/maintenance/checkAutoLoader.php
+++ b/maintenance/checkAutoLoader.php
@@ -1,29 +1,57 @@
<?php
-if ( php_sapi_name() != 'cli' ) exit;
+/**
+ * Check the autoloader
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @ingroup Maintenance
+ */
-$IP = dirname(__FILE__) .'/..';
-require( "$IP/includes/AutoLoader.php" );
-$files = array_unique( $wgAutoloadLocalClasses );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-foreach ( $files as $file ) {
- if( function_exists( 'parsekit_compile_file' ) ){
- $parseInfo = parsekit_compile_file( "$IP/$file" );
- $classes = array_keys( $parseInfo['class_table'] );
- } else {
- $contents = file_get_contents( "$IP/$file" );
- $m = array();
- preg_match_all( '/\n\s*class\s+([a-zA-Z0-9_]+)/', $contents, $m, PREG_PATTERN_ORDER );
- $classes = $m[1];
+class CheckAutoLoader extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "AutoLoader sanity checks";
}
- foreach ( $classes as $class ) {
- if ( !isset( $wgAutoloadLocalClasses[$class] ) ) {
- //printf( "%-50s Unlisted, in %s\n", $class, $file );
- echo " '$class' => '$file',\n";
- } elseif ( $wgAutoloadLocalClasses[$class] !== $file ) {
- echo "$class: Wrong file: found in $file, listed in " . $wgAutoloadLocalClasses[$class] . "\n";
+ public function execute() {
+ global $wgAutoloadLocalClasses, $IP;
+ $files = array_unique( $wgAutoloadLocalClasses );
+
+ foreach( $files as $file ) {
+ if( function_exists( 'parsekit_compile_file' ) ){
+ $parseInfo = parsekit_compile_file( "$IP/$file" );
+ $classes = array_keys( $parseInfo['class_table'] );
+ } else {
+ $contents = file_get_contents( "$IP/$file" );
+ $m = array();
+ preg_match_all( '/\n\s*class\s+([a-zA-Z0-9_]+)/', $contents, $m, PREG_PATTERN_ORDER );
+ $classes = $m[1];
+ }
+ foreach ( $classes as $class ) {
+ if ( !isset( $wgAutoloadLocalClasses[$class] ) ) {
+ //printf( "%-50s Unlisted, in %s\n", $class, $file );
+ $this->output( "\t'$class' => '$file',\n" );
+ } elseif ( $wgAutoloadLocalClasses[$class] !== $file ) {
+ $this->output( "$class: Wrong file: found in $file, listed in " . $wgAutoloadLocalClasses[$class] . "\n" );
+ }
+ }
}
}
-
}
-
+$maintClass = "CheckAutoLoader";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/checkBadRedirects.php b/maintenance/checkBadRedirects.php
index 48a4b0e6..32f04f45 100644
--- a/maintenance/checkBadRedirects.php
+++ b/maintenance/checkBadRedirects.php
@@ -1,30 +1,59 @@
<?php
+/**
+ * CheckBadRedirects - See if pages marked as being redirects
+ * really are.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @ingroup Maintenance
+ */
+
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-require "commandLine.inc";
-
-echo "Fetching redirects...\n";
-$dbr = wfGetDB( DB_SLAVE );
-$result = $dbr->select(
- array( 'page' ),
- array( 'page_namespace','page_title', 'page_latest' ),
- array( 'page_is_redirect' => 1 ) );
-
-$count = $result->numRows();
-echo "Found $count total redirects.\n";
-echo "Looking for bad redirects:\n";
-echo "\n";
+class CheckBadRedirects extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Look for bad redirects";
+ }
-foreach( $result as $row ) {
- $title = Title::makeTitle( $row->page_namespace, $row->page_title );
- $rev = Revision::newFromId( $row->page_latest );
- if( $rev ) {
- $target = Title::newFromRedirect( $rev->getText() );
- if( !$target ) {
- echo $title->getPrefixedText();
- echo "\n";
+ public function execute() {
+ $this->output( "Fetching redirects...\n" );
+ $dbr = wfGetDB( DB_SLAVE );
+ $result = $dbr->select(
+ array( 'page' ),
+ array( 'page_namespace','page_title', 'page_latest' ),
+ array( 'page_is_redirect' => 1 ) );
+
+ $count = $result->numRows();
+ $this->output( "Found $count total redirects.\n" .
+ "Looking for bad redirects:\n\n" );
+
+ foreach( $result as $row ) {
+ $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+ $rev = Revision::newFromId( $row->page_latest );
+ if( $rev ) {
+ $target = Title::newFromRedirect( $rev->getText() );
+ if( !$target ) {
+ $this->output( $title->getPrefixedText() . "\n" );
+ }
+ }
}
+ $this->output( "\ndone.\n" );
}
}
-echo "\n";
-echo "done.\n";
+$maintClass = "CheckBadRedirects";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/checkImages.php b/maintenance/checkImages.php
index 378caa34..5dcaac28 100644
--- a/maintenance/checkImages.php
+++ b/maintenance/checkImages.php
@@ -1,51 +1,82 @@
<?php
+/**
+ * Check images to see if they exist, are readable, etc etc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @ingroup Maintenance
+ */
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-require( 'commandLine.inc' );
+class CheckImages extends Maintenance {
-$batchSize = 1000;
-$start = '';
-$dbr = wfGetDB( DB_SLAVE );
-$localRepo = RepoGroup::singleton()->getLocalRepo();
-
-$numImages = 0;
-$numGood = 0;
-
-do {
- $res = $dbr->select( 'image', '*', array( 'img_name > ' . $dbr->addQuotes( $start ) ),
- 'checkImages.php', array( 'LIMIT' => $batchSize ) );
- foreach ( $res as $row ) {
- $numImages++;
- $start = $row->img_name;
- $file = $localRepo->newFileFromRow( $row );
- $path = $file->getPath();
- if ( !$path ) {
- echo "{$row->img_name}: not locally accessible\n";
- continue;
- }
- $stat = @stat( $file->getPath() );
- if ( !$stat ) {
- echo "{$row->img_name}: missing\n";
- continue;
- }
-
- if ( $stat['mode'] & 040000 ) {
- echo "{$row->img_name}: is a directory\n";
- continue;
- }
-
- if ( $stat['size'] == 0 && $row->img_size != 0 ) {
- echo "{$row->img_name}: truncated, was {$row->img_size}\n";
- continue;
- }
-
- if ( $stat['size'] != $row->img_size ) {
- echo "{$row->img_name}: size mismatch DB={$row->img_size}, actual={$stat['size']}\n";
- continue;
- }
-
- $numGood++;
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Check images to see if they exist, are readable, etc";
+ $this->setBatchSize( 1000 );
}
+
+ public function execute() {
+ $start = '';
+ $dbr = wfGetDB( DB_SLAVE );
-} while ( $res->numRows() );
+ $numImages = 0;
+ $numGood = 0;
+
+ do {
+ $res = $dbr->select( 'image', '*', array( 'img_name > ' . $dbr->addQuotes( $start ) ),
+ __METHOD__, array( 'LIMIT' => $this->mBatchSize ) );
+ foreach ( $res as $row ) {
+ $numImages++;
+ $start = $row->img_name;
+ $file = RepoGroup::singleton()->getLocalRepo()->newFileFromRow( $row );
+ $path = $file->getPath();
+ if ( !$path ) {
+ $this->output( "{$row->img_name}: not locally accessible\n" );
+ continue;
+ }
+ $stat = @stat( $file->getPath() );
+ if ( !$stat ) {
+ $this->output( "{$row->img_name}: missing\n" );
+ continue;
+ }
+
+ if ( $stat['mode'] & 040000 ) {
+ $this->output( "{$row->img_name}: is a directory\n" );
+ continue;
+ }
+
+ if ( $stat['size'] == 0 && $row->img_size != 0 ) {
+ $this->output( "{$row->img_name}: truncated, was {$row->img_size}\n" );
+ continue;
+ }
+
+ if ( $stat['size'] != $row->img_size ) {
+ $this->output( "{$row->img_name}: size mismatch DB={$row->img_size}, actual={$stat['size']}\n" );
+ continue;
+ }
+
+ $numGood++;
+ }
+
+ } while ( $res->numRows() );
+
+ $this->output( "Good images: $numGood/$numImages\n" );
+ }
+}
-echo "Good images: $numGood/$numImages\n";
+$maintClass = "CheckImages";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/checkSyntax.php b/maintenance/checkSyntax.php
new file mode 100644
index 00000000..22832dce
--- /dev/null
+++ b/maintenance/checkSyntax.php
@@ -0,0 +1,296 @@
+<?php
+/**
+ * Check syntax of all PHP files in MediaWiki
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @ingroup Maintenance
+ */
+
+require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+
+class CheckSyntax extends Maintenance {
+
+ // List of files we're going to check
+ private $mFiles = array(), $mFailures = array(), $mWarnings = array();
+ private $mIgnorePaths = array(), $mNoStyleCheckPaths = array();
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Check syntax for all PHP files in MediaWiki";
+ $this->addOption( 'with-extensions', 'Also recurse the extensions folder' );
+ $this->addOption( 'path', 'Specific path (file or directory) to check, either with absolute path or relative to the root of this MediaWiki installation',
+ false, true);
+ $this->addOption( 'list-file', 'Text file containing list of files or directories to check', false, true);
+ $this->addOption( 'modified', 'Check only files that were modified (requires SVN command-line client)' );
+ $this->addOption( 'syntax-only', 'Check for syntax validity only, skip code style warnings' );
+ }
+
+ public function getDbType() {
+ return Maintenance::DB_NONE;
+ }
+
+ public function execute() {
+ $this->buildFileList();
+
+ // ParseKit is broken on PHP 5.3+, disabled until this is fixed
+ $useParseKit = function_exists( 'parsekit_compile_file' ) && version_compare( PHP_VERSION, '5.3', '<' );
+
+ $str = 'Checking syntax (using ' . ( $useParseKit ?
+ 'parsekit)' : ' php -l, this can take a long time)' );
+ $this->output( $str );
+ foreach( $this->mFiles as $f ) {
+ if( $useParseKit ) {
+ $this->checkFileWithParsekit( $f );
+ } else {
+ $this->checkFileWithCli( $f );
+ }
+ if( !$this->hasOption( 'syntax-only' ) ) {
+ $this->checkForMistakes( $f );
+ }
+ }
+ $this->output( "\nDone! " . count( $this->mFiles ) . " files checked, " .
+ count( $this->mFailures ) . " failures and " . count( $this->mWarnings ) .
+ " warnings found\n" );
+ }
+
+ /**
+ * Build the list of files we'll check for syntax errors
+ */
+ private function buildFileList() {
+ global $IP;
+
+ $this->mIgnorePaths = array(
+ // Compat stuff, explodes on PHP 5.3
+ "includes/NamespaceCompat.php$",
+ "DiscussionThreading/REV",
+ );
+
+ $this->mNoStyleCheckPaths = array(
+ // Third-party code we don't care about
+ "/activemq_stomp/",
+ "EmailPage/phpMailer",
+ "FCKeditor/fckeditor/",
+ '\bphplot-',
+ "/svggraph/",
+ "\bjsmin.php$",
+ "OggHandler/PEAR/",
+ "QPoll/Excel/",
+ "/geshi/",
+ "/smarty/",
+ );
+
+ if ( $this->hasOption( 'path' ) ) {
+ $path = $this->getOption( 'path' );
+ if ( !$this->addPath( $path ) ) {
+ $this->error( "Error: can't find file or directory $path\n", true );
+ }
+ return; // process only this path
+ } elseif ( $this->hasOption( 'list-file' ) ) {
+ $file = $this->getOption( 'list-file' );
+ $f = @fopen( $file, 'r' );
+ if ( !$f ) {
+ $this->error( "Can't open file $file\n", true );
+ }
+ while( $path = trim( fgets( $f ) ) ) {
+ $this->addPath( $path );
+ }
+ fclose( $f );
+ return;
+ } elseif ( $this->hasOption( 'modified' ) ) {
+ $this->output( "Retrieving list from Subversion... " );
+ $parentDir = wfEscapeShellArg( dirname( __FILE__ ) . '/..' );
+ $output = wfShellExec( "svn status --ignore-externals $parentDir", $retval );
+ if ( $retval ) {
+ $this->error( "Error retrieving list from Subversion!\n", true );
+ } else {
+ $this->output( "done\n" );
+ }
+
+ preg_match_all( '/^\s*[AM].{7}(.*?)\r?$/m', $output, $matches );
+ foreach ( $matches[1] as $file ) {
+ if ( self::isSuitableFile( $file ) && !is_dir( $file ) ) {
+ $this->mFiles[] = $file;
+ }
+ }
+ return;
+ }
+
+ $this->output( 'Building file list...', 'listfiles' );
+
+ // Only check files in these directories.
+ // Don't just put $IP, because the recursive dir thingie goes into all subdirs
+ $dirs = array(
+ $IP . '/includes',
+ $IP . '/config',
+ $IP . '/languages',
+ $IP . '/maintenance',
+ $IP . '/skins',
+ );
+ if( $this->hasOption( 'with-extensions' ) ) {
+ $dirs[] = $IP . '/extensions';
+ }
+
+ foreach( $dirs as $d ) {
+ $this->addDirectoryContent( $d );
+ }
+
+ // Manually add two user-editable files that are usually sources of problems
+ if ( file_exists( "$IP/LocalSettings.php" ) ) {
+ $this->mFiles[] = "$IP/LocalSettings.php";
+ }
+ if ( file_exists( "$IP/AdminSettings.php" ) ) {
+ $this->mFiles[] = "$IP/AdminSettings.php";
+ }
+
+ $this->output( 'done.', 'listfiles' );
+ }
+
+ /**
+ * Returns true if $file is of a type we can check
+ */
+ private function isSuitableFile( $file ) {
+ $ext = pathinfo( $file, PATHINFO_EXTENSION );
+ if ( $ext != 'php' && $ext != 'inc' && $ext != 'php5' )
+ return false;
+ foreach( $this->mIgnorePaths as $regex ) {
+ $m = array();
+ if ( preg_match( "~{$regex}~", $file, $m ) )
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Add given path to file list, searching it in include path if needed
+ */
+ private function addPath( $path ) {
+ global $IP;
+ return $this->addFileOrDir( $path ) || $this->addFileOrDir( "$IP/$path" );
+ }
+
+ /**
+ * Add given file to file list, or, if it's a directory, add its content
+ */
+ private function addFileOrDir( $path ) {
+ if ( is_dir( $path ) ) {
+ $this->addDirectoryContent( $path );
+ } elseif ( file_exists( $path ) ) {
+ $this->mFiles[] = $path;
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Add all suitable files in given directory or its subdirectories to the file list
+ *
+ * @param $dir String: directory to process
+ */
+ private function addDirectoryContent( $dir ) {
+ $iterator = new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator( $dir ),
+ RecursiveIteratorIterator::SELF_FIRST
+ );
+ foreach ( $iterator as $file ) {
+ if ( $this->isSuitableFile( $file->getRealPath() ) ) {
+ $this->mFiles[] = $file->getRealPath();
+ }
+ }
+ }
+
+ /**
+ * Check a file for syntax errors using Parsekit. Shamelessly stolen
+ * from tools/lint.php by TimStarling
+ * @param $file String Path to a file to check for syntax errors
+ * @return boolean
+ */
+ private function checkFileWithParsekit( $file ) {
+ static $okErrors = array(
+ 'Redefining already defined constructor',
+ 'Assigning the return value of new by reference is deprecated',
+ );
+ $errors = array();
+ parsekit_compile_file( $file, $errors, PARSEKIT_SIMPLE );
+ $ret = true;
+ if ( $errors ) {
+ foreach ( $errors as $error ) {
+ foreach ( $okErrors as $okError ) {
+ if ( substr( $error['errstr'], 0, strlen( $okError ) ) == $okError ) {
+ continue 2;
+ }
+ }
+ $ret = false;
+ $this->output( "Error in $file line {$error['lineno']}: {$error['errstr']}\n" );
+ $this->mFailures[$file] = $errors;
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Check a file for syntax errors using php -l
+ * @param $file String Path to a file to check for syntax errors
+ * @return boolean
+ */
+ private function checkFileWithCli( $file ) {
+ $res = exec( 'php -l ' . wfEscapeShellArg( $file ) );
+ if( strpos( $res, 'No syntax errors detected' ) === false ) {
+ $this->mFailures[$file] = $res;
+ $this->output( $res . "\n" );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Check a file for non-fatal coding errors, such as byte-order marks in the beginning
+ * or pointless ?> closing tags at the end.
+ *
+ * @param $file String String Path to a file to check for errors
+ * @return boolean
+ */
+ private function checkForMistakes( $file ) {
+ foreach( $this->mNoStyleCheckPaths as $regex ) {
+ $m = array();
+ if ( preg_match( "~{$regex}~", $file, $m ) )
+ return;
+ }
+
+ $text = file_get_contents( $file );
+
+ $this->checkRegex( $file, $text, '/^[\s\r\n]+<\?/', 'leading whitespace' );
+ $this->checkRegex( $file, $text, '/\?>[\s\r\n]*$/', 'trailing ?>' );
+ $this->checkRegex( $file, $text, '/^[\xFF\xFE\xEF]/', 'byte-order mark' );
+ }
+
+ private function checkRegex( $file, $text, $regex, $desc ) {
+ if ( !preg_match( $regex, $text ) ) {
+ return;
+ }
+
+ if ( !isset( $this->mWarnings[$file] ) ) {
+ $this->mWarnings[$file] = array();
+ }
+ $this->mWarnings[$file][] = $desc;
+ $this->output( "Warning in file $file: $desc found.\n" );
+ }
+}
+
+$maintClass = "CheckSyntax";
+require_once( DO_MAINTENANCE );
+
diff --git a/maintenance/checkUsernames.php b/maintenance/checkUsernames.php
index 77565b99..85a3d157 100644
--- a/maintenance/checkUsernames.php
+++ b/maintenance/checkUsernames.php
@@ -3,40 +3,52 @@
* This script verifies that database usernames are actually valid.
* An existing usernames can become invalid if User::isValidUserName()
* is altered or if we change the $wgMaxNameChars
- * @file
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-error_reporting(E_ALL ^ E_NOTICE);
-require_once 'commandLine.inc';
-class checkUsernames {
- var $stderr, $log;
+require_once( dirname(__FILE__) . '/Maintenance.php' );
- function checkUsernames() {
- $this->stderr = fopen( 'php://stderr', 'wt' );
+class CheckUsernames extends Maintenance {
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Verify that database usernames are actually valid";
}
- function main() {
- $fname = 'checkUsernames::main';
+ function execute() {
$dbr = wfGetDB( DB_SLAVE );
$res = $dbr->select( 'user',
array( 'user_id', 'user_name' ),
null,
- $fname
+ __METHOD__
);
- while ( $row = $dbr->fetchObject( $res ) ) {
+ foreach ( $res as $row ) {
if ( ! User::isValidUserName( $row->user_name ) ) {
- $out = sprintf( "%s: %6d: '%s'\n", wfWikiID(), $row->user_id, $row->user_name );
- fwrite( $this->stderr, $out );
+ $this->error( sprintf( "%s: %6d: '%s'\n", wfWikiID(), $row->user_id, $row->user_name ) );
wfDebugLog( 'checkUsernames', $out );
}
}
}
}
-$cun = new checkUsernames();
-$cun->main();
-
+$maintClass = "CheckUsernames";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/cleanupCaps.php b/maintenance/cleanupCaps.php
index 46ca18d6..6a48ea83 100644
--- a/maintenance/cleanupCaps.php
+++ b/maintenance/cleanupCaps.php
@@ -24,38 +24,35 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @file
* @author Brion Vibber <brion at pobox.com>
* @ingroup maintenance
*/
-$optionsWithArgs = array( 'namespace' );
+require_once( dirname(__FILE__) . '/cleanupTable.inc' );
-require_once( 'commandLine.inc' );
-require_once( 'cleanupTable.inc' );
-
-/**
- * @ingroup Maintenance
- */
class CapsCleanup extends TableCleanup {
- function __construct( $dryrun = false, $namespace = 0 ) {
- parent::__construct( 'page', $dryrun );
- $this->namespace = intval( $namespace );
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Script to cleanup capitalization";
+ $this->addOption( 'namespace', 'Namespace number to run caps cleanup on', false, true );
}
- function cleanup() {
- global $wgCapitalLinks;
- if( $wgCapitalLinks ) {
- echo "\$wgCapitalLinks is on -- no need for caps links cleanup.\n";
- return false;
- }
+ public function execute() {
+ global $wgCapitalLinks, $wgUser;
+ $this->namespace = intval( $this->getOption( 'namespace', 0 ) );
+ $this->dryrun = $this->hasOption( 'dry-run' );
+ $wgUser->setName( 'Conversion script' );
+ if( $wgCapitalLinks )
+ $this->error( "\$wgCapitalLinks is on -- no need for caps links cleanup.", true );
- $this->runTable( $this->targetTable,
- 'WHERE page_namespace=' . $this->namespace,
- array( &$this, 'processPage' ) );
+ $this->runTable( array(
+ 'table' => 'page',
+ 'conds' => array( 'page_namespace' => $this->namespace ),
+ 'index' => 'page_id',
+ 'callback' => 'processRow' ) );
}
- function processPage( $row ) {
+ protected function processRow( $row ) {
global $wgContLang;
$current = Title::makeTitle( $row->page_namespace, $row->page_title );
@@ -63,42 +60,38 @@ class CapsCleanup extends TableCleanup {
$upper = $row->page_title;
$lower = $wgContLang->lcfirst( $row->page_title );
if( $upper == $lower ) {
- $this->log( "\"$display\" already lowercase." );
+ $this->output( "\"$display\" already lowercase.\n" );
return $this->progress( 0 );
}
$target = Title::makeTitle( $row->page_namespace, $lower );
$targetDisplay = $target->getPrefixedText();
if( $target->exists() ) {
- $this->log( "\"$display\" skipped; \"$targetDisplay\" already exists" );
+ $this->output( "\"$display\" skipped; \"$targetDisplay\" already exists\n" );
return $this->progress( 0 );
}
if( $this->dryrun ) {
- $this->log( "\"$display\" -> \"$targetDisplay\": DRY RUN, NOT MOVED" );
+ $this->output( "\"$display\" -> \"$targetDisplay\": DRY RUN, NOT MOVED\n" );
$ok = true;
} else {
$ok = $current->moveTo( $target, false, 'Converting page titles to lowercase' );
- $this->log( "\"$display\" -> \"$targetDisplay\": $ok" );
+ $this->output( "\"$display\" -> \"$targetDisplay\": $ok\n" );
}
if( $ok === true ) {
$this->progress( 1 );
-
if( $row->page_namespace == $this->namespace ) {
$talk = $target->getTalkPage();
$row->page_namespace = $talk->getNamespace();
if( $talk->exists() ) {
- return $this->processPage( $row );
+ return $this->processRow( $row );
}
}
} else {
$this->progress( 0 );
}
}
-
}
-$wgUser->setName( 'Conversion script' );
-$ns = isset( $options['namespace'] ) ? $options['namespace'] : 0;
-$caps = new CapsCleanup( isset( $options['dry-run'] ), $ns );
-$caps->cleanup();
+$maintClass = "CapsCleanup";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/cleanupImages.php b/maintenance/cleanupImages.php
index 00903f22..db13f4c9 100644
--- a/maintenance/cleanupImages.php
+++ b/maintenance/cleanupImages.php
@@ -24,23 +24,26 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @file
* @author Brion Vibber <brion at pobox.com>
* @ingroup Maintenance
*/
-require_once( 'commandLine.inc' );
-require_once( 'cleanupTable.inc' );
+require_once( dirname(__FILE__) . '/cleanupTable.inc' );
-/**
- * @ingroup Maintenance
- */
class ImageCleanup extends TableCleanup {
- function __construct( $dryrun = false ) {
- parent::__construct( 'image', $dryrun );
+ protected $defaultParams = array(
+ 'table' => 'image',
+ 'conds' => array(),
+ 'index' => 'img_name',
+ 'callback' => 'processRow',
+ );
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Script to clean up broken, unparseable upload filenames";
}
- function processPage( $row ) {
+ protected function processRow( $row ) {
global $wgContLang;
$source = $row->img_name;
@@ -62,12 +65,12 @@ class ImageCleanup extends TableCleanup {
$cleaned = $wgContLang->checkTitleEncoding( $cleaned );
// Many of remainder look like non-normalized unicode
- $cleaned = UtfNormal::cleanUp( $cleaned );
+ $cleaned = $wgContLang->normalize( $cleaned );
$title = Title::makeTitleSafe( NS_FILE, $cleaned );
if( is_null( $title ) ) {
- $this->log( "page $source ($cleaned) is illegal." );
+ $this->output( "page $source ($cleaned) is illegal.\n" );
$safe = $this->buildSafeTitle( $cleaned );
if( $safe === false )
return $this->progress( 0 );
@@ -77,19 +80,19 @@ class ImageCleanup extends TableCleanup {
if( $title->getDBkey() !== $source ) {
$munged = $title->getDBkey();
- $this->log( "page $source ($munged) doesn't match self." );
+ $this->output( "page $source ($munged) doesn't match self.\n" );
$this->pokeFile( $source, $munged );
return $this->progress( 1 );
}
$this->progress( 0 );
}
-
- function killRow( $name ) {
+
+ private function killRow( $name ) {
if( $this->dryrun ) {
- $this->log( "DRY RUN: would delete bogus row '$name'" );
+ $this->output( "DRY RUN: would delete bogus row '$name'\n" );
} else {
- $this->log( "deleting bogus row '$name'" );
+ $this->output( "deleting bogus row '$name'\n" );
$db = wfGetDB( DB_MASTER );
$db->delete( 'image',
array( 'img_name' => $name ),
@@ -97,37 +100,55 @@ class ImageCleanup extends TableCleanup {
}
}
- function filePath( $name ) {
+ private function filePath( $name ) {
if ( !isset( $this->repo ) ) {
$this->repo = RepoGroup::singleton()->getLocalRepo();
}
return $this->repo->getRootDirectory() . '/' . $this->repo->getHashPath( $name ) . $name;
}
+
+ private function imageExists( $name, $db ) {
+ return $db->selectField( 'image', '1', array( 'img_name' => $name ), __METHOD__ );
+ }
+
+ private function pageExists( $name, $db ) {
+ return $db->selectField( 'page', '1', array( 'page_namespace' => NS_FILE, 'page_title' => $name ), __METHOD__ );
+ }
- function pokeFile( $orig, $new ) {
+ private function pokeFile( $orig, $new ) {
$path = $this->filePath( $orig );
if( !file_exists( $path ) ) {
- $this->log( "missing file: $path" );
+ $this->output( "missing file: $path\n" );
return $this->killRow( $orig );
}
$db = wfGetDB( DB_MASTER );
+
+ /*
+ * To prevent key collisions in the update() statements below,
+ * if the target title exists in the image table, or if both the
+ * original and target titles exist in the page table, append
+ * increasing version numbers until the target title exists in
+ * neither. (See also bug 16916.)
+ */
$version = 0;
$final = $new;
+ $conflict = ( $this->imageExists( $final, $db ) ||
+ ( $this->pageExists( $orig, $db ) && $this->pageExists( $final, $db ) ) );
- while( $db->selectField( 'image', 'img_name', array( 'img_name' => $final ), __METHOD__ ) ||
- Title::makeTitle( NS_FILE, $final )->exists() ) {
- $this->log( "Rename conflicts with '$final'..." );
+ while( $conflict ) {
+ $this->output( "Rename conflicts with '$final'...\n" );
$version++;
$final = $this->appendTitle( $new, "_$version" );
+ $conflict = ( $this->imageExists( $final, $db ) || $this->pageExists( $final, $db ) );
}
$finalPath = $this->filePath( $final );
if( $this->dryrun ) {
- $this->log( "DRY RUN: would rename $path to $finalPath" );
+ $this->output( "DRY RUN: would rename $path to $finalPath\n" );
} else {
- $this->log( "renaming $path to $finalPath" );
+ $this->output( "renaming $path to $finalPath\n" );
// XXX: should this use File::move()? FIXME?
$db->begin();
$db->update( 'image',
@@ -153,18 +174,18 @@ class ImageCleanup extends TableCleanup {
if( rename( $path, $finalPath ) ) {
$db->commit();
} else {
- $this->log( "RENAME FAILED" );
+ $this->error( "RENAME FAILED" );
$db->rollback();
}
}
}
-
- function appendTitle( $name, $suffix ) {
+
+ private function appendTitle( $name, $suffix ) {
return preg_replace( '/^(.*)(\..*?)$/',
"\\1$suffix\\2", $name );
}
-
- function buildSafeTitle( $name ) {
+
+ private function buildSafeTitle( $name ) {
global $wgLegalTitleChars;
$x = preg_replace_callback(
"/([^$wgLegalTitleChars]|~)/",
@@ -173,7 +194,7 @@ class ImageCleanup extends TableCleanup {
$test = Title::makeTitleSafe( NS_FILE, $x );
if( is_null( $test ) || $test->getDBkey() !== $x ) {
- $this->log( "Unable to generate safe title from '$name', got '$x'" );
+ $this->error( "Unable to generate safe title from '$name', got '$x'" );
return false;
}
@@ -181,8 +202,5 @@ class ImageCleanup extends TableCleanup {
}
}
-$wgUser->setName( 'Conversion script' );
-$caps = new ImageCleanup( !isset( $options['fix'] ) );
-$caps->cleanup();
-
-
+$maintClass = "ImageCleanup";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/cleanupSpam.php b/maintenance/cleanupSpam.php
index eb9bd914..e78ffe41 100644
--- a/maintenance/cleanupSpam.php
+++ b/maintenance/cleanupSpam.php
@@ -1,114 +1,133 @@
<?php
/**
- * @file
+ * Cleanup all spam from a given hostname
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-require_once( 'commandLine.inc' );
-require_once( "$IP/includes/LinkFilter.php" );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-function cleanupArticle( $id, $domain ) {
- $title = Title::newFromID( $id );
- if ( !$title ) {
- print "Internal error: no page for ID $id\n";
- return;
+class CleanupSpam extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Cleanup all spam from a given hostname";
+ $this->addOption( 'all', 'Check all wikis in $wgLocalDatabases' );
+ $this->addArg( 'hostname', 'Hostname that was spamming' );
}
- print $title->getPrefixedDBkey() . " ...";
- $rev = Revision::newFromTitle( $title );
- $revId = $rev->getId();
- $currentRevId = $revId;
-
- while ( $rev && LinkFilter::matchEntry( $rev->getText() , $domain ) ) {
- # Revision::getPrevious can't be used in this way before MW 1.6 (Revision.php 1.26)
- #$rev = $rev->getPrevious();
- $revId = $title->getPreviousRevisionID( $revId );
- if ( $revId ) {
- $rev = Revision::newFromTitle( $title, $revId );
- } else {
- $rev = false;
+ public function execute() {
+ global $wgLocalDatabases;
+
+ $username = wfMsg( 'spambot_username' );
+ $wgUser = User::newFromName( $username );
+ // Create the user if necessary
+ if ( !$wgUser->getId() ) {
+ $wgUser->addToDatabase();
}
- }
- if ( $revId == $currentRevId ) {
- // The regex didn't match the current article text
- // This happens e.g. when a link comes from a template rather than the page itself
- print "False match\n";
- } else {
- $dbw = wfGetDB( DB_MASTER );
- $dbw->immediateBegin();
- if ( !$rev ) {
- // Didn't find a non-spammy revision, blank the page
- print "blanking\n";
- $article = new Article( $title );
- $article->updateArticle( '', wfMsg( 'spam_blanking', $domain ),
- false, false );
+ $spec = $this->getArg();
+ $like = LinkFilter::makeLikeArray( $spec );
+ if ( !$like ) {
+ $this->error( "Not a valid hostname specification: $spec", true );
+ }
+
+ if ( $this->hasOption('all') ) {
+ // Clean up spam on all wikis
+ $this->output( "Finding spam on " . count( $wgLocalDatabases ) . " wikis\n" );
+ $found = false;
+ foreach ( $wgLocalDatabases as $wikiID ) {
+ $dbr = wfGetDB( DB_SLAVE, array(), $wikiID );
+ $count = $dbr->selectField( 'externallinks', 'COUNT(*)',
+ array( 'el_index' . $dbr->buildLike( $like ) ), __METHOD__ );
+ if ( $count ) {
+ $found = true;
+ passthru( "php cleanupSpam.php --wiki='$wikiID' $spec | sed 's/^/$wikiID: /'" );
+ }
+ }
+ if ( $found ) {
+ $this->output( "All done\n" );
+ } else {
+ $this->output( "None found\n" );
+ }
} else {
- // Revert to this revision
- print "reverting\n";
- $article = new Article( $title );
- $article->updateArticle( $rev->getText(), wfMsg( 'spam_reverting', $domain ), false, false );
+ // Clean up spam on this wiki
+
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( 'externallinks', array( 'DISTINCT el_from' ),
+ array( 'el_index' . $dbr->buildLike( $like ) ), __METHOD__ );
+ $count = $dbr->numRows( $res );
+ $this->output( "Found $count articles containing $spec\n" );
+ foreach ( $res as $row ) {
+ $this->cleanupArticle( $row->el_from, $spec );
+ }
+ if ( $count ) {
+ $this->output( "Done\n" );
+ }
}
- $dbw->immediateCommit();
- wfDoUpdates();
}
-}
-//------------------------------------------------------------------------------
-
-
-
-$username = wfMsg( 'spambot_username' );
-$fname = $username;
-$wgUser = User::newFromName( $username );
-// Create the user if necessary
-if ( !$wgUser->getId() ) {
- $wgUser->addToDatabase();
-}
-
-if ( !isset( $args[0] ) ) {
- print "Usage: php cleanupSpam.php <hostname>\n";
- exit(1);
-}
-$spec = $args[0];
-$like = LinkFilter::makeLike( $spec );
-if ( !$like ) {
- print "Not a valid hostname specification: $spec\n";
- exit(1);
-}
-
-$dbr = wfGetDB( DB_SLAVE );
-
-if ( isset($options['all']) ) {
- // Clean up spam on all wikis
- $dbr = wfGetDB( DB_SLAVE );
- print "Finding spam on " . count($wgLocalDatabases) . " wikis\n";
- $found = false;
- foreach ( $wgLocalDatabases as $db ) {
- $count = $dbr->selectField( "`$db`.externallinks", 'COUNT(*)',
- array( 'el_index LIKE ' . $dbr->addQuotes( $like ) ), $fname );
- if ( $count ) {
- $found = true;
- passthru( "php cleanupSpam.php $db $spec | sed s/^/$db: /" );
+ private function cleanupArticle( $id, $domain ) {
+ $title = Title::newFromID( $id );
+ if ( !$title ) {
+ $this->error( "Internal error: no page for ID $id" );
+ return;
+ }
+
+ $this->output( $title->getPrefixedDBkey() . " ..." );
+ $rev = Revision::newFromTitle( $title );
+ $revId = $rev->getId();
+ $currentRevId = $revId;
+
+ while ( $rev && LinkFilter::matchEntry( $rev->getText() , $domain ) ) {
+ # Revision::getPrevious can't be used in this way before MW 1.6 (Revision.php 1.26)
+ #$rev = $rev->getPrevious();
+ $revId = $title->getPreviousRevisionID( $revId );
+ if ( $revId ) {
+ $rev = Revision::newFromTitle( $title, $revId );
+ } else {
+ $rev = false;
+ }
+ }
+ if ( $revId == $currentRevId ) {
+ // The regex didn't match the current article text
+ // This happens e.g. when a link comes from a template rather than the page itself
+ $this->output( "False match\n" );
+ } else {
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin();
+ if ( !$rev ) {
+ // Didn't find a non-spammy revision, blank the page
+ $this->output( "blanking\n" );
+ $article = new Article( $title );
+ $article->updateArticle( '', wfMsg( 'spam_blanking', $domain ),
+ false, false );
+
+ } else {
+ // Revert to this revision
+ $this->output( "reverting\n" );
+ $article = new Article( $title );
+ $article->updateArticle( $rev->getText(), wfMsg( 'spam_reverting', $domain ), false, false );
+ }
+ $dbw->commit();
+ wfDoUpdates();
}
- }
- if ( $found ) {
- print "All done\n";
- } else {
- print "None found\n";
- }
-} else {
- // Clean up spam on this wiki
- $res = $dbr->select( 'externallinks', array( 'DISTINCT el_from' ),
- array( 'el_index LIKE ' . $dbr->addQuotes( $like ) ), $fname );
- $count = $dbr->numRows( $res );
- print "Found $count articles containing $spec\n";
- while ( $row = $dbr->fetchObject( $res ) ) {
- cleanupArticle( $row->el_from, $spec );
- }
- if ( $count ) {
- print "Done\n";
}
}
-
+$maintClass = "CleanupSpam";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/cleanupTable.inc b/maintenance/cleanupTable.inc
index 75699c52..3549a9a1 100644
--- a/maintenance/cleanupTable.inc
+++ b/maintenance/cleanupTable.inc
@@ -1,31 +1,58 @@
<?php
-
-require_once( 'FiveUpgrade.inc' );
-
/**
+ * Generic table cleanup class. Already subclasses maintenance
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-abstract class TableCleanup extends FiveUpgrade {
- function __construct( $table, $dryrun = false ) {
- parent::__construct();
- $this->targetTable = $table;
- $this->maxLag = 10; # if slaves are lagged more than 10 secs, wait
- $this->dryrun = $dryrun;
+require_once( dirname(__FILE__) . '/Maintenance.php' );
+
+class TableCleanup extends Maintenance {
+ protected $defaultParams = array(
+ 'table' => 'page',
+ 'conds' => array(),
+ 'index' => 'page_id',
+ 'callback' => 'processRow',
+ );
+
+ protected $dryrun = false;
+ protected $maxLag = 10; # if slaves are lagged more than 10 secs, wait
+ public $batchSize = 100;
+ public $reportInterval = 100;
+
+ public function __construct() {
+ parent::__construct();
+ $this->addOption( 'dry-run', 'Perform a dry run' );
}
- function cleanup() {
+ public function execute() {
+ global $wgUser;
+ $wgUser->setName( 'Conversion script' );
+ $this->dryrun = $this->hasOption( 'dry-run' );
if( $this->dryrun ) {
- echo "Checking for bad titles...\n";
+ $this->output( "Checking for bad titles...\n" );
} else {
- echo "Checking and fixing bad titles...\n";
+ $this->output( "Checking and fixing bad titles...\n" );
}
- $this->runTable( $this->targetTable,
- '', //'WHERE page_namespace=0',
- array( $this, 'processPage' ) );
+ $this->runTable( $this->defaultParams );
}
- function init( $count, $table ) {
+ protected function init( $count, $table ) {
$this->processed = 0;
$this->updated = 0;
$this->count = $count;
@@ -33,10 +60,10 @@ abstract class TableCleanup extends FiveUpgrade {
$this->table = $table;
}
- function progress( $updated ) {
+ protected function progress( $updated ) {
$this->updated += $updated;
$this->processed++;
- if( $this->processed % 100 != 0 ) {
+ if( $this->processed % $this->reportInterval != 0 ) {
return;
}
$portion = $this->processed / $this->count;
@@ -47,39 +74,89 @@ abstract class TableCleanup extends FiveUpgrade {
$estimatedTotalTime = $delta / $portion;
$eta = $this->startTime + $estimatedTotalTime;
- printf( "%s %s: %6.2f%% done on %s; ETA %s [%d/%d] %.2f/sec <%.2f%% updated>\n",
- wfWikiID(),
- wfTimestamp( TS_DB, intval( $now ) ),
- $portion * 100.0,
- $this->table,
- wfTimestamp( TS_DB, intval( $eta ) ),
- $this->processed,
- $this->count,
- $this->processed / $delta,
- $updateRate * 100.0 );
+ $this->output(
+ sprintf( "%s %s: %6.2f%% done on %s; ETA %s [%d/%d] %.2f/sec <%.2f%% updated>\n",
+ wfWikiID(),
+ wfTimestamp( TS_DB, intval( $now ) ),
+ $portion * 100.0,
+ $this->table,
+ wfTimestamp( TS_DB, intval( $eta ) ),
+ $this->processed,
+ $this->count,
+ $this->processed / $delta,
+ $updateRate * 100.0
+ )
+ );
flush();
}
- function runTable( $table, $where, $callback ) {
- $count = $this->dbw->selectField( $table, 'count(*)', '', __METHOD__ );
+ public function runTable( $params ) {
+ $dbr = wfGetDB( DB_SLAVE );
+
+ if ( array_diff( array_keys( $params ),
+ array( 'table', 'conds', 'index', 'callback' ) ) )
+ {
+ throw new MWException( __METHOD__.': Missing parameter ' . implode( ', ', $params ) );
+ }
+
+ $table = $params['table'];
+ $count = $dbr->selectField( $table, 'count(*)', $params['conds'], __METHOD__ );
$this->init( $count, $table );
- $this->log( "Processing $table..." );
+ $this->output( "Processing $table...\n" );
- $tableName = $this->dbr->tableName( $table );
- $sql = "SELECT * FROM $tableName $where";
- $result = $this->dbr->query( $sql, __METHOD__ );
- foreach( $result as $row ) {
- call_user_func( $callback, $row );
+ $index = (array)$params['index'];
+ $indexConds = array();
+ $options = array(
+ 'ORDER BY' => implode( ',', $index ),
+ 'LIMIT' => $this->batchSize
+ );
+ $callback = array( $this, $params['callback'] );
+
+ while ( true ) {
+ $conds = array_merge( $params['conds'], $indexConds );
+ $res = $dbr->select( $table, '*', $conds, __METHOD__, $options );
+ if ( !$res->numRows() ) {
+ // Done
+ break;
+ }
+
+ foreach ( $res as $row ) {
+ call_user_func( $callback, $row );
+ }
+
+ if ( $res->numRows() < $this->batchSize ) {
+ // Done
+ break;
+ }
+
+ // Update the conditions to select the next batch.
+ // Construct a condition string by starting with the least significant part
+ // of the index, and adding more significant parts progressively to the left
+ // of the string.
+ $nextCond = '';
+ foreach ( array_reverse( $index ) as $field ) {
+ $encValue = $dbr->addQuotes( $row->$field );
+ if ( $nextCond === '' ) {
+ $nextCond = "$field > $encValue";
+ } else {
+ $nextCond = "$field > $encValue OR ($field = $encValue AND ($nextCond))";
+ }
+ }
+ $indexConds = array( $nextCond );
}
- $this->log( "Finished $table... $this->updated of $this->processed rows updated" );
- $result->free();
+
+ $this->output( "Finished $table... $this->updated of $this->processed rows updated\n" );
}
- function hexChar( $matches ) {
+ protected function hexChar( $matches ) {
return sprintf( "\\x%02x", ord( $matches[1] ) );
}
-
- abstract function processPage( $row );
-
}
+
+class TableCleanupTest extends TableCleanup {
+ function processRow( $row ) {
+ $this->progress( mt_rand( 0, 1 ) );
+ }
+}
+
diff --git a/maintenance/cleanupTitles.php b/maintenance/cleanupTitles.php
index 4d76ac7a..ed714b2d 100644
--- a/maintenance/cleanupTitles.php
+++ b/maintenance/cleanupTitles.php
@@ -24,49 +24,47 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @file
* @author Brion Vibber <brion at pobox.com>
* @ingroup Maintenance
*/
-require_once( 'commandLine.inc' );
-require_once( 'cleanupTable.inc' );
+require_once( dirname(__FILE__) . '/cleanupTable.inc' );
-/**
- * @ingroup Maintenance
- */
class TitleCleanup extends TableCleanup {
- function __construct( $dryrun = false ) {
- parent::__construct( 'page', $dryrun );
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Script to clean up broken, unparseable titles";
}
- function processPage( $row ) {
- $current = Title::makeTitle( $row->page_namespace, $row->page_title );
- $display = $current->getPrefixedText();
-
- $verified = UtfNormal::cleanUp( $display );
-
+ protected function processRow( $row ) {
+ global $wgContLang;
+ $display = Title::makeName( $row->page_namespace, $row->page_title );
+ $verified = $wgContLang->normalize( $display );
$title = Title::newFromText( $verified );
- if( !is_null( $title ) && $title->equals( $current ) && $title->canExist() ) {
+ if( !is_null( $title )
+ && $title->canExist()
+ && $title->getNamespace() == $row->page_namespace
+ && $title->getDBkey() === $row->page_title )
+ {
return $this->progress( 0 ); // all is fine
}
if( $row->page_namespace == NS_FILE && $this->fileExists( $row->page_title ) ) {
- $this->log( "file $row->page_title needs cleanup, please run cleanupImages.php." );
+ $this->output( "file $row->page_title needs cleanup, please run cleanupImages.php.\n" );
return $this->progress( 0 );
} elseif( is_null( $title ) ) {
- $this->log( "page $row->page_id ($display) is illegal." );
+ $this->output( "page $row->page_id ($display) is illegal.\n" );
$this->moveIllegalPage( $row );
return $this->progress( 1 );
} else {
- $this->log( "page $row->page_id ($display) doesn't match self." );
+ $this->output( "page $row->page_id ($display) doesn't match self.\n" );
$this->moveInconsistentPage( $row, $title );
return $this->progress( 1 );
}
}
- function fileExists( $name ) {
+ protected function fileExists( $name ) {
// XXX: Doesn't actually check for file existence, just presence of image record.
// This is reasonable, since cleanupImages.php only iterates over the image table.
$dbr = wfGetDB( DB_SLAVE );
@@ -74,7 +72,7 @@ class TitleCleanup extends TableCleanup {
return $row !== false;
}
- function moveIllegalPage( $row ) {
+ protected function moveIllegalPage( $row ) {
$legal = 'A-Za-z0-9_/\\\\-';
$legalized = preg_replace_callback( "!([^$legal])!",
array( &$this, 'hexChar' ),
@@ -86,28 +84,28 @@ class TitleCleanup extends TableCleanup {
$title = Title::newFromText( $legalized );
if( is_null( $title ) ) {
$clean = 'Broken/id:' . $row->page_id;
- $this->log( "Couldn't legalize; form '$legalized' still invalid; using '$clean'" );
+ $this->output( "Couldn't legalize; form '$legalized' still invalid; using '$clean'\n" );
$title = Title::newFromText( $clean );
} elseif( $title->exists() ) {
$clean = 'Broken/id:' . $row->page_id;
- $this->log( "Legalized for '$legalized' exists; using '$clean'" );
+ $this->output( "Legalized for '$legalized' exists; using '$clean'\n" );
$title = Title::newFromText( $clean );
}
$dest = $title->getDBkey();
if( $this->dryrun ) {
- $this->log( "DRY RUN: would rename $row->page_id ($row->page_namespace,'$row->page_title') to ($row->page_namespace,'$dest')" );
+ $this->output( "DRY RUN: would rename $row->page_id ($row->page_namespace,'$row->page_title') to ($row->page_namespace,'$dest')\n" );
} else {
- $this->log( "renaming $row->page_id ($row->page_namespace,'$row->page_title') to ($row->page_namespace,'$dest')" );
+ $this->output( "renaming $row->page_id ($row->page_namespace,'$row->page_title') to ($row->page_namespace,'$dest')\n" );
$dbw = wfGetDB( DB_MASTER );
$dbw->update( 'page',
array( 'page_title' => $dest ),
array( 'page_id' => $row->page_id ),
- 'cleanupTitles::moveInconsistentPage' );
+ __METHOD__ );
}
}
- function moveInconsistentPage( $row, $title ) {
+ protected function moveInconsistentPage( $row, $title ) {
if( $title->exists() || $title->getInterwiki() ) {
if( $title->getInterwiki() ) {
$prior = $title->getPrefixedDbKey();
@@ -118,20 +116,20 @@ class TitleCleanup extends TableCleanup {
$verified = Title::makeTitleSafe( $row->page_namespace, $clean );
if( $verified->exists() ) {
$blah = "Broken/id:" . $row->page_id;
- $this->log( "Couldn't legalize; form '$clean' exists; using '$blah'" );
+ $this->output( "Couldn't legalize; form '$clean' exists; using '$blah'\n" );
$verified = Title::makeTitleSafe( $row->page_namespace, $blah );
}
$title = $verified;
}
if( is_null( $title ) ) {
- wfDie( "Something awry; empty title.\n" );
+ $this->error( "Something awry; empty title.", true );
}
$ns = $title->getNamespace();
$dest = $title->getDBkey();
if( $this->dryrun ) {
- $this->log( "DRY RUN: would rename $row->page_id ($row->page_namespace,'$row->page_title') to ($row->page_namespace,'$dest')" );
+ $this->output( "DRY RUN: would rename $row->page_id ($row->page_namespace,'$row->page_title') to ($row->page_namespace,'$dest')\n" );
} else {
- $this->log( "renaming $row->page_id ($row->page_namespace,'$row->page_title') to ($ns,'$dest')" );
+ $this->output( "renaming $row->page_id ($row->page_namespace,'$row->page_title') to ($ns,'$dest')\n" );
$dbw = wfGetDB( DB_MASTER );
$dbw->update( 'page',
array(
@@ -139,15 +137,12 @@ class TitleCleanup extends TableCleanup {
'page_title' => $dest
),
array( 'page_id' => $row->page_id ),
- 'cleanupTitles::moveInconsistentPage' );
+ __METHOD__ );
$linkCache = LinkCache::singleton();
$linkCache->clear();
}
}
}
-$wgUser->setName( 'Conversion script' );
-$caps = new TitleCleanup( !isset( $options['fix'] ) );
-$caps->cleanup();
-
-
+$maintClass = "TitleCleanup";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/cleanupWatchlist.php b/maintenance/cleanupWatchlist.php
index bfc3a1e2..ed84b268 100644
--- a/maintenance/cleanupWatchlist.php
+++ b/maintenance/cleanupWatchlist.php
@@ -24,54 +24,64 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @file
* @author Brion Vibber <brion at pobox.com>
* @ingroup Maintenance
*/
-require_once( 'commandLine.inc' );
-require_once( 'cleanupTable.inc' );
+require_once( dirname(__FILE__) . '/cleanupTable.inc' );
-/**
- * @ingroup Maintenance
- */
class WatchlistCleanup extends TableCleanup {
- function __construct( $dryrun = false ) {
- parent::__construct( 'watchlist', $dryrun );
+ protected $defaultParams = array(
+ 'table' => 'watchlist',
+ 'index' => array( 'wl_user', 'wl_namespace', 'wl_title' ),
+ 'conds' => array(),
+ 'callback' => 'processRow'
+ );
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Script to remove broken, unparseable titles in the Watchlist";
+ $this->addOption( 'fix', 'Actually remove entries; without will only report.' );
+ }
+
+ function execute() {
+ if ( !$this->hasOption( 'fix' ) ) {
+ $this->output( "Dry run only: use --fix to enable updates\n" );
+ }
+ parent::execute();
}
- function processPage( $row ) {
+ protected function processRow( $row ) {
+ global $wgContLang;
$current = Title::makeTitle( $row->wl_namespace, $row->wl_title );
$display = $current->getPrefixedText();
-
- $verified = UtfNormal::cleanUp( $display );
-
+ $verified = $wgContLang->normalize( $display );
$title = Title::newFromText( $verified );
if( $row->wl_user == 0 || is_null( $title ) || !$title->equals( $current ) ) {
- $this->log( "invalid watch by {$row->wl_user} for ({$row->wl_namespace}, \"{$row->wl_title}\")" );
- $this->removeWatch( $row );
- return $this->progress( 1 );
+ $this->output( "invalid watch by {$row->wl_user} for ({$row->wl_namespace}, \"{$row->wl_title}\")\n" );
+ $updated = $this->removeWatch( $row );
+ $this->progress( $updated );
+ return;
}
-
$this->progress( 0 );
}
-
- function removeWatch( $row ) {
- if( !$this->dryrun ) {
+
+ private function removeWatch( $row ) {
+ if( !$this->dryrun && $this->hasOption( 'fix' ) ) {
$dbw = wfGetDB( DB_MASTER );
$dbw->delete( 'watchlist', array(
'wl_user' => $row->wl_user,
'wl_namespace' => $row->wl_namespace,
'wl_title' => $row->wl_title ),
__METHOD__ );
- $this->log( '- removed' );
+ $this->output( "- removed\n" );
+ return 1;
+ } else {
+ return 0;
}
}
}
-$wgUser->setName( 'Conversion script' );
-$caps = new WatchlistCleanup( !isset( $options['fix'] ) );
-$caps->cleanup();
-
-
+$maintClass = "WatchlistCleanup";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/clear_interwiki_cache.php b/maintenance/clear_interwiki_cache.php
index ce154779..a3510a06 100644
--- a/maintenance/clear_interwiki_cache.php
+++ b/maintenance/clear_interwiki_cache.php
@@ -1,27 +1,53 @@
<?php
/**
* This script is used to clear the interwiki links for ALL languages in
- * memcached.
+ * the cache.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
*
- * @file
* @ingroup Maintenance
*/
-/** */
-require_once('commandLine.inc');
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-$dbr = wfGetDB( DB_SLAVE );
-$res = $dbr->select( 'interwiki', array( 'iw_prefix' ), false );
-$prefixes = array();
-while ( $row = $dbr->fetchObject( $res ) ) {
- $prefixes[] = $row->iw_prefix;
-}
+class ClearInterwikiCache extends Maintenance {
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Clear all interwiki links for all languages from the cache";
+ }
+
+ public function execute() {
+ global $wgLocalDatabases, $wgMemc;
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( 'interwiki', array( 'iw_prefix' ), false );
+ $prefixes = array();
+ foreach ( $res as $row ) {
+ $prefixes[] = $row->iw_prefix;
+ }
-foreach ( $wgLocalDatabases as $db ) {
- print "$db ";
- foreach ( $prefixes as $prefix ) {
- $wgMemc->delete("$db:interwiki:$prefix");
+ foreach ( $wgLocalDatabases as $db ) {
+ $this->output( "$db..." );
+ foreach ( $prefixes as $prefix ) {
+ $wgMemc->delete("$db:interwiki:$prefix");
+ }
+ $this->output( "done\n" );
+ }
}
}
-print "\n";
+$maintClass = "ClearInterwikiCache";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/clear_stats.php b/maintenance/clear_stats.php
index 4cacd74c..6a6a4981 100644
--- a/maintenance/clear_stats.php
+++ b/maintenance/clear_stats.php
@@ -1,38 +1,53 @@
<?php
/**
- * This script remove all statistics tracking from memcached
+ * This script remove all statistics tracking from the cache
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-require_once('commandLine.inc');
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-foreach ( $wgLocalDatabases as $db ) {
- noisyDelete("$db:stats:request_with_session");
- noisyDelete("$db:stats:request_without_session");
- noisyDelete("$db:stats:pcache_hit");
- noisyDelete("$db:stats:pcache_miss_invalid");
- noisyDelete("$db:stats:pcache_miss_expired");
- noisyDelete("$db:stats:pcache_miss_absent");
- noisyDelete("$db:stats:pcache_miss_stub");
- noisyDelete("$db:stats:image_cache_hit");
- noisyDelete("$db:stats:image_cache_miss");
- noisyDelete("$db:stats:image_cache_update");
- noisyDelete("$db:stats:diff_cache_hit");
- noisyDelete("$db:stats:diff_cache_miss");
- noisyDelete("$db:stats:diff_uncacheable");
-}
+class clear_stats extends Maintenance {
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Remove all statistics tracking from the cache";
+ }
-function noisyDelete( $key ) {
- global $wgMemc;
- /*
- print "$key ";
- if ( $wgMemc->delete($key) ) {
- print "deleted\n";
- } else {
- print "FAILED\n";
- }*/
- $wgMemc->delete($key);
+ public function execute() {
+ global $wgLocalDatabases, $wgMemc;
+ foreach ( $wgLocalDatabases as $db ) {
+ $wgMemc->delete("$db:stats:request_with_session");
+ $wgMemc->delete("$db:stats:request_without_session");
+ $wgMemc->delete("$db:stats:pcache_hit");
+ $wgMemc->delete("$db:stats:pcache_miss_invalid");
+ $wgMemc->delete("$db:stats:pcache_miss_expired");
+ $wgMemc->delete("$db:stats:pcache_miss_absent");
+ $wgMemc->delete("$db:stats:pcache_miss_stub");
+ $wgMemc->delete("$db:stats:image_cache_hit");
+ $wgMemc->delete("$db:stats:image_cache_miss");
+ $wgMemc->delete("$db:stats:image_cache_update");
+ $wgMemc->delete("$db:stats:diff_cache_hit");
+ $wgMemc->delete("$db:stats:diff_cache_miss");
+ $wgMemc->delete("$db:stats:diff_uncacheable");
+ }
+ }
}
+$maintClass = "clear_stats";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/commandLine.inc b/maintenance/commandLine.inc
index e83a1916..332527ba 100644
--- a/maintenance/commandLine.inc
+++ b/maintenance/commandLine.inc
@@ -1,261 +1,46 @@
<?php
+
/**
- * @file
- * @todo document
- * @ingroup Maintenance
- * @defgroup Maintenance Maintenance
+ * Backwards-compatibility wrapper for old-style maintenance scripts
*/
-
-$wgRequestTime = microtime(true);
-
-/** */
-# Abort if called from a web server
-if ( isset( $_SERVER ) && array_key_exists( 'REQUEST_METHOD', $_SERVER ) ) {
- print "This script must be run from the command line\n";
- exit();
-}
-
-if( version_compare( PHP_VERSION, '5.0.0' ) < 0 ) {
- print "Sorry! This version of MediaWiki requires PHP 5; you are running " .
- PHP_VERSION . ".\n\n" .
- "If you are sure you already have PHP 5 installed, it may be " .
- "installed\n" .
- "in a different path from PHP 4. Check with your system administrator.\n";
- die( -1 );
-}
-
-define('MEDIAWIKI',true);
-
-# Process command line arguments
-# $options becomes an array with keys set to the option names
-# $optionsWithArgs is an array of GNU-style options that take an argument. The arguments are returned
-# in the values of $options.
-# $args becomes a zero-based array containing the non-option arguments
+require( dirname(__FILE__) . '/Maintenance.php' );
if ( !isset( $optionsWithArgs ) ) {
- $optionsWithArgs = array();
+ $optionsWithArgs = array();
}
-$optionsWithArgs[] = 'conf'; # For specifying the location of LocalSettings.php
-$optionsWithArgs[] = 'aconf'; # As above for AdminSettings.php
-$optionsWithArgs[] = 'wiki'; # For specifying the wiki ID
-
-$self = array_shift( $argv );
-$IP = strval( getenv('MW_INSTALL_PATH') ) !== ''
- ? getenv('MW_INSTALL_PATH')
- : realpath( dirname( __FILE__ ) . '/..' );
-#chdir( $IP );
-require_once( "$IP/StartProfiler.php" );
-$options = array();
-$args = array();
-
-
-# Parse arguments
-for( $arg = reset( $argv ); $arg !== false; $arg = next( $argv ) ) {
- if ( $arg == '--' ) {
- # End of options, remainder should be considered arguments
- $arg = next( $argv );
- while( $arg !== false ) {
- $args[] = $arg;
- $arg = next( $argv );
- }
- break;
- } elseif ( substr( $arg, 0, 2 ) == '--' ) {
- # Long options
- $option = substr( $arg, 2 );
- if ( in_array( $option, $optionsWithArgs ) ) {
- $param = next( $argv );
- if ( $param === false ) {
- echo "$arg needs a value after it\n";
- die( -1 );
- }
- $options[$option] = $param;
- } else {
- $bits = explode( '=', $option, 2 );
- if( count( $bits ) > 1 ) {
- $option = $bits[0];
- $param = $bits[1];
- } else {
- $param = 1;
- }
- $options[$option] = $param;
+class CommandLineInc extends Maintenance {
+ public function __construct() {
+ global $optionsWithArgs;
+ parent::__construct();
+ foreach ( $optionsWithArgs as $name ) {
+ $this->addOption( $name, '', false, true );
}
- } elseif ( substr( $arg, 0, 1 ) == '-' ) {
- # Short options
- for ( $p=1; $p<strlen( $arg ); $p++ ) {
- $option = $arg{$p};
- if ( in_array( $option, $optionsWithArgs ) ) {
- $param = next( $argv );
- if ( $param === false ) {
- echo "$arg needs a value after it\n";
- die( -1 );
- }
- $options[$option] = $param;
- } else {
- $options[$option] = 1;
- }
- }
- } else {
- $args[] = $arg;
}
-}
-
-
-# General initialisation
-
-$wgCommandLineMode = true;
-# Turn off output buffering if it's on
-@ob_end_flush();
-$sep = PATH_SEPARATOR;
-
-if (!isset( $wgUseNormalUser ) ) {
- $wgUseNormalUser = false;
-}
-
-if ( file_exists( dirname(__FILE__).'/wikimedia-mode' ) ) {
- $wgWikiFarm = true;
- $cluster = 'pmtpa';
- require_once( "$IP/includes/AutoLoader.php" );
- require_once( "$IP/includes/SiteConfiguration.php" );
-
- # Get $wgConf
- require( "$IP/wgConf.php" );
- if ( empty( $wgNoDBParam ) ) {
- # Check if we were passed a db name
- if ( isset( $options['wiki'] ) ) {
- $db = $options['wiki'];
- } else {
- $db = array_shift( $args );
- }
- list( $site, $lang ) = $wgConf->siteFromDB( $db );
+ public function getDbType() {
+ global $wgUseNormalUser;
- # If not, work out the language and site the old way
- if ( is_null( $site ) || is_null( $lang ) ) {
- if ( !$db ) {
- $lang = 'aa';
- } else {
- $lang = $db;
- }
- if ( isset( $args[0] ) ) {
- $site = array_shift( $args );
- } else {
- $site = 'wikipedia';
- }
- }
- } else {
- $lang = 'aa';
- $site = 'wikipedia';
+ return ( isset( $wgUseNormalUser ) && $wgUseNormalUser ) ?
+ Maintenance::DB_STD : Maintenance::DB_ADMIN;
}
- # This is for the IRC scripts, which now run as the apache user
- # The apache user doesn't have access to the wikiadmin_pass command
- if ( $_ENV['USER'] == 'apache' ) {
- #if ( posix_geteuid() == 48 ) {
- $wgUseNormalUser = true;
+ /**
+ * No help, it would just be misleading since it misses custom options
+ */
+ protected function maybeHelp( $force = false ) {
+ if ( !$force )
+ return;
+ parent::maybeHelp( true );
}
- putenv( 'wikilang='.$lang);
-
- $DP = $IP;
- ini_set( 'include_path', ".:$IP:$IP/includes:$IP/languages:$IP/maintenance" );
-
- if ( $lang == 'test' && $site == 'wikipedia' ) {
- define( 'TESTWIKI', 1 );
- }
-
- #require_once( $IP.'/includes/ProfilerStub.php' );
- require( $IP.'/includes/Defines.php' );
- require( $IP.'/CommonSettings.php' );
- if ( !$wgUseNormalUser ) {
- require( $IP.'/AdminSettings.php' );
- }
-} else {
- $wgWikiFarm = false;
- if ( isset( $options['conf'] ) ) {
- $settingsFile = $options['conf'];
- } else {
- $settingsFile = "$IP/LocalSettings.php";
- }
- if ( isset( $options['wiki'] ) ) {
- $bits = explode( '-', $options['wiki'] );
- if ( count( $bits ) == 1 ) {
- $bits[] = '';
- }
- define( 'MW_DB', $bits[0] );
- define( 'MW_PREFIX', $bits[1] );
- }
-
- if ( ! is_readable( $settingsFile ) ) {
- print "A copy of your installation's LocalSettings.php\n" .
- "must exist and be readable in the source directory.\n";
- exit( 1 );
- }
- $wgCommandLineMode = true;
- $DP = $IP;
- require_once( "$IP/includes/AutoLoader.php" );
- #require_once( $IP.'/includes/ProfilerStub.php' );
- require_once( $IP.'/includes/Defines.php' );
- require_once( $settingsFile );
- /* ini_set( 'include_path', ".$sep$IP$sep$IP/includes$sep$IP/languages$sep$IP/maintenance" ); */
-
- $adminSettings = isset( $options['aconf'] )
- ? $options['aconf']
- : "{$IP}/AdminSettings.php";
- if( is_readable( $adminSettings ) )
- require_once( $adminSettings );
-
-}
-
-# Turn off output buffering again, it might have been turned on in the settings files
-if( ob_get_level() ) {
- ob_end_flush();
-}
-# Same with these
-$wgCommandLineMode = true;
-
-if ( empty( $wgUseNormalUser ) && isset( $wgDBadminuser ) ) {
- $wgDBuser = $wgDBadminuser;
- $wgDBpassword = $wgDBadminpassword;
-
- if( $wgDBservers ) {
- foreach ( $wgDBservers as $i => $server ) {
- $wgDBservers[$i]['user'] = $wgDBuser;
- $wgDBservers[$i]['password'] = $wgDBpassword;
- }
- }
- if( isset( $wgLBFactoryConf['serverTemplate'] ) ) {
- $wgLBFactoryConf['serverTemplate']['user'] = $wgDBuser;
- $wgLBFactoryConf['serverTemplate']['password'] = $wgDBpassword;
- }
-}
-
-if ( defined( 'MW_CMDLINE_CALLBACK' ) ) {
- $fn = MW_CMDLINE_CALLBACK;
- $fn();
-}
-
-ini_set( 'memory_limit', -1 );
-
-if( version_compare( phpversion(), '5.2.4' ) >= 0 ) {
- // Send PHP warnings and errors to stderr instead of stdout.
- // This aids in diagnosing problems, while keeping messages
- // out of redirected output.
- if( ini_get( 'display_errors' ) ) {
- ini_set( 'display_errors', 'stderr' );
+ public function execute() {
+ global $args, $options;
+ $args = $this->mArgs;
+ $options = $this->mOptions;
}
-
- // Don't touch the setting on earlier versions of PHP,
- // as setting it would disable output if you'd wanted it.
-
- // Note that exceptions are also sent to stderr when
- // command-line mode is on, regardless of PHP version.
}
-$wgShowSQLErrors = true;
-require_once( "$IP/includes/Setup.php" );
-require_once( "$IP/install-utils.inc" );
-$wgTitle = null; # Much much faster startup than creating a title object
-@set_time_limit(0);
+$maintClass = 'CommandLineInc';
+require( DO_MAINTENANCE );
-$wgProfiling = false; // only for Profiler.php mode; avoids OOM errors
diff --git a/maintenance/convertLinks.inc b/maintenance/convertLinks.inc
index 4aff81ed..7c7b8aff 100644
--- a/maintenance/convertLinks.inc
+++ b/maintenance/convertLinks.inc
@@ -45,8 +45,10 @@ function convertLinks() {
$dbw = wfGetDB( DB_MASTER );
list ($cur, $links, $links_temp, $links_backup) = $dbw->tableNamesN( 'cur', 'links', 'links_temp', 'links_backup' );
-
- $res = $dbw->query( "SELECT l_from FROM $links LIMIT 1" );
+
+ // Get database-agnostic limit clause
+ $sql_limit = $dbw->limitResult( "SELECT l_from FROM $links", 1 );
+ $res = $dbw->query( $sql_limit );
if ( $dbw->fieldType( $res, 0 ) == "int" ) {
wfOut( "Schema already converted\n" );
return;
diff --git a/maintenance/convertLinks.php b/maintenance/convertLinks.php
index e86d1e7c..415662a0 100644
--- a/maintenance/convertLinks.php
+++ b/maintenance/convertLinks.php
@@ -3,12 +3,241 @@
* Convert from the old links schema (string->ID) to the new schema (ID->ID)
* The wiki should be put into read-only mode while this script executes
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-/** */
-require_once( "commandLine.inc" );
-require_once( "convertLinks.inc" );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
+
+class ConvertLinks extends Maintenance {
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Convert from the old links schema (string->ID) to the new schema (ID->ID)
+The wiki should be put into read-only mode while this script executes";
+ }
+
+ public function execute() {
+ global $wgDBtype;
+ if( $wgDBtype == 'postgres' ) {
+ $this->output( "Links table already ok on Postgres.\n" );
+ return;
+ }
+
+ $this->output( "Converting links table to ID-ID...\n" );
+
+ global $wgLang, $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname;
+ global $noKeys, $logPerformance, $fh;
+
+ $tuplesAdded = $numBadLinks = $curRowsRead = 0; #counters etc
+ $totalTuplesInserted = 0; # total tuples INSERTed into links_temp
+
+ $reportCurReadProgress = true; #whether or not to give progress reports while reading IDs from cur table
+ $curReadReportInterval = 1000; #number of rows between progress reports
+
+ $reportLinksConvProgress = true; #whether or not to give progress reports during conversion
+ $linksConvInsertInterval = 1000; #number of rows per INSERT
+
+ $initialRowOffset = 0;
+ #$finalRowOffset = 0; # not used yet; highest row number from links table to process
+
+ # Overwrite the old links table with the new one. If this is set to false,
+ # the new table will be left at links_temp.
+ $overwriteLinksTable = true;
+
+ # Don't create keys, and so allow duplicates in the new links table.
+ # This gives a huge speed improvement for very large links tables which are MyISAM. (What about InnoDB?)
+ $noKeys = false;
+
+
+ $logPerformance = false; # output performance data to a file
+ $perfLogFilename = "convLinksPerf.txt";
+ #--------------------------------------------------------------------
+
+ $dbw = wfGetDB( DB_MASTER );
+ list ($cur, $links, $links_temp, $links_backup) = $dbw->tableNamesN( 'cur', 'links', 'links_temp', 'links_backup' );
+
+ $res = $dbw->query( "SELECT l_from FROM $links LIMIT 1" );
+ if ( $dbw->fieldType( $res, 0 ) == "int" ) {
+ $this->output( "Schema already converted\n" );
+ return;
+ }
+
+ $res = $dbw->query( "SELECT COUNT(*) AS count FROM $links" );
+ $row = $dbw->fetchObject($res);
+ $numRows = $row->count;
+ $dbw->freeResult( $res );
+
+ if ( $numRows == 0 ) {
+ $this->output( "Updating schema (no rows to convert)...\n" );
+ $this->createTempTable();
+ } else {
+ if ( $logPerformance ) { $fh = fopen ( $perfLogFilename, "w" ); }
+ $baseTime = $startTime = $this->getMicroTime();
+ # Create a title -> cur_id map
+ $this->output( "Loading IDs from $cur table...\n" );
+ $this->performanceLog ( "Reading $numRows rows from cur table...\n" );
+ $this->performanceLog ( "rows read vs seconds elapsed:\n" );
+
+ $dbw->bufferResults( false );
+ $res = $dbw->query( "SELECT cur_namespace,cur_title,cur_id FROM $cur" );
+ $ids = array();
+
+ while ( $row = $dbw->fetchObject( $res ) ) {
+ $title = $row->cur_title;
+ if ( $row->cur_namespace ) {
+ $title = $wgLang->getNsText( $row->cur_namespace ) . ":$title";
+ }
+ $ids[$title] = $row->cur_id;
+ $curRowsRead++;
+ if ($reportCurReadProgress) {
+ if (($curRowsRead % $curReadReportInterval) == 0) {
+ $this->performanceLog( $curRowsRead . " " . ($this->getMicroTime() - $baseTime) . "\n" );
+ $this->output( "\t$curRowsRead rows of $cur table read.\n" );
+ }
+ }
+ }
+ $dbw->freeResult( $res );
+ $dbw->bufferResults( true );
+ $this->output( "Finished loading IDs.\n\n" );
+ $this->performanceLog( "Took " . ($this->getMicroTime() - $baseTime) . " seconds to load IDs.\n\n" );
+ #--------------------------------------------------------------------
+
+ # Now, step through the links table (in chunks of $linksConvInsertInterval rows),
+ # convert, and write to the new table.
+ $this->createTempTable();
+ $this->performanceLog( "Resetting timer.\n\n" );
+ $baseTime = $this->getMicroTime();
+ $this->output( "Processing $numRows rows from $links table...\n" );
+ $this->performanceLog( "Processing $numRows rows from $links table...\n" );
+ $this->performanceLog( "rows inserted vs seconds elapsed:\n" );
+
+ for ($rowOffset = $initialRowOffset; $rowOffset < $numRows; $rowOffset += $linksConvInsertInterval) {
+ $sqlRead = "SELECT * FROM $links ";
+ $sqlRead = $dbw->limitResult($sqlRead, $linksConvInsertInterval,$rowOffset);
+ $res = $dbw->query($sqlRead);
+ if ( $noKeys ) {
+ $sqlWrite = array("INSERT INTO $links_temp (l_from,l_to) VALUES ");
+ } else {
+ $sqlWrite = array("INSERT IGNORE INTO $links_temp (l_from,l_to) VALUES ");
+ }
+
+ $tuplesAdded = 0; # no tuples added to INSERT yet
+ while ( $row = $dbw->fetchObject($res) ) {
+ $fromTitle = $row->l_from;
+ if ( array_key_exists( $fromTitle, $ids ) ) { # valid title
+ $from = $ids[$fromTitle];
+ $to = $row->l_to;
+ if ( $tuplesAdded != 0 ) {
+ $sqlWrite[] = ",";
+ }
+ $sqlWrite[] = "($from,$to)";
+ $tuplesAdded++;
+ } else { # invalid title
+ $numBadLinks++;
+ }
+ }
+ $dbw->freeResult($res);
+ #$this->output( "rowOffset: $rowOffset\ttuplesAdded: $tuplesAdded\tnumBadLinks: $numBadLinks\n" );
+ if ( $tuplesAdded != 0 ) {
+ if ($reportLinksConvProgress) {
+ $this->output( "Inserting $tuplesAdded tuples into $links_temp..." );
+ }
+ $dbw->query( implode("",$sqlWrite) );
+ $totalTuplesInserted += $tuplesAdded;
+ if ($reportLinksConvProgress)
+ $this->output( " done. Total $totalTuplesInserted tuples inserted.\n" );
+ $this->performanceLog( $totalTuplesInserted . " " . ($this->getMicroTime() - $baseTime) . "\n" );
+ }
+ }
+ $this->output( "$totalTuplesInserted valid titles and $numBadLinks invalid titles were processed.\n\n" );
+ $this->performanceLog( "$totalTuplesInserted valid titles and $numBadLinks invalid titles were processed.\n" );
+ $this->performanceLog( "Total execution time: " . ($this->getMicroTime() - $startTime) . " seconds.\n" );
+ if ( $logPerformance ) { fclose ( $fh ); }
+ }
+ #--------------------------------------------------------------------
+
+ if ( $overwriteLinksTable ) {
+ $dbConn = Database::newFromParams( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname );
+ if (!($dbConn->isOpen())) {
+ $this->output( "Opening connection to database failed.\n" );
+ return;
+ }
+ # Check for existing links_backup, and delete it if it exists.
+ $this->output( "Dropping backup links table if it exists..." );
+ $dbConn->query( "DROP TABLE IF EXISTS $links_backup", DB_MASTER);
+ $this->output( " done.\n" );
+
+ # Swap in the new table, and move old links table to links_backup
+ $this->output( "Swapping tables '$links' to '$links_backup'; '$links_temp' to '$links'..." );
+ $dbConn->query( "RENAME TABLE links TO $links_backup, $links_temp TO $links", DB_MASTER );
+ $this->output( " done.\n\n" );
+
+ $dbConn->close();
+ $this->output( "Conversion complete. The old table remains at $links_backup;\n" );
+ $this->output( "delete at your leisure.\n" );
+ } else {
+ $this->output( "Conversion complete. The converted table is at $links_temp;\n" );
+ $this->output( "the original links table is unchanged.\n" );
+ }
+ }
+
+ private function createTempTable() {
+ global $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname;
+ global $noKeys;
+ $dbConn = Database::newFromParams( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname );
+
+ if (!($dbConn->isOpen())) {
+ $this->output( "Opening connection to database failed.\n" );
+ return;
+ }
+ $links_temp = $dbConn->tableName( 'links_temp' );
+
+ $this->output( "Dropping temporary links table if it exists..." );
+ $dbConn->query( "DROP TABLE IF EXISTS $links_temp");
+ $this->output( " done.\n" );
+
+ $this->output( "Creating temporary links table..." );
+ if ( $noKeys ) {
+ $dbConn->query( "CREATE TABLE $links_temp ( " .
+ "l_from int(8) unsigned NOT NULL default '0', " .
+ "l_to int(8) unsigned NOT NULL default '0')");
+ } else {
+ $dbConn->query( "CREATE TABLE $links_temp ( " .
+ "l_from int(8) unsigned NOT NULL default '0', " .
+ "l_to int(8) unsigned NOT NULL default '0', " .
+ "UNIQUE KEY l_from(l_from,l_to), " .
+ "KEY (l_to))");
+ }
+ $this->output( " done.\n\n" );
+ }
+
+ private function performanceLog( $text ) {
+ global $logPerformance, $fh;
+ if ( $logPerformance ) {
+ fwrite( $fh, $text );
+ }
+ }
+
+ private function getMicroTime() { # return time in seconds, with microsecond accuracy
+ list($usec, $sec) = explode(" ", microtime());
+ return ((float)$usec + (float)$sec);
+ }
+}
-convertLinks();
+$maintClass = "ConvertLinks";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/convertUserOptions.php b/maintenance/convertUserOptions.php
new file mode 100644
index 00000000..657a82c1
--- /dev/null
+++ b/maintenance/convertUserOptions.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * Do each user sequentially, since accounts can't be deleted
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @ingroup Maintenance
+ */
+
+require_once( dirname(__FILE__) . '/Maintenance.php' );
+
+class ConvertUserOptions extends Maintenance {
+
+ private $mConversionCount = 0;
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Convert user options from old to new system";
+ }
+
+ public function execute() {
+ $this->output( "Beginning batch conversion of user options.\n" );
+ $id = 0;
+ $dbw = wfGetDB( DB_MASTER );
+
+ while ($id !== null) {
+ $idCond = 'user_id>'.$dbw->addQuotes( $id );
+ $optCond = "user_options!=".$dbw->addQuotes( '' ); // For compatibility
+ $res = $dbw->select( 'user', '*',
+ array( $optCond, $idCond ), __METHOD__,
+ array( 'LIMIT' => 50, 'FOR UPDATE' ) );
+ $id = $this->convertOptionBatch( $res, $dbw );
+ $dbw->commit();
+
+ wfWaitForSlaves( 1 );
+
+ if ($id)
+ $this->output( "--Converted to ID $id\n" );
+ }
+ $this->output( "Conversion done. Converted " . $this->mConversionCount . " user records.\n" );
+ }
+
+ function convertOptionBatch( $res, $dbw ) {
+ $id = null;
+ foreach ( $res as $row ) {
+ $this->mConversionCount++;
+
+ $u = User::newFromRow( $row );
+
+ $u->saveSettings();
+ $id = $row->user_id;
+ }
+
+ return $id;
+ }
+}
+
+$maintClass = "ConvertUserOptions";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/counter.php b/maintenance/counter.php
deleted file mode 100644
index 67575ec1..00000000
--- a/maintenance/counter.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-/**
- * Helper file for update.php
- *
- * @file
- * @ingroup Maintenance
- */
-
-function print_c($last, $current) {
- echo str_repeat( chr(8), strlen( $last ) ) . $current;
-}
-
diff --git a/maintenance/createAndPromote.php b/maintenance/createAndPromote.php
index a5a8f88d..391d1226 100644
--- a/maintenance/createAndPromote.php
+++ b/maintenance/createAndPromote.php
@@ -3,66 +3,73 @@
/**
* Maintenance script to create an account and grant it administrator rights
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
* @author Rob Church <robchur@gmail.com>
*/
-$options = array( 'help', 'bureaucrat' );
-require_once( 'commandLine.inc' );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-if( isset( $options['help'] ) ) {
- showHelp();
- exit( 1 );
-}
+class CreateAndPromote extends Maintenance {
-if( count( $args ) < 2 ) {
- echo( "Please provide a username and password for the new account.\n" );
- die( 1 );
-}
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Create a new user account with administrator rights";
+ $this->addOption( "bureaucrat", "Grant the account bureaucrat rights" );
+ $this->addArg( "username", "Username of new user" );
+ $this->addArg( "password", "Password to set" );
+ }
-$username = $args[0];
-$password = $args[1];
+ public function execute() {
+ $username = $this->getArg(0);
+ $password = $this->getArg(1);
+
+ $this->output( wfWikiID() . ": Creating and promoting User:{$username}..." );
+
+ $user = User::newFromName( $username );
+ if( !is_object( $user ) ) {
+ $this->error( "invalid username.", true );
+ } elseif( 0 != $user->idForName() ) {
+ $this->error( "account exists.", true );
+ }
-echo( wfWikiID() . ": Creating and promoting User:{$username}..." );
+ # Try to set the password
+ try {
+ $user->setPassword( $password );
+ } catch( PasswordError $pwe ) {
+ $this->error( $pwe->getText(), true );
+ }
-# Validate username and check it doesn't exist
-$user = User::newFromName( $username );
-if( !is_object( $user ) ) {
- echo( "invalid username.\n" );
- die( 1 );
-} elseif( 0 != $user->idForName() ) {
- echo( "account exists.\n" );
- die( 1 );
+ # Insert the account into the database
+ $user->addToDatabase();
+ $user->saveSettings();
+
+ # Promote user
+ $user->addGroup( 'sysop' );
+ if( $this->hasOption( 'bureaucrat' ) )
+ $user->addGroup( 'bureaucrat' );
+
+ # Increment site_stats.ss_users
+ $ssu = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
+ $ssu->doUpdate();
+
+ $this->output( "done.\n" );
+ }
}
-# Insert the account into the database
-$user->addToDatabase();
-$user->setPassword( $password );
-$user->saveSettings();
-
-# Promote user
-$user->addGroup( 'sysop' );
-if( isset( $option['bureaucrat'] ) )
- $user->addGroup( 'bureaucrat' );
-
-# Increment site_stats.ss_users
-$ssu = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
-$ssu->doUpdate();
-
-echo( "done.\n" );
-
-function showHelp() {
- echo( <<<EOT
-Create a new user account with administrator rights
-
-USAGE: php createAndPromote.php [--bureaucrat|--help] <username> <password>
-
- --bureaucrat
- Grant the account bureaucrat rights
- --help
- Show this help information
-
-EOT
- );
-} \ No newline at end of file
+$maintClass = "CreateAndPromote";
+require_once( DO_MAINTENANCE ); \ No newline at end of file
diff --git a/maintenance/deleteArchivedFiles.inc b/maintenance/deleteArchivedFiles.inc
deleted file mode 100644
index da1c14d5..00000000
--- a/maintenance/deleteArchivedFiles.inc
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
- * Support functions for the deleteArchivedFiles script
- *
- * @file
- * @ingroup Maintenance
- * @author Aaron Schulz
- */
-
-require_once( "$IP/includes/FileStore.php" );
-require_once( "$IP/includes/filerepo/File.php" );
-
-function DeleteArchivedFiles( $delete = false ) {
-
- # Data should come off the master, wrapped in a transaction
- $dbw = wfGetDB( DB_MASTER );
-
- $transaction = new FSTransaction();
- if( !FileStore::lock() ) {
- wfDebug( __METHOD__.": failed to acquire file store lock, aborting\n" );
- return false;
- }
-
- $tbl_arch = $dbw->tableName( 'filearchive' );
-
- # Get "active" revisions from the filearchive table
- echo( "Searching for and deleting archived files...\n" );
- $res = $dbw->query( "SELECT fa_id,fa_storage_group,fa_storage_key FROM $tbl_arch" );
- while( $row = $dbw->fetchObject( $res ) ) {
- $key = $row->fa_storage_key;
- $group = $row->fa_storage_group;
- $id = $row->fa_id;
-
- $store = FileStore::get( $group );
- if( $store ) {
- $path = $store->filePath( $key );
- $sha1 = substr( $key, 0, strcspn( $key, '.' ) );
- $inuse = $dbw->selectField( 'oldimage', '1',
- array( 'oi_sha1' => $sha1,
- 'oi_deleted & '.File::DELETED_FILE => File::DELETED_FILE ),
- __METHOD__, array( 'FOR UPDATE' ) );
- if ( $path && file_exists($path) && !$inuse ) {
- $transaction->addCommit( FSTransaction::DELETE_FILE, $path );
- $dbw->query( "DELETE FROM $tbl_arch WHERE fa_id = $id" );
- } else {
- echo( "Notice - file '$key' not found in group '$group'\n" );
- }
- } else {
- echo( "Notice - invalid file storage group '$group' for file '$key'\n" );
- }
- }
- echo( "done.\n" );
-
- $transaction->commit();
-}
diff --git a/maintenance/deleteArchivedFiles.php b/maintenance/deleteArchivedFiles.php
index 97dc5824..af4bbb74 100644
--- a/maintenance/deleteArchivedFiles.php
+++ b/maintenance/deleteArchivedFiles.php
@@ -3,29 +3,80 @@
/**
* Delete archived (non-current) files from the database
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
* @author Aaron Schulz
* Based on deleteOldRevisions.php by Rob Church
*/
-$options = array( 'delete', 'help' );
-require_once( 'commandLine.inc' );
-require_once( 'deleteArchivedFiles.inc' );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-echo( "Delete Archived Images\n\n" );
-
-if( @$options['help'] ) {
- ShowUsage();
-} else {
- DeleteArchivedFiles( @$options['delete'] );
-}
+class DeleteArchivedFiles extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Deletes all archived images.";
+ $this->addOption( 'delete', 'Perform the deletion' );
+ $this->addOption( 'force', 'Force deletion of rows from filearchive' );
+ }
-function ShowUsage() {
- echo( "Deletes all archived images.\n\n" );
- echo( "These images will no longer be restorable.\n\n" );
- echo( "Usage: php deleteArchivedRevisions.php [--delete|--help]\n\n" );
- echo( "delete : Performs the deletion\n" );
- echo( " help : Show this usage information\n" );
+ public function execute() {
+ if( !$this->hasOption('delete') ) {
+ $this->output( "Use --delete to actually confirm this script\n" );
+ return;
+ }
+ $force = $this->hasOption( 'force' );
+ # Data should come off the master, wrapped in a transaction
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin();
+ $tbl_arch = $dbw->tableName( 'filearchive' );
+ $repo = RepoGroup::singleton()->getLocalRepo();
+ # Get "active" revisions from the filearchive table
+ $this->output( "Searching for and deleting archived files...\n" );
+ $res = $dbw->query( "SELECT fa_id,fa_storage_group,fa_storage_key FROM $tbl_arch" );
+ $count = 0;
+ foreach( $res as $row ) {
+ $key = $row->fa_storage_key;
+ $group = $row->fa_storage_group;
+ $id = $row->fa_id;
+ $path = $repo->getZonePath( 'deleted' ).'/'.$repo->getDeletedHashPath($key).$key;
+ $sha1 = substr( $key, 0, strcspn( $key, '.' ) );
+ // Check if the file is used anywhere...
+ $inuse = $dbw->selectField( 'oldimage', '1',
+ array( 'oi_sha1' => $sha1,
+ 'oi_deleted & '.File::DELETED_FILE => File::DELETED_FILE ),
+ __METHOD__,
+ array( 'FOR UPDATE' )
+ );
+ if ( $path && file_exists($path) && !$inuse ) {
+ unlink($path); // delete
+ $count++;
+ $dbw->query( "DELETE FROM $tbl_arch WHERE fa_id = $id" );
+ } else {
+ $this->output( "Notice - file '$key' not found in group '$group'\n" );
+ if ( $force ) {
+ $this->output( "Got --force, deleting DB entry\n" );
+ $dbw->query( "DELETE FROM $tbl_arch WHERE fa_id = $id" );
+ }
+ }
+ }
+ $dbw->commit();
+ $this->output( "Done! [$count file(s)]\n" );
+ }
}
+$maintClass = "DeleteArchivedFiles";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/deleteArchivedRevisions.inc b/maintenance/deleteArchivedRevisions.inc
deleted file mode 100644
index 67e4c5a2..00000000
--- a/maintenance/deleteArchivedRevisions.inc
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-/**
- * Support functions for the deleteArchivedRevisions script
- *
- * @file
- * @ingroup Maintenance
- * @author Aaron Schulz
- */
-
-require_once( 'purgeOldText.inc' );
-
-function DeleteArchivedRevisions( $delete = false ) {
-
- # Data should come off the master, wrapped in a transaction
- $dbw = wfGetDB( DB_MASTER );
- $dbw->begin();
-
- $tbl_arch = $dbw->tableName( 'archive' );
- # Delete as appropriate
- echo( "Deleting archived revisions..." );
- $dbw->query( "TRUNCATE TABLE $tbl_arch" );
- echo( "done.\n" );
-
- $delete = $dbw->affectedRows() != 0;
-
- # This bit's done
- # Purge redundant text records
- $dbw->commit();
- if( $delete ) {
- PurgeRedundantText( true );
- }
-
-}
diff --git a/maintenance/deleteArchivedRevisions.php b/maintenance/deleteArchivedRevisions.php
index 87eebfad..c3f8bf11 100644
--- a/maintenance/deleteArchivedRevisions.php
+++ b/maintenance/deleteArchivedRevisions.php
@@ -3,29 +3,66 @@
/**
* Delete archived (deleted from public) revisions from the database
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
* @author Aaron Schulz
* Shamelessly stolen from deleteOldRevisions.php by Rob Church :)
*/
-$options = array( 'delete', 'help' );
-require_once( 'commandLine.inc' );
-require_once( 'deleteArchivedRevisions.inc' );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-echo( "Delete Archived Revisions\n\n" );
-
-if( @$options['help'] ) {
- ShowUsage();
-} else {
- DeleteArchivedRevisions( @$options['delete'] );
-}
+class DeleteArchivedRevisions extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Deletes all archived revisions\nThese revisions will no longer be restorable";
+ $this->addOption( 'delete', 'Performs the deletion' );
+ }
-function ShowUsage() {
- echo( "Deletes all archived revisions.\n\n" );
- echo( "These revisions will no longer be restorable.\n\n" );
- echo( "Usage: php deleteArchivedRevisions.php [--delete|--help]\n\n" );
- echo( "delete : Performs the deletion\n" );
- echo( " help : Show this usage information\n" );
+ public function execute() {
+ $this->output( "Delete archived revisions\n\n" );
+ # Data should come off the master, wrapped in a transaction
+ $dbw = wfGetDB( DB_MASTER );
+ if( $this->hasOption('delete') ) {
+ $dbw->begin();
+
+ $tbl_arch = $dbw->tableName( 'archive' );
+
+ # Delete as appropriate
+ $this->output( "Deleting archived revisions... " );
+ $dbw->query( "TRUNCATE TABLE $tbl_arch" );
+
+ $count = $dbw->affectedRows();
+ $deletedRows = $count != 0;
+
+ $this->output( "done. $count revisions deleted.\n" );
+
+ # This bit's done
+ # Purge redundant text records
+ $dbw->commit();
+ if( $deletedRows ) {
+ $this->purgeRedundantText( true );
+ }
+ } else {
+ $res = $dbw->selectRow( 'archive', 'COUNT(*) as count', array(), __FUNCTION__ );
+ $this->output( "Found {$res->count} revisions to delete.\n" );
+ $this->output( "Please run the script again with the --delete option to really delete the revisions.\n" );
+ }
+ }
}
+$maintClass = "DeleteArchivedRevisions";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/deleteBatch.php b/maintenance/deleteBatch.php
index 5aeea781..56afd86c 100644
--- a/maintenance/deleteBatch.php
+++ b/maintenance/deleteBatch.php
@@ -1,5 +1,4 @@
<?php
-
/**
* Deletes a batch of pages
* Usage: php deleteBatch.php [-u <user>] [-r <reason>] [-i <interval>] [listfile]
@@ -10,89 +9,104 @@
* <reason> is the delete reason
* <interval> is the number of seconds to sleep for after each delete
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-
-$oldCwd = getcwd();
-$optionsWithArgs = array( 'u', 'r', 'i' );
-require_once( 'commandLine.inc' );
-
-chdir( $oldCwd );
-
-# Options processing
-
-$filename = 'php://stdin';
-$user = 'Delete page script';
-$reason = '';
-$interval = 0;
-
-if ( isset( $args[0] ) ) {
- $filename = $args[0];
-}
-if ( isset( $options['u'] ) ) {
- $user = $options['u'];
-}
-if ( isset( $options['r'] ) ) {
- $reason = $options['r'];
-}
-if ( isset( $options['i'] ) ) {
- $interval = $options['i'];
-}
-
-$wgUser = User::newFromName( $user );
-
-
-# Setup complete, now start
-
-$file = fopen( $filename, 'r' );
-if ( !$file ) {
- print "Unable to read file, exiting\n";
- exit;
-}
-
-$dbw = wfGetDB( DB_MASTER );
-
-for ( $linenum = 1; !feof( $file ); $linenum++ ) {
- $line = trim( fgets( $file ) );
- if ( $line == '' ) {
- continue;
- }
- $page = Title::newFromText( $line );
- if ( is_null( $page ) ) {
- print "Invalid title '$line' on line $linenum\n";
- continue;
+
+require_once( dirname(__FILE__) . '/Maintenance.php' );
+
+class DeleteBatch extends Maintenance {
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Deletes a batch of pages";
+ $this->addOption( 'u', "User to perform deletion", false, true );
+ $this->addOption( 'r', "Reason to delete page", false, true );
+ $this->addOption( 'i', "Interval to sleep between deletions" );
+ $this->addArg( 'listfile', 'File with titles to delete, separated by newlines', false );
}
- if( !$page->exists() ) {
- print "Skipping nonexistent page '$line'\n";
- continue;
- }
-
-
- print $page->getPrefixedText();
- $dbw->begin();
- if( $page->getNamespace() == NS_FILE ) {
- $art = new ImagePage( $page );
- $img = wfFindFile( $art->mTitle );
- if( !$img || !$img->delete( $reason ) ) {
- print "FAILED to delete image file... ";
+
+ public function execute() {
+ global $wgUser;
+
+ # Change to current working directory
+ $oldCwd = getcwd();
+ chdir( $oldCwd );
+
+ # Options processing
+ $user = $this->getOption( 'u', 'Delete page script' );
+ $reason = $this->getOption( 'r', '' );
+ $interval = $this->getOption( 'i', 0 );
+ if( $this->hasArg() ) {
+ $file = fopen( $this->getArg(), 'r' );
+ } else {
+ $file = $this->getStdin();
}
- } else {
- $art = new Article( $page );
- }
- $success = $art->doDeleteArticle( $reason );
- $dbw->immediateCommit();
- if ( $success ) {
- print "\n";
- } else {
- print " FAILED to delete image page\n";
- }
- if ( $interval ) {
- sleep( $interval );
+ # Setup
+ if( !$file ) {
+ $this->error( "Unable to read file, exiting", true );
+ }
+ $wgUser = User::newFromName( $user );
+ $dbw = wfGetDB( DB_MASTER );
+
+ # Handle each entry
+ for ( $linenum = 1; !feof( $file ); $linenum++ ) {
+ $line = trim( fgets( $file ) );
+ if ( $line == '' ) {
+ continue;
+ }
+ $page = Title::newFromText( $line );
+ if ( is_null( $page ) ) {
+ $this->output( "Invalid title '$line' on line $linenum\n" );
+ continue;
+ }
+ if( !$page->exists() ) {
+ $this->output( "Skipping nonexistent page '$line'\n" );
+ continue;
+ }
+
+
+ $this->output( $page->getPrefixedText() );
+ $dbw->begin();
+ if( $page->getNamespace() == NS_FILE ) {
+ $art = new ImagePage( $page );
+ $img = wfFindFile( $art->mTitle );
+ if( !$img || !$img->delete( $reason ) ) {
+ $this->output( "FAILED to delete image file... " );
+ }
+ } else {
+ $art = new Article( $page );
+ }
+ $success = $art->doDeleteArticle( $reason );
+ $dbw->commit();
+ if ( $success ) {
+ $this->output( "\n" );
+ } else {
+ $this->output( " FAILED to delete article\n" );
+ }
+
+ if ( $interval ) {
+ sleep( $interval );
+ }
+ wfWaitForSlaves( 5 );
+}
}
- wfWaitForSlaves( 5 );
}
-
-
+$maintClass = "DeleteBatch";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/deleteDefaultMessages.php b/maintenance/deleteDefaultMessages.php
index 77e85741..3f0e1b1c 100644
--- a/maintenance/deleteDefaultMessages.php
+++ b/maintenance/deleteDefaultMessages.php
@@ -1,48 +1,72 @@
<?php
-
/**
* Deletes all pages in the MediaWiki namespace which were last edited by
* "MediaWiki default".
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-if ( !defined( 'MEDIAWIKI' ) ) {
- require_once( 'commandLine.inc' );
- deleteDefaultMessages();
-}
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-function deleteDefaultMessages() {
- $user = 'MediaWiki default';
- $reason = 'No longer required';
+class DeleteDefaultMessages extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Deletes all pages in the MediaWiki namespace" .
+ " which were last edited by \"MediaWiki default\"";
+ }
- global $wgUser;
- $wgUser = User::newFromName( $user );
- $wgUser->addGroup( 'bot' );
+ public function execute() {
+ self::reallyExecute();
+ }
- $dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select( array( 'page', 'revision' ),
- array( 'page_namespace', 'page_title' ),
- array(
- 'page_namespace' => NS_MEDIAWIKI,
- 'page_latest=rev_id',
- 'rev_user_text' => 'MediaWiki default',
- )
- );
-
- $dbw = wfGetDB( DB_MASTER );
-
- while ( $row = $dbr->fetchObject( $res ) ) {
- if ( function_exists( 'wfWaitForSlaves' ) ) {
- wfWaitForSlaves( 5 );
+ public static function reallyExecute() {
+ $user = 'MediaWiki default';
+ $reason = 'No longer required';
+
+ global $wgUser;
+ $wgUser = User::newFromName( $user );
+ $wgUser->addGroup( 'bot' );
+
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select( array( 'page', 'revision' ),
+ array( 'page_namespace', 'page_title' ),
+ array(
+ 'page_namespace' => NS_MEDIAWIKI,
+ 'page_latest=rev_id',
+ 'rev_user_text' => 'MediaWiki default',
+ )
+ );
+
+ $dbw = wfGetDB( DB_MASTER );
+
+ foreach ( $res as $row ) {
+ if ( function_exists( 'wfWaitForSlaves' ) ) {
+ wfWaitForSlaves( 5 );
+ }
+ $dbw->ping();
+ $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+ $article = new Article( $title );
+ $dbw->begin();
+ $article->doDeleteArticle( $reason );
+ $dbw->commit();
}
- $dbw->ping();
- $title = Title::makeTitle( $row->page_namespace, $row->page_title );
- $article = new Article( $title );
- $dbw->begin();
- $article->doDeleteArticle( $reason );
- $dbw->commit();
}
}
+$maintClass = "DeleteDefaultMessages";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/deleteImageMemcached.php b/maintenance/deleteImageMemcached.php
index 2c3afa86..9becddb8 100644
--- a/maintenance/deleteImageMemcached.php
+++ b/maintenance/deleteImageMemcached.php
@@ -1,33 +1,43 @@
<?php
/**
- * This script delete image information from memcached.
+ * This script delete image information from the cache.
*
* Usage example:
- * php deleteImageMemcached.php --until "2005-09-05 00:00:00" --sleep 0 --report 10
+ * php deleteImageMemcached.php --until "2005-09-05 00:00:00" --sleep 0
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
*
- * @file
* @ingroup Maintenance
*/
-$optionsWithArgs = array( 'until', 'sleep', 'report' );
-
-require_once 'commandLine.inc';
-
-/**
- * @ingroup Maintenance
- */
-class DeleteImageCache {
- var $until, $sleep, $report;
+require_once( dirname(__FILE__) . '/Maintenance.php' );
- function DeleteImageCache( $until, $sleep, $report ) {
- $this->until = $until;
- $this->sleep = $sleep;
- $this->report = $report;
+class DeleteImageCache extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Delete image information from the cache";
+ $this->addOption( 'sleep', 'How many seconds to sleep between deletions', true, true );
+ $this->addOption( 'until', 'Timestamp to delete all entries prior to', true, true );
}
- function main() {
+ public function execute() {
global $wgMemc;
- $fname = 'DeleteImageCache::main';
+
+ $until = preg_replace( "/[^\d]/", '', $this->getOption('until') );
+ $sleep = (int)$this->getOption('sleep') * 1000; // milliseconds
ini_set( 'display_errors', false );
@@ -35,38 +45,31 @@ class DeleteImageCache {
$res = $dbr->select( 'image',
array( 'img_name' ),
- array( "img_timestamp < {$this->until}" ),
- $fname
+ array( "img_timestamp < {$until}" ),
+ __METHOD__
);
$i = 0;
$total = $this->getImageCount();
- while ( $row = $dbr->fetchObject( $res ) ) {
+ foreach ( $res as $row ) {
if ($i % $this->report == 0)
- printf("%s: %13s done (%s)\n", wfWikiID(), "$i/$total", wfPercent( $i / $total * 100 ));
+ $this->output( sprintf("%s: %13s done (%s)\n", wfWikiID(), "$i/$total", wfPercent( $i / $total * 100 ) ) );
$md5 = md5( $row->img_name );
$wgMemc->delete( wfMemcKey( 'Image', $md5 ) );
- if ($this->sleep != 0)
- usleep( $this->sleep );
+ if ($sleep != 0)
+ usleep( $sleep );
++$i;
}
}
- function getImageCount() {
- $fname = 'DeleteImageCache::getImageCount';
-
+ private function getImageCount() {
$dbr = wfGetDB( DB_SLAVE );
- return $dbr->selectField( 'image', 'COUNT(*)', array(), $fname );
+ return $dbr->selectField( 'image', 'COUNT(*)', array(), __METHOD__ );
}
}
-$until = preg_replace( "/[^\d]/", '', $options['until'] );
-$sleep = (int)$options['sleep'] * 1000; // milliseconds
-$report = (int)$options['report'];
-
-$dic = new DeleteImageCache( $until, $sleep, $report );
-$dic->main();
-
+$maintClass = "DeleteImageCache";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/deleteOldRevisions.inc b/maintenance/deleteOldRevisions.inc
deleted file mode 100644
index b681b9d0..00000000
--- a/maintenance/deleteOldRevisions.inc
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-/**
- * Support functions for the deleteOldRevisions script
- *
- * @file
- * @ingroup Maintenance
- * @author Rob Church <robchur@gmail.com>
- */
-
-require_once( 'purgeOldText.inc' );
-
-function DeleteOldRevisions( $delete = false, $args = array() ) {
-
- # Data should come off the master, wrapped in a transaction
- $dbw = wfGetDB( DB_MASTER );
- $dbw->begin();
-
- $tbl_pag = $dbw->tableName( 'page' );
- $tbl_rev = $dbw->tableName( 'revision' );
-
- $pageIdClause = '';
- $revPageClause = '';
-
- # If a list of page_ids was provided, limit results to that set of page_ids
- if ( sizeof( $args ) > 0 ) {
- $pageIdList = implode( ',', $args );
- $pageIdClause = " WHERE page_id IN ({$pageIdList})";
- $revPageClause = " AND rev_page IN ({$pageIdList})";
- echo( "Limiting to {$tbl_pag}.page_id IN ({$pageIdList})\n" );
- }
-
- # Get "active" revisions from the page table
- echo( "Searching for active revisions..." );
- $res = $dbw->query( "SELECT page_latest FROM $tbl_pag{$pageIdClause}" );
- while( $row = $dbw->fetchObject( $res ) ) {
- $cur[] = $row->page_latest;
- }
- echo( "done.\n" );
-
- # Get all revisions that aren't in this set
- echo( "Searching for inactive revisions..." );
- $set = implode( ', ', $cur );
- $res = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_id NOT IN ( $set ){$revPageClause}" );
- while( $row = $dbw->fetchObject( $res ) ) {
- $old[] = $row->rev_id;
- }
- echo( "done.\n" );
-
- # Inform the user of what we're going to do
- $count = count( $old );
- echo( "$count old revisions found.\n" );
-
- # Delete as appropriate
- if( $delete && $count ) {
- echo( "Deleting..." );
- $set = implode( ', ', $old );
- $dbw->query( "DELETE FROM $tbl_rev WHERE rev_id IN ( $set )" );
- echo( "done.\n" );
- }
-
- # This bit's done
- # Purge redundant text records
- $dbw->commit();
- if( $delete ) {
- PurgeRedundantText( true );
- }
-
-}
diff --git a/maintenance/deleteOldRevisions.php b/maintenance/deleteOldRevisions.php
index c283c607..1f4dc4c9 100644
--- a/maintenance/deleteOldRevisions.php
+++ b/maintenance/deleteOldRevisions.php
@@ -3,27 +3,99 @@
/**
* Delete old (non-current) revisions from the database
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
* @author Rob Church <robchur@gmail.com>
*/
-$options = array( 'delete', 'help' );
-require_once( 'commandLine.inc' );
-require_once( 'deleteOldRevisions.inc' );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-echo( "Delete Old Revisions\n\n" );
+class DeleteOldRevisions extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Delete old (non-current) revisions from the database";
+ $this->addOption( 'delete', 'Actually perform the deletion' );
+ $this->addOption( 'page_id', 'List of page ids to work on', false );
+ }
+
+ public function execute() {
+ $this->output( "Delete old revisions\n\n" );
+ $this->doDelete( $this->hasOption( 'delete' ), $this->mArgs );
+ }
+
+ function doDelete( $delete = false, $args = array() ) {
-if( @$options['help'] ) {
- ShowUsage();
-} else {
- DeleteOldRevisions( @$options['delete'], $args );
+ # Data should come off the master, wrapped in a transaction
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin();
+
+ $tbl_pag = $dbw->tableName( 'page' );
+ $tbl_rev = $dbw->tableName( 'revision' );
+
+ $pageIdClause = '';
+ $revPageClause = '';
+
+ # If a list of page_ids was provided, limit results to that set of page_ids
+ if ( sizeof( $args ) > 0 ) {
+ $pageIdList = implode( ',', $args );
+ $pageIdClause = " WHERE page_id IN ({$pageIdList})";
+ $revPageClause = " AND rev_page IN ({$pageIdList})";
+ $this->output( "Limiting to {$tbl_pag}.page_id IN ({$pageIdList})\n" );
+ }
+
+ # Get "active" revisions from the page table
+ $this->output( "Searching for active revisions..." );
+ $res = $dbw->query( "SELECT page_latest FROM $tbl_pag{$pageIdClause}" );
+ foreach( $res as $row ) {
+ $cur[] = $row->page_latest;
+ }
+ $this->output( "done.\n" );
+
+ # Get all revisions that aren't in this set
+ $old = array();
+ $this->output( "Searching for inactive revisions..." );
+ $set = implode( ', ', $cur );
+ $res = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_id NOT IN ( $set ){$revPageClause}" );
+ foreach( $res as $row ) {
+ $old[] = $row->rev_id;
+ }
+ $this->output( "done.\n" );
+
+ # Inform the user of what we're going to do
+ $count = count( $old );
+ $this->output( "$count old revisions found.\n" );
+
+ # Delete as appropriate
+ if( $delete && $count ) {
+ $this->output( "Deleting..." );
+ $set = implode( ', ', $old );
+ $dbw->query( "DELETE FROM $tbl_rev WHERE rev_id IN ( $set )" );
+ $this->output( "done.\n" );
+ }
+
+ # This bit's done
+ # Purge redundant text records
+ $dbw->commit();
+ if( $delete ) {
+ $this->purgeRedundantText( true );
+ }
+ }
}
-function ShowUsage() {
- echo( "Deletes non-current revisions from the database.\n\n" );
- echo( "Usage: php deleteOldRevisions.php [--delete|--help] [<page_id> ...]\n\n" );
- echo( "delete : Performs the deletion\n" );
- echo( " help : Show this usage information\n" );
-}
+$maintClass = "DeleteOldRevisions";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/deleteOrphanedRevisions.inc.php b/maintenance/deleteOrphanedRevisions.inc.php
deleted file mode 100644
index 6678d5b8..00000000
--- a/maintenance/deleteOrphanedRevisions.inc.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-/**
- * Support functions for the deleteOrphanedRevisions maintenance script
- *
- * @file
- * @ingroup Maintenance
- * @author Rob Church <robchur@gmail.com>
- */
-
-/**
- * Delete one or more revisions from the database
- * Do this inside a transaction
- *
- * @param $id Array of revision id values
- * @param $db Database class (needs to be a master)
- */
-function deleteRevisions( $id, &$dbw ) {
- if( !is_array( $id ) )
- $id = array( $id );
- $dbw->delete( 'revision', array( 'rev_id' => $id ), 'deleteRevision' );
-}
-
-/**
- * Spit out script usage information and exit
- */
-function showUsage() {
- echo( "Finds revisions which refer to nonexisting pages and deletes them from the database\n" );
- echo( "USAGE: php deleteOrphanedRevisions.php [--report]\n\n" );
- echo( " --report : Prints out a count of affected revisions but doesn't delete them\n\n" );
-}
-
diff --git a/maintenance/deleteOrphanedRevisions.php b/maintenance/deleteOrphanedRevisions.php
index 78441f8e..1146befb 100644
--- a/maintenance/deleteOrphanedRevisions.php
+++ b/maintenance/deleteOrphanedRevisions.php
@@ -4,51 +4,87 @@
* Maintenance script to delete revisions which refer to a nonexisting page
* Sometimes manual deletion done in a rush leaves crap in the database
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
* @author Rob Church <robchur@gmail.com>
* @todo More efficient cleanup of text records
*/
-
-$options = array( 'report', 'help' );
-require_once( 'commandLine.inc' );
-require_once( 'deleteOrphanedRevisions.inc.php' );
-echo( "Delete Orphaned Revisions\n" );
-
-if( isset( $options['help'] ) )
- showUsage();
-
-$report = isset( $options['report'] );
-
-$dbw = wfGetDB( DB_MASTER );
-$dbw->immediateBegin();
-extract( $dbw->tableNames( 'page', 'revision' ) );
-
-# Find all the orphaned revisions
-echo( "Checking for orphaned revisions..." );
-$sql = "SELECT rev_id FROM {$revision} LEFT JOIN {$page} ON rev_page = page_id WHERE page_namespace IS NULL";
-$res = $dbw->query( $sql, 'deleteOrphanedRevisions' );
-
-# Stash 'em all up for deletion (if needed)
-while( $row = $dbw->fetchObject( $res ) )
- $revisions[] = $row->rev_id;
-$dbw->freeResult( $res );
-$count = count( $revisions );
-echo( "found {$count}.\n" );
-
-# Nothing to do?
-if( $report || $count == 0 ) {
- $dbw->immediateCommit();
- exit();
-}
-# Delete each revision
-echo( "Deleting..." );
-deleteRevisions( $revisions, $dbw );
-echo( "done.\n" );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
+
+class DeleteOrphanedRevisions extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Maintenance script to delete revisions which refer to a nonexisting page";
+ $this->addOption( 'report', 'Prints out a count of affected revisions but doesn\'t delete them' );
+ }
+
+ public function execute() {
+ $this->output( "Delete Orphaned Revisions\n" );
+
+ $report = $this->hasOption('report');
+
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin();
+ list( $page, $revision ) = $dbw->tableNamesN( 'page', 'revision' );
+
+ # Find all the orphaned revisions
+ $this->output( "Checking for orphaned revisions..." );
+ $sql = "SELECT rev_id FROM {$revision} LEFT JOIN {$page} ON rev_page = page_id WHERE page_namespace IS NULL";
+ $res = $dbw->query( $sql, 'deleteOrphanedRevisions' );
+
+ # Stash 'em all up for deletion (if needed)
+ $revisions = array();
+ foreach( $res as $row )
+ $revisions[] = $row->rev_id;
+ $dbw->freeResult( $res );
+ $count = count( $revisions );
+ $this->output( "found {$count}.\n" );
+
+ # Nothing to do?
+ if( $report || $count == 0 ) {
+ $dbw->commit();
+ exit(0);
+ }
+
+ # Delete each revision
+ $this->output( "Deleting..." );
+ $this->deleteRevs( $revisions, $dbw );
+ $this->output( "done.\n" );
+
+ # Close the transaction and call the script to purge unused text records
+ $dbw->commit();
+ $this->purgeRedundantText( true );
+ }
+
+ /**
+ * Delete one or more revisions from the database
+ * Do this inside a transaction
+ *
+ * @param $id Array of revision id values
+ * @param $db Database class (needs to be a master)
+ */
+ private function deleteRevs( $id, &$dbw ) {
+ if( !is_array( $id ) )
+ $id = array( $id );
+ $dbw->delete( 'revision', array( 'rev_id' => $id ), __METHOD__ );
+ }
+}
-# Close the transaction and call the script to purge unused text records
-$dbw->immediateCommit();
-require_once( 'purgeOldText.inc' );
-PurgeRedundantText( true );
+$maintClass = "DeleteOrphanedRevisions";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/deleteRevision.php b/maintenance/deleteRevision.php
index 0c203ab0..5dc0b59f 100644
--- a/maintenance/deleteRevision.php
+++ b/maintenance/deleteRevision.php
@@ -2,46 +2,80 @@
/**
* Delete one or more revisions by moving them to the archive table.
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-require_once( 'commandLine.inc' );
-
-$dbw = wfGetDB( DB_MASTER );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-if ( count( $args ) == 0 ) {
- echo "Usage: php deleteRevision.php <revid> [<revid> ...]\n";
- exit(1);
-}
-
-echo "Deleting revision(s) " . implode( ',', $args ) . " from ".wfWikiID()."...\n";
+class DeleteRevision extends Maintenance {
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Delete one or more revisions by moving them to the archive table";
+ }
+
+ public function execute() {
+ if( count( $this->mArgs ) == 0 ) {
+ $this->error( "No revisions specified", true );
+ }
-$affected = 0;
-foreach ( $args as $revID ) {
- $dbw->insertSelect( 'archive', array( 'page', 'revision' ),
- array(
- 'ar_namespace' => 'page_namespace',
- 'ar_title' => 'page_title',
- 'ar_comment' => 'rev_comment',
- 'ar_user' => 'rev_user',
- 'ar_user_text' => 'rev_user_text',
- 'ar_timestamp' => 'rev_timestamp',
- 'ar_minor_edit' => 'rev_minor_edit',
- 'ar_rev_id' => 'rev_id',
- 'ar_text_id' => 'rev_text_id',
- ), array(
- 'rev_id' => $revID,
- 'page_id = rev_page'
- ), $fname
- );
- if ( !$dbw->affectedRows() ) {
- echo "Revision $revID not found\n";
- } else {
- $affected += $dbw->affectedRows();
- $dbw->delete( 'revision', array( 'rev_id' => $revID ) );
+ $this->output( "Deleting revision(s) " . implode( ',', $this->mArgs ) .
+ " from " . wfWikiID() . "...\n" );
+ $dbw = wfGetDB( DB_MASTER );
+
+ $affected = 0;
+ foreach ( $this->mArgs as $revID ) {
+ $dbw->insertSelect( 'archive', array( 'page', 'revision' ),
+ array(
+ 'ar_namespace' => 'page_namespace',
+ 'ar_title' => 'page_title',
+ 'ar_page_id' => 'page_id',
+ 'ar_comment' => 'rev_comment',
+ 'ar_user' => 'rev_user',
+ 'ar_user_text' => 'rev_user_text',
+ 'ar_timestamp' => 'rev_timestamp',
+ 'ar_minor_edit' => 'rev_minor_edit',
+ 'ar_rev_id' => 'rev_id',
+ 'ar_text_id' => 'rev_text_id',
+ 'ar_deleted' => 'rev_deleted',
+ 'ar_len' => 'rev_len',
+ ), array(
+ 'rev_id' => $revID,
+ 'page_id = rev_page'
+ ), __METHOD__
+ );
+ if ( !$dbw->affectedRows() ) {
+ $this->output( "Revision $revID not found\n" );
+ } else {
+ $affected += $dbw->affectedRows();
+ $pageID = $dbw->selectField( 'revision', 'rev_page', array( 'rev_id' => $revID ), __METHOD__ );
+ $pageLatest = $dbw->selectField( 'page', 'page_latest', array( 'page_id' => $pageID ), __METHOD__ );
+ $dbw->delete( 'revision', array( 'rev_id' => $revID ) );
+ if ( $pageLatest == $revID ) {
+ // Database integrity
+ $newLatest = $dbw->selectField( 'revision', 'rev_id', array( 'rev_page' => $pageID ), __METHOD__, array( 'ORDER BY' => 'rev_timestamp DESC' ) );
+ $dbw->update( 'page', array( 'page_latest' => $newLatest ), array( 'page_id' => $pageID ), __METHOD__ );
+ }
+ }
+ }
+ $this->output( "Deleted $affected revisions\n" );
}
}
-print "Deleted $affected revisions\n";
-
+$maintClass = "DeleteRevision";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/deleteSelfExternals.php b/maintenance/deleteSelfExternals.php
new file mode 100644
index 00000000..1ab2839e
--- /dev/null
+++ b/maintenance/deleteSelfExternals.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * We want to make this whole thing as seamless as possible to the
+ * end-user. Unfortunately, we can't do _all_ of the work in the class
+ * because A) included files are not in global scope, but in the scope
+ * of their caller, and B) MediaWiki has way too many globals. So instead
+ * we'll kinda fake it, and do the requires() inline. <3 PHP
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @ingroup Maintenance
+ */
+
+require_once( "Maintenance.php" );
+
+
+class DeleteSelfExternals extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = 'Delete self-references to $wgServer from externallinks';
+ $this->mBatchSize = 1000;
+ }
+
+ public function execute() {
+ global $wgServer;
+ $this->output( "Deleting self externals from $wgServer\n" );
+ $db = wfGetDB(DB_MASTER);
+ while (1) {
+ wfWaitForSlaves( 2 );
+ $db->commit();
+ $q = $db->limitResult( "DELETE /* deleteSelfExternals */ FROM externallinks WHERE el_to"
+ . $db->buildLike( $wgServer . '/', $db->anyString() ), $this->mBatchSize );
+ $this->output( "Deleting a batch\n" );
+ $db->query($q);
+ if (!$db->affectedRows()) exit(0);
+ }
+ }
+}
+
+$maintClass = "DeleteSelfExternals";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/doMaintenance.php b/maintenance/doMaintenance.php
new file mode 100644
index 00000000..008c5b87
--- /dev/null
+++ b/maintenance/doMaintenance.php
@@ -0,0 +1,102 @@
+<?php
+/**
+ * We want to make this whole thing as seamless as possible to the
+ * end-user. Unfortunately, we can't do _all_ of the work in the class
+ * because A) included files are not in global scope, but in the scope
+ * of their caller, and B) MediaWiki has way too many globals. So instead
+ * we'll kinda fake it, and do the requires() inline. <3 PHP
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @author Chad Horohoe <chad@anyonecanedit.org>
+ * @file
+ * @ingroup Maintenance
+ */
+
+if ( !defined( 'DO_MAINTENANCE' ) ) {
+ echo "This file must be included after Maintenance.php\n";
+ exit( 1 );
+}
+
+if( !$maintClass || !class_exists( $maintClass ) ) {
+ echo "\$maintClass is not set or is set to a non-existent class.\n";
+ exit( 1 );
+}
+
+if( defined( 'MW_NO_SETUP' ) ) {
+ return;
+}
+
+// Get an object to start us off
+$maintenance = new $maintClass();
+
+// Basic sanity checks and such
+$maintenance->setup();
+
+// We used to call this variable $self, but it was moved
+// to $maintenance->mSelf. Keep that here for b/c
+$self = $maintenance->getName();
+
+# Setup the profiler
+if ( file_exists( "$IP/StartProfiler.php" ) ) {
+ require_once( "$IP/StartProfiler.php" );
+} else {
+ require_once( "$IP/includes/ProfilerStub.php" );
+}
+
+// Some other requires
+require_once( "$IP/includes/AutoLoader.php" );
+require_once( "$IP/includes/Defines.php" );
+
+// Load settings, using wikimedia-mode if needed
+// Fixme: replace this hack with general farm-friendly code
+if( file_exists( "$IP/wmf-config/wikimedia-mode" ) ) {
+ # TODO FIXME! Wikimedia-specific stuff needs to go away to an ext
+ # Maybe a hook?
+ global $cluster;
+ $wgWikiFarm = true;
+ $cluster = 'pmtpa';
+ require_once( "$IP/includes/SiteConfiguration.php" );
+ require( "$IP/wmf-config/wgConf.php" );
+ $maintenance->loadWikimediaSettings();
+ require( $IP.'/wmf-config/CommonSettings.php' );
+} else {
+ require_once( $maintenance->loadSettings() );
+}
+if ( $maintenance->getDbType() === Maintenance::DB_ADMIN &&
+ is_readable( "$IP/AdminSettings.php" ) )
+{
+ require( "$IP/AdminSettings.php" );
+}
+$maintenance->finalSetup();
+// Some last includes
+require_once( "$IP/includes/Setup.php" );
+require_once( "$IP/maintenance/install-utils.inc" );
+
+// Much much faster startup than creating a title object
+$wgTitle = null;
+
+// Do the work
+try {
+ $maintenance->execute();
+
+ // Potentially debug globals
+ $maintenance->globals();
+} catch( MWException $mwe ) {
+ echo( $mwe->getText() );
+ exit( 1 );
+}
+
diff --git a/maintenance/dumpBackup.php b/maintenance/dumpBackup.php
index 0e28734c..3f4530ed 100644
--- a/maintenance/dumpBackup.php
+++ b/maintenance/dumpBackup.php
@@ -26,7 +26,7 @@ $originalDir = getcwd();
$optionsWithArgs = array( 'pagelist', 'start', 'end' );
-require_once( 'commandLine.inc' );
+require_once( dirname(__FILE__) . '/commandLine.inc' );
require_once( 'backup.inc' );
$dumper = new BackupDumper( $argv );
@@ -69,30 +69,30 @@ if( isset( $options['full'] ) ) {
$dumper->dump( WikiExporter::LOGS );
} else {
$dumper->progress( <<<ENDS
-This script dumps the wiki page database into an XML interchange wrapper
-format for export or backup.
+This script dumps the wiki page or logging database into an
+XML interchange wrapper format for export or backup.
XML output is sent to stdout; progress reports are sent to stderr.
Usage: php dumpBackup.php <action> [<options>]
Actions:
- --full Dump complete history of every page.
- --current Includes only the latest revision of each page.
- --logs Dump action logs for every page.
+ --full Dump all revisions of every page.
+ --current Dump only the latest revision of every page.
+ --logs Dump all log events.
Options:
--quiet Don't dump status reports to stderr.
--report=n Report position and speed after every n pages processed.
(Default: 100)
--server=h Force reading from MySQL server h
- --start=n Start from page_id n
- --end=n Stop before page_id n (exclusive)
+ --start=n Start from page_id or log_id n
+ --end=n Stop before page_id or log_id n (exclusive)
--skip-header Don't output the <mediawiki> header
--skip-footer Don't output the </mediawiki> footer
--stub Don't perform old_text lookups; for 2-pass dump
--uploads Include upload records (experimental)
-Fancy stuff:
+Fancy stuff: (Works? Add examples please.)
--plugin=<class>[:<file>] Load a dump plugin class
--output=<type>:<file> Begin a filtered output stream;
<type>s: file, gzip, bzip2, 7zip
@@ -101,5 +101,3 @@ Fancy stuff:
ENDS
);
}
-
-
diff --git a/maintenance/dumpInterwiki.inc b/maintenance/dumpInterwiki.inc
index 481e21cc..c366b08c 100644
--- a/maintenance/dumpInterwiki.inc
+++ b/maintenance/dumpInterwiki.inc
@@ -6,6 +6,7 @@
* @file
* @todo document
* @ingroup Maintenance
+ * @ingroup Wikimedia
*/
/**
@@ -201,7 +202,7 @@ function makeLink( $entry, $source ) {
array_key_exists($entry['iw_prefix'],$prefixRewrites[$source]))
$entry['iw_prefix'] = $prefixRewrites[$source][$entry['iw_prefix']];
if ($dbFile)
- dba_insert("{$source}:{$entry['iw_prefix']}", trim("{$entry['iw_local']} {$entry['iw_url']}"),$dbFile);
+ $dbFile->set( "{$source}:{$entry['iw_prefix']}", trim("{$entry['iw_local']} {$entry['iw_url']}") );
else
print "{$source}:{$entry['iw_prefix']} {$entry['iw_url']} {$entry['iw_local']}\n";
diff --git a/maintenance/dumpInterwiki.php b/maintenance/dumpInterwiki.php
index f5f22f3c..045e393b 100644
--- a/maintenance/dumpInterwiki.php
+++ b/maintenance/dumpInterwiki.php
@@ -6,20 +6,21 @@
* @file
* @todo document
* @ingroup Maintenance
+ * @ingroup Wikimedia
*/
/** */
$oldCwd = getcwd();
$optionsWithArgs = array( "o" );
-require( "commandLine.inc" );
-require( "dumpInterwiki.inc" );
+require_once( dirname(__FILE__) . '/commandLine.inc' );
+require( dirname(__FILE__)."/dumpInterwiki.inc" );
chdir( $oldCwd );
# Output
if ( isset( $options['o'] ) ) {
# To database specified with -o
- $dbFile = dba_open( $options['o'], "n", "cdb_make" );
+ $dbFile = CdbWriter::open( $options['o'] );
}
getRebuildInterwikiDump();
diff --git a/maintenance/dumpLinks.php b/maintenance/dumpLinks.php
index 65dfac64..529cd1aa 100644
--- a/maintenance/dumpLinks.php
+++ b/maintenance/dumpLinks.php
@@ -26,38 +26,48 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @file
* @ingroup Mainatenance
*/
-require_once 'commandLine.inc';
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-$dbr = wfGetDB( DB_SLAVE );
-$result = $dbr->select( array( 'pagelinks', 'page' ),
- array(
- 'page_id',
- 'page_namespace',
- 'page_title',
- 'pl_namespace',
- 'pl_title' ),
- array( 'page_id=pl_from' ),
- 'dumpLinks',
- array( 'ORDER BY' => 'page_id' ) );
+class DumpLinks extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Quick demo hack to generate a plaintext link dump";
+ }
-$lastPage = null;
-while( $row = $dbr->fetchObject( $result ) ) {
- if( $lastPage != $row->page_id ) {
- if( isset( $lastPage ) ) {
- print "\n";
+ public function execute() {
+ $dbr = wfGetDB( DB_SLAVE );
+ $result = $dbr->select( array( 'pagelinks', 'page' ),
+ array(
+ 'page_id',
+ 'page_namespace',
+ 'page_title',
+ 'pl_namespace',
+ 'pl_title' ),
+ array( 'page_id=pl_from' ),
+ __METHOD__,
+ array( 'ORDER BY' => 'page_id' ) );
+
+ $lastPage = null;
+ foreach( $result as $row ) {
+ if( $lastPage != $row->page_id ) {
+ if( isset( $lastPage ) ) {
+ $this->output( "\n" );
+ }
+ $page = Title::makeTitle( $row->page_namespace, $row->page_title );
+ $this->output( $page->getPrefixedUrl() );
+ $lastPage = $row->page_id;
+ }
+ $link = Title::makeTitle( $row->pl_namespace, $row->pl_title );
+ $this->output( " " . $link->getPrefixedUrl() );
}
- $page = Title::makeTitle( $row->page_namespace, $row->page_title );
- print $page->getPrefixedUrl();
- $lastPage = $row->page_id;
+ if( isset( $lastPage ) )
+ $this->output( "\n" );
}
- $link = Title::makeTitle( $row->pl_namespace, $row->pl_title );
- print " " . $link->getPrefixedUrl();
}
-if( isset( $lastPage ) )
- print "\n";
+$maintClass = "DumpLinks";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/dumpSisterSites.php b/maintenance/dumpSisterSites.php
index 2a7369c0..d9fd28a6 100644
--- a/maintenance/dumpSisterSites.php
+++ b/maintenance/dumpSisterSites.php
@@ -21,29 +21,36 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
- * @file
- * @ingroup SpecialPage
+ * @ingroup Maintenance
*/
-require_once( 'commandLine.inc' );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-$dbr = wfGetDB( DB_SLAVE );
-$dbr->bufferResults( false );
-$result = $dbr->select( 'page',
- array( 'page_namespace', 'page_title' ),
- array(
- 'page_namespace' => NS_MAIN,
- 'page_is_redirect' => 0,
- ),
- 'dumpSisterSites' );
+class DumpSisterSites extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Quickie page name dump script for SisterSites usage";
+ }
+
+ public function execute() {
+ $dbr = wfGetDB( DB_SLAVE );
+ $dbr->bufferResults( false );
+ $result = $dbr->select( 'page',
+ array( 'page_namespace', 'page_title' ),
+ array( 'page_namespace' => NS_MAIN,
+ 'page_is_redirect' => 0,
+ ),
+ __METHOD__ );
-while( $row = $dbr->fetchObject( $result ) ) {
- $title = Title::makeTitle( $row->page_namespace, $row->page_title );
- $url = $title->getFullUrl();
- $text = $title->getPrefixedText();
- echo "$url $text\n";
+ foreach( $result as $row ) {
+ $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+ $url = $title->getFullUrl();
+ $text = $title->getPrefixedText();
+ $this->output( "$url $text\n" );
+ }
+ $dbr->freeResult( $result );
+ }
}
-$dbr->freeResult( $result );
-
-
+$maintClass = "DumpSisterSites";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/dumpTextPass.php b/maintenance/dumpTextPass.php
index e85fe421..2e639e68 100644
--- a/maintenance/dumpTextPass.php
+++ b/maintenance/dumpTextPass.php
@@ -24,79 +24,10 @@
$originalDir = getcwd();
-require_once( 'commandLine.inc' );
+require_once( dirname(__FILE__) . '/commandLine.inc' );
require_once( 'backup.inc' );
/**
- * Stream wrapper around 7za filter program.
- * Required since we can't pass an open file resource to XMLReader->open()
- * which is used for the text prefetch.
- *
- * @ingroup Maintenance
- */
-class SevenZipStream {
- var $stream;
-
- private function stripPath( $path ) {
- $prefix = 'mediawiki.compress.7z://';
- return substr( $path, strlen( $prefix ) );
- }
-
- function stream_open( $path, $mode, $options, &$opened_path ) {
- if( $mode{0} == 'r' ) {
- $options = 'e -bd -so';
- } elseif( $mode{0} == 'w' ) {
- $options = 'a -bd -si';
- } else {
- return false;
- }
- $arg = wfEscapeShellArg( $this->stripPath( $path ) );
- $command = "7za $options $arg";
- if( !wfIsWindows() ) {
- // Suppress the stupid messages on stderr
- $command .= ' 2>/dev/null';
- }
- $this->stream = popen( $command, $mode );
- return ($this->stream !== false);
- }
-
- function url_stat( $path, $flags ) {
- return stat( $this->stripPath( $path ) );
- }
-
- // This is all so lame; there should be a default class we can extend
-
- function stream_close() {
- return fclose( $this->stream );
- }
-
- function stream_flush() {
- return fflush( $this->stream );
- }
-
- function stream_read( $count ) {
- return fread( $this->stream, $count );
- }
-
- function stream_write( $data ) {
- return fwrite( $this->stream, $data );
- }
-
- function stream_tell() {
- return ftell( $this->stream );
- }
-
- function stream_eof() {
- return feof( $this->stream );
- }
-
- function stream_seek( $offset, $whence ) {
- return fseek( $this->stream, $offset, $whence );
- }
-}
-stream_wrapper_register( 'mediawiki.compress.7z', 'SevenZipStream' );
-
-/**
* @ingroup Maintenance
*/
class TextPassDumper extends BackupDumper {
@@ -305,6 +236,7 @@ class TextPassDumper extends BackupDumper {
* May throw a database error if, say, the server dies during query.
*/
private function getTextDb( $id ) {
+ global $wgContLang;
$id = intval( $id );
$row = $this->db->selectRow( 'text',
array( 'old_text', 'old_flags' ),
@@ -315,7 +247,7 @@ class TextPassDumper extends BackupDumper {
return false;
}
$stripped = str_replace( "\r", "", $text );
- $normalized = UtfNormal::cleanUp( $stripped );
+ $normalized = $wgContLang->normalize( $stripped );
return $normalized;
}
@@ -390,6 +322,8 @@ class TextPassDumper extends BackupDumper {
}
private function getTextSpawnedOnce( $id ) {
+ global $wgContLang;
+
$ok = fwrite( $this->spawnWrite, "$id\n" );
//$this->progress( ">> $id" );
if( !$ok ) return false;
@@ -408,7 +342,7 @@ class TextPassDumper extends BackupDumper {
// Subprocess may not send everything at once, we have to loop.
while( $nbytes > strlen( $text ) ) {
$buffer = fread( $this->spawnRead, $nbytes - strlen( $text ) );
- if( $text === false ) break;
+ if( $buffer === false ) break;
$text .= $buffer;
}
@@ -420,7 +354,7 @@ class TextPassDumper extends BackupDumper {
// Do normalization in the dump thread...
$stripped = str_replace( "\r", "", $text );
- $normalized = UtfNormal::cleanUp( $stripped );
+ $normalized = $wgContLang->normalize( $stripped );
return $normalized;
}
diff --git a/maintenance/dumpUploads.php b/maintenance/dumpUploads.php
index c237feee..c8f1667b 100644
--- a/maintenance/dumpUploads.php
+++ b/maintenance/dumpUploads.php
@@ -1,37 +1,55 @@
<?php
/**
- * @file
+ * Dump a the list of files uploaded, for feeding to tar or similar
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-require_once 'commandLine.inc';
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-class UploadDumper {
- function __construct( $args ) {
+class UploadDumper extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Generates list of uploaded files which can be fed to tar or similar.
+By default, outputs relative paths against the parent directory of \$wgUploadDirectory.";
+ $this->addOption( 'base', 'Set base relative path instead of wiki include root', false, true );
+ $this->addOption( 'local', 'List all local files, used or not. No shared files included' );
+ $this->addOption( 'used', 'Skip local images that are not used' );
+ $this->addOption( 'shared', 'Include images used from shared repository' );
+ }
+
+ public function execute() {
global $IP, $wgUseSharedUploads;
$this->mAction = 'fetchLocal';
- $this->mBasePath = $IP;
+ $this->mBasePath = $this->getOption( 'base', $IP );
$this->mShared = false;
$this->mSharedSupplement = false;
-
- if( isset( $args['help'] ) ) {
- $this->mAction = 'help';
- }
-
- if( isset( $args['base'] ) ) {
- $this->mBasePath = $args['base'];
- }
-
- if( isset( $args['local'] ) ) {
+
+ if( $this->hasOption('local') ) {
$this->mAction = 'fetchLocal';
}
- if( isset( $args['used'] ) ) {
+ if( $this->hasOption('used') ) {
$this->mAction = 'fetchUsed';
}
- if( isset( $args['shared'] ) ) {
- if( isset( $args['used'] ) ) {
+ if( $this->hasOption('shared') ) {
+ if( $this->hasOption('used') ) {
// Include shared-repo files in the used check
$this->mShared = true;
} else {
@@ -39,34 +57,12 @@ class UploadDumper {
$this->mSharedSupplement = true;
}
}
- }
-
- function run() {
$this->{$this->mAction}( $this->mShared );
if( $this->mSharedSupplement ) {
$this->fetchUsed( true );
}
}
-
- function help() {
- echo <<<END
-Generates list of uploaded files which can be fed to tar or similar.
-By default, outputs relative paths against the parent directory of
-\$wgUploadDirectory.
-Usage:
-php dumpUploads.php [options] > list-o-files.txt
-
-Options:
---base=<path> Set base relative path instead of wiki include root
-
---local List all local files, used or not. No shared files included.
---used Skip local images that are not used
---shared Include images used from shared repository
-
-END;
- }
-
/**
* Fetch a list of all or used images from a particular image source.
* @param string $table
@@ -89,7 +85,7 @@ END;
}
$dbr->freeResult( $result );
}
-
+
function fetchLocal( $shared ) {
$dbr = wfGetDB( DB_SLAVE );
$result = $dbr->select( 'image',
@@ -108,17 +104,16 @@ END;
if( $file && $this->filterItem( $file, $shared ) ) {
$filename = $file->getFullPath();
$rel = wfRelativePath( $filename, $this->mBasePath );
- echo "$rel\n";
+ $this->output( "$rel\n" );
} else {
wfDebug( __METHOD__ . ": base file? $name\n" );
}
}
-
+
function filterItem( $file, $shared ) {
return $shared || $file->isLocal();
}
}
-$dumper = new UploadDumper( $options );
-$dumper->run();
-
+$maintClass = "UploadDumper";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/edit.php b/maintenance/edit.php
index 64178045..8d0068c3 100644
--- a/maintenance/edit.php
+++ b/maintenance/edit.php
@@ -1,77 +1,89 @@
<?php
/**
- * @file
+ * Make an edit
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-$optionsWithArgs = array( 'u', 's' );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-require_once( 'commandLine.inc' );
+class EditCLI extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Edit an article from the command line, text is from stdin";
+ $this->addOption( 'u', 'Username', false, true );
+ $this->addOption( 's', 'Edit summary', false, true );
+ $this->addOption( 'm', 'Minor edit' );
+ $this->addOption( 'b', 'Bot edit' );
+ $this->addOption( 'a', 'Enable autosummary' );
+ $this->addOption( 'no-rc', 'Do not show the change in recent changes' );
+ $this->addArg( 'title', 'Title of article to edit' );
+ }
-if ( count( $args ) == 0 || isset( $options['help'] ) ) {
- print <<<EOT
-Edit an article from the command line
+ public function execute() {
+ global $wgUser, $wgTitle, $wgArticle;
-Usage: php edit.php [options...] <title>
-
-Options:
- -u <user> Username
- -s <summary> Edit summary
- -m Minor edit
- -b Bot (hidden) edit
- -a Enable autosummary
- --no-rc Do not show the change in recent changes
-
-If the specified user does not exist, it will be created.
-The text for the edit will be read from stdin.
-
-EOT;
- exit( 1 );
-}
-
-$userName = isset( $options['u'] ) ? $options['u'] : 'Maintenance script';
-$summary = isset( $options['s'] ) ? $options['s'] : '';
-$minor = isset( $options['m'] );
-$bot = isset( $options['b'] );
-$autoSummary = isset( $options['a'] );
-$noRC = isset( $options['no-rc'] );
-
-$wgUser = User::newFromName( $userName );
-if ( !$wgUser ) {
- print "Invalid username\n";
- exit( 1 );
-}
-if ( $wgUser->isAnon() ) {
- $wgUser->addToDatabase();
+ $userName = $this->getOption( 'u', 'Maintenance script' );
+ $summary = $this->getOption( 's', '' );
+ $minor = $this->hasOption( 'm' );
+ $bot = $this->hasOption( 'b' );
+ $autoSummary = $this->hasOption( 'a' );
+ $noRC = $this->hasOption( 'no-rc' );
+
+ $wgUser = User::newFromName( $userName );
+ if ( !$wgUser ) {
+ $this->error( "Invalid username", true );
+ }
+ if ( $wgUser->isAnon() ) {
+ $wgUser->addToDatabase();
+ }
+
+ $wgTitle = Title::newFromText( $this->getArg() );
+ if ( !$wgTitle ) {
+ $this->error( "Invalid title", true );
+ }
+
+ $wgArticle = new Article( $wgTitle );
+
+ # Read the text
+ $text = $this->getStdin( Maintenance::STDIN_ALL );
+
+ # Do the edit
+ $this->output( "Saving... " );
+ $status = $wgArticle->doEdit( $text, $summary,
+ ( $minor ? EDIT_MINOR : 0 ) |
+ ( $bot ? EDIT_FORCE_BOT : 0 ) |
+ ( $autoSummary ? EDIT_AUTOSUMMARY : 0 ) |
+ ( $noRC ? EDIT_SUPPRESS_RC : 0 ) );
+ if ( $status->isOK() ) {
+ $this->output( "done\n" );
+ $exit = 0;
+ } else {
+ $this->output( "failed\n" );
+ $exit = 1;
+ }
+ if ( !$status->isGood() ) {
+ $this->output( $status->getWikiText() . "\n" );
+ }
+ exit( $exit );
+ }
}
-$wgTitle = Title::newFromText( $args[0] );
-if ( !$wgTitle ) {
- print "Invalid title\n";
- exit( 1 );
-}
-
-$wgArticle = new Article( $wgTitle );
-
-# Read the text
-$text = file_get_contents( 'php://stdin' );
-
-# Do the edit
-print "Saving... ";
-$status = $wgArticle->doEdit( $text, $summary,
- ( $minor ? EDIT_MINOR : 0 ) |
- ( $bot ? EDIT_FORCE_BOT : 0 ) |
- ( $autoSummary ? EDIT_AUTOSUMMARY : 0 ) |
- ( $noRC ? EDIT_SUPPRESS_RC : 0 ) );
-if ( $status->isOK() ) {
- print "done\n";
- $exit = 0;
-} else {
- print "failed\n";
- $exit = 1;
-}
-if ( !$status->isGood() ) {
- print $status->getWikiText() . "\n";
-}
-exit( $exit );
+$maintClass = "EditCLI";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/fetchInterwiki.pl b/maintenance/fetchInterwiki.pl
deleted file mode 100644
index cb56a6df..00000000
--- a/maintenance/fetchInterwiki.pl
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/env perl
-# Copyright (C) 2005 Ævar Arnfjörð Bjarmason
-use strict;
-use warnings;
-use Socket;
-
-# Conf
-my $map = &get(&url('http://usemod.com/intermap.txt'));
-
-# --- #
-my $cont;
-my @map = split /\n/, $map;
-
-$cont .= '<?php
-# Note: this file is generated by maintenance/fetchInterwiki.pl
-# Edit and rerun that script rather than modifying this directly.
-
-/* private */ $wgValidInterwikis = array(
-';
-
-$cont .= "\t# The usemod interwiki map\n";
-for (my $i=0;$i<=$#map;++$i) {
- my ($name, $url) = $map[$i] =~ m#^([^ ]+) (.+)#i;
- $cont .= "\t'$name' => '$url\$1',\n";
-}
-
-my @iso = qw(
-aa ab af als am ar as ay az ba be bg bh bi bn bo bs ca chr co cs csb cy da de dk:da dz el en eo
-es et eu fa fi fj fo fr fy ga gd gl gn gu gv ha he hi hr hu hy ia id ik io is it iu ja jv ka kk
-kl km kn ko ks ku ky la lo lt lv mg mi mk ml mn mo mr ms my na nah nb nds ne nl no oc om or pa
-pl ps pt qu rm rn ro ru rw sa sd sg sh si sk sl sm sn so sq sr ss st su sv sw ta te tg th ti tk
-tl tn to tp tpi tr ts tt tw ug uk ur uz vi vo wa wo xh yi yo za zh zh-cn zh-tw zu);
-
-$cont .= '
- # Some custom additions:
- "ReVo" => "http://purl.org/NET/voko/revo/art/$1.html",
- # eg [[ReVo:cerami]], [[ReVo:astero]] - note X-sensitive!
- "EcheI" => "http://www.ikso.net/cgi-bin/wiki.pl?$1",
- "E\\xc4\\x89eI" => "http://www.ikso.net/cgi-bin/wiki.pl?$1",
- "UnuMondo" => "http://unumondo.com/cgi-bin/wiki.pl?$1", # X-sensitive!
- "JEFO" => "http://esperanto.jeunes.free.fr/vikio/index.php?$1",
- "PMEG" => "http://www.bertilow.com/pmeg/$1.php",
- # ekz [[PMEG:gramatiko/kunligaj vortetoj/au]]
- "EnciclopediaLibre" => "http://enciclopedia.us.es/wiki.phtml?title=$1",
-
- # Wikipedia-specific stuff:
- # Special cases
- "w" => "http://www.wikipedia.org/wiki/$1",
- "m" => "http://meta.wikipedia.org/wiki/$1",
- "meta" => "http://meta.wikipedia.org/wiki/$1",
- "sep11" => "http://sep11.wikipedia.org/wiki/$1",
- "simple"=> "http://simple.wikipedia.com/wiki.cgi?$1",
- "wiktionary" => "http://wiktionary.wikipedia.org/wiki/$1",
- "PageHistory" => "http://www.wikipedia.org/w/wiki.phtml?title=$1&action=history",
- "UserContributions" => "http://www.wikipedia.org/w/wiki.phtml?title=Special:Contributions&target=$1",
- "BackLinks" => "http://www.wikipedia.org/w/wiki.phtml?title=Special:Whatlinkshere&target=$1",
-
- # ISO 639 2-letter language codes
-';
-
-for(my $i=0; $i<=$#iso;++$i) {
- my @arr = split /:/, $iso[$i];
- $cont .= "\t";
- $cont .= "'$arr[0]' => 'http://";
-
- if ($arr[1]) {
- $cont .= $arr[1];
- } else {
- $cont .= $arr[0];
- }
- $cont .= ".wikipedia.org/wiki/\$1',\n";
-}
-
-$cont .= '
-);
-?>
-';
-
-open IW, ">Interwiki.php";
-print IW $cont;
-close IW;
-
-sub get {
- my ($host, $url) = @_;
- my $cont;
- my $eat;
-
- my $proto = getprotobyname('tcp');
- socket(Socket, AF_INET, SOCK_STREAM, $proto);
- my $iaddr = inet_aton("$host");
- my $port = getservbyname('http', 'tcp');
- my $sin = sockaddr_in($port, $iaddr);
- connect(Socket, $sin);
- send Socket, "GET $url HTTP/1.0\r\nHost: $host\r\n\r\n",0;
- while (<Socket>) {
- $cont .= $_ if $eat; # mmm, food
- ++$eat if ($_ =~ /^(\n|\r\n|)$/);
- }
- return $cont;
-}
-
-sub url {my ($server, $path) = $_[0] =~ m#.*(?=//)//([^/]*)(.*)#g;}
diff --git a/maintenance/fetchText.php b/maintenance/fetchText.php
index 91b78be3..746ef8ad 100644
--- a/maintenance/fetchText.php
+++ b/maintenance/fetchText.php
@@ -2,38 +2,66 @@
/**
* Communications protocol...
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-require "commandLine.inc";
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-$db = wfGetDB( DB_SLAVE );
-$stdin = fopen( "php://stdin", "rt" );
-while( !feof( $stdin ) ) {
- $line = fgets( $stdin );
- if( $line === false ) {
- // We appear to have lost contact...
- break;
+class FetchText extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Fetch the revision text from an old_id";
}
- $textId = intval( $line );
- $text = doGetText( $db, $textId );
- echo strlen( $text ) . "\n";
- echo $text;
-}
-/**
- * May throw a database error if, say, the server dies during query.
- */
-function doGetText( $db, $id ) {
- $id = intval( $id );
- $row = $db->selectRow( 'text',
- array( 'old_text', 'old_flags' ),
- array( 'old_id' => $id ),
- 'TextPassDumper::getText' );
- $text = Revision::getRevisionText( $row );
- if( $text === false ) {
- return false;
+ public function execute() {
+ $db = wfGetDB( DB_SLAVE );
+ $stdin = $this->getStdin();
+ while( !feof( $stdin ) ) {
+ $line = fgets( $stdin );
+ if( $line === false ) {
+ // We appear to have lost contact...
+ break;
+ }
+ $textId = intval( $line );
+ $text = $this->doGetText( $db, $textId );
+ $this->output( strlen( $text ) . "\n". $text );
+ }
+ }
+
+ /**
+ * May throw a database error if, say, the server dies during query.
+ * @param $db Database object
+ * @param $id int The old_id
+ * @return String
+ */
+ private function doGetText( $db, $id ) {
+ $id = intval( $id );
+ $row = $db->selectRow( 'text',
+ array( 'old_text', 'old_flags' ),
+ array( 'old_id' => $id ),
+ 'TextPassDumper::getText' );
+ $text = Revision::getRevisionText( $row );
+ if( $text === false ) {
+ return false;
+ }
+ return $text;
}
- return $text;
}
+
+$maintClass = "FetchText";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/findhooks.php b/maintenance/findhooks.php
index 64fe9a54..13236b6b 100644
--- a/maintenance/findhooks.php
+++ b/maintenance/findhooks.php
@@ -12,7 +12,21 @@
*
* Any instance of wfRunHooks that doesn't meet these parameters will be noted.
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*
* @author Ashar Voultoiz <hashar@altern.org>
@@ -20,139 +34,174 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public Licence 2.0 or later
*/
-/** This is a command line script*/
-require('commandLine.inc');
-# GLOBALS
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-$doc = $IP . '/docs/hooks.txt';
-$pathinc = array(
- $IP.'/',
- $IP.'/includes/',
- $IP.'/includes/api/',
- $IP.'/includes/db/',
- $IP.'/includes/diff/',
- $IP.'/includes/filerepo/',
- $IP.'/includes/parser/',
- $IP.'/includes/specials/',
- $IP.'/languages/',
- $IP.'/maintenance/',
- $IP.'/skins/',
-);
-
-# FUNCTIONS
+class FindHooks extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Find hooks that are undocumented, missing, or just plain wrong";
+ $this->addOption( 'online', 'Check against mediawiki.org hook documentation' );
+ }
-/**
- * @return array of documented hooks
- */
-function getHooksFromDoc() {
- global $doc, $options;
- $m = array();
- if( isset( $options['online'] ) ){
- $content = Http::get( 'http://www.mediawiki.org/w/index.php?title=Manual:Hooks&action=raw' );
- preg_match_all( '/\[\[\/([a-zA-Z0-9-_:]+)\|/', $content, $m );
- } else {
- $content = file_get_contents( $doc );
- preg_match_all( "/\n'(.*?)'/", $content, $m );
+ public function getDbType() {
+ return Maintenance::DB_NONE;
}
- return array_unique( $m[1] );
-}
-/**
- * Get hooks from a PHP file
- * @param $file Full filename to the PHP file.
- * @return array of hooks found.
- */
-function getHooksFromFile( $file ) {
- $content = file_get_contents( $file );
- $m = array();
- preg_match_all( '/wfRunHooks\(\s*([\'"])(.*?)\1/', $content, $m);
- return $m[2];
-}
+ public function execute() {
+ global $IP;
-/**
- * Get hooks from the source code.
- * @param $path Directory where the include files can be found
- * @return array of hooks found.
- */
-function getHooksFromPath( $path ) {
- $hooks = array();
- if( $dh = opendir($path) ) {
- while(($file = readdir($dh)) !== false) {
- if( filetype($path.$file) == 'file' ) {
- $hooks = array_merge( $hooks, getHooksFromFile($path.$file) );
+ $documented = $this->getHooksFromDoc( $IP . '/docs/hooks.txt' );
+ $potential = array();
+ $bad = array();
+ $pathinc = array(
+ $IP.'/',
+ $IP.'/includes/',
+ $IP.'/includes/api/',
+ $IP.'/includes/db/',
+ $IP.'/includes/diff/',
+ $IP.'/includes/filerepo/',
+ $IP.'/includes/parser/',
+ $IP.'/includes/search/',
+ $IP.'/includes/specials/',
+ $IP.'/includes/upload/',
+ $IP.'/languages/',
+ $IP.'/maintenance/',
+ $IP.'/skins/',
+ );
+
+ foreach( $pathinc as $dir ) {
+ $potential = array_merge( $potential, $this->getHooksFromPath( $dir ) );
+ $bad = array_merge( $bad, $this->getBadHooksFromPath( $dir ) );
+ }
+
+ $potential = array_unique( $potential );
+ $bad = array_unique( $bad );
+ $todo = array_diff( $potential, $documented );
+ $deprecated = array_diff( $documented, $potential );
+
+ // let's show the results:
+ $this->printArray('Undocumented', $todo );
+ $this->printArray('Documented and not found', $deprecated );
+ $this->printArray('Unclear hook calls', $bad );
+
+ if ( count( $todo ) == 0 && count( $deprecated ) == 0 && count( $bad ) == 0 )
+ $this->output( "Looks good!\n" );
+ }
+
+ /**
+ * Get the hook documentation, either locally or from mediawiki.org
+ * @return array of documented hooks
+ */
+ private function getHooksFromDoc( $doc ) {
+ if( $this->hasOption( 'online' ) ){
+ // All hooks
+ $allhookdata = Http::get( 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:MediaWiki_hooks&cmlimit=500&format=php' );
+ $allhookdata = unserialize( $allhookdata );
+ $allhooks = array();
+ foreach( $allhookdata['query']['categorymembers'] as $page ) {
+ $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches );
+ if( $found ) {
+ $hook = str_replace( ' ', '_', $matches[1] );
+ $allhooks[] = $hook;
+ }
+ }
+ // Removed hooks
+ $oldhookdata = Http::get( 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:Removed_hooks&cmlimit=500&format=php' );
+ $oldhookdata = unserialize( $oldhookdata );
+ $removed = array();
+ foreach( $oldhookdata['query']['categorymembers'] as $page ) {
+ $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches );
+ if( $found ) {
+ $hook = str_replace( ' ', '_', $matches[1] );
+ $removed[] = $hook;
+ }
}
+ return array_diff( $allhooks, $removed );
+ } else {
+ $m = array();
+ $content = file_get_contents( $doc );
+ preg_match_all( "/\n'(.*?)'/", $content, $m );
+ return array_unique( $m[1] );
}
- closedir($dh);
}
- return $hooks;
-}
-/**
- * Get bad hooks (where the hook name could not be determined) from a PHP file
- * @param $file Full filename to the PHP file.
- * @return array of bad wfRunHooks() lines
- */
-function getBadHooksFromFile( $file ) {
- $content = file_get_contents( $file );
- $m = array();
- # We want to skip the "function wfRunHooks()" one. :)
- preg_match_all( '/(?<!function )wfRunHooks\(\s*[^\s\'"].*/', $content, $m);
- $list = array();
- foreach( $m[0] as $match ){
- $list[] = $match . "(" . $file . ")";
+ /**
+ * Get hooks from a PHP file
+ * @param $file Full filename to the PHP file.
+ * @return array of hooks found.
+ */
+ private function getHooksFromFile( $file ) {
+ $content = file_get_contents( $file );
+ $m = array();
+ preg_match_all( '/wfRunHooks\(\s*([\'"])(.*?)\1/', $content, $m);
+ return $m[2];
}
- return $list;
-}
-/**
- * Get bad hooks from the source code.
- * @param $path Directory where the include files can be found
- * @return array of bad wfRunHooks() lines
- */
-function getBadHooksFromPath( $path ) {
- $hooks = array();
- if( $dh = opendir($path) ) {
- while(($file = readdir($dh)) !== false) {
- # We don't want to read this file as it contains bad calls to wfRunHooks()
- if( filetype( $path.$file ) == 'file' && !$path.$file == __FILE__ ) {
- $hooks = array_merge( $hooks, getBadHooksFromFile($path.$file) );
+ /**
+ * Get hooks from the source code.
+ * @param $path Directory where the include files can be found
+ * @return array of hooks found.
+ */
+ private function getHooksFromPath( $path ) {
+ $hooks = array();
+ if( $dh = opendir($path) ) {
+ while(($file = readdir($dh)) !== false) {
+ if( filetype($path.$file) == 'file' ) {
+ $hooks = array_merge( $hooks, $this->getHooksFromFile($path.$file) );
+ }
}
+ closedir($dh);
}
- closedir($dh);
+ return $hooks;
}
- return $hooks;
-}
-/**
- * Nicely output the array
- * @param $msg A message to show before the value
- * @param $arr An array
- * @param $sort Boolean : wheter to sort the array (Default: true)
- */
-function printArray( $msg, $arr, $sort = true ) {
- if($sort) asort($arr);
- foreach($arr as $v) echo "$msg: $v\n";
-}
+ /**
+ * Get bad hooks (where the hook name could not be determined) from a PHP file
+ * @param $file Full filename to the PHP file.
+ * @return array of bad wfRunHooks() lines
+ */
+ private function getBadHooksFromFile( $file ) {
+ $content = file_get_contents( $file );
+ $m = array();
+ # We want to skip the "function wfRunHooks()" one. :)
+ preg_match_all( '/(?<!function )wfRunHooks\(\s*[^\s\'"].*/', $content, $m);
+ $list = array();
+ foreach( $m[0] as $match ){
+ $list[] = $match . "(" . $file . ")";
+ }
+ return $list;
+ }
-# MAIN
+ /**
+ * Get bad hooks from the source code.
+ * @param $path Directory where the include files can be found
+ * @return array of bad wfRunHooks() lines
+ */
+ private function getBadHooksFromPath( $path ) {
+ $hooks = array();
+ if( $dh = opendir($path) ) {
+ while(($file = readdir($dh)) !== false) {
+ # We don't want to read this file as it contains bad calls to wfRunHooks()
+ if( filetype( $path.$file ) == 'file' && !$path.$file == __FILE__ ) {
+ $hooks = array_merge( $hooks, $this->getBadHooksFromFile($path.$file) );
+ }
+ }
+ closedir($dh);
+ }
+ return $hooks;
+ }
-$documented = getHooksFromDoc($doc);
-$potential = array();
-$bad = array();
-foreach( $pathinc as $dir ) {
- $potential = array_merge( $potential, getHooksFromPath( $dir ) );
- $bad = array_merge( $bad, getBadHooksFromPath( $dir ) );
+ /**
+ * Nicely output the array
+ * @param $msg A message to show before the value
+ * @param $arr An array
+ * @param $sort Boolean : wheter to sort the array (Default: true)
+ */
+ private function printArray( $msg, $arr, $sort = true ) {
+ if($sort) asort($arr);
+ foreach($arr as $v) $this->output( "$msg: $v\n" );
+ }
}
-$potential = array_unique( $potential );
-$bad = array_unique( $bad );
-$todo = array_diff( $potential, $documented );
-$deprecated = array_diff( $documented, $potential );
-
-// let's show the results:
-printArray('undocumented', $todo );
-printArray('not found', $deprecated );
-printArray('unclear hook calls', $bad );
-
-if ( count( $todo ) == 0 && count( $deprecated ) == 0 && count( $bad ) == 0 )
- echo "Looks good!\n";
+$maintClass = "FindHooks";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/fixSlaveDesync.php b/maintenance/fixSlaveDesync.php
index 7817bc56..c585beb1 100644
--- a/maintenance/fixSlaveDesync.php
+++ b/maintenance/fixSlaveDesync.php
@@ -1,196 +1,217 @@
<?php
/**
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-$wgUseRootUser = true;
-require_once( 'commandLine.inc' );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-//$wgDebugLogFile = '/dev/stdout';
+class FixSlaveDesync extends Maintenance {
+ public function __construct() {
+ global $wgUseRootUser;
+ $wgUseRootUser = true;
-$slaveIndexes = array();
-for ( $i = 1; $i < count( $wgDBservers ); $i++ ) {
- if ( wfGetLB()->isNonZeroLoad( $i ) ) {
- $slaveIndexes[] = $i;
+ parent::__construct();
+ $this->mDescription = "";
+
}
-}
-/*
-foreach ( wfGetLB()->mServers as $i => $server ) {
- wfGetLB()->mServers[$i]['flags'] |= DBO_DEBUG;
-}*/
-$reportingInterval = 1000;
-
-if ( isset( $args[0] ) ) {
- desyncFixPage( $args[0] );
-} else {
- $dbw = wfGetDB( DB_MASTER );
- $maxPage = $dbw->selectField( 'page', 'MAX(page_id)', false, 'fixDesync.php' );
- $corrupt = findPageLatestCorruption();
- foreach ( $corrupt as $id => $dummy ) {
- desyncFixPage( $id );
- }
- /*
- for ( $i=1; $i <= $maxPage; $i++ ) {
- desyncFixPage( $i );
- if ( !($i % $reportingInterval) ) {
- print "$i\n";
+
+ public function execute() {
+ global $slaveIndexes, $wgDBservers;
+ $slaveIndexes = array();
+ for ( $i = 1; $i < count( $wgDBservers ); $i++ ) {
+ if ( wfGetLB()->isNonZeroLoad( $i ) ) {
+ $slaveIndexes[] = $i;
+ }
}
- }*/
-}
-function findPageLatestCorruption() {
- $desync = array();
- $n = 0;
- $dbw = wfGetDB( DB_MASTER );
- $masterIDs = array();
- $res = $dbw->select( 'page', array( 'page_id', 'page_latest' ), array( 'page_id<6054123' ), __METHOD__ );
- print "Number of pages: " . $dbw->numRows( $res ) . "\n";
- while ( $row = $dbw->fetchObject( $res ) ) {
- $masterIDs[$row->page_id] = $row->page_latest;
- if ( !( ++$n % 10000 ) ) {
- print "$n\r";
+ if ( $this->hasArg() ) {
+ $this->desyncFixPage( $this->getArg() );
+ } else {
+ $dbw = wfGetDB( DB_MASTER );
+ $maxPage = $dbw->selectField( 'page', 'MAX(page_id)', false, __METHOD__ );
+ $corrupt = $this->findPageLatestCorruption();
+ foreach ( $corrupt as $id => $dummy ) {
+ $this->desyncFixPage( $id );
+ }
}
}
- print "\n";
- $dbw->freeResult( $res );
-
- global $slaveIndexes;
- foreach ( $slaveIndexes as $i ) {
- $db = wfGetDB( $i );
- $res = $db->select( 'page', array( 'page_id', 'page_latest' ), array( 'page_id<6054123' ), __METHOD__ );
- while ( $row = $db->fetchObject( $res ) ) {
- if ( isset( $masterIDs[$row->page_id] ) && $masterIDs[$row->page_id] != $row->page_latest ) {
- $desync[$row->page_id] = true;
- print $row->page_id . "\t";
+
+ /**
+ * Find all pages that have a corrupted page_latest
+ * @return array
+ */
+ private function findPageLatestCorruption() {
+ $desync = array();
+ $n = 0;
+ $dbw = wfGetDB( DB_MASTER );
+ $masterIDs = array();
+ $res = $dbw->select( 'page', array( 'page_id', 'page_latest' ), array( 'page_id<6054123' ), __METHOD__ );
+ $this->output( "Number of pages: " . $dbw->numRows( $res ) . "\n" );
+ foreach ( $res as $row ) {
+ $masterIDs[$row->page_id] = $row->page_latest;
+ if ( !( ++$n % 10000 ) ) {
+ $this->output( "$n\r" );
}
}
- $db->freeResult( $res );
+ $this->output( "\n" );
+ $dbw->freeResult( $res );
+
+ global $slaveIndexes;
+ foreach ( $slaveIndexes as $i ) {
+ $db = wfGetDB( $i );
+ $res = $db->select( 'page', array( 'page_id', 'page_latest' ), array( 'page_id<6054123' ), __METHOD__ );
+ foreach ( $res as $row ) {
+ if ( isset( $masterIDs[$row->page_id] ) && $masterIDs[$row->page_id] != $row->page_latest ) {
+ $desync[$row->page_id] = true;
+ $this->output( $row->page_id . "\t" );
+ }
+ }
+ $db->freeResult( $res );
+ }
+ $this->output( "\n" );
+ return $desync;
}
- print "\n";
- return $desync;
-}
-function desyncFixPage( $pageID ) {
- global $slaveIndexes;
- $fname = 'desyncFixPage';
+ /**
+ * Fix a broken page entry
+ * @param $pageID int The page_id to fix
+ */
+ private function desyncFixPage( $pageID ) {
+ global $slaveIndexes;
- # Check for a corrupted page_latest
- $dbw = wfGetDB( DB_MASTER );
- $dbw->begin();
- $realLatest = $dbw->selectField( 'page', 'page_latest', array( 'page_id' => $pageID ),
- $fname, 'FOR UPDATE' );
- #list( $masterFile, $masterPos ) = $dbw->getMasterPos();
- $found = false;
- foreach ( $slaveIndexes as $i ) {
- $db = wfGetDB( $i );
- /*
- if ( !$db->masterPosWait( $masterFile, $masterPos, 10 ) ) {
- echo "Slave is too lagged, aborting\n";
- $dbw->commit();
- sleep(10);
- return;
- }*/
- $latest = $db->selectField( 'page', 'page_latest', array( 'page_id' => $pageID ), $fname );
- $max = $db->selectField( 'revision', 'MAX(rev_id)', false, $fname );
- if ( $latest != $realLatest && $realLatest < $max ) {
- print "page_latest corrupted in page $pageID, server $i\n";
- $found = true;
- break;
+ # Check for a corrupted page_latest
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin();
+ $realLatest = $dbw->selectField( 'page', 'page_latest', array( 'page_id' => $pageID ),
+ __METHOD__, 'FOR UPDATE' );
+ #list( $masterFile, $masterPos ) = $dbw->getMasterPos();
+ $found = false;
+ foreach ( $slaveIndexes as $i ) {
+ $db = wfGetDB( $i );
+ /*
+ if ( !$db->masterPosWait( $masterFile, $masterPos, 10 ) ) {
+ $this->output( "Slave is too lagged, aborting\n" );
+ $dbw->commit();
+ sleep(10);
+ return;
+ }*/
+ $latest = $db->selectField( 'page', 'page_latest', array( 'page_id' => $pageID ), __METHOD__ );
+ $max = $db->selectField( 'revision', 'MAX(rev_id)', false, __METHOD__ );
+ if ( $latest != $realLatest && $realLatest < $max ) {
+ $this->output( "page_latest corrupted in page $pageID, server $i\n" );
+ $found = true;
+ break;
+ }
+ }
+ if ( !$found ) {
+ $this->output( "page_id $pageID seems fine\n" );
+ $dbw->commit();
+ return;
}
- }
- if ( !$found ) {
- print "page_id $pageID seems fine\n";
- $dbw->commit();
- return;
- }
- # Find the missing revisions
- $res = $dbw->select( 'revision', array( 'rev_id' ), array( 'rev_page' => $pageID ),
- $fname, 'FOR UPDATE' );
- $masterIDs = array();
- while ( $row = $dbw->fetchObject( $res ) ) {
- $masterIDs[] = $row->rev_id;
- }
- $dbw->freeResult( $res );
+ # Find the missing revisions
+ $res = $dbw->select( 'revision', array( 'rev_id' ), array( 'rev_page' => $pageID ),
+ __METHOD__, 'FOR UPDATE' );
+ $masterIDs = array();
+ foreach ( $res as $row ) {
+ $masterIDs[] = $row->rev_id;
+ }
+ $dbw->freeResult( $res );
- $res = $db->select( 'revision', array( 'rev_id' ), array( 'rev_page' => $pageID ), $fname );
- $slaveIDs = array();
- while ( $row = $db->fetchObject( $res ) ) {
- $slaveIDs[] = $row->rev_id;
- }
- $db->freeResult( $res );
- if ( count( $masterIDs ) < count( $slaveIDs ) ) {
- $missingIDs = array_diff( $slaveIDs, $masterIDs );
- if ( count( $missingIDs ) ) {
- print "Found " . count( $missingIDs ) . " lost in master, copying from slave... ";
- $dbFrom = $db;
- $found = true;
- $toMaster = true;
- } else {
- $found = false;
+ $res = $db->select( 'revision', array( 'rev_id' ), array( 'rev_page' => $pageID ), __METHOD__ );
+ $slaveIDs = array();
+ foreach ( $res as $row ) {
+ $slaveIDs[] = $row->rev_id;
}
- } else {
- $missingIDs = array_diff( $masterIDs, $slaveIDs );
- if ( count( $missingIDs ) ) {
- print "Found " . count( $missingIDs ) . " missing revision(s), copying from master... ";
- $dbFrom = $dbw;
- $found = true;
- $toMaster = false;
+ $db->freeResult( $res );
+ if ( count( $masterIDs ) < count( $slaveIDs ) ) {
+ $missingIDs = array_diff( $slaveIDs, $masterIDs );
+ if ( count( $missingIDs ) ) {
+ $this->output( "Found " . count( $missingIDs ) . " lost in master, copying from slave... " );
+ $dbFrom = $db;
+ $found = true;
+ $toMaster = true;
+ } else {
+ $found = false;
+ }
} else {
- $found = false;
+ $missingIDs = array_diff( $masterIDs, $slaveIDs );
+ if ( count( $missingIDs ) ) {
+ $this->output( "Found " . count( $missingIDs ) . " missing revision(s), copying from master... " );
+ $dbFrom = $dbw;
+ $found = true;
+ $toMaster = false;
+ } else {
+ $found = false;
+ }
}
- }
- if ( $found ) {
- foreach ( $missingIDs as $rid ) {
- print "$rid ";
- # Revision
- $row = $dbFrom->selectRow( 'revision', '*', array( 'rev_id' => $rid ), $fname );
- if ( $toMaster ) {
- $id = $dbw->selectField( 'revision', 'rev_id', array( 'rev_id' => $rid ),
- $fname, 'FOR UPDATE' );
- if ( $id ) {
- echo "Revision already exists\n";
- $found = false;
- break;
+ if ( $found ) {
+ foreach ( $missingIDs as $rid ) {
+ $this->output( "$rid " );
+ # Revision
+ $row = $dbFrom->selectRow( 'revision', '*', array( 'rev_id' => $rid ), __METHOD__ );
+ if ( $toMaster ) {
+ $id = $dbw->selectField( 'revision', 'rev_id', array( 'rev_id' => $rid ),
+ __METHOD__, 'FOR UPDATE' );
+ if ( $id ) {
+ $this->output( "Revision already exists\n" );
+ $found = false;
+ break;
+ } else {
+ $dbw->insert( 'revision', get_object_vars( $row ), __METHOD__, 'IGNORE' );
+ }
} else {
- $dbw->insert( 'revision', get_object_vars( $row ), $fname, 'IGNORE' );
+ foreach ( $slaveIndexes as $i ) {
+ $db = wfGetDB( $i );
+ $db->insert( 'revision', get_object_vars( $row ), __METHOD__, 'IGNORE' );
+ }
}
- } else {
- foreach ( $slaveIndexes as $i ) {
- $db = wfGetDB( $i );
- $db->insert( 'revision', get_object_vars( $row ), $fname, 'IGNORE' );
+
+ # Text
+ $row = $dbFrom->selectRow( 'text', '*', array( 'old_id' => $row->rev_text_id ), __METHOD__ );
+ if ( $toMaster ) {
+ $dbw->insert( 'text', get_object_vars( $row ), __METHOD__, 'IGNORE' );
+ } else {
+ foreach ( $slaveIndexes as $i ) {
+ $db = wfGetDB( $i );
+ $db->insert( 'text', get_object_vars( $row ), __METHOD__, 'IGNORE' );
+ }
}
}
+ $this->output( "done\n" );
+ }
- # Text
- $row = $dbFrom->selectRow( 'text', '*', array( 'old_id' => $row->rev_text_id ), $fname );
+ if ( $found ) {
+ $this->output( "Fixing page_latest... " );
if ( $toMaster ) {
- $dbw->insert( 'text', get_object_vars( $row ), $fname, 'IGNORE' );
+ #$dbw->update( 'page', array( 'page_latest' => $realLatest ), array( 'page_id' => $pageID ), __METHOD__ );
} else {
foreach ( $slaveIndexes as $i ) {
$db = wfGetDB( $i );
- $db->insert( 'text', get_object_vars( $row ), $fname, 'IGNORE' );
+ $db->update( 'page', array( 'page_latest' => $realLatest ), array( 'page_id' => $pageID ), __METHOD__ );
}
}
+ $this->output( "done\n" );
}
- print "done\n";
- }
-
- if ( $found ) {
- print "Fixing page_latest... ";
- if ( $toMaster ) {
- #$dbw->update( 'page', array( 'page_latest' => $realLatest ), array( 'page_id' => $pageID ), $fname );
- } else {
- foreach ( $slaveIndexes as $i ) {
- $db = wfGetDB( $i );
- $db->update( 'page', array( 'page_latest' => $realLatest ), array( 'page_id' => $pageID ), $fname );
- }
- }
- print "done\n";
+ $dbw->commit();
}
- $dbw->commit();
}
-
+$maintClass = "FixSlaveDesync";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/fixTimestamps.php b/maintenance/fixTimestamps.php
index f6794141..ea102fb8 100644
--- a/maintenance/fixTimestamps.php
+++ b/maintenance/fixTimestamps.php
@@ -6,101 +6,117 @@
* and must bracket the damage. There must be a majority of good timestamps in the
* search period.
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-
-require_once( 'commandLine.inc' );
-
-if ( count( $args ) < 3 ) {
- echo "Usage: php fixTimestamps.php <offset in hours> <start time> <end time>\n";
- exit(1);
-}
-
-$offset = $args[0] * 3600;
-$start = $args[1];
-$end = $args[2];
-$fname = 'fixTimestamps.php';
-$grace = 60; // maximum normal clock offset
-
-# Find bounding revision IDs
-$dbw = wfGetDB( DB_MASTER );
-$revisionTable = $dbw->tableName( 'revision' );
-$res = $dbw->query( "SELECT MIN(rev_id) as minrev, MAX(rev_id) as maxrev FROM $revisionTable " .
- "WHERE rev_timestamp BETWEEN '{$start}' AND '{$end}'", $fname );
-$row = $dbw->fetchObject( $res );
-
-if ( is_null( $row->minrev ) ) {
- echo "No revisions in search period.\n";
- exit(0);
-}
-
-$minRev = $row->minrev;
-$maxRev = $row->maxrev;
-
-# Select all timestamps and IDs
-$sql = "SELECT rev_id, rev_timestamp FROM $revisionTable " .
- "WHERE rev_id BETWEEN $minRev AND $maxRev";
-if ( $offset > 0 ) {
- $sql .= " ORDER BY rev_id DESC";
- $expectedSign = -1;
-} else {
- $expectedSign = 1;
-}
-
-$res = $dbw->query( $sql, $fname );
-
-$lastNormal = 0;
-$badRevs = array();
-$numGoodRevs = 0;
-
-while ( $row = $dbw->fetchObject( $res ) ) {
- $timestamp = wfTimestamp( TS_UNIX, $row->rev_timestamp );
- $delta = $timestamp - $lastNormal;
- $sign = $delta == 0 ? 0 : $delta / abs( $delta );
- if ( $sign == 0 || $sign == $expectedSign ) {
- // Monotonic change
- $lastNormal = $timestamp;
- ++ $numGoodRevs;
- continue;
- } elseif ( abs( $delta ) <= $grace ) {
- // Non-monotonic change within grace interval
- ++ $numGoodRevs;
- continue;
- } else {
- // Non-monotonic change larger than grace interval
- $badRevs[] = $row->rev_id;
+
+require_once( dirname(__FILE__) . '/Maintenance.php' );
+
+class FixTimestamps extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "";
+ $this->addArg( 'offset', '' );
+ $this->addArg( 'start', 'Starting timestamp' );
+ $this->addArg( 'end', 'Ending timestamp' );
}
-}
-$dbw->freeResult( $res );
-
-$numBadRevs = count( $badRevs );
-if ( $numBadRevs > $numGoodRevs ) {
- echo
-"The majority of revisions in the search interval are marked as bad.
-Are you sure the offset ($offset) has the right sign? Positive means the clock
-was incorrectly set forward, negative means the clock was incorrectly set back.
-
-If the offset is right, then increase the search interval until there are enough
-good revisions to provide a majority reference.
-";
-
- exit(1);
-} elseif ( $numBadRevs == 0 ) {
- echo "No bad revisions found.\n";
- exit(0);
+ public function execute() {
+ $offset = $this->getArg(0) * 3600;
+ $start = $this->getArg(1);
+ $end = $this->getArg(2);
+ $grace = 60; // maximum normal clock offset
+
+ # Find bounding revision IDs
+ $dbw = wfGetDB( DB_MASTER );
+ $revisionTable = $dbw->tableName( 'revision' );
+ $res = $dbw->query( "SELECT MIN(rev_id) as minrev, MAX(rev_id) as maxrev FROM $revisionTable " .
+ "WHERE rev_timestamp BETWEEN '{$start}' AND '{$end}'", __METHOD__ );
+ $row = $dbw->fetchObject( $res );
+
+ if ( is_null( $row->minrev ) ) {
+ $this->error( "No revisions in search period.", true );
+ }
+
+ $minRev = $row->minrev;
+ $maxRev = $row->maxrev;
+
+ # Select all timestamps and IDs
+ $sql = "SELECT rev_id, rev_timestamp FROM $revisionTable " .
+ "WHERE rev_id BETWEEN $minRev AND $maxRev";
+ if ( $offset > 0 ) {
+ $sql .= " ORDER BY rev_id DESC";
+ $expectedSign = -1;
+ } else {
+ $expectedSign = 1;
+ }
+
+ $res = $dbw->query( $sql, __METHOD__ );
+
+ $lastNormal = 0;
+ $badRevs = array();
+ $numGoodRevs = 0;
+
+ foreach ( $res as $row ) {
+ $timestamp = wfTimestamp( TS_UNIX, $row->rev_timestamp );
+ $delta = $timestamp - $lastNormal;
+ $sign = $delta == 0 ? 0 : $delta / abs( $delta );
+ if ( $sign == 0 || $sign == $expectedSign ) {
+ // Monotonic change
+ $lastNormal = $timestamp;
+ ++ $numGoodRevs;
+ continue;
+ } elseif ( abs( $delta ) <= $grace ) {
+ // Non-monotonic change within grace interval
+ ++ $numGoodRevs;
+ continue;
+ } else {
+ // Non-monotonic change larger than grace interval
+ $badRevs[] = $row->rev_id;
+ }
+ }
+ $dbw->freeResult( $res );
+
+ $numBadRevs = count( $badRevs );
+ if ( $numBadRevs > $numGoodRevs ) {
+ $this->error(
+ "The majority of revisions in the search interval are marked as bad.
+
+ Are you sure the offset ($offset) has the right sign? Positive means the clock
+ was incorrectly set forward, negative means the clock was incorrectly set back.
+
+ If the offset is right, then increase the search interval until there are enough
+ good revisions to provide a majority reference.", true );
+ } elseif ( $numBadRevs == 0 ) {
+ $this->output( "No bad revisions found.\n" );
+ exit(0);
+ }
+
+ $this->output( sprintf( "Fixing %d revisions (%.2f%% of revisions in search interval)\n",
+ $numBadRevs, $numBadRevs / ($numGoodRevs + $numBadRevs) * 100 ) );
+
+ $fixup = -$offset;
+ $sql = "UPDATE $revisionTable " .
+ "SET rev_timestamp=DATE_FORMAT(DATE_ADD(rev_timestamp, INTERVAL $fixup SECOND), '%Y%m%d%H%i%s') " .
+ "WHERE rev_id IN (" . $dbw->makeList( $badRevs ) . ')';
+ $dbw->query( $sql, __METHOD__ );
+ $this->output( "Done\n" );
+ }
}
-printf( "Fixing %d revisions (%.2f%% of revisions in search interval)\n",
- $numBadRevs, $numBadRevs / ($numGoodRevs + $numBadRevs) * 100 );
-
-$fixup = -$offset;
-$sql = "UPDATE $revisionTable " .
- "SET rev_timestamp=DATE_FORMAT(DATE_ADD(rev_timestamp, INTERVAL $fixup SECOND), '%Y%m%d%H%i%s') " .
- "WHERE rev_id IN (" . $dbw->makeList( $badRevs ) . ')';
-//echo "$sql\n";
-$dbw->query( $sql, $fname );
-echo "Done\n";
-
-
+$maintClass = "FixTimestamps";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/fixUserRegistration.php b/maintenance/fixUserRegistration.php
index eb5b7f7d..d3305358 100644
--- a/maintenance/fixUserRegistration.php
+++ b/maintenance/fixUserRegistration.php
@@ -3,32 +3,53 @@
* Fix the user_registration field.
* In particular, for values which are NULL, set them to the date of the first edit
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-require_once( 'commandLine.inc' );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-$fname = 'fixUserRegistration.php';
-
-$dbr = wfGetDB( DB_SLAVE );
-$dbw = wfGetDB( DB_MASTER );
+class FixUserRegistration extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Fix the user_registration field";
+ }
-// Get user IDs which need fixing
-$res = $dbr->select( 'user', 'user_id', 'user_registration IS NULL', $fname );
+ public function execute() {
+ $dbr = wfGetDB( DB_SLAVE );
+ $dbw = wfGetDB( DB_MASTER );
-while ( $row = $dbr->fetchObject( $res ) ) {
- $id = $row->user_id;
- // Get first edit time
- $timestamp = $dbr->selectField( 'revision', 'MIN(rev_timestamp)', array( 'rev_user' => $id ), $fname );
- // Update
- if ( !empty( $timestamp ) ) {
- $dbw->update( 'user', array( 'user_registration' => $timestamp ), array( 'user_id' => $id ), $fname );
- print "$id $timestamp\n";
- } else {
- print "$id NULL\n";
+ // Get user IDs which need fixing
+ $res = $dbr->select( 'user', 'user_id', 'user_registration IS NULL', __METHOD__ );
+ foreach ( $res as $row ) {
+ $id = $row->user_id;
+ // Get first edit time
+ $timestamp = $dbr->selectField( 'revision', 'MIN(rev_timestamp)', array( 'rev_user' => $id ), __METHOD__ );
+ // Update
+ if ( !empty( $timestamp ) ) {
+ $dbw->update( 'user', array( 'user_registration' => $timestamp ), array( 'user_id' => $id ), __METHOD__ );
+ $this->output( "$id $timestamp\n" );
+ } else {
+ $this->output( "$id NULL\n" );
+ }
+ }
+ $this->output( "\n" );
}
}
-print "\n";
-
+$maintClass = "FixUserRegistration";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/fuzz-tester.php b/maintenance/fuzz-tester.php
index 9c1ddaff..6d8c57f2 100644
--- a/maintenance/fuzz-tester.php
+++ b/maintenance/fuzz-tester.php
@@ -138,7 +138,7 @@ Wiki configuration for testing:
}
// --------- End ---------
- Also add/change this in AdminSettings.php:
+ Also add/change this in LocalSettings.php:
// --------- Start ---------
$wgEnableProfileInfo = true;
$wgDBserver = "localhost"; // replace with DB server hostname
@@ -169,7 +169,7 @@ TODO:
/////////////////////////// COMMAND LINE HELP ////////////////////////////////////
// This is a command line script, load MediaWiki env (gives command line options);
-require('commandLine.inc');
+require_once( dirname(__FILE__) . '/commandLine.inc' );
// if the user asked for an explanation of command line options.
if ( isset( $options["help"] ) ) {
@@ -1320,7 +1320,7 @@ class viewPageTest extends pageTest {
"nds-nl", "nl", "nn", "no", "non", "nv", "oc", "or", "os", "pa",
"pl", "pms", "ps", "pt", "pt-br", "qu", "rmy", "ro", "ru", "sc",
"sd", "sk", "sl", "sq", "sr", "sr-ec", "sr-el",
- "su", "sv", "ta", "te", "th", "tlh", "tr", "tt", "ty", "tyv", "udm",
+ "su", "sv", "ta", "te", "th", "tr", "tt", "ty", "tyv", "udm",
"ug", "uk", "ur", "utf8", "vec", "vi", "wa", "xal", "yi", "za",
"zh", "zh-cn", "zh-hk", "zh-sg", "zh-tw", "zh-tw") ),
"returnto" => wikiFuzz::makeFuzz(2),
@@ -2447,7 +2447,7 @@ function validateHTML($text) {
if (curl_error($ch)) {
trigger_error("Curl error #: " . curl_errno($ch) . " - " . curl_error ($ch) );
print "Curl error #: " . curl_errno($ch) . " - " . curl_error ($ch) . " - exiting.\n";
- exit();
+ exit(1);
}
curl_close ($ch);
diff --git a/maintenance/gearman/gearman.inc b/maintenance/gearman/gearman.inc
index a2a4018a..514b9bac 100644
--- a/maintenance/gearman/gearman.inc
+++ b/maintenance/gearman/gearman.inc
@@ -12,7 +12,7 @@ class MWGearmanJob extends Net_Gearman_Job_Common {
socket_close( $this->conn );
# Close some more sockets
- wfGetLBFactory()->shutdown();
+ LBFactory::destroyInstance();
global $wgMemc;
$wgMemc->disconnect_all();
diff --git a/maintenance/gearman/gearmanWorker.php b/maintenance/gearman/gearmanWorker.php
index 0b26ff9f..d6f3949f 100644
--- a/maintenance/gearman/gearmanWorker.php
+++ b/maintenance/gearman/gearmanWorker.php
@@ -4,6 +4,8 @@ $optionsWithArgs = array( 'fake-job', 'procs' );
require( dirname(__FILE__).'/../commandLine.inc' );
require( dirname(__FILE__).'/gearman.inc' );
+ini_set('memory_limit', '150M' );
+
if ( isset( $options['procs'] ) ) {
$procs = $options['procs'];
if ( $procs < 1 || $procs > 1000 ) {
diff --git a/maintenance/generateSitemap.php b/maintenance/generateSitemap.php
index 737c4eba..8ca79341 100644
--- a/maintenance/generateSitemap.php
+++ b/maintenance/generateSitemap.php
@@ -4,6 +4,21 @@ define( 'GS_TALK', -1 );
/**
* Creates a sitemap for the site
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*
* @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason
@@ -16,7 +31,9 @@ define( 'GS_TALK', -1 );
* @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
*/
-class GenerateSitemap {
+require_once( dirname(__FILE__) . '/Maintenance.php' );
+
+class GenerateSitemap extends Maintenance {
/**
* The maximum amount of urls in a sitemap file
*
@@ -68,29 +85,7 @@ class GenerateSitemap {
*
* @var array
*/
- var $priorities = array(
- // Custom main namespaces
- GS_MAIN => '0.5',
- // Custom talk namesspaces
- GS_TALK => '0.1',
- // MediaWiki standard namespaces
- NS_MAIN => '1.0',
- NS_TALK => '0.1',
- NS_USER => '0.5',
- NS_USER_TALK => '0.1',
- NS_PROJECT => '0.5',
- NS_PROJECT_TALK => '0.1',
- NS_FILE => '0.5',
- NS_FILE_TALK => '0.1',
- NS_MEDIAWIKI => '0.0',
- NS_MEDIAWIKI_TALK => '0.1',
- NS_TEMPLATE => '0.0',
- NS_TEMPLATE_TALK => '0.1',
- NS_HELP => '0.5',
- NS_HELP_TALK => '0.1',
- NS_CATEGORY => '0.5',
- NS_CATEGORY_TALK => '0.1',
- );
+ var $priorities = array();
/**
* A one-dimensional array of namespaces in the wiki
@@ -129,36 +124,58 @@ class GenerateSitemap {
var $file;
/**
- * A resource pointing to php://stderr
- *
- * @var resource
+ * Constructor
*/
- var $stderr;
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Creates a sitemap for the site";
+ $this->addOption( 'fspath', 'The file system path to save to, e.g. /tmp/sitemap' .
+ "\n\t\tdefaults to current directory", false, true );
+ $this->addOption( 'server', "The protocol and server name to use in URLs, e.g.\n" .
+ "\t\thttp://en.wikipedia.org. This is sometimes necessary because\n" .
+ "\t\tserver name detection may fail in command line scripts.", false, true );
+ $this->addOption( 'compress', 'Compress the sitemap files, can take value yes|no, default yes', false, true );
+ }
/**
- * Constructor
- *
- * @param string $fspath The path to prepend to the filenames, used to
- * save them somewhere else than in the root directory
- * @param string $path The path to append to the domain name
- * @param bool $compress Whether to compress the sitemap files
+ * Execute
*/
- function GenerateSitemap( $fspath, $compress ) {
+ public function execute() {
global $wgScriptPath;
-
+ $this->setNamespacePriorities();
$this->url_limit = 50000;
$this->size_limit = pow( 2, 20 ) * 10;
- $this->fspath = self::init_path( $fspath );
-
- $this->compress = $compress;
-
- $this->stderr = fopen( 'php://stderr', 'wt' );
+ $this->fspath = self::init_path( $this->getOption( 'fspath', getcwd() ) );
+ $this->compress = $this->getOption( 'compress', 'yes' ) !== 'no';
$this->dbr = wfGetDB( DB_SLAVE );
$this->generateNamespaces();
$this->timestamp = wfTimestamp( TS_ISO_8601, wfTimestampNow() );
-
-
$this->findex = fopen( "{$this->fspath}sitemap-index-" . wfWikiID() . ".xml", 'wb' );
+ $this->main();
+ }
+
+ private function setNamespacePriorities() {
+ // Custom main namespaces
+ $this->priorities[GS_MAIN] = '0.5';
+ // Custom talk namesspaces
+ $this->priorities[GS_TALK] = '0.1';
+ // MediaWiki standard namespaces
+ $this->priorities[NS_MAIN] = '1.0';
+ $this->priorities[NS_TALK] = '0.1';
+ $this->priorities[NS_USER] = '0.5';
+ $this->priorities[NS_USER_TALK] = '0.1';
+ $this->priorities[NS_PROJECT] = '0.5';
+ $this->priorities[NS_PROJECT_TALK] = '0.1';
+ $this->priorities[NS_FILE] = '0.5';
+ $this->priorities[NS_FILE_TALK] = '0.1';
+ $this->priorities[NS_MEDIAWIKI] = '0.0';
+ $this->priorities[NS_MEDIAWIKI_TALK] = '0.1';
+ $this->priorities[NS_TEMPLATE] = '0.0';
+ $this->priorities[NS_TEMPLATE_TALK] = '0.1';
+ $this->priorities[NS_HELP] = '0.5';
+ $this->priorities[NS_HELP_TALK] = '0.1';
+ $this->priorities[NS_CATEGORY] = '0.5';
+ $this->priorities[NS_CATEGORY_TALK] = '0.1';
}
/**
@@ -170,7 +187,7 @@ class GenerateSitemap {
}
# Create directory if needed
if( $fspath && !is_dir( $fspath ) ) {
- mkdir( $fspath, 0755 ) or die("Can not create directory $fspath.\n");
+ wfMkdirParents( $fspath ) or die("Can not create directory $fspath.\n");
}
return realpath( $fspath ). DIRECTORY_SEPARATOR ;
@@ -180,8 +197,6 @@ class GenerateSitemap {
* Generate a one-dimensional array of existing namespaces
*/
function generateNamespaces() {
- $fname = 'GenerateSitemap::generateNamespaces';
-
// Only generate for specific namespaces if $wgSitemapNamespaces is an array.
global $wgSitemapNamespaces;
if( is_array( $wgSitemapNamespaces ) ) {
@@ -192,14 +207,14 @@ class GenerateSitemap {
$res = $this->dbr->select( 'page',
array( 'page_namespace' ),
array(),
- $fname,
+ __METHOD__,
array(
'GROUP BY' => 'page_namespace',
'ORDER BY' => 'page_namespace',
)
);
- while ( $row = $this->dbr->fetchObject( $res ) )
+ foreach ( $res as $row )
$this->namespaces[] = $row->page_namespace;
}
@@ -236,8 +251,6 @@ class GenerateSitemap {
* @return resource
*/
function getPageRes( $namespace ) {
- $fname = 'GenerateSitemap::getPageRes';
-
return $this->dbr->select( 'page',
array(
'page_namespace',
@@ -245,7 +258,7 @@ class GenerateSitemap {
'page_touched',
),
array( 'page_namespace' => $namespace ),
- $fname
+ __METHOD__
);
}
@@ -267,8 +280,8 @@ class GenerateSitemap {
$i = $smcount = 0;
$fns = $wgContLang->getFormattedNsText( $namespace );
- $this->debug( "$namespace ($fns)" );
- while ( $row = $this->dbr->fetchObject( $res ) ) {
+ $this->output( "$namespace ($fns)" );
+ foreach ( $res as $row ) {
if ( $i++ === 0 || $i === $this->url_limit + 1 || $length + $this->limit[1] + $this->limit[2] > $this->size_limit ) {
if ( $this->file !== false ) {
$this->write( $this->file, $this->closeFile() );
@@ -278,7 +291,7 @@ class GenerateSitemap {
$this->file = $this->open( $this->fspath . $filename, 'wb' );
$this->write( $this->file, $this->openFile() );
fwrite( $this->findex, $this->indexEntry( $filename ) );
- $this->debug( "\t$this->fspath$filename" );
+ $this->output( "\t$this->fspath$filename\n" );
$length = $this->limit[0];
$i = 1;
}
@@ -450,13 +463,6 @@ class GenerateSitemap {
}
/**
- * Write a string to stderr followed by a UNIX newline
- */
- function debug( $str ) {
- fwrite( $this->stderr, "$str\n" );
- }
-
- /**
* Populate $this->limit
*/
function generateLimit( $namespace ) {
@@ -470,31 +476,5 @@ class GenerateSitemap {
}
}
-if ( in_array( '--help', $argv ) ) {
- echo <<<EOT
-Usage: php generateSitemap.php [options]
- --help show this message
-
- --fspath=<path> The file system path to save to, e.g /tmp/sitemap
- Saves to current directory if not given.
-
- --server=<server> The protocol and server name to use in URLs, e.g.
- http://en.wikipedia.org. This is sometimes necessary because
- server name detection may fail in command line scripts.
-
- --compress=[yes|no] compress the sitemap files, default yes
-
-EOT;
- die( -1 );
-}
-
-$optionsWithArgs = array( 'fspath', 'server', 'compress' );
-require_once( dirname( __FILE__ ) . '/commandLine.inc' );
-
-if ( isset( $options['server'] ) ) {
- $wgServer = $options['server'];
-}
-
-$gs = new GenerateSitemap( @$options['fspath'], @$options['compress'] !== 'no' );
-$gs->main();
-
+$maintClass = "GenerateSitemap";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/getLagTimes.php b/maintenance/getLagTimes.php
index 0f750caf..bc14ae71 100644
--- a/maintenance/getLagTimes.php
+++ b/maintenance/getLagTimes.php
@@ -1,29 +1,54 @@
<?php
/**
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-require 'commandLine.inc';
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-$lb = wfGetLB();
+class GetLagTimes extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Dump replication lag times";
+ }
+
+ public function execute() {
+ $lb = wfGetLB();
-if( $lb->getServerCount() == 1 ) {
- echo "This script dumps replication lag times, but you don't seem to have\n";
- echo "a multi-host db server configuration.\n";
-} else {
- $lags = $lb->getLagTimes();
- foreach( $lags as $n => $lag ) {
- $host = $lb->getServerName( $n );
- if( IP::isValid( $host ) ) {
- $ip = $host;
- $host = gethostbyaddr( $host );
+ if( $lb->getServerCount() == 1 ) {
+ $this->error( "This script dumps replication lag times, but you don't seem to have\n"
+ . "a multi-host db server configuration." );
} else {
- $ip = gethostbyname( $host );
+ $lags = $lb->getLagTimes();
+ foreach( $lags as $n => $lag ) {
+ $host = $lb->getServerName( $n );
+ if( IP::isValid( $host ) ) {
+ $ip = $host;
+ $host = gethostbyaddr( $host );
+ } else {
+ $ip = gethostbyname( $host );
+ }
+ $starLen = min( intval( $lag ), 40 );
+ $stars = str_repeat( '*', $starLen );
+ $this->output( sprintf( "%10s %20s %3d %s\n", $ip, $host, $lag, $stars ) );
+ }
}
- $starLen = min( intval( $lag ), 40 );
- $stars = str_repeat( '*', $starLen );
- printf( "%10s %20s %3d %s\n", $ip, $host, $lag, $stars );
}
}
+$maintClass = "GetLagTimes";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/getSlaveServer.php b/maintenance/getSlaveServer.php
index 25258267..eac97a59 100644
--- a/maintenance/getSlaveServer.php
+++ b/maintenance/getSlaveServer.php
@@ -2,27 +2,49 @@
/**
* This script reports the hostname of a slave server.
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
+
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-require_once( dirname(__FILE__).'/commandLine.inc' );
-
-if ( $wgAllDBsAreLocalhost ) {
- # Can't fool the backup script
- print "localhost\n";
- exit;
+class GetSlaveServer extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->addOption( "group", "Query group to check specifically" );
+ $this->mDescription = "Report the hostname of a slave server";
+ }
+ public function execute() {
+ global $wgAllDBsAreLocalhost;
+ if( $wgAllDBsAreLocalhost ) {
+ $host = 'localhost';
+ } else {
+ if( $this->hasOption('group') ) {
+ $db = wfGetDB( DB_SLAVE, $this->getOption('group') );
+ $host = $db->getServer();
+ } else {
+ $lb = wfGetLB();
+ $i = $lb->getReaderIndex();
+ $host = $lb->getServerName( $i );
+ }
+ }
+ $this->output( "$host\n" );
+ }
}
-if( isset( $options['group'] ) ) {
- $db = wfGetDB( DB_SLAVE, $options['group'] );
- $host = $db->getServer();
-} else {
- $lb = wfGetLB();
- $i = $lb->getReaderIndex();
- $host = $lb->getServerName( $i );
-}
-
-print "$host\n";
-
-
+$maintClass = "GetSlaveServer";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/getText.php b/maintenance/getText.php
new file mode 100644
index 00000000..6326267d
--- /dev/null
+++ b/maintenance/getText.php
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Outputs page text to stdout, useful for command-line editing automation.
+ * Example: php getText.php "page title" | sed -e '...' | php edit.php "page title"
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @ingroup Maintenance
+ */
+
+require_once( dirname(__FILE__) . '/Maintenance.php' );
+
+class GetTextMaint extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = 'Outputs page text to stdout';
+ $this->addOption( 'show-private', 'Show the text even if it\'s not available to the public' );
+ $this->addArg( 'title', 'Page title' );
+ }
+
+ public function execute() {
+ $this->db = wfGetDB( DB_SLAVE );
+
+ $titleText = $this->getArg( 0 );
+ $title = Title::newFromText( $titleText );
+ if ( !$title ) {
+ $this->error( "$titleText is not a valid title.\n", true );
+ }
+
+ $rev = Revision::newFromTitle( $title );
+ if ( !$rev ) {
+ $titleText = $title->getPrefixedText();
+ $this->error( "Page $titleText does not exist.\n", true );
+ }
+ $text = $rev->getText( $this->hasOption('show-private') ? Revision::RAW : Revision::FOR_PUBLIC );
+ if ( $text === false ) {
+ $titleText = $title->getPrefixedText();
+ $this->error( "Couldn't extract the text from $titleText.\n", true );
+ }
+ $this->output( $text );
+ }
+}
+
+$maintClass = "GetTextMaint";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/httpSessionDownload.php b/maintenance/httpSessionDownload.php
new file mode 100644
index 00000000..cab6e872
--- /dev/null
+++ b/maintenance/httpSessionDownload.php
@@ -0,0 +1,57 @@
+<?php
+/*
+ * Simple entry point to initiate a background download
+ *
+ * arguments:
+ * --sid {$session_id} --usk {$upload_session_key} --wiki {wfWikiId()}
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @ingroup Maintenance
+ */
+
+require_once( dirname(__FILE__) . '/Maintenance.php' );
+
+class HttpSessionDownload extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Simple entry point to initiate a background download";
+ $this->addOption( 'sid', 'Session ID', true, true );
+ $this->addOption( 'usk', 'Upload session key', true, true );
+ }
+
+ public function execute() {
+ wfProfileIn(__METHOD__);
+
+ //run the download:
+ Http::doSessionIdDownload( $this->getOption('sid'), $this->getOption('usk') );
+
+ // close up shop:
+ // Execute any deferred updates
+ wfDoUpdates();
+
+ // Log what the user did, for book-keeping purposes.
+ wfLogProfilingData();
+
+ // Shut down the database before exit
+ wfGetLBFactory()->shutdown();
+
+ wfProfileOut(__METHOD__);
+ }
+}
+
+$maintClass = "HttpSessionDownload";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/ibm_db2/README b/maintenance/ibm_db2/README
index 4a2c0f60..3c3f381c 100644
--- a/maintenance/ibm_db2/README
+++ b/maintenance/ibm_db2/README
@@ -1,41 +1,3 @@
-== Syntax differences between other databases and IBM DB2 ==
-{| border cellspacing=0 cellpadding=4
-!MySQL!!IBM DB2
-|-
-
-|SELECT 1 FROM $table LIMIT 1
-|SELECT COUNT(*) FROM SYSIBM.SYSTABLES ST
-WHERE ST.NAME = '$table' AND ST.CREATOR = '$schema'
-|-
-|MySQL code tries to read one row and interprets lack of error as proof of existence.
-|DB2 code counts the number of TABLES of that name in the database. There ought to be 1 for it to exist.
-|-
-|BEGIN
-|(implicit)
-|-
-|TEXT
-|VARCHAR(255) or CLOB
-|-
-|TIMESTAMPTZ
-|TIMESTAMP
-|-
-|BYTEA
-|VARGRAPHIC(255)
-|-
-|DEFAULT nextval('some_kind_of_sequence'),
-|GENERATED ALWAYS AS IDENTITY (START WITH 0, INCREMENT BY 1),
-|-
-|CIDR
-|VARCHAR(255)
-|-
-|LIMIT 10
-|FETCH FIRST 10 ROWS ONLY
-|-
-|ROLLBACK TO
-|ROLLBACK TO SAVEPOINT
-|-
-|RELEASE
-|RELEASE SAVEPOINT
-|}
== See also ==
+*[http://www.mediawiki.org/wiki/Manual:IBM_DB2 Installation instructions]
*[http://ca.php.net/manual/en/function.db2-connect.php PHP Manual for DB2 functions] \ No newline at end of file
diff --git a/maintenance/ibm_db2/tables.sql b/maintenance/ibm_db2/tables.sql
index 5e91102d..71c161c6 100644
--- a/maintenance/ibm_db2/tables.sql
+++ b/maintenance/ibm_db2/tables.sql
@@ -5,98 +5,110 @@
-- not have to run it by itself unless doing a manual install.
-- This is the IBM DB2 version.
-- For information about each table, please see the notes in maintenance/tables.sql
--- Please make sure all dollar-quoting uses $mw$ at the start of the line
--- TODO: Change CHAR/SMALLINT to BOOL (still used in a non-bool fashion in PHP code)
-
-
-CREATE SEQUENCE user_user_id_seq AS INTEGER START WITH 0 INCREMENT BY 1;
-CREATE TABLE mwuser ( -- replace reserved word 'user'
- user_id INTEGER NOT NULL PRIMARY KEY, -- DEFAULT nextval('user_user_id_seq'),
+CREATE TABLE user (
+ user_id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
user_name VARCHAR(255) NOT NULL UNIQUE,
user_real_name VARCHAR(255),
- user_password clob(1K),
- user_newpassword clob(1K),
- user_newpass_time TIMESTAMP,
+ user_password VARCHAR(1024),
+ user_newpassword VARCHAR(1024),
+ user_newpass_time TIMESTAMP(3),
user_token VARCHAR(255),
- user_email VARCHAR(255),
+ user_email VARCHAR(1024),
user_email_token VARCHAR(255),
- user_email_token_expires TIMESTAMP,
- user_email_authenticated TIMESTAMP,
- user_options CLOB(64K),
- user_touched TIMESTAMP,
- user_registration TIMESTAMP,
+ user_email_token_expires TIMESTAMP(3),
+ user_email_authenticated TIMESTAMP(3),
+ -- obsolete, replace by user_properties table
+ user_options CLOB(64K) INLINE LENGTH 4096,
+ user_touched TIMESTAMP(3),
+ user_registration TIMESTAMP(3),
user_editcount INTEGER
);
-CREATE INDEX user_email_token_idx ON mwuser (user_email_token);
+CREATE INDEX user_email_token_idx ON user (user_email_token);
+--leonsp:
+CREATE UNIQUE INDEX user_include_idx
+ ON user(user_id)
+ INCLUDE (user_name, user_real_name, user_password, user_newpassword, user_newpass_time, user_token,
+ user_email, user_email_token, user_email_token_expires, user_email_authenticated,
+ user_touched, user_registration, user_editcount);
-- Create a dummy user to satisfy fk contraints especially with revisions
-INSERT INTO mwuser
- VALUES (NEXTVAL FOR user_user_id_seq,'Anonymous','', NULL,NULL,CURRENT_TIMESTAMP,NULL, NULL,NULL,NULL,NULL, NULL,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,0);
+INSERT INTO user(
+user_name, user_real_name, user_password, user_newpassword, user_newpass_time,
+user_email, user_email_authenticated, user_options, user_token, user_registration, user_editcount)
+VALUES (
+'Anonymous','', NULL, NULL, CURRENT_TIMESTAMP,
+NULL, NULL, NULL, NULL, CURRENT_timestamp, 0);
+
CREATE TABLE user_groups (
- ug_user INTEGER REFERENCES mwuser(user_id) ON DELETE CASCADE,
+ ug_user BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES user(user_id) ON DELETE CASCADE,
ug_group VARCHAR(255) NOT NULL
);
CREATE UNIQUE INDEX user_groups_unique ON user_groups (ug_user, ug_group);
+--leonsp:
+CREATE UNIQUE INDEX user_groups_include_idx
+ ON user_groups(ug_user)
+ INCLUDE (ug_group);
+
CREATE TABLE user_newtalk (
- user_id INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE,
- user_ip VARCHAR(255),
- user_last_timestamp TIMESTAMP
+ -- registered users key
+ user_id BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES user(user_id) ON DELETE CASCADE,
+ -- anonymous users key
+ user_ip VARCHAR(40),
+ user_last_timestamp TIMESTAMP(3)
);
CREATE INDEX user_newtalk_id_idx ON user_newtalk (user_id);
CREATE INDEX user_newtalk_ip_idx ON user_newtalk (user_ip);
+--leonsp:
+CREATE UNIQUE INDEX user_newtalk_include_idx
+ ON user_newtalk(user_id, user_ip)
+ INCLUDE (user_last_timestamp);
-CREATE SEQUENCE page_page_id_seq;
CREATE TABLE page (
- page_id INTEGER NOT NULL PRIMARY KEY, -- DEFAULT NEXT VALUE FOR user_user_id_seq,
+ page_id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
page_namespace SMALLINT NOT NULL,
page_title VARCHAR(255) NOT NULL,
- page_restrictions clob(1K),
+ page_restrictions VARCHAR(1024),
page_counter BIGINT NOT NULL DEFAULT 0,
page_is_redirect SMALLINT NOT NULL DEFAULT 0,
page_is_new SMALLINT NOT NULL DEFAULT 0,
page_random NUMERIC(15,14) NOT NULL,
- page_touched TIMESTAMP,
- page_latest INTEGER NOT NULL, -- FK?
- page_len INTEGER NOT NULL
+ page_touched TIMESTAMP(3),
+ page_latest BIGINT NOT NULL, -- FK?
+ page_len BIGINT NOT NULL
);
CREATE UNIQUE INDEX page_unique_name ON page (page_namespace, page_title);
---CREATE INDEX page_main_title ON page (page_title) WHERE page_namespace = 0;
---CREATE INDEX page_talk_title ON page (page_title) WHERE page_namespace = 1;
---CREATE INDEX page_user_title ON page (page_title) WHERE page_namespace = 2;
---CREATE INDEX page_utalk_title ON page (page_title) WHERE page_namespace = 3;
---CREATE INDEX page_project_title ON page (page_title) WHERE page_namespace = 4;
CREATE INDEX page_random_idx ON page (page_random);
CREATE INDEX page_len_idx ON page (page_len);
+--leonsp:
+CREATE UNIQUE INDEX page_id_include
+ ON page (page_id)
+ INCLUDE (page_namespace, page_title, page_restrictions, page_counter, page_is_redirect, page_is_new, page_random, page_touched, page_latest, page_len);
+CREATE UNIQUE INDEX page_name_include
+ ON page (page_namespace, page_title)
+ INCLUDE (page_id, page_restrictions, page_counter, page_is_redirect, page_is_new, page_random, page_touched, page_latest, page_len);
---CREATE FUNCTION page_deleted() RETURNS TRIGGER LANGUAGE plpgsql AS
---$mw$
---BEGIN
---DELETE FROM recentchanges WHERE rc_namespace = OLD.page_namespace AND rc_title = OLD.page_title;
---RETURN NULL;
---END;
---$mw$;
---CREATE TRIGGER page_deleted AFTER DELETE ON page
--- FOR EACH ROW EXECUTE PROCEDURE page_deleted();
-
-CREATE SEQUENCE rev_rev_id_val;
CREATE TABLE revision (
- rev_id INTEGER NOT NULL UNIQUE, --DEFAULT nextval('rev_rev_id_val'),
- rev_page INTEGER REFERENCES page (page_id) ON DELETE CASCADE,
- rev_text_id INTEGER, -- FK
- rev_comment clob(1K), -- changed from VARCHAR(255)
- rev_user INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE RESTRICT,
- rev_user_text VARCHAR(255) NOT NULL,
- rev_timestamp TIMESTAMP NOT NULL,
- rev_minor_edit SMALLINT NOT NULL DEFAULT 0,
- rev_deleted SMALLINT NOT NULL DEFAULT 0,
- rev_len INTEGER,
- rev_parent_id INTEGER
+ rev_id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
+ rev_page BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES page (page_id) ON DELETE CASCADE,
+ rev_text_id BIGINT, -- FK
+ rev_comment VARCHAR(1024),
+ rev_user BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES user(user_id) ON DELETE RESTRICT,
+ rev_user_text VARCHAR(255) NOT NULL,
+ rev_timestamp TIMESTAMP(3) NOT NULL,
+ rev_minor_edit SMALLINT NOT NULL DEFAULT 0,
+ rev_deleted SMALLINT NOT NULL DEFAULT 0,
+ rev_len BIGINT,
+ rev_parent_id BIGINT DEFAULT NULL
);
CREATE UNIQUE INDEX revision_unique ON revision (rev_page, rev_id);
CREATE INDEX rev_text_id_idx ON revision (rev_text_id);
@@ -105,34 +117,41 @@ CREATE INDEX rev_user_idx ON revision (rev_user);
CREATE INDEX rev_user_text_idx ON revision (rev_user_text);
-CREATE SEQUENCE text_old_id_val;
-CREATE TABLE pagecontent ( -- replaces reserved word 'text'
- old_id INTEGER NOT NULL,
+
+CREATE TABLE text ( -- replaces reserved word 'text'
+ --old_id INTEGER NOT NULL,
+ old_id INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
--PRIMARY KEY DEFAULT nextval('text_old_id_val'),
- old_text CLOB(16M),
- old_flags clob(1K)
+ old_text CLOB(16M) INLINE LENGTH 4096,
+ old_flags VARCHAR(1024)
);
-CREATE SEQUENCE pr_id_val;
+
CREATE TABLE page_restrictions (
- pr_id INTEGER NOT NULL UNIQUE,
- --DEFAULT nextval('pr_id_val'),
- pr_page INTEGER NOT NULL
+ --pr_id INTEGER NOT NULL UNIQUE, --DEFAULT nextval('pr_id_val'),
+ --pr_id INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
+ pr_id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0),
+ pr_page INTEGER NOT NULL DEFAULT 0,
--(used to be nullable)
- REFERENCES page (page_id) ON DELETE CASCADE,
- pr_type VARCHAR(255) NOT NULL,
- pr_level VARCHAR(255) NOT NULL,
+ -- REFERENCES page (page_id) ON DELETE CASCADE,
+ pr_type VARCHAR(60) NOT NULL,
+ pr_level VARCHAR(60) NOT NULL,
pr_cascade SMALLINT NOT NULL,
pr_user INTEGER,
- pr_expiry TIMESTAMP,
- PRIMARY KEY (pr_page, pr_type)
+ pr_expiry TIMESTAMP(3)
+ --PRIMARY KEY (pr_page, pr_type)
);
--ALTER TABLE page_restrictions ADD CONSTRAINT page_restrictions_pk PRIMARY KEY (pr_page,pr_type);
+CREATE UNIQUE INDEX pr_pagetype ON page_restrictions (pr_page,pr_type);
+CREATE INDEX pr_typelevel ON page_restrictions (pr_type,pr_level);
+CREATE INDEX pr_level ON page_restrictions (pr_level);
+CREATE INDEX pr_cascade ON page_restrictions (pr_cascade);
CREATE TABLE page_props (
- pp_page INTEGER NOT NULL REFERENCES page (page_id) ON DELETE CASCADE,
+ pp_page INTEGER NOT NULL DEFAULT 0,
+ -- REFERENCES page (page_id) ON DELETE CASCADE,
pp_propname VARCHAR(255) NOT NULL,
- pp_value CLOB(64K) NOT NULL,
+ pp_value CLOB(64K) INLINE LENGTH 4096 NOT NULL,
PRIMARY KEY (pp_page,pp_propname)
);
--ALTER TABLE page_props ADD CONSTRAINT page_props_pk PRIMARY KEY (pp_page,pp_propname);
@@ -143,19 +162,21 @@ CREATE INDEX page_props_propname ON page_props (pp_propname);
CREATE TABLE archive (
ar_namespace SMALLINT NOT NULL,
ar_title VARCHAR(255) NOT NULL,
- ar_text CLOB(16M),
- ar_page_id INTEGER,
- ar_parent_id INTEGER,
- ar_comment clob(1K),
- ar_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
+ ar_text CLOB(16M) INLINE LENGTH 4096,
+ ar_comment VARCHAR(1024),
+ ar_user BIGINT NOT NULL,
+ -- no foreign keys in MySQL
+ -- REFERENCES user(user_id) ON DELETE SET NULL,
ar_user_text VARCHAR(255) NOT NULL,
- ar_timestamp TIMESTAMP NOT NULL,
+ ar_timestamp TIMESTAMP(3) NOT NULL,
ar_minor_edit SMALLINT NOT NULL DEFAULT 0,
- ar_flags clob(1K),
+ ar_flags VARCHAR(1024),
ar_rev_id INTEGER,
ar_text_id INTEGER,
ar_deleted SMALLINT NOT NULL DEFAULT 0,
- ar_len INTEGER
+ ar_len INTEGER,
+ ar_page_id INTEGER,
+ ar_parent_id INTEGER
);
CREATE INDEX archive_name_title_timestamp ON archive (ar_namespace,ar_title,ar_timestamp);
CREATE INDEX archive_user_text ON archive (ar_user_text);
@@ -163,38 +184,49 @@ CREATE INDEX archive_user_text ON archive (ar_user_text);
CREATE TABLE redirect (
- rd_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
- rd_namespace SMALLINT NOT NULL,
- rd_title VARCHAR(255) NOT NULL
+ rd_from BIGINT NOT NULL PRIMARY KEY,
+ --REFERENCES page(page_id) ON DELETE CASCADE,
+ rd_namespace SMALLINT NOT NULL DEFAULT 0,
+ rd_title VARCHAR(255) NOT NULL DEFAULT '',
+ rd_interwiki varchar(32),
+ rd_fragment VARCHAR(255)
);
CREATE INDEX redirect_ns_title ON redirect (rd_namespace,rd_title,rd_from);
CREATE TABLE pagelinks (
- pl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
+ pl_from BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES page(page_id) ON DELETE CASCADE,
pl_namespace SMALLINT NOT NULL,
pl_title VARCHAR(255) NOT NULL
);
CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title);
CREATE TABLE templatelinks (
- tl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
+ tl_from BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES page(page_id) ON DELETE CASCADE,
tl_namespace SMALLINT NOT NULL,
tl_title VARCHAR(255) NOT NULL
);
CREATE UNIQUE INDEX templatelinks_unique ON templatelinks (tl_namespace,tl_title,tl_from);
+CREATE UNIQUE INDEX tl_from_idx ON templatelinks (tl_from,tl_namespace,tl_title);
CREATE TABLE imagelinks (
- il_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
+ il_from BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES page(page_id) ON DELETE CASCADE,
il_to VARCHAR(255) NOT NULL
);
-CREATE UNIQUE INDEX il_from ON imagelinks (il_to,il_from);
+CREATE UNIQUE INDEX il_from_idx ON imagelinks (il_to,il_from);
+CREATE UNIQUE INDEX il_to_idx ON imagelinks (il_from,il_to);
CREATE TABLE categorylinks (
- cl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
+ cl_from BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES page(page_id) ON DELETE CASCADE,
cl_to VARCHAR(255) NOT NULL,
- cl_sortkey VARCHAR(255),
- cl_timestamp TIMESTAMP NOT NULL
+ -- cl_sortkey has to be at least 86 wide
+ -- in order to be compatible with the old MySQL schema from MW 1.10
+ cl_sortkey VARCHAR(86),
+ cl_timestamp TIMESTAMP(3) NOT NULL
);
CREATE UNIQUE INDEX cl_from ON categorylinks (cl_from, cl_to);
CREATE INDEX cl_sortkey ON categorylinks (cl_to, cl_sortkey, cl_from);
@@ -202,16 +234,38 @@ CREATE INDEX cl_sortkey ON categorylinks (cl_to, cl_sortkey, cl_from);
CREATE TABLE externallinks (
- el_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
- el_to VARCHAR(255) NOT NULL,
- el_index VARCHAR(255) NOT NULL
+ el_from BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES page(page_id) ON DELETE CASCADE,
+ el_to VARCHAR(1024) NOT NULL,
+ el_index VARCHAR(1024) NOT NULL
);
CREATE INDEX externallinks_from_to ON externallinks (el_from,el_to);
CREATE INDEX externallinks_index ON externallinks (el_index);
+
+--
+-- Track external user accounts, if ExternalAuth is used
+--
+CREATE TABLE external_user (
+ -- Foreign key to user_id
+ eu_local_id BIGINT NOT NULL PRIMARY KEY,
+
+ -- Some opaque identifier provided by the external database
+ eu_external_id VARCHAR(255) NOT NULL
+);
+CREATE UNIQUE INDEX eu_external_id_idx
+ ON external_user (eu_external_id)
+ INCLUDE (eu_local_id);
+CREATE UNIQUE INDEX eu_local_id_idx
+ ON external_user (eu_local_id)
+ INCLUDE (eu_external_id);
+
+
+
CREATE TABLE langlinks (
- ll_from INTEGER NOT NULL REFERENCES page (page_id) ON DELETE CASCADE,
- ll_lang VARCHAR(255),
+ ll_from BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES page (page_id) ON DELETE CASCADE,
+ ll_lang VARCHAR(20),
ll_title VARCHAR(255)
);
CREATE UNIQUE INDEX langlinks_unique ON langlinks (ll_from,ll_lang);
@@ -219,12 +273,13 @@ CREATE INDEX langlinks_lang_title ON langlinks (ll_lang,ll_title);
CREATE TABLE site_stats (
- ss_row_id INTEGER NOT NULL UNIQUE,
- ss_total_views INTEGER DEFAULT 0,
- ss_total_edits INTEGER DEFAULT 0,
- ss_good_articles INTEGER DEFAULT 0,
+ ss_row_id BIGINT NOT NULL UNIQUE,
+ ss_total_views BIGINT DEFAULT 0,
+ ss_total_edits BIGINT DEFAULT 0,
+ ss_good_articles BIGINT DEFAULT 0,
ss_total_pages INTEGER DEFAULT -1,
ss_users INTEGER DEFAULT -1,
+ ss_active_users INTEGER DEFAULT -1,
ss_admins INTEGER DEFAULT -1,
ss_images INTEGER DEFAULT 0
);
@@ -233,25 +288,27 @@ CREATE TABLE hitcounter (
hc_id BIGINT NOT NULL
);
-CREATE SEQUENCE ipblocks_ipb_id_val;
CREATE TABLE ipblocks (
ipb_id INTEGER NOT NULL PRIMARY KEY,
--DEFAULT nextval('ipblocks_ipb_id_val'),
- ipb_address VARCHAR(255),
- ipb_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
- ipb_by INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE,
+ ipb_address VARCHAR(1024),
+ ipb_user BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES user(user_id) ON DELETE SET NULL,
+ ipb_by BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES user(user_id) ON DELETE CASCADE,
ipb_by_text VARCHAR(255) NOT NULL DEFAULT '',
- ipb_reason VARCHAR(255) NOT NULL,
- ipb_timestamp TIMESTAMP NOT NULL,
+ ipb_reason VARCHAR(1024) NOT NULL,
+ ipb_timestamp TIMESTAMP(3) NOT NULL,
ipb_auto SMALLINT NOT NULL DEFAULT 0,
ipb_anon_only SMALLINT NOT NULL DEFAULT 0,
ipb_create_account SMALLINT NOT NULL DEFAULT 1,
ipb_enable_autoblock SMALLINT NOT NULL DEFAULT 1,
- ipb_expiry TIMESTAMP NOT NULL,
- ipb_range_start VARCHAR(255),
- ipb_range_end VARCHAR(255),
+ ipb_expiry TIMESTAMP(3) NOT NULL,
+ ipb_range_start VARCHAR(1024),
+ ipb_range_end VARCHAR(1024),
ipb_deleted SMALLINT NOT NULL DEFAULT 0,
- ipb_block_email SMALLINT NOT NULL DEFAULT 0
+ ipb_block_email SMALLINT NOT NULL DEFAULT 0,
+ ipb_allow_usertalk SMALLINT NOT NULL DEFAULT 0
);
CREATE INDEX ipb_address ON ipblocks (ipb_address);
@@ -262,18 +319,19 @@ CREATE INDEX ipb_range ON ipblocks (ipb_range_start,ipb_range_end);
CREATE TABLE image (
img_name VARCHAR(255) NOT NULL PRIMARY KEY,
- img_size INTEGER NOT NULL,
+ img_size BIGINT NOT NULL,
img_width INTEGER NOT NULL,
img_height INTEGER NOT NULL,
- img_metadata CLOB(16M) NOT NULL DEFAULT '',
+ img_metadata CLOB(16M) INLINE LENGTH 4096 NOT NULL DEFAULT '',
img_bits SMALLINT,
img_media_type VARCHAR(255),
img_major_mime VARCHAR(255) DEFAULT 'unknown',
- img_minor_mime VARCHAR(255) DEFAULT 'unknown',
- img_description clob(1K) NOT NULL DEFAULT '',
- img_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
+ img_minor_mime VARCHAR(32) DEFAULT 'unknown',
+ img_description VARCHAR(1024) NOT NULL DEFAULT '',
+ img_user BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES user(user_id) ON DELETE SET NULL,
img_user_text VARCHAR(255) NOT NULL DEFAULT '',
- img_timestamp TIMESTAMP,
+ img_timestamp TIMESTAMP(3),
img_sha1 VARCHAR(255) NOT NULL DEFAULT ''
);
CREATE INDEX img_size_idx ON image (img_size);
@@ -281,23 +339,24 @@ CREATE INDEX img_timestamp_idx ON image (img_timestamp);
CREATE INDEX img_sha1 ON image (img_sha1);
CREATE TABLE oldimage (
- oi_name VARCHAR(255) NOT NULL,
+ oi_name VARCHAR(255) NOT NULL DEFAULT '',
oi_archive_name VARCHAR(255) NOT NULL,
- oi_size INTEGER NOT NULL,
+ oi_size BIGINT NOT NULL,
oi_width INTEGER NOT NULL,
oi_height INTEGER NOT NULL,
oi_bits SMALLINT NOT NULL,
- oi_description clob(1K),
- oi_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
+ oi_description VARCHAR(1024),
+ oi_user BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES user(user_id) ON DELETE SET NULL,
oi_user_text VARCHAR(255) NOT NULL,
- oi_timestamp TIMESTAMP NOT NULL,
- oi_metadata CLOB(16M) NOT NULL DEFAULT '',
+ oi_timestamp TIMESTAMP(3) NOT NULL,
+ oi_metadata CLOB(16M) INLINE LENGTH 4096 NOT NULL DEFAULT '',
oi_media_type VARCHAR(255) ,
oi_major_mime VARCHAR(255) NOT NULL DEFAULT 'unknown',
oi_minor_mime VARCHAR(255) NOT NULL DEFAULT 'unknown',
oi_deleted SMALLINT NOT NULL DEFAULT 0,
- oi_sha1 VARCHAR(255) NOT NULL DEFAULT '',
- FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE
+ oi_sha1 VARCHAR(255) NOT NULL DEFAULT ''
+ --FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE
);
--ALTER TABLE oldimage ADD CONSTRAINT oldimage_oi_name_fkey_cascade FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE;
CREATE INDEX oi_name_timestamp ON oldimage (oi_name,oi_timestamp);
@@ -305,29 +364,31 @@ CREATE INDEX oi_name_archive_name ON oldimage (oi_name,oi_archive_name);
CREATE INDEX oi_sha1 ON oldimage (oi_sha1);
-CREATE SEQUENCE filearchive_fa_id_seq;
+
CREATE TABLE filearchive (
fa_id INTEGER NOT NULL PRIMARY KEY,
--PRIMARY KEY DEFAULT nextval('filearchive_fa_id_seq'),
fa_name VARCHAR(255) NOT NULL,
fa_archive_name VARCHAR(255),
fa_storage_group VARCHAR(255),
- fa_storage_key VARCHAR(255),
- fa_deleted_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
- fa_deleted_timestamp TIMESTAMP NOT NULL,
+ fa_storage_key VARCHAR(64) DEFAULT '',
+ fa_deleted_user BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES user(user_id) ON DELETE SET NULL,
+ fa_deleted_timestamp TIMESTAMP(3) NOT NULL,
fa_deleted_reason VARCHAR(255),
- fa_size INTEGER NOT NULL,
+ fa_size BIGINT NOT NULL,
fa_width INTEGER NOT NULL,
fa_height INTEGER NOT NULL,
- fa_metadata CLOB(16M) NOT NULL DEFAULT '',
+ fa_metadata CLOB(16M) INLINE LENGTH 4096 NOT NULL DEFAULT '',
fa_bits SMALLINT,
fa_media_type VARCHAR(255),
fa_major_mime VARCHAR(255) DEFAULT 'unknown',
fa_minor_mime VARCHAR(255) DEFAULT 'unknown',
- fa_description clob(1K) NOT NULL,
- fa_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
+ fa_description VARCHAR(1024) NOT NULL,
+ fa_user BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES user(user_id) ON DELETE SET NULL,
fa_user_text VARCHAR(255) NOT NULL,
- fa_timestamp TIMESTAMP,
+ fa_timestamp TIMESTAMP(3),
fa_deleted SMALLINT NOT NULL DEFAULT 0
);
CREATE INDEX fa_name_time ON filearchive (fa_name, fa_timestamp);
@@ -335,13 +396,14 @@ CREATE INDEX fa_dupe ON filearchive (fa_storage_group, fa_storage_key);
CREATE INDEX fa_notime ON filearchive (fa_deleted_timestamp);
CREATE INDEX fa_nouser ON filearchive (fa_deleted_user);
-CREATE SEQUENCE rc_rc_id_seq;
+
CREATE TABLE recentchanges (
rc_id INTEGER NOT NULL PRIMARY KEY,
--PRIMARY KEY DEFAULT nextval('rc_rc_id_seq'),
- rc_timestamp TIMESTAMP NOT NULL,
- rc_cur_time TIMESTAMP NOT NULL,
- rc_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
+ rc_timestamp TIMESTAMP(3) NOT NULL,
+ rc_cur_time TIMESTAMP(3) NOT NULL,
+ rc_user BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES user(user_id) ON DELETE SET NULL,
rc_user_text VARCHAR(255) NOT NULL,
rc_namespace SMALLINT NOT NULL,
rc_title VARCHAR(255) NOT NULL,
@@ -349,21 +411,23 @@ CREATE TABLE recentchanges (
rc_minor SMALLINT NOT NULL DEFAULT 0,
rc_bot SMALLINT NOT NULL DEFAULT 0,
rc_new SMALLINT NOT NULL DEFAULT 0,
- rc_cur_id INTEGER REFERENCES page(page_id) ON DELETE SET NULL,
- rc_this_oldid INTEGER NOT NULL,
- rc_last_oldid INTEGER NOT NULL,
+ rc_cur_id BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES page(page_id) ON DELETE SET NULL,
+ rc_this_oldid BIGINT NOT NULL,
+ rc_last_oldid BIGINT NOT NULL,
rc_type SMALLINT NOT NULL DEFAULT 0,
rc_moved_to_ns SMALLINT,
rc_moved_to_title VARCHAR(255),
rc_patrolled SMALLINT NOT NULL DEFAULT 0,
- rc_ip VARCHAR(255), -- was CIDR type
+ rc_ip VARCHAR(40), -- was CIDR type
rc_old_len INTEGER,
rc_new_len INTEGER,
rc_deleted SMALLINT NOT NULL DEFAULT 0,
- rc_logid INTEGER NOT NULL DEFAULT 0,
+ rc_logid BIGINT NOT NULL DEFAULT 0,
rc_log_type VARCHAR(255),
rc_log_action VARCHAR(255),
- rc_params CLOB(64K)
+ rc_params CLOB(64K) INLINE LENGTH 4096
+
);
CREATE INDEX rc_timestamp ON recentchanges (rc_timestamp);
CREATE INDEX rc_namespace_title ON recentchanges (rc_namespace, rc_title);
@@ -374,26 +438,27 @@ CREATE INDEX rc_ip ON recentchanges (rc_ip);
CREATE TABLE watchlist (
- wl_user INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE,
+ wl_user BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES user(user_id) ON DELETE CASCADE,
wl_namespace SMALLINT NOT NULL DEFAULT 0,
wl_title VARCHAR(255) NOT NULL,
- wl_notificationtimestamp TIMESTAMP
+ wl_notificationtimestamp TIMESTAMP(3)
);
CREATE UNIQUE INDEX wl_user_namespace_title ON watchlist (wl_namespace, wl_title, wl_user);
CREATE TABLE math (
- math_inputhash VARGRAPHIC(255) NOT NULL UNIQUE,
- math_outputhash VARGRAPHIC(255) NOT NULL,
+ math_inputhash VARCHAR(16) FOR BIT DATA NOT NULL UNIQUE,
+ math_outputhash VARCHAR(16) FOR BIT DATA NOT NULL,
math_html_conservativeness SMALLINT NOT NULL,
- math_html VARCHAR(255),
- math_mathml VARCHAR(255)
+ math_html CLOB(64K) INLINE LENGTH 4096,
+ math_mathml CLOB(64K) INLINE LENGTH 4096
);
CREATE TABLE interwiki (
- iw_prefix VARCHAR(255) NOT NULL UNIQUE,
- iw_url CLOB(64K) NOT NULL,
+ iw_prefix VARCHAR(32) NOT NULL UNIQUE,
+ iw_url CLOB(64K) INLINE LENGTH 4096 NOT NULL,
iw_local SMALLINT NOT NULL,
iw_trans SMALLINT NOT NULL DEFAULT 0
);
@@ -401,8 +466,8 @@ CREATE TABLE interwiki (
CREATE TABLE querycache (
qc_type VARCHAR(255) NOT NULL,
- qc_value INTEGER NOT NULL,
- qc_namespace SMALLINT NOT NULL,
+ qc_value BIGINT NOT NULL,
+ qc_namespace INTEGER NOT NULL,
qc_title VARCHAR(255) NOT NULL
);
CREATE INDEX querycache_type_value ON querycache (qc_type, qc_value);
@@ -411,13 +476,13 @@ CREATE INDEX querycache_type_value ON querycache (qc_type, qc_value);
CREATE TABLE querycache_info (
qci_type VARCHAR(255) UNIQUE NOT NULL,
- qci_timestamp TIMESTAMP
+ qci_timestamp TIMESTAMP(3)
);
CREATE TABLE querycachetwo (
qcc_type VARCHAR(255) NOT NULL,
- qcc_value INTEGER NOT NULL DEFAULT 0,
+ qcc_value BIGINT NOT NULL DEFAULT 0,
qcc_namespace INTEGER NOT NULL DEFAULT 0,
qcc_title VARCHAR(255) NOT NULL DEFAULT '',
qcc_namespacetwo INTEGER NOT NULL DEFAULT 0,
@@ -429,8 +494,8 @@ CREATE INDEX querycachetwo_titletwo ON querycachetwo (qcc_type,qcc_namespacetw
CREATE TABLE objectcache (
keyname VARCHAR(255) NOT NULL UNIQUE, -- was nullable
- value CLOB(16M) NOT NULL DEFAULT '',
- exptime TIMESTAMP NOT NULL
+ value CLOB(16M) INLINE LENGTH 4096 NOT NULL DEFAULT '',
+ exptime TIMESTAMP(3) NOT NULL
);
CREATE INDEX objectcacache_exptime ON objectcache (exptime);
@@ -438,49 +503,57 @@ CREATE INDEX objectcacache_exptime ON objectcache (exptime);
CREATE TABLE transcache (
tc_url VARCHAR(255) NOT NULL UNIQUE,
- tc_contents VARCHAR(255) NOT NULL,
- tc_time TIMESTAMP NOT NULL
+ tc_contents CLOB(64K) INLINE LENGTH 4096 NOT NULL,
+ tc_time TIMESTAMP(3) NOT NULL
);
-CREATE SEQUENCE log_log_id_seq;
+
CREATE TABLE logging (
- log_id INTEGER NOT NULL PRIMARY KEY,
+ log_id BIGINT NOT NULL PRIMARY KEY,
--PRIMARY KEY DEFAULT nextval('log_log_id_seq'),
- log_type VARCHAR(255) NOT NULL,
- log_action VARCHAR(255) NOT NULL,
- log_timestamp TIMESTAMP NOT NULL,
- log_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
- log_namespace SMALLINT NOT NULL,
- log_title VARCHAR(255) NOT NULL,
- log_comment VARCHAR(255),
- log_params CLOB(64K),
- log_deleted SMALLINT NOT NULL DEFAULT 0
+ log_type VARCHAR(32) NOT NULL,
+ log_action VARCHAR(32) NOT NULL,
+ log_timestamp TIMESTAMP(3) NOT NULL,
+ log_user BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES user(user_id) ON DELETE SET NULL,
+ -- Name of the user who performed this action
+ log_user_text VARCHAR(255) NOT NULL default '',
+ log_namespace SMALLINT NOT NULL,
+ log_title VARCHAR(255) NOT NULL,
+ log_page BIGINT,
+ log_comment VARCHAR(255),
+ log_params CLOB(64K) INLINE LENGTH 4096,
+ log_deleted SMALLINT NOT NULL DEFAULT 0
);
CREATE INDEX logging_type_name ON logging (log_type, log_timestamp);
CREATE INDEX logging_user_time ON logging (log_timestamp, log_user);
CREATE INDEX logging_page_time ON logging (log_namespace, log_title, log_timestamp);
+CREATE INDEX log_user_type_time ON logging (log_user, log_type, log_timestamp);
+CREATE INDEX log_page_id_time ON logging (log_page,log_timestamp);
+
+
-CREATE SEQUENCE trackbacks_tb_id_seq;
CREATE TABLE trackbacks (
tb_id INTEGER NOT NULL PRIMARY KEY,
--PRIMARY KEY DEFAULT nextval('trackbacks_tb_id_seq'),
- tb_page INTEGER REFERENCES page(page_id) ON DELETE CASCADE,
+ -- foreign key also in MySQL
+ tb_page INTEGER REFERENCES page(page_id) ON DELETE CASCADE,
tb_title VARCHAR(255) NOT NULL,
- tb_url CLOB(64K) NOT NULL,
- tb_ex VARCHAR(255),
+ tb_url CLOB(64K) INLINE LENGTH 4096 NOT NULL,
+ tb_ex CLOB(64K) INLINE LENGTH 4096,
tb_name VARCHAR(255)
);
CREATE INDEX trackback_page ON trackbacks (tb_page);
-CREATE SEQUENCE job_job_id_seq;
+
CREATE TABLE job (
- job_id INTEGER NOT NULL PRIMARY KEY,
+ job_id BIGINT NOT NULL PRIMARY KEY,
--PRIMARY KEY DEFAULT nextval('job_job_id_seq'),
job_cmd VARCHAR(255) NOT NULL,
job_namespace SMALLINT NOT NULL,
job_title VARCHAR(255) NOT NULL,
- job_params CLOB(64K) NOT NULL
+ job_params CLOB(64K) INLINE LENGTH 4096 NOT NULL
);
CREATE INDEX job_cmd_namespace_title ON job (job_cmd, job_namespace, job_title);
@@ -504,7 +577,7 @@ CREATE INDEX job_cmd_namespace_title ON job (job_cmd, job_namespace, job_title);
-- FOR EACH ROW EXECUTE PROCEDURE ts2_page_title();
---ALTER TABLE pagecontent ADD textvector tsvector;
+--ALTER TABLE text ADD textvector tsvector;
--CREATE FUNCTION ts2_page_text() RETURNS TRIGGER LANGUAGE plpgsql AS
--$mw$
--BEGIN
@@ -517,14 +590,14 @@ CREATE INDEX job_cmd_namespace_title ON job (job_cmd, job_namespace, job_title);
--END;
--$mw$;
---CREATE TRIGGER ts2_page_text BEFORE INSERT OR UPDATE ON pagecontent
+--CREATE TRIGGER ts2_page_text BEFORE INSERT OR UPDATE ON text
-- FOR EACH ROW EXECUTE PROCEDURE ts2_page_text();
-- These are added by the setup script due to version compatibility issues
-- If using 8.1, we switch from "gin" to "gist"
--CREATE INDEX ts2_page_title ON page USING gin(titlevector);
---CREATE INDEX ts2_page_text ON pagecontent USING gin(textvector);
+--CREATE INDEX ts2_page_text ON text USING gin(textvector);
--TODO
--CREATE FUNCTION add_interwiki (TEXT,INT,SMALLINT) RETURNS INT LANGUAGE SQL AS
@@ -536,7 +609,7 @@ CREATE INDEX job_cmd_namespace_title ON job (job_cmd, job_namespace, job_title);
-- hack implementation
-- should be replaced with OmniFind, Contains(), etc
CREATE TABLE searchindex (
- si_page int NOT NULL,
+ si_page BIGINT NOT NULL,
si_title varchar(255) NOT NULL default '',
si_text clob NOT NULL
);
@@ -552,13 +625,14 @@ CREATE TABLE profiling (
CREATE UNIQUE INDEX pf_name_server ON profiling (pf_name, pf_server);
CREATE TABLE protected_titles (
- pt_namespace SMALLINT NOT NULL,
+ pt_namespace INTEGER NOT NULL,
pt_title VARCHAR(255) NOT NULL,
- pt_user INTEGER REFERENCES mwuser(user_id) ON DELETE SET NULL,
- pt_reason clob(1K),
- pt_timestamp TIMESTAMP NOT NULL,
- pt_expiry TIMESTAMP ,
- pt_create_perm VARCHAR(255) NOT NULL DEFAULT ''
+ pt_user BIGINT NOT NULL DEFAULT 0,
+ -- REFERENCES user(user_id) ON DELETE SET NULL,
+ pt_reason VARCHAR(1024),
+ pt_timestamp TIMESTAMP(3) NOT NULL,
+ pt_expiry TIMESTAMP(3) ,
+ pt_create_perm VARCHAR(60) NOT NULL DEFAULT ''
);
CREATE UNIQUE INDEX protected_titles_unique ON protected_titles(pt_namespace, pt_title);
@@ -568,7 +642,7 @@ CREATE TABLE updatelog (
ul_key VARCHAR(255) NOT NULL PRIMARY KEY
);
-CREATE SEQUENCE category_id_seq;
+
CREATE TABLE category (
cat_id INTEGER NOT NULL PRIMARY KEY,
--PRIMARY KEY DEFAULT nextval('category_id_seq'),
@@ -581,24 +655,101 @@ CREATE TABLE category (
CREATE UNIQUE INDEX category_title ON category(cat_title);
CREATE INDEX category_pages ON category(cat_pages);
+-- added for 1.15
+
+-- A table to track tags for revisions, logs and recent changes.
+CREATE TABLE change_tag (
+ ct_rc_id INTEGER,
+ ct_log_id INTEGER,
+ ct_rev_id INTEGER,
+ ct_tag varchar(255) NOT NULL,
+ ct_params CLOB(64K) INLINE LENGTH 4096
+);
+CREATE UNIQUE INDEX change_tag_rc_tag ON change_tag (ct_rc_id,ct_tag);
+CREATE UNIQUE INDEX change_tag_log_tag ON change_tag (ct_log_id,ct_tag);
+CREATE UNIQUE INDEX change_tag_rev_tag ON change_tag (ct_rev_id,ct_tag);
+-- Covering index, so we can pull all the info only out of the index.
+CREATE INDEX change_tag_tag_id ON change_tag (ct_tag,ct_rc_id,ct_rev_id,ct_log_id);
+
+
+-- Rollup table to pull a LIST of tags simply
+CREATE TABLE tag_summary (
+ ts_rc_id INTEGER,
+ ts_log_id INTEGER,
+ ts_rev_id INTEGER,
+ ts_tags CLOB(64K) INLINE LENGTH 4096 NOT NULL
+);
+CREATE UNIQUE INDEX tag_summary_rc_id ON tag_summary (ts_rc_id);
+CREATE UNIQUE INDEX tag_summary_log_id ON tag_summary (ts_log_id);
+CREATE UNIQUE INDEX tag_summary_rev_id ON tag_summary (ts_rev_id);
+
+
+CREATE TABLE valid_tag (
+ vt_tag varchar(255) NOT NULL PRIMARY KEY
+);
+
+--
+-- User preferences and perhaps other fun stuff. :)
+-- Replaces the old user.user_options blob, with a couple nice properties:
+--
+-- 1) We only store non-default settings, so changes to the defaults
+-- are now reflected for everybody, not just new accounts.
+-- 2) We can more easily do bulk lookups, statistics, or modifications of
+-- saved options since it's a sane table structure.
+--
+CREATE TABLE user_properties (
+ -- Foreign key to user.user_id
+ up_user BIGINT NOT NULL,
+
+ -- Name of the option being saved. This is indexed for bulk lookup.
+ up_property VARCHAR(32) FOR BIT DATA NOT NULL,
+
+ -- Property value as a string.
+ up_value CLOB(64K) INLINE LENGTH 4096
+);
+CREATE UNIQUE INDEX user_properties_user_property ON user_properties (up_user,up_property);
+CREATE INDEX user_properties_property ON user_properties (up_property);
+
+CREATE TABLE log_search (
+ -- The type of ID (rev ID, log ID, rev TIMESTAMP(3), username)
+ ls_field VARCHAR(32) FOR BIT DATA NOT NULL,
+ -- The value of the ID
+ ls_value varchar(255) NOT NULL,
+ -- Key to log_id
+ ls_log_id BIGINT NOT NULL default 0
+);
+CREATE UNIQUE INDEX ls_field_val ON log_search (ls_field,ls_value,ls_log_id);
+CREATE INDEX ls_log_id ON log_search (ls_log_id);
+
CREATE TABLE mediawiki_version (
- type VARCHAR(255) NOT NULL,
- mw_version VARCHAR(255) NOT NULL,
- notes VARCHAR(255) ,
+ type VARCHAR(1024) NOT NULL,
+ mw_version VARCHAR(1024) NOT NULL,
+ notes VARCHAR(1024) ,
- pg_version VARCHAR(255) ,
- pg_dbname VARCHAR(255) ,
- pg_user VARCHAR(255) ,
- pg_port VARCHAR(255) ,
- mw_schema VARCHAR(255) ,
- ts2_schema VARCHAR(255) ,
- ctype VARCHAR(255) ,
+ pg_version VARCHAR(1024) ,
+ pg_dbname VARCHAR(1024) ,
+ pg_user VARCHAR(1024) ,
+ pg_port VARCHAR(1024) ,
+ mw_schema VARCHAR(1024) ,
+ ts2_schema VARCHAR(1024) ,
+ ctype VARCHAR(1024) ,
- sql_version VARCHAR(255) ,
- sql_date VARCHAR(255) ,
- cdate TIMESTAMP NOT NULL DEFAULT CURRENT TIMESTAMP
+ sql_version VARCHAR(1024) ,
+ sql_date VARCHAR(1024) ,
+ cdate TIMESTAMP(3) NOT NULL DEFAULT CURRENT TIMESTAMP
);
INSERT INTO mediawiki_version (type,mw_version,sql_version,sql_date)
VALUES ('Creation','??','$LastChangedRevision: 34049 $','$LastChangedDate: 2008-04-30 10:20:36 -0400 (Wed, 30 Apr 2008) $');
+-- Table for storing localisation data
+CREATE TABLE l10n_cache (
+ -- Language code
+ lc_lang VARCHAR(32) NOT NULL,
+ -- Cache key
+ lc_key VARCHAR(255) NOT NULL,
+ -- Value
+ lc_value CLOB(16M) INLINE LENGTH 4096 NOT NULL
+);
+CREATE INDEX lc_lang_key ON l10n_cache (lc_lang, lc_key);
+
diff --git a/maintenance/importDump.php b/maintenance/importDump.php
index eb51126a..714d76d8 100644
--- a/maintenance/importDump.php
+++ b/maintenance/importDump.php
@@ -24,7 +24,7 @@
$optionsWithArgs = array( 'report' );
-require_once( 'commandLine.inc' );
+require_once( dirname(__FILE__) . '/commandLine.inc' );
/**
* @ingroup Maintenance
@@ -115,10 +115,19 @@ class BackupReader {
}
function importFromFile( $filename ) {
+ $t = true;
if( preg_match( '/\.gz$/', $filename ) ) {
$filename = 'compress.zlib://' . $filename;
}
- $file = fopen( $filename, 'rt' );
+ elseif( preg_match( '/\.bz2$/', $filename ) ) {
+ $filename = 'compress.bzip2://' . $filename;
+ }
+ elseif( preg_match( '/\.7z$/', $filename ) ) {
+ $filename = 'mediawiki.compress.7z://' . $filename;
+ $t = false;
+ }
+
+ $file = fopen( $filename, $t ? 'rt' : 't' ); //our 7zip wrapper uses popen, which seems not to like two-letter modes
return $this->importFromHandle( $file );
}
diff --git a/maintenance/importImages.inc.php b/maintenance/importImages.inc
index 290f3c07..7bb50eb8 100644
--- a/maintenance/importImages.inc.php
+++ b/maintenance/importImages.inc
@@ -6,6 +6,7 @@
* @file
* @ingroup Maintenance
* @author Rob Church <robchur@gmail.com>
+ * @author Mij <mij@bitchx.it>
*/
/**
@@ -18,6 +19,7 @@
function findFiles( $dir, $exts ) {
if( is_dir( $dir ) ) {
if( $dhl = opendir( $dir ) ) {
+ $files = array();
while( ( $file = readdir( $dhl ) ) !== false ) {
if( is_file( $dir . '/' . $file ) ) {
list( /* $name */, $ext ) = splitFilename( $dir . '/' . $file );
@@ -27,10 +29,10 @@ function findFiles( $dir, $exts ) {
}
return $files;
} else {
- return false;
+ return array();
}
} else {
- return false;
+ return array();
}
}
@@ -85,4 +87,26 @@ function findAuxFile( $file, $auxExtension, $maxStrip = 1 ) {
}
return false;
-} \ No newline at end of file
+}
+
+# FIXME: Access the api in a saner way and performing just one query (preferably batching files too).
+function getFileCommentFromSourceWiki($wiki_host, $file) {
+ $url = $wiki_host . '/api.php?action=query&format=xml&titles=File:' . rawurlencode( $file ) . '&prop=imageinfo&&iiprop=comment';
+ $body = Http::get($url);
+ if (preg_match('#<ii comment="([^"]*)" />#', $body, $matches) == 0) {
+ return false;
+ }
+
+ return html_entity_decode( $matches[1] );
+}
+
+function getFileUserFromSourceWiki($wiki_host, $file) {
+ $url = $wiki_host . '/api.php?action=query&format=xml&titles=File:' . rawurlencode( $file ) . '&prop=imageinfo&&iiprop=user';
+ $body = Http::get($url);
+ if (preg_match('#<ii user="([^"]*)" />#', $body, $matches) == 0) {
+ return false;
+ }
+
+ return html_entity_decode( $matches[1] );
+}
+
diff --git a/maintenance/importImages.php b/maintenance/importImages.php
index 7997b0d5..f0dd388a 100644
--- a/maintenance/importImages.php
+++ b/maintenance/importImages.php
@@ -2,17 +2,25 @@
/**
* Maintenance script to import one or more images from the local file system into
- * the wiki without using the web-based interface
+ * the wiki without using the web-based interface.
+ *
+ * "Smart import" additions:
+ * - aim: preserve the essential metadata (user, description) when importing medias from an existing wiki
+ * - process:
+ * - interface with the source wiki, don't use bare files only (see --source-wiki-url).
+ * - fetch metadata from source wiki for each file to import.
+ * - commit the fetched metadata to the destination wiki while submitting.
*
* @file
* @ingroup Maintenance
* @author Rob Church <robchur@gmail.com>
+ * @author Mij <mij@bitchx.it>
*/
-$optionsWithArgs = array( 'extensions', 'comment', 'comment-file', 'comment-ext', 'user', 'license' );
-require_once( 'commandLine.inc' );
-require_once( 'importImages.inc.php' );
-$added = $skipped = $overwritten = 0;
+$optionsWithArgs = array( 'extensions', 'comment', 'comment-file', 'comment-ext', 'user', 'license', 'sleep', 'limit', 'from', 'source-wiki-url' );
+require_once( dirname(__FILE__) . '/commandLine.inc' );
+require_once( dirname(__FILE__) . '/importImages.inc' );
+$processed = $added = $ignored = $skipped = $overwritten = $failed = 0;
echo( "Import Images\n\n" );
@@ -25,7 +33,7 @@ if( count( $args ) > 0 ) {
if (isset($options['protect']) && isset($options['unprotect']))
die("Cannot specify both protect and unprotect. Only 1 is allowed.\n");
- if ($options['protect'] == 1)
+if (isset($options['protect']) && $options['protect'] == 1)
die("You must specify a protection option.\n");
# Prepare the list of allowed extensions
@@ -45,8 +53,27 @@ if( count( $args ) > 0 ) {
$user = User::newFromName( 'Maintenance script' );
$wgUser = $user;
+ # Get block check. If a value is given, this specified how often the check is performed
+ if ( isset( $options['check-userblock'] ) ) {
+ if ( !$options['check-userblock'] ) $checkUserBlock = 1;
+ else $checkUserBlock = (int)$options['check-userblock'];
+ } else {
+ $checkUserBlock = false;
+ }
+
+ # Get --from
+ $from = @$options['from'];
+
+ # Get sleep time.
+ $sleep = @$options['sleep'];
+ if ( $sleep ) $sleep = (int)$sleep;
+
+ # Get limit number
+ $limit = @$options['limit'];
+ if ( $limit ) $limit = (int)$limit;
+
# Get the upload comment
- $comment = 'Importing image file';
+ $comment = NULL;
if ( isset( $options['comment-file'] ) ) {
$comment = file_get_contents( $options['comment-file'] );
@@ -76,6 +103,23 @@ if( count( $args ) > 0 ) {
continue;
}
+ if ( $from ) {
+ if ( $from == $title->getDBkey() ) {
+ $from = NULL;
+ } else {
+ $ignored++;
+ continue;
+ }
+ }
+
+ if ( $checkUserBlock && ( ( $processed % $checkUserBlock ) == 0 ) ) {
+ $user->clearInstanceCache( 'name' ); //reload from DB!
+ if ( $user->isBlocked() ) {
+ echo( $user->getName() . " was blocked! Aborting.\n" );
+ break;
+ }
+ }
+
# Check existence
$image = wfLocalFile( $title );
if( $image->exists() ) {
@@ -88,36 +132,73 @@ if( count( $args ) > 0 ) {
continue;
}
} else {
- echo( "Importing {$base}..." );
- $svar = 'added';
- }
+ if ( isset( $options['skip-dupes'] ) ) {
+ $repo = $image->getRepo();
+ $sha1 = File::sha1Base36( $file ); #XXX: we end up calculating this again when actually uploading. that sucks.
- # Find comment text
- $commentText = false;
+ $dupes = $repo->findBySha1( $sha1 );
- if ( $commentExt ) {
- $f = findAuxFile( $file, $commentExt );
- if ( !$f ) {
- echo( " No comment file with extension {$commentExt} found for {$file}, using default comment. " );
- } else {
- $commentText = file_get_contents( $f );
- if ( !$f ) {
- echo( " Failed to load comment file {$f}, using default comment. " );
+ if ( $dupes ) {
+ echo( "{$base} already exists as " . $dupes[0]->getName() . ", skipping\n" );
+ $skipped++;
+ continue;
}
}
- }
- if ( !$commentText ) {
- $commentText = $comment;
+ echo( "Importing {$base}..." );
+ $svar = 'added';
}
+ if (isset( $options['source-wiki-url'])) {
+ /* find comment text directly from source wiki, through MW's API */
+ $real_comment = getFileCommentFromSourceWiki($options['source-wiki-url'], $base);
+ if ($real_comment === false)
+ $commentText = $comment;
+ else
+ $commentText = $real_comment;
+
+ /* find user directly from source wiki, through MW's API */
+ $real_user = getFileUserFromSourceWiki($options['source-wiki-url'], $base);
+ if ($real_user === false) {
+ $wgUser = $user;
+ } else {
+ $wgUser = User::newFromName($real_user);
+ if ($wgUser === false) {
+ # user does not exist in target wiki
+ echo ("failed: user '$real_user' does not exist in target wiki.");
+ continue;
+ }
+ }
+ } else {
+ # Find comment text
+ $commentText = false;
+
+ if ( $commentExt ) {
+ $f = findAuxFile( $file, $commentExt );
+ if ( !$f ) {
+ echo( " No comment file with extension {$commentExt} found for {$file}, using default comment. " );
+ } else {
+ $commentText = file_get_contents( $f );
+ if ( !$f ) {
+ echo( " Failed to load comment file {$f}, using default comment. " );
+ }
+ }
+ }
+
+ if ( !$commentText ) {
+ $commentText = $comment;
+ }
+ }
+
+
# Import the file
if ( isset( $options['dry'] ) ) {
- echo( " publishing {$file}... " );
+ echo( " publishing {$file} by '" . $wgUser->getName() . "', comment '$commentText'... " );
} else {
$archive = $image->publish( $file );
if( WikiError::isError( $archive ) || !$archive->isGood() ) {
echo( "failed.\n" );
+ $failed++;
continue;
}
}
@@ -141,7 +222,6 @@ if( count( $args ) > 0 ) {
}
- $$svar++;
if ( isset( $options['dry'] ) ) {
echo( "done.\n" );
} else if ( $image->recordUpload( $archive->value, $commentText, $license ) ) {
@@ -164,14 +244,24 @@ if( count( $args ) > 0 ) {
} else {
echo( "failed.\n" );
+ $svar = 'failed';
}
+ $$svar++;
+ $processed++;
+
+ if ( $limit && $processed >= $limit )
+ break;
+
+ if ( $sleep )
+ sleep( $sleep );
}
# Print out some statistics
echo( "\n" );
- foreach( array( 'count' => 'Found', 'added' => 'Added',
- 'skipped' => 'Skipped', 'overwritten' => 'Overwritten' ) as $var => $desc ) {
+ foreach( array( 'count' => 'Found', 'limit' => 'Limit', 'ignored' => 'Ignored',
+ 'added' => 'Added', 'skipped' => 'Skipped', 'overwritten' => 'Overwritten',
+ 'failed' => 'Failed' ) as $var => $desc ) {
if( $$var > 0 )
echo( "{$desc}: {$$var}\n" );
}
@@ -184,14 +274,14 @@ if( count( $args ) > 0 ) {
showUsage();
}
-exit();
+exit(0);
function showUsage( $reason = false ) {
if( $reason ) {
echo( $reason . "\n" );
}
- echo <<<END
+ echo <<<TEXT
Imports images and other media files into the wiki
USAGE: php importImages.php [options] <dir>
@@ -199,17 +289,25 @@ USAGE: php importImages.php [options] <dir>
Options:
--extensions=<exts> Comma-separated list of allowable extensions, defaults to \$wgFileExtensions
---overwrite Overwrite existing images if a conflicting-named image is found
+--overwrite Overwrite existing images with the same name (default is to skip them)
+--limit=<num> Limit the number of images to process. Ignored or skipped images are not counted.
+--from=<name> Ignore all files until the one with the given name. Useful for resuming
+ aborted imports. <name> should be the file's canonical database form.
+--skip-dupes Skip images that were already uploaded under a different name (check SHA1)
+--sleep=<sec> Sleep between files. Useful mostly for debugging.
--user=<username> Set username of uploader, default 'Maintenance script'
---comment=<text> Set upload summary comment, default 'Importing image file'
+--check-userblock Check if the user got blocked during import.
+--comment=<text> Set upload summary comment, default 'Importing image file'.
--comment-file=<file> Set upload summary comment the the content of <file>.
--comment-ext=<ext> Causes the comment for each file to be loaded from a file with the same name
- but the extension <ext>.
+ but the extension <ext>. If a global comment is also given, it is appended.
--license=<code> Use an optional license template
--dry Dry run, don't import anything
--protect=<protect> Specify the protect value (autoconfirmed,sysop)
--unprotect Unprotects all uploaded images
+--source-wiki-url if specified, take User and Comment data for each imported file from this URL.
+ For example, --source-wiki-url="http://en.wikipedia.org/"
-END;
- exit();
-} \ No newline at end of file
+TEXT;
+ exit(1);
+}
diff --git a/maintenance/importLogs.inc b/maintenance/importLogs.inc
deleted file mode 100644
index a008e6c7..00000000
--- a/maintenance/importLogs.inc
+++ /dev/null
@@ -1,144 +0,0 @@
-<?php
-# Copyright (C) 2004 Brion Vibber <brion@pobox.com>
-# http://www.mediawiki.org/
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-# http://www.gnu.org/copyleft/gpl.html
-
-/**
- * Attempt to import existing log pages into the log tables.
- *
- * Not yet complete.
- *
- * @file
- * @todo document
- * @ingroup Maintenance
- */
-
-/** */
-require_once( 'GlobalFunctions.php' );
-require_once( 'Database.php' );
-require_once( 'Article.php' );
-require_once( 'LogPage.php' );
-
-/**
- * Log importer
- * @todo document
- * @ingroup Maintenance
- */
-class LogImporter {
- var $dummy = false;
-
- function LogImporter( $type ) {
- $this->type = $type;
- $this->db = wfGetDB( DB_MASTER );
- $this->actions = $this->setupActions();
- }
-
- function setupActions() {
- $actions = array();
- foreach( LogPage::validActions( $this->type ) as $action ) {
- $key = "{$this->type}/$action";
- $actions[$key] = $this->makeLineRegexp( $this->type, $action );
- }
- return $actions;
- }
-
- function makeLineRegexp( $type, $action ) {
- $linkRegexp = '(?:\[\[)?([^|\]]+?)(?:\|[^\]]+?)?(?:\]\])?';
- $linkRegexp2 = '\[\[([^|\]]+?)(?:\|[^\]]+?)?\]\]';
-
- $text = LogPage::actionText( $type, $action );
- $text = preg_quote( $text, '/' );
- $text = str_replace( '\$1', $linkRegexp, $text );
- $text = '^(.*?) ' . $linkRegexp2 . ' ' . $text;
- $text .= '(?: <em>\((.*)\)<\/em>)?';
- $text = "/$text/";
- return $text;
- }
-
- function importText( $text ) {
- if( $this->dummy ) {
- print $text;
- var_dump( $this->actions );
- }
- $lines = explode( '<li>', $text );
- foreach( $lines as $line ) {
- $matches = array();
- if( preg_match( '!^(.*)</li>!', $line, $matches ) ) {
- $this->importLine( $matches[1] );
- }
- }
- }
-
- function fixDate( $date ) {
- # Yuck! Parsing multilingual date formats??!!!!???!!??!
- # 01:55, 23 Aug 2004 - won't take in strtotimr
- # "Aug 23 2004 01:55" - seems ok
- # TODO: multilingual attempt to extract from the data in Language
- $matches = array();
- if( preg_match( '/^(\d+:\d+(?::\d+)?), (.*)$/', $date, $matches ) ) {
- $date = $matches[2] . ' ' . $matches[1];
- }
- $n = strtotime( $date ) + date("Z");
- # print gmdate( 'D, d M Y H:i:s T', $n ) . "\n";
- $timestamp = wfTimestamp( TS_MW, $n );
- return $timestamp;
- }
-
- function importLine( $line ) {
- foreach( $this->actions as $action => $regexp ) {
- $matches = array();
- if( preg_match( $regexp, $line, $matches ) ) {
- if( $this->dummy ) {
- #var_dump( $matches );
- }
- $date = $this->fixDate( $matches[1] );
- $user = Title::newFromText( $matches[2] );
- $target = Title::newFromText( $matches[3] );
- if( isset( $matches[4] ) ) {
- $comment = $matches[4];
- } else {
- $comment = '';
- }
-
- $insert = array(
- 'log_type' => $this->type,
- 'log_action' => preg_replace( '!^.*/!', '', $action ),
- 'log_timestamp' => $date,
- 'log_user' => intval( User::idFromName( $user->getText() ) ),
- 'log_namespace' => $target->getNamespace(),
- 'log_title' => $target->getDBkey(),
- 'log_comment' => wfUnescapeWikiText( $comment ),
- );
- if( $this->dummy ) {
- var_dump( $insert );
- } else {
- # FIXME: avoid duplicates!
- $this->db->insert( 'logging', $insert );
- }
- break;
- }
- }
- }
-}
-
-function wfUnescapeWikiText( $text ) {
- $text = str_replace(
- array( '&#91;', '&#124;', '&#39;', 'ISBN&#32;', '&#58;//' , "\n&#61;", '&#123;&#123;' ),
- array( '[', '|', "'", 'ISBN ' , '://' , "\n=", '{{' ),
- $text );
- return $text;
-}
diff --git a/maintenance/importLogs.php b/maintenance/importLogs.php
deleted file mode 100644
index 059c2f17..00000000
--- a/maintenance/importLogs.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-/**
- * @todo document
- * @file
- * @ingroup Maintenance
- */
-
-/** */
-require_once( "commandLine.inc" );
-require_once( "importLogs.inc" );
-
-#print $text;
-#exit();
-
-foreach( LogPage::validTypes() as $type ) {
- if( $type == '' ) continue;
-
- $page = LogPage::logName( $type );
- $log = new Article( Title::makeTitleSafe( NS_PROJECT, $page ) );
- $text = $log->fetchContent();
-
- $importer = new LogImporter( $type );
- $importer->dummy = true;
- $importer->importText( $text );
-}
-
-
diff --git a/maintenance/importTextFile.php b/maintenance/importTextFile.php
index bfb852e0..955d01f4 100644
--- a/maintenance/importTextFile.php
+++ b/maintenance/importTextFile.php
@@ -11,7 +11,7 @@
$options = array( 'help', 'nooverwrite', 'norc' );
$optionsWithArgs = array( 'title', 'user', 'comment' );
-require_once( 'commandLine.inc' );
+require_once( dirname(__FILE__) . '/commandLine.inc' );
echo( "Import Text File\n\n" );
if( count( $args ) < 1 || isset( $options['help'] ) ) {
@@ -23,7 +23,7 @@ if( count( $args ) < 1 || isset( $options['help'] ) ) {
if( is_file( $filename ) ) {
$title = isset( $options['title'] ) ? $options['title'] : titleFromFilename( $filename );
- $title = Title::newFromUrl( $title );
+ $title = Title::newFromURL( $title );
if( is_object( $title ) ) {
diff --git a/maintenance/importUseModWiki.php b/maintenance/importUseModWiki.php
index 05a4c78c..0d014145 100644
--- a/maintenance/importUseModWiki.php
+++ b/maintenance/importUseModWiki.php
@@ -59,7 +59,7 @@ function importPages()
global $wgRootDirectory;
$gt = '>';
- echo <<<END
+ echo <<<XML
<?xml version="1.0" encoding="UTF-8" ?$gt
<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.1/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -69,7 +69,7 @@ function importPages()
xml:lang="en">
<!-- generated by importUseModWiki.php -->
-END;
+XML;
$letters = array(
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
@@ -79,10 +79,10 @@ END;
if( is_dir( $dir ) )
importPageDirectory( $dir );
}
- echo <<<END
+ echo <<<XML
</mediawiki>
-END;
+XML;
}
function importPageDirectory( $dir, $prefix = "" )
@@ -227,11 +227,11 @@ function importPage( $title )
*/
$revisions = array( $page );
}
- $xml = <<<END
+ $xml = <<<XML
<page>
<title>$newtitle</title>
-END;
+XML;
# History
$revisions = array_merge( $revisions, fetchKeptPages( $title ) );
@@ -247,7 +247,7 @@ END;
$timestamp = xmlsafe( timestamp2ISO8601( $rev->ts ) );
$comment = xmlsafe( recodeText( $rev->summary ) );
- $xml .= <<<END
+ $xml .= <<<XML
<revision>
<timestamp>$timestamp</timestamp>
<contributor><username>$username</username></contributor>
@@ -256,7 +256,7 @@ END;
<text>$text</text>
</revision>
-END;
+XML;
}
$xml .= "</page>\n\n";
return $xml;
diff --git a/maintenance/initEditCount.php b/maintenance/initEditCount.php
index d26349bb..b7301643 100644
--- a/maintenance/initEditCount.php
+++ b/maintenance/initEditCount.php
@@ -1,89 +1,108 @@
<?php
/**
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-require_once "commandLine.inc";
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-if( isset( $options['help'] ) ) {
- die( "Batch-recalculate user_editcount fields from the revision table.
-Options:
- --quick Force the update to be done in a single query.
- --background Force replication-friendly mode; may be inefficient but
- avoids locking tables or lagging slaves with large updates;
- calculates counts on a slave if possible.
+class InitEditCount extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->addOption( 'quick', 'Force the update to be done in a single query' );
+ $this->addOption( 'background', 'Force replication-friendly mode; may be inefficient but
+ avoids locking tables or lagging slaves with large updates;
+ calculates counts on a slave if possible.
Background mode will be automatically used if the server is MySQL 4.0
(which does not support subqueries) or if multiple servers are listed
-in \$wgDBservers, usually indicating a replication environment.
+in $wgDBservers, usually indicating a replication environment.' );
+ $this->mDescription = "Batch-recalculate user_editcount fields from the revision table";
+ }
-");
-}
-$dbw = wfGetDB( DB_MASTER );
-$user = $dbw->tableName( 'user' );
-$revision = $dbw->tableName( 'revision' );
+ public function execute() {
+ global $wgDBservers;
+ $dbw = wfGetDB( DB_MASTER );
+ $user = $dbw->tableName( 'user' );
+ $revision = $dbw->tableName( 'revision' );
-$dbver = $dbw->getServerVersion();
+ $dbver = $dbw->getServerVersion();
-// Autodetect mode...
-$backgroundMode = count( $wgDBservers ) > 1 ||
- ($dbw instanceof DatabaseMySql && version_compare( $dbver, '4.1' ) < 0);
+ // Autodetect mode...
+ $backgroundMode = count( $wgDBservers ) > 1 ||
+ ($dbw instanceof DatabaseMySql && version_compare( $dbver, '4.1' ) < 0);
+
+ if( $this->hasOption('background') ) {
+ $backgroundMode = true;
+ } elseif( $this->hasOption('quick') ) {
+ $backgroundMode = false;
+ }
-if( isset( $options['background'] ) ) {
- $backgroundMode = true;
-} elseif( isset( $options['quick'] ) ) {
- $backgroundMode = false;
-}
+ if( $backgroundMode ) {
+ $this->output( "Using replication-friendly background mode...\n" );
-if( $backgroundMode ) {
- echo "Using replication-friendly background mode...\n";
-
- $dbr = wfGetDB( DB_SLAVE );
- $chunkSize = 100;
- $lastUser = $dbr->selectField( 'user', 'MAX(user_id)', '', __FUNCTION__ );
-
- $start = microtime( true );
- $migrated = 0;
- for( $min = 0; $min <= $lastUser; $min += $chunkSize ) {
- $max = $min + $chunkSize;
- $result = $dbr->query(
- "SELECT
- user_id,
- COUNT(rev_user) AS user_editcount
- FROM $user
- LEFT OUTER JOIN $revision ON user_id=rev_user
- WHERE user_id > $min AND user_id <= $max
- GROUP BY user_id",
- __FUNCTION__ );
-
- while( $row = $dbr->fetchObject( $result ) ) {
- $dbw->update( 'user',
- array( 'user_editcount' => $row->user_editcount ),
- array( 'user_id' => $row->user_id ),
- __FUNCTION__ );
- ++$migrated;
+ $dbr = wfGetDB( DB_SLAVE );
+ $chunkSize = 100;
+ $lastUser = $dbr->selectField( 'user', 'MAX(user_id)', '', __METHOD__ );
+
+ $start = microtime( true );
+ $migrated = 0;
+ for( $min = 0; $min <= $lastUser; $min += $chunkSize ) {
+ $max = $min + $chunkSize;
+ $result = $dbr->query(
+ "SELECT
+ user_id,
+ COUNT(rev_user) AS user_editcount
+ FROM $user
+ LEFT OUTER JOIN $revision ON user_id=rev_user
+ WHERE user_id > $min AND user_id <= $max
+ GROUP BY user_id",
+ __METHOD__ );
+
+ foreach( $result as $row ) {
+ $dbw->update( 'user',
+ array( 'user_editcount' => $row->user_editcount ),
+ array( 'user_id' => $row->user_id ),
+ __METHOD__ );
+ ++$migrated;
+ }
+ $dbr->freeResult( $result );
+
+ $delta = microtime( true ) - $start;
+ $rate = ($delta == 0.0) ? 0.0 : $migrated / $delta;
+ $this->output( sprintf( "%s %d (%0.1f%%) done in %0.1f secs (%0.3f accounts/sec).\n",
+ wfWikiID(),
+ $migrated,
+ min( $max, $lastUser ) / $lastUser * 100.0,
+ $delta,
+ $rate ) );
+
+ wfWaitForSlaves( 10 );
+ }
+ } else {
+ // Subselect should work on modern MySQLs etc
+ $this->output( "Using single-query mode...\n" );
+ $sql = "UPDATE $user SET user_editcount=(SELECT COUNT(*) FROM $revision WHERE rev_user=user_id)";
+ $dbw->query( $sql );
}
- $dbr->freeResult( $result );
-
- $delta = microtime( true ) - $start;
- $rate = ($delta == 0.0) ? 0.0 : $migrated / $delta;
- printf( "%s %d (%0.1f%%) done in %0.1f secs (%0.3f accounts/sec).\n",
- $wgDBname,
- $migrated,
- min( $max, $lastUser ) / $lastUser * 100.0,
- $delta,
- $rate );
-
- wfWaitForSlaves( 10 );
+
+ $this->output( "Done!\n" );
}
-} else {
- // Subselect should work on modern MySQLs etc
- echo "Using single-query mode...\n";
- $sql = "UPDATE $user SET user_editcount=(SELECT COUNT(*) FROM $revision WHERE rev_user=user_id)";
- $dbw->query( $sql );
}
-echo "Done!\n";
-
-
+$maintClass = "InitEditCount";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/initStats.inc b/maintenance/initStats.inc
deleted file mode 100644
index b1660ce2..00000000
--- a/maintenance/initStats.inc
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-/**
- * @file
- * @ingroup Maintenance
- */
-
-function wfInitStats( $options=array() ) {
- $dbr = wfGetDB( DB_SLAVE );
-
- wfOut( "Counting total edits..." );
- $edits = $dbr->selectField( 'revision', 'COUNT(*)', '', __METHOD__ );
- $edits += $dbr->selectField( 'archive', 'COUNT(*)', '', __METHOD__ );
- wfOut( "{$edits}\nCounting number of articles..." );
-
- global $wgContentNamespaces;
- $good = $dbr->selectField( 'page', 'COUNT(*)', array( 'page_namespace' => $wgContentNamespaces, 'page_is_redirect' => 0, 'page_len > 0' ), __METHOD__ );
- wfOut( "{$good}\nCounting total pages..." );
-
- $pages = $dbr->selectField( 'page', 'COUNT(*)', '', __METHOD__ );
- wfOut( "{$pages}\nCounting number of users..." );
-
- $users = $dbr->selectField( 'user', 'COUNT(*)', '', __METHOD__ );
- wfOut( "{$users}\nCounting number of admins..." );
-
- $admin = $dbr->selectField( 'user_groups', 'COUNT(*)', array( 'ug_group' => 'sysop' ), __METHOD__ );
- wfOut( "{$admin}\nCounting number of images..." );
-
- $image = $dbr->selectField( 'image', 'COUNT(*)', '', __METHOD__ );
- wfOut( "{$image}\n" );
-
- if( !isset( $options['noviews'] ) ) {
- wfOut( "Counting total page views..." );
- $views = $dbr->selectField( 'page', 'SUM(page_counter)', '', __METHOD__ );
- wfOut( "{$views}\n" );
- }
-
- wfOut( "\nUpdating site statistics..." );
-
- $dbw = wfGetDB( DB_MASTER );
- $values = array( 'ss_total_edits' => $edits,
- 'ss_good_articles' => $good,
- 'ss_total_pages' => $pages,
- 'ss_users' => $users,
- 'ss_admins' => $admin,
- 'ss_images' => $image );
- $conds = array( 'ss_row_id' => 1 );
- $views = array( 'ss_total_views' => isset( $views ) ? $views : 0 );
-
- if( isset( $options['update'] ) ) {
- $dbw->update( 'site_stats', $values, $conds, __METHOD__ );
- } else {
- $dbw->delete( 'site_stats', $conds, __METHOD__ );
- $dbw->insert( 'site_stats', array_merge( $values, $conds, $views ), __METHOD__ );
- }
-
- wfOut( "done.\n" );
-}
diff --git a/maintenance/initStats.php b/maintenance/initStats.php
index d206c202..b92d46c5 100644
--- a/maintenance/initStats.php
+++ b/maintenance/initStats.php
@@ -3,29 +3,82 @@
/**
* Maintenance script to re-initialise or update the site statistics table
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
* @author Brion Vibber
* @author Rob Church <robchur@gmail.com>
* @licence GNU General Public Licence 2.0 or later
*/
-
-$options = array( 'help', 'update', 'noviews' );
-require_once( 'commandLine.inc' );
-echo( "Refresh Site Statistics\n\n" );
-
-if( isset( $options['help'] ) ) {
- showHelp();
- exit();
-}
-require "$IP/maintenance/initStats.inc";
-wfInitStats( $options );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
+
+class InitStats extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Re-initialise the site statistics tables";
+ $this->addOption( 'update', 'Update the existing statistics (preserves the ss_total_views field)' );
+ $this->addOption( 'noviews', "Don't update the page view counter" );
+ $this->addOption( 'active', 'Also update active users count' );
+ $this->addOption( 'use-master', 'Count using the master database' );
+ }
+
+ public function execute() {
+ $this->output( "Refresh Site Statistics\n\n" );
+ $counter = new SiteStatsInit( $this->hasOption( 'use-master' ) );
+
+ $this->output( "Counting total edits..." );
+ $edits = $counter->edits();
+ $this->output( "{$edits}\nCounting number of articles..." );
+
+ $good = $counter->articles();
+ $this->output( "{$good}\nCounting total pages..." );
+
+ $pages = $counter->pages();
+ $this->output( "{$pages}\nCounting number of users..." );
+
+ $users = $counter->users();
+ $this->output( "{$users}\nCounting number of images..." );
+
+ $image = $counter->files();
+ $this->output( "{$image}\n" );
+
+ if( !$this->hasOption('noviews') ) {
+ $this->output( "Counting total page views..." );
+ $views = $counter->views();
+ $this->output( "{$views}\n" );
+ }
+
+ if( $this->hasOption( 'active' ) ) {
+ $this->output( "Counting active users..." );
+ $active = SiteStatsUpdate::cacheUpdate();
+ $this->output( "{$active}\n" );
+ }
+
+ $this->output( "\nUpdating site statistics..." );
+
+ if( $this->hasOption( 'update' ) ) {
+ $counter->update();
+ } else {
+ $counter->refresh();
+ }
-function showHelp() {
- echo( "Re-initialise the site statistics tables.\n\n" );
- echo( "Usage: php initStats.php [--update|--noviews]\n\n" );
- echo( " --update : Update the existing statistics (preserves the ss_total_views field)\n" );
- echo( "--noviews : Don't update the page view counter\n\n" );
+ $this->output( "done.\n" );
+ }
}
+$maintClass = "InitStats";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/install-utils.inc b/maintenance/install-utils.inc
new file mode 100644
index 00000000..f2d48e9a
--- /dev/null
+++ b/maintenance/install-utils.inc
@@ -0,0 +1,219 @@
+<?php
+
+/**
+ * This file contains functions used by the install script (config/index.php)
+ * and maintenance scripts. It is not loaded in normal web requests.
+ *
+ * @file
+ */
+
+function install_version_checks() {
+ # We dare not turn output buffer _off_ since this will break completely
+ # if PHP is globally configured to run through a gzip filter.
+ @ob_implicit_flush( true );
+
+ if( !function_exists( 'version_compare' ) ) {
+ # version_compare was introduced in 4.1.0
+ echo "Your PHP version is much too old; 4.0.x will _not_ work. 5.1.0 or higher is required. ABORTING.\n";
+ die( 1 );
+ }
+ if( version_compare( phpversion(), '5.1.0' ) < 0 ) {
+ echo "PHP 5.1.0 or higher is required. If PHP 5 is available only when \n".
+ "PHP files have a .php5 extension, please navigate to <a href=\"index.php5\">index.php5</a> \n".
+ "to continue installation. ABORTING.\n";
+ die( 1 );
+ }
+
+ $test = new PhpXmlBugTester();
+ if( !$test->ok ) {
+ echo "Your system has a combination of PHP and libxml2 versions which is buggy\n" .
+ "and can cause hidden data corruption in MediaWiki and other web apps.\n" .
+ "Upgrade to PHP 5.2.9 or later and libxml2 2.7.3 or later!\n" .
+ "ABORTING (http://bugs.php.net/bug.php?id=45996 for details).\n";
+ die( 1 );
+ }
+
+ $test = new PhpRefCallBugTester;
+ $test->execute();
+ if ( !$test->ok ) {
+ echo "PHP 5.3.1 is not compatible with MediaWiki due to a bug involving\n" .
+ "reference parameters to __call. Upgrade to PHP 5.3.2 or higher, or \n" .
+ "downgrade to PHP 5.3.0 to fix this.\n" .
+ "ABORTING (see http://bugs.php.net/bug.php?id=50394 for details)\n";
+ die( 1 );
+ }
+
+ global $wgCommandLineMode;
+ $wgCommandLineMode = true;
+ umask( 000 );
+ @set_time_limit( 0 );
+}
+
+/**
+ * Test for PHP+libxml2 bug which breaks XML input subtly with certain versions.
+ * http://bugs.php.net/bug.php?id=45996
+ * Known fixed with PHP 5.2.9 + libxml2-2.7.3
+ */
+class PhpXmlBugTester {
+ private $parsedData = '';
+ public $ok = false;
+ public function __construct() {
+ $charData = '<b>c</b>';
+ $xml = '<a>' . htmlspecialchars( $charData ) . '</a>';
+
+ $parser = xml_parser_create();
+ xml_set_character_data_handler( $parser, array( $this, 'chardata' ) );
+ $parsedOk = xml_parse($parser, $xml, true);
+ $this->ok = $parsedOk && ($this->parsedData == $charData);
+ }
+ public function chardata($parser, $data) {
+ $this->parsedData .= $data;
+ }
+}
+
+/**
+ * Test for PHP bug #50394 (PHP 5.3.x conversion to null only, not 5.2.x)
+ */
+class PhpRefCallBugTester {
+ public $ok = false;
+
+ function __call( $name, $args ) {
+ $old = error_reporting( E_ALL & ~E_WARNING );
+ call_user_func_array( array( $this, 'checkForBrokenRef' ), $args );
+ error_reporting( $old );
+ }
+
+ function checkForBrokenRef( &$var ) {
+ if ( $var ) {
+ $this->ok = true;
+ }
+ }
+
+ function execute() {
+ $var = true;
+ call_user_func_array( array( $this, 'foo' ), array( &$var ) );
+ }
+}
+
+function readconsole( $prompt = '' ) {
+ static $isatty = null;
+ if ( is_null( $isatty ) ) {
+ if ( !function_exists( 'posix_isatty' ) || posix_isatty( 0 /*STDIN*/ ) ) {
+ $isatty = true;
+ } else {
+ $isatty = false;
+ }
+ }
+
+ if ( $isatty && function_exists( 'readline' ) ) {
+ return readline( $prompt );
+ } else {
+ if ( $isatty ) {
+ $st = readlineEmulation( $prompt );
+ } else {
+ if ( feof( STDIN ) ) {
+ $st = false;
+ } else {
+ $st = fgets(STDIN, 1024);
+ }
+ }
+ if ($st === false) return false;
+ $resp = trim( $st );
+ return $resp;
+ }
+}
+
+function findExecutable( $name ) {
+ $paths = explode( PATH_SEPARATOR, getenv( "PATH" ) );
+ foreach( $paths as $path ) {
+ $full = $path . DIRECTORY_SEPARATOR . $name;
+ if( file_exists( $full ) ) {
+ if( wfIsWindows() || is_executable( $full ) ) {
+ return $full;
+ }
+ }
+ }
+ return false;
+}
+
+function readlineEmulation( $prompt ) {
+ $bash = "bash";
+ if( !wfIsWindows() && findExecutable( $bash ) ) {
+ $retval = false;
+ $encPrompt = wfEscapeShellArg( $prompt );
+ $command = "read -er -p $encPrompt && echo \"\$REPLY\"";
+ $encCommand = wfEscapeShellArg( $command );
+ $line = wfShellExec( "$bash -c $encCommand", $retval );
+
+ if( $retval == 0 ) {
+ return $line;
+ } elseif( $retval == 127 ) {
+ // Couldn't execute bash even though we thought we saw it.
+ // Shell probably spit out an error message, sorry :(
+ // Fall through to fgets()...
+ } else {
+ // EOF/ctrl+D
+ return false;
+ }
+ }
+
+ // Fallback... we'll have no editing controls, EWWW
+ if ( feof( STDIN ) ) {
+ return false;
+ }
+ print $prompt;
+ return fgets(STDIN, 1024);
+}
+
+
+#
+# Read and execute SQL commands from a file
+#
+function dbsource( $fname, $db = false ) {
+ wfDeprecated( __METHOD__ );
+ if ( !$db ) {
+ // Try $wgDatabase, which is used in the install and update scripts
+ global $wgDatabase;
+ if ( isset( $wgDatabase ) ) {
+ $db = $wgDatabase;
+ } else {
+ // No? Well, we must be outside of those scripts, so use the standard method
+ $db = wfGetDB( DB_MASTER );
+ }
+ }
+ $error = $db->sourceFile( $fname );
+ if ( $error !== true ) {
+ print $error;
+ exit(1);
+ }
+}
+
+/**
+ * Get the value of session.save_path
+ *
+ * Per http://www.php.net/manual/en/ref.session.php#ini.session.save-path,
+ * this might have some additional preceding parts which need to be
+ * ditched
+ *
+ * @return string
+ */
+function mw_get_session_save_path() {
+ $path = ini_get( 'session.save_path' );
+ $path = substr( $path, strrpos( $path, ';' ) );
+ return $path;
+}
+
+/**
+ * Is dl() available to us?
+ *
+ * According to http://www.php.net/manual/en/function.dl.php, dl()
+ * is *not* available when `enable_dl` is off, or under `safe_mode`
+ *
+ * @return bool
+ */
+function mw_have_dl() {
+ return function_exists( 'dl' )
+ && is_callable( 'dl' )
+ && wfIniGetBool( 'enable_dl' )
+ && !wfIniGetBool( 'safe_mode' );
+}
diff --git a/maintenance/installExtension.php b/maintenance/installExtension.php
index d5c4f4be..ea4c191c 100644
--- a/maintenance/installExtension.php
+++ b/maintenance/installExtension.php
@@ -23,7 +23,7 @@
$optionsWithArgs = array( 'target', 'repository', 'repos' );
-require_once( 'commandLine.inc' );
+require_once( dirname(__FILE__) . '/commandLine.inc' );
define('EXTINST_NOPATCH', 0);
define('EXTINST_WRITEPATCH', 6);
diff --git a/maintenance/interwiki.sql b/maintenance/interwiki.sql
index 2521d381..2ce0e23f 100644
--- a/maintenance/interwiki.sql
+++ b/maintenance/interwiki.sql
@@ -39,13 +39,14 @@ REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local) VALUES
('lugkr','http://lug-kr.sourceforge.net/cgi-bin/lugwiki.pl?$1',0),
('mathsongswiki','http://SeedWiki.com/page.cfm?wikiid=237&doc=$1',0),
('meatball','http://www.usemod.com/cgi-bin/mb.pl?$1',0),
-('mediazilla','http://bugzilla.wikipedia.org/$1',1),
+('mediazilla','https://bugzilla.wikimedia.org/$1',1),
('mediawikiwiki','http://www.mediawiki.org/wiki/$1',0),
('memoryalpha','http://www.memory-alpha.org/en/index.php/$1',0),
('metawiki','http://sunir.org/apps/meta.pl?$1',0),
('metawikipedia','http://meta.wikimedia.org/wiki/$1',0),
('moinmoin','http://purl.net/wiki/moin/$1',0),
('mozillawiki','http://wiki.mozilla.org/index.php/$1',0),
+('mw','http://www.mediawiki.org/wiki/$1',0),
('oeis','http://www.research.att.com/cgi-bin/access.cgi/as/njas/sequences/eisA.cgi?Anum=$1',0),
('openfacts','http://openfacts.berlios.de/index.phtml?title=$1',0),
('openwiki','http://openwiki.com/?$1',0),
@@ -79,19 +80,20 @@ REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local) VALUES
('wiki','http://c2.com/cgi/wiki?$1',0),
('wikia','http://www.wikia.com/wiki/$1',0),
('wikibooks','http://en.wikibooks.org/wiki/$1',1),
-('wikicities','http://www.wikicities.com/index.php/$1',0),
+('wikicities','http://www.wikia.com/wiki/$1',0),
('wikif1','http://www.wikif1.org/$1',0),
('wikihow','http://www.wikihow.com/$1',0),
('wikinfo','http://www.wikinfo.org/index.php/$1',0),
('wikimedia','http://wikimediafoundation.org/wiki/$1',0),
-('wikiquote','http://en.wikiquote.org/wiki/$1',1),
('wikinews','http://en.wikinews.org/wiki/$1',1),
-('wikisource','http://sources.wikipedia.org/wiki/$1',1),
-('wikispecies','http://species.wikipedia.org/wiki/$1',1),
+('wikiquote','http://en.wikiquote.org/wiki/$1',1),
+('wikipedia', 'http://en.wikipedia.org/wiki/$1', 1),
+('wikisource','http://wikisource.org/wiki/$1',1),
+('wikispecies','http://species.wikimedia.org/wiki/$1',1),
('wikitravel','http://wikitravel.org/en/$1',0),
+('wikiversity','http://en.wikiversity.org/wiki/$1',1),
+('wikt','http://en.wiktionary.org/wiki/$1',1),
('wiktionary','http://en.wiktionary.org/wiki/$1',1),
-('wikipedia', 'http://en.wikipedia.org/wiki/$1', 1),
('wlug','http://www.wlug.org.nz/$1',0),
('zwiki','http://zwiki.org/$1',0),
-('zzz wiki','http://wiki.zzz.ee/index.php/$1',0),
-('wikt','http://en.wiktionary.org/wiki/$1',1);
+('zzz wiki','http://wiki.zzz.ee/index.php/$1',0);
diff --git a/maintenance/lag.php b/maintenance/lag.php
new file mode 100644
index 00000000..47b4c47b
--- /dev/null
+++ b/maintenance/lag.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * Shows database lag
+ *
+ * @ingroup Maintenance
+ */
+
+require_once( dirname(__FILE__) . '/Maintenance.php' );
+
+class DatabaseLag extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Shows database lag";
+ $this->addOption( 'r', "Don't exit immediately, but show the lag every 5 seconds" );
+ }
+
+ public function execute() {
+ if ( $this->hasOption( 'r' ) ) {
+ $lb = wfGetLB();
+ echo 'time ';
+ for( $i = 1; $i < $lb->getServerCount(); $i++ ) {
+ $hostname = $lb->getServerName( $i );
+ printf( "%-12s ", $hostname );
+ }
+ echo "\n";
+
+ while( 1 ) {
+ $lb->clearLagTimeCache();
+ $lags = $lb->getLagTimes();
+ unset( $lags[0] );
+ echo gmdate( 'H:i:s' ) . ' ';
+ foreach( $lags as $i => $lag ) {
+ printf( "%-12s " , $lag === false ? 'false' : $lag );
+ }
+ echo "\n";
+ sleep( 5 );
+ }
+ } else {
+ $lb = wfGetLB();
+ $lags = $lb->getLagTimes();
+ foreach( $lags as $i => $lag ) {
+ $name = $lb->getServerName( $i );
+ $this->output( sprintf( "%-20s %s\n" , $name, $lag === false ? 'false' : $lag ) );
+ }
+ }
+ }
+}
+
+$maintClass = "DatabaseLag";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/language/StatOutputs.php b/maintenance/language/StatOutputs.php
index b41278b4..169a4d41 100644
--- a/maintenance/language/StatOutputs.php
+++ b/maintenance/language/StatOutputs.php
@@ -32,7 +32,7 @@ class statsOutput {
class wikiStatsOutput extends statsOutput {
function heading() {
global $IP;
- $version = SpecialVersion::getVersion( $IP );
+ $version = SpecialVersion::getVersion( 'nodb' );
echo "'''Statistics are based on:''' <code>" . $version . "</code>\n\n";
echo "'''Note:''' These statistics can be generated by running <code>php maintenance/language/transstat.php</code>.\n\n";
echo "For additional information on specific languages (the message names, the actual problems, etc.), run <code>php maintenance/language/checkLanguage.php --lang=foo</code>.\n\n";
@@ -72,18 +72,6 @@ class wikiStatsOutput extends statsOutput {
}
}
-/** Outputs WikiText and appends category and text only used for Meta-Wiki */
-class metawikiStatsOutput extends wikiStatsOutput {
- function heading() {
- echo "See [[MediaWiki localisation]] to learn how you can help translating MediaWiki.\n\n";
- parent::heading();
- }
- function footer() {
- parent::footer();
- echo "\n[[Category:Localisation|Statistics]]\n";
- }
-}
-
/** Output text. To be used on a terminal for example. */
class textStatsOutput extends statsOutput {
function element( $in, $heading = false ) {
diff --git a/maintenance/language/alltrans.php b/maintenance/language/alltrans.php
index 67c870e6..420386fd 100644
--- a/maintenance/language/alltrans.php
+++ b/maintenance/language/alltrans.php
@@ -1,16 +1,40 @@
<?php
/**
- * @file
- * @ingroup MaintenanceLanguage
- *
* Get all the translations messages, as defined in the English language file.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @ingroup MaintenanceLanguage
*/
-require_once( dirname(__FILE__).'/../commandLine.inc' );
+require_once( dirname(__FILE__) . '/../Maintenance.php' );
-$wgEnglishMessages = array_keys( Language::getMessagesFor( 'en' ) );
-foreach( $wgEnglishMessages as $key ) {
- echo "$key\n";
-}
+class AllTrans extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Get all messages as defined by the English language file";
+ }
+ public function execute() {
+ $wgEnglishMessages = array_keys( Language::getMessagesFor( 'en' ) );
+ foreach( $wgEnglishMessages as $key ) {
+ $this->output( "$key\n" );
+ }
+ }
+}
+$maintClass = "AllTrans";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/language/checkDupeMessages.php b/maintenance/language/checkDupeMessages.php
new file mode 100644
index 00000000..81eafccf
--- /dev/null
+++ b/maintenance/language/checkDupeMessages.php
@@ -0,0 +1,118 @@
+<?php
+/**
+ * @todo document
+ * @file
+ * @ingroup MaintenanceLanguage
+ */
+
+require_once( dirname(__FILE__).'/../commandLine.inc' );
+$messagesDir = dirname(__FILE__).'/../../languages/messages/';
+$runTest = false;
+$run = false;
+$runMode = 'text';
+
+// Check parameters
+if ( isset( $options['lang'] ) && isset( $options['clang'] )) {
+ if (!isset( $options['mode'] )) {
+ $runMode = 'text';
+ } else {
+ if (!strcmp($options['mode'],'wiki')) {
+ $runMode = 'wiki';
+ } else if (!strcmp($options['mode'],'php')) {
+ $runMode = 'php';
+ } else if (!strcmp($options['mode'],'raw')) {
+ $runMode = 'raw';
+ } else {
+ }
+ }
+ $runTest = true;
+} else {
+ echo <<<TEXT
+Run this script to print out the duplicates against a message array.
+Parameters:
+ * lang: Language code to be checked.
+ * clang: Language code to be compared.
+Options:
+ * mode: Output format, can be either:
+ * text: Text output on the console (default)
+ * wiki: Wiki format, with * at beginning of each line
+ * php: Output text as PHP syntax in a array $dupeMessages
+ * raw: Raw output for duplicates
+TEXT;
+}
+
+// Check file exists
+if ( $runTest ) {
+ $langCode = $options['lang'];
+ $langCodeC = $options['clang'];
+ $langCodeF = ucfirst(strtolower(preg_replace('/-/','_',$langCode)));
+ $langCodeFC = ucfirst(strtolower(preg_replace('/-/','_',$langCodeC)));
+ $messagesFile = $messagesDir.'Messages'.$langCodeF.'.php';
+ $messagesFileC = $messagesDir.'Messages'.$langCodeFC.'.php';
+ if (file_exists($messagesFile) && file_exists($messagesFileC)) {
+ $run = true;
+ }
+ else {
+ echo "Messages file(s) could not be found.\nMake sure both files are exists.\n";
+ }
+}
+
+// Run to check the dupes
+if ( $run ) {
+ if (!strcmp($runMode,'wiki')) {
+ $runMode = 'wiki';
+ } else if (!strcmp($runMode,'raw')) {
+ $runMode = 'raw';
+ }
+ include( $messagesFile );
+ $messageExist = isset($messages);
+ if ($messageExist)
+ $wgMessages[$langCode] = $messages;
+ include( $messagesFileC );
+ $messageCExist = isset($messages);
+ if ($messageCExist)
+ $wgMessages[$langCodeC] = $messages;
+ $count = 0;
+
+ if (($messageExist) && ($messageCExist)) {
+
+ if (!strcmp($runMode,'php')) {
+ print("<?php\n");
+ print('$dupeMessages = array('."\n");
+ }
+ foreach ($wgMessages[$langCodeC] as $key => $value) {
+ foreach ($wgMessages[$langCode] as $ckey => $cvalue) {
+ if (!strcmp($key,$ckey)) {
+ if ((!strcmp($key,$ckey)) && (!strcmp($value,$cvalue))) {
+ if (!strcmp($runMode,'raw')) {
+ print("$key\n");
+ } else if (!strcmp($runMode,'php')) {
+ print("'$key' => '',\n");
+ } else if (!strcmp($runMode,'wiki')) {
+ $uKey = ucfirst($key);
+ print("* MediaWiki:$uKey/$langCode\n");
+ } else {
+ print("* $key\n");
+ }
+ $count++;
+ }
+ }
+ }
+ }
+ if (!strcmp($runMode,'php')) {
+ print(");\n");
+ }
+ if (!strcmp($runMode,'text')) {
+ if ($count == 1) {
+ echo "\nThere are $count duplicated message in $langCode, against to $langCodeC.\n";
+ } else {
+ echo "\nThere are $count duplicated messages in $langCode, against to $langCodeC.\n";
+ }
+ }
+ } else {
+ if (!$messageExist)
+ echo "There are no messages defined in $langCode.\n";
+ if (!$messageCExist)
+ echo "There are no messages defined in $langCodeC.\n";
+ }
+}
diff --git a/maintenance/language/checkExtensions.php b/maintenance/language/checkExtensions.php
index ab6f9ba8..ed1855c1 100644
--- a/maintenance/language/checkExtensions.php
+++ b/maintenance/language/checkExtensions.php
@@ -11,13 +11,13 @@ require_once( 'languages.inc' );
require_once( 'checkLanguage.inc' );
if( !class_exists( 'MessageGroups' ) || !class_exists( 'PremadeMediawikiExtensionGroups' ) ) {
- echo <<<END
+ echo <<<TEXT
Please add the Translate extension to LocalSettings.php, and enable the extension groups:
require_once( 'extensions/Translate/Translate.php' );
\$wgTranslateEC = array_keys( \$wgTranslateAC );
If you still get this message, update Translate to its latest version.
-END;
+TEXT;
exit(-1);
}
diff --git a/maintenance/language/checkLanguage.inc b/maintenance/language/checkLanguage.inc
index 52281b57..fc77aad3 100644
--- a/maintenance/language/checkLanguage.inc
+++ b/maintenance/language/checkLanguage.inc
@@ -7,6 +7,7 @@ class CheckLanguageCLI {
protected $code = null;
protected $level = 2;
protected $doLinks = false;
+ protected $linksPrefix = '';
protected $wikiCode = 'en';
protected $checkAll = false;
protected $output = 'plain';
@@ -24,7 +25,7 @@ class CheckLanguageCLI {
public function __construct( Array $options ) {
if ( isset( $options['help'] ) ) {
echo $this->help();
- exit();
+ exit(1);
}
if ( isset( $options['lang'] ) ) {
@@ -42,6 +43,10 @@ class CheckLanguageCLI {
$this->includeExif = !isset( $options['noexif'] );
$this->checkAll = isset( $options['all'] );
+ if ( isset( $options['prefix'] ) ) {
+ $this->linksPrefix = $options['prefix'];
+ }
+
if ( isset( $options['wikilang'] ) ) {
$this->wikiCode = $options['wikilang'];
}
@@ -190,6 +195,7 @@ Parameters:
* help: Show this help.
* level: Show the following display level (default: 2).
* links: Link the message values (default off).
+ * prefix: prefix to add to links.
* wikilang: For the links, what is the content language of the wiki to display the output in (default en).
* whitelist: Do only the following checks (form: code,code).
* blacklist: Don't do the following checks (form: code,code).
@@ -292,7 +298,7 @@ ENDS;
foreach ( $this->checks as $check ) {
if ( isset( $checkBlacklist[$code] ) &&
in_array( $check, $checkBlacklist[$code] ) ) {
- $result[$check] = array();
+ $results[$check] = array();
continue;
}
@@ -316,9 +322,9 @@ ENDS;
if ( $this->doLinks ) {
$displayKey = ucfirst( $key );
if ( $code == $this->wikiCode ) {
- return "[[MediaWiki:$displayKey|$key]]";
+ return "[[{$this->linksPrefix}MediaWiki:$displayKey|$key]]";
} else {
- return "[[MediaWiki:$displayKey/$code|$key]]";
+ return "[[{$this->linksPrefix}MediaWiki:$displayKey/$code|$key]]";
}
} else {
return $key;
@@ -378,7 +384,7 @@ ENDS;
function outputWiki() {
global $wgContLang, $IP;
$detailText = '';
- $rows[] = '! Language !! Code !! Total !! ' . implode( ' !! ', $this->checks );
+ $rows[] = '! Language !! Code !! Total !! ' . implode( ' !! ', array_diff( $this->checks, $this->nonMessageChecks() ) );
foreach ( $this->results as $code => $results ) {
$detailTextForLang = "==$code==\n";
$numbers = array();
@@ -418,7 +424,7 @@ ENDS;
$tableRows = implode( "\n|-\n", $rows );
- $version = SpecialVersion::getVersion( $IP );
+ $version = SpecialVersion::getVersion( 'nodb' );
echo <<<EOL
'''Check results are for:''' <code>$version</code>
@@ -459,7 +465,7 @@ class CheckExtensionsCLI extends CheckLanguageCLI {
public function __construct( Array $options, $extension ) {
if ( isset( $options['help'] ) ) {
echo $this->help();
- exit();
+ exit(1);
}
if ( isset( $options['lang'] ) ) {
@@ -587,7 +593,7 @@ Check codes (ideally, all of them should result 0; all the checks are executed b
* untranslated: Messages which are required to translate, but are not translated.
* duplicate: Messages which translation equal to fallback
* obsolete: Messages which are untranslatable, but translated.
- * variables: Messages without variables which should be used, or with variables which shouldn't be used.
+ * variables: Messages without variables which should be used, or with variables which should not be used.
* empty: Empty messages.
* whitespace: Messages which have trailing whitespace.
* xhtml: Messages which are not well-formed XHTML (checks only few common errors).
@@ -645,25 +651,40 @@ ENDS;
# Blacklist some checks for some languages
$checkBlacklist = array(
#'code' => array( 'check1', 'check2' ... )
+'az' => array( 'plural' ),
+'bo' => array( 'plural' ),
+'dz' => array( 'plural' ),
+'id' => array( 'plural' ),
+'fa' => array( 'plural' ),
'gan' => array( 'plural' ),
+'gan-hans' => array( 'plural' ),
+'gan-hant' => array( 'plural' ),
'gn' => array( 'plural' ),
'hak' => array( 'plural' ),
'hu' => array( 'plural' ),
'ja' => array( 'plural' ), // Does not use plural
+'jv' => array( 'plural' ),
'ka' => array( 'plural' ),
'kk-arab' => array( 'plural' ),
'kk-cyrl' => array( 'plural' ),
'kk-latn' => array( 'plural' ),
+'km' => array( 'plural' ),
+'kn' => array( 'plural' ),
'ko' => array( 'plural' ),
+'lzh' => array( 'plural' ),
'mn' => array( 'plural' ),
'ms' => array( 'plural' ),
-'my' => array( 'chars' ), // Uses a lot zwnj
+'my' => array( 'plural', 'chars' ), // Uses a lot zwnj
'sah' => array( 'plural' ),
'sq' => array( 'plural' ),
'tet' => array( 'plural' ),
'th' => array( 'plural' ),
+'to' => array( 'plural' ),
+'tr' => array( 'plural' ),
+'vi' => array( 'plural' ),
'wuu' => array( 'plural' ),
'xmf' => array( 'plural' ),
+'yo' => array( 'plural' ),
'yue' => array( 'plural' ),
'zh' => array( 'plural' ),
'zh-classical' => array( 'plural' ),
diff --git a/maintenance/language/countMessages.php b/maintenance/language/countMessages.php
index 7d16915a..826c43cb 100644
--- a/maintenance/language/countMessages.php
+++ b/maintenance/language/countMessages.php
@@ -1,40 +1,65 @@
<?php
+/**
+ * Count how many messages we have defined for each language.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @ingroup MaintenanceLanguage
+ */
-require_once( dirname(__FILE__).'/../commandLine.inc' );
+require_once( dirname(__FILE__) . '/../Maintenance.php' );
-global $IP;
-
-if ( !isset( $args[0] ) ) {
- $dir = "$IP/languages/messages";
-} else {
- $dir = $args[0];
-}
-
-$total = 0;
-$nonZero = 0;
-foreach ( glob( "$dir/*.php" ) as $file ) {
- $baseName = basename( $file );
- if( !preg_match( '/Messages([A-Z][a-z_]+)\.php$/', $baseName, $m ) ) {
- continue;
+class CountMessages extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Count how many messages we have defined for each language";
}
- $code = str_replace( '_', '-', strtolower( $m[1] ) );
- $numMessages = wfGetNumMessages( $file );
- //print "$code: $numMessages\n";
- $total += $numMessages;
- if ( $numMessages > 0 ) {
- $nonZero ++;
+
+ public function execute() {
+ global $IP;
+ $dir = $this->getArg( 0, "$IP/languages/messages" );
+ $total = 0;
+ $nonZero = 0;
+ foreach ( glob( "$dir/*.php" ) as $file ) {
+ $baseName = basename( $file );
+ if( !preg_match( '/Messages([A-Z][a-z_]+)\.php$/', $baseName, $m ) ) {
+ continue;
+ }
+ $code = str_replace( '_', '-', strtolower( $m[1] ) );
+ $numMessages = $this->getNumMessages( $file );
+ //print "$code: $numMessages\n";
+ $total += $numMessages;
+ if ( $numMessages > 0 ) {
+ $nonZero ++;
+ }
+ }
+ $this->output( "\nTotal: $total\n" );
+ $this->output( "Languages: $nonZero\n" );
}
-}
-print "\nTotal: $total\n";
-print "Languages: $nonZero\n";
-function wfGetNumMessages( $file ) {
- // Separate function to limit scope
- require( $file );
- if ( isset( $messages ) ) {
- return count( $messages );
- } else {
- return 0;
+ private function getNumMessages( $file ) {
+ // Separate function to limit scope
+ require( $file );
+ if ( isset( $messages ) ) {
+ return count( $messages );
+ } else {
+ return 0;
+ }
}
}
+$maintClass = "CountMessages";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/language/date-formats.php b/maintenance/language/date-formats.php
index 834d2bd8..54a6a26d 100644
--- a/maintenance/language/date-formats.php
+++ b/maintenance/language/date-formats.php
@@ -1,50 +1,76 @@
<?php
/**
- * @file
+ * Test various language time and date functions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup MaintenanceLanguage
*/
-$ts = '20010115123456';
+require_once( dirname(__FILE__) . '/../Maintenance.php' );
-
-$IP = dirname( __FILE__ ) . '/../..';
-require_once( "$IP/maintenance/commandLine.inc" );
+class DateFormats extends Maintenance {
-foreach ( glob( "$IP/languages/messages/Messages*.php" ) as $filename ) {
- $base = basename( $filename );
- $m = array();
- if ( !preg_match( '/Messages(.*)\.php$/', $base, $m ) ) {
- continue;
- }
- $code = str_replace( '_', '-', strtolower( $m[1] ) );
- print "$code ";
- $lang = Language::factory( $code );
- $prefs = $lang->getDatePreferences();
- if ( !$prefs ) {
- $prefs = array( 'default' );
- }
- print "date: ";
- foreach ( $prefs as $index => $pref ) {
- if ( $index > 0 ) {
- print ' | ';
- }
- print $lang->date( $ts, false, $pref );
- }
- print "\n$code time: ";
- foreach ( $prefs as $index => $pref ) {
- if ( $index > 0 ) {
- print ' | ';
- }
- print $lang->time( $ts, false, $pref );
+ private $ts = '20010115123456';
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Test various language time and date functions";
}
- print "\n$code both: ";
- foreach ( $prefs as $index => $pref ) {
- if ( $index > 0 ) {
- print ' | ';
+
+ public function execute() {
+ global $IP;
+ foreach ( glob( "$IP/languages/messages/Messages*.php" ) as $filename ) {
+ $base = basename( $filename );
+ $m = array();
+ if ( !preg_match( '/Messages(.*)\.php$/', $base, $m ) ) {
+ continue;
+ }
+ $code = str_replace( '_', '-', strtolower( $m[1] ) );
+ $this->output( "$code " );
+ $lang = Language::factory( $code );
+ $prefs = $lang->getDatePreferences();
+ if ( !$prefs ) {
+ $prefs = array( 'default' );
+ }
+ $this->output( "date: " );
+ foreach ( $prefs as $index => $pref ) {
+ if ( $index > 0 ) {
+ $this->output( ' | ' );
+ }
+ $this->output( $lang->date( $this->ts, false, $pref ) );
+ }
+ $this->output( "\n$code time: " );
+ foreach ( $prefs as $index => $pref ) {
+ if ( $index > 0 ) {
+ $this->output( ' | ' );
+ }
+ $this->output( $lang->time( $this->ts, false, $pref ) );
+ }
+ $this->output( "\n$code both: " );
+ foreach ( $prefs as $index => $pref ) {
+ if ( $index > 0 ) {
+ $this->output( ' | ' );
+ }
+ $this->output( $lang->timeanddate( $this->ts, false, $pref ) );
+ }
+ $this->output( "\n\n" );
}
- print $lang->timeanddate( $ts, false, $pref );
}
- print "\n\n";
}
-
+$maintClass = "DateFormats";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/language/diffLanguage.php b/maintenance/language/diffLanguage.php
index 9d395b3c..bbdb8653 100644
--- a/maintenance/language/diffLanguage.php
+++ b/maintenance/language/diffLanguage.php
@@ -69,7 +69,7 @@ function ucfirstlcrest($string) {
/**
* Return a $wgAllmessages array shipped in MediaWiki
- * @param string $languageCode Formated language code
+ * @param $languageCode String: formated language code
* @return array The MediaWiki default $wgAllMessages array requested
*/
function getMediawikiMessages($languageCode = 'En') {
@@ -93,8 +93,8 @@ function getMediawikiMessages($languageCode = 'En') {
/**
* Return a $wgAllmessages array in a given file. Language of the array
* need to be given cause we can not detect which language it provides
- * @param string $filename Filename of the file containing a message array
- * @param string $languageCode Language of the external array
+ * @param $filename String: filename of the file containing a message array
+ * @param $languageCode String: language of the external array
* @return array A $wgAllMessages array from an external file.
*/
function getExternalMessages($filename, $languageCode) {
diff --git a/maintenance/language/digit2html.php b/maintenance/language/digit2html.php
index b020d812..54630af0 100644
--- a/maintenance/language/digit2html.php
+++ b/maintenance/language/digit2html.php
@@ -1,29 +1,61 @@
<?php
/**
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup MaintenanceLanguage
*/
-require( '../commandLine.inc' );
+require_once( dirname(__FILE__).'/../Maintenance.php' );
-# A list of unicode numerals is available at:
-# http://www.fileformat.info/info/unicode/category/Nd/list.htm
-$langs = array( 'Ar', 'As', 'Bh', 'Bo', 'Dz', 'Fa', 'Gu', 'Hi', 'Km', 'Kn', 'Ks', 'Lo', 'Ml', 'Mr', 'Ne', 'New', 'Or', 'Pa', 'Pi', 'Sa' );
+class Digit2Html extends Maintenance {
-foreach( $langs as $code ) {
- $filename = Language::getMessagesFileName( $code );
- echo "Loading language [$code] ... ";
- unset( $digitTransformTable );
- require_once( $filename );
- if( !isset( $digitTransformTable ) ) {
- print "\$digitTransformTable not found\n";
- continue;
+ # A list of unicode numerals is available at:
+ # http://www.fileformat.info/info/unicode/category/Nd/list.htm
+ private $mLangs = array(
+ 'Ar', 'As', 'Bh', 'Bo', 'Dz',
+ 'Fa', 'Gu', 'Hi', 'Km', 'Kn',
+ 'Ks', 'Lo', 'Ml', 'Mr', 'Ne',
+ 'New', 'Or', 'Pa', 'Pi', 'Sa'
+ );
+
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Check digit transformation";
}
- print "OK\n\$digitTransformTable = array(\n";
- foreach( $digitTransformTable as $latin => $translation ) {
- $htmlent = utf8ToHexSequence( $translation );
- print "'$latin' => '$translation', # &#x$htmlent;\n";
+ public function execute() {
+ foreach( $this->mLangs as $code ) {
+ $filename = Language::getMessagesFileName( $code );
+ $this->output( "Loading language [$code] ... " );
+ unset( $digitTransformTable );
+ require_once( $filename );
+ if( !isset( $digitTransformTable ) ) {
+ $this->error( "\$digitTransformTable not found for lang: $code" );
+ continue;
+ }
+
+ $this->output( "OK\n\$digitTransformTable = array(\n" );
+ foreach( $digitTransformTable as $latin => $translation ) {
+ $htmlent = utf8ToHexSequence( $translation );
+ $this->output( "'$latin' => '$translation', # &#x$htmlent;\n" );
+ }
+ $this->output( ");\n" );
+ }
}
- print ");\n";
}
+
+$maintClass = "Digit2Html";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/language/dumpMessages.php b/maintenance/language/dumpMessages.php
index 35aeeb75..a0f0a9ab 100644
--- a/maintenance/language/dumpMessages.php
+++ b/maintenance/language/dumpMessages.php
@@ -1,18 +1,44 @@
<?php
/**
- * @todo document
- * @file
+ * Dump an entire language, using the keys from English
+ * so we get all the values, not just the customized ones
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup MaintenanceLanguage
+ * @todo Make this more useful, right now just dumps $wgContentLang
*/
-/** */
-require_once( dirname(__FILE__).'/../commandLine.inc' );
-$messages = array();
-$wgEnglishMessages = array_keys( Language::getMessagesFor( 'en' ) );
-foreach ( $wgEnglishMessages as $key ) {
- $messages[$key] = wfMsg( $key );
-}
-print "MediaWiki $wgVersion language file\n";
-print serialize( $messages );
+require_once( dirname(__FILE__) . '/../Maintenance.php' );
+class DumpMessages extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Dump an entire language, using the keys from English";
+ }
+
+ public function execute() {
+ $messages = array();
+ foreach ( array_keys( Language::getMessagesFor( 'en' ) ) as $key ) {
+ $messages[$key] = wfMsg( $key );
+ }
+ $this->output( "MediaWiki $wgVersion language file\n" );
+ $this->output( serialize( $messages ) );
+ }
+}
+$maintClass = "DumpMessages";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/language/generateNormalizerData.php b/maintenance/language/generateNormalizerData.php
new file mode 100644
index 00000000..d6b7aaa6
--- /dev/null
+++ b/maintenance/language/generateNormalizerData.php
@@ -0,0 +1,137 @@
+<?php
+
+require_once( dirname( __FILE__ ) . '/../Maintenance.php' );
+
+require_once( dirname( __FILE__ ) . '/../../includes/normal/UtfNormalUtil.php' );
+
+/**
+ * Generates normalizer data files for Arabic and Malayalam.
+ * For NFC see includes/normal.
+ */
+class GenerateNormalizerData extends Maintenance {
+ var $dataFile;
+
+ public function __construct() {
+ parent::__construct();
+ $this->addOption( 'unicode-data-file', 'The local location of the data file ' .
+ 'from http://unicode.org/Public/UNIDATA/UnicodeData.txt', false, true );
+ }
+
+ public function execute() {
+ if ( !$this->hasOption( 'unicode-data-file' ) ) {
+ $this->dataFile = 'UnicodeData.txt';
+ if ( !file_exists( $this->dataFile ) ) {
+ $this->error( "Unable to find UnicodeData.txt. Please specify its location with --unicode-data-file=<FILE>" );
+ exit( 1 );
+ }
+ } else {
+ $this->dataFile = $this->getOption( 'unicode-data-file' );
+ if ( !file_exists( $this->dataFile ) ) {
+ $this->error( 'Unable to find the specified data file.' );
+ exit( 1 );
+ }
+ }
+
+ $this->generateArabic();
+ $this->generateMalayalam();
+ }
+
+ function generateArabic() {
+ $file = fopen( $this->dataFile, 'r' );
+ if ( !$file ) {
+ $this->error( 'Unable to open the data file.' );
+ exit( 1 );
+ }
+
+ // For the file format, see http://www.unicode.org/reports/tr44/
+ $fieldNames = array(
+ 'Code',
+ 'Name',
+ 'General_Category',
+ 'Canonical_Combining_Class',
+ 'Bidi_Class',
+ 'Decomposition_Type_Mapping',
+ 'Numeric_Type_Value',
+ 'Bidi_Mirrored',
+ 'Unicode_1_Name',
+ 'ISO_Comment',
+ 'Simple_Uppercase_Mapping',
+ 'Simple_Lowercase_Mapping',
+ 'Simple_Titlecase_Mapping'
+ );
+
+ $pairs = array();
+
+ $lineNum = 0;
+ while ( false !== ( $line = fgets( $file ) ) ) {
+ ++$lineNum;
+
+ # Strip comments
+ $line = trim( substr( $line, 0, strcspn( $line, '#' ) ) );
+ if ( $line === '' ) {
+ continue;
+ }
+
+ # Split fields
+ $numberedData = explode( ';', $line );
+ $data = array();
+ foreach ( $fieldNames as $number => $name ) {
+ $data[$name] = $numberedData[$number];
+ }
+
+ $code = base_convert( $data['Code'], 16, 10 );
+ if ( ( $code >= 0xFB50 && $code <= 0xFDFF ) # Arabic presentation forms A
+ || ( $code >= 0xFE70 && $code <= 0xFEFF ) ) # Arabic presentation forms B
+ {
+ if ( $data['Decomposition_Type_Mapping'] === '' ) {
+ // No decomposition
+ continue;
+ }
+ if ( !preg_match( '/^ *(<\w*>) +([0-9A-F ]*)$/',
+ $data['Decomposition_Type_Mapping'], $m ) )
+ {
+ $this->error( "Can't parse Decomposition_Type/Mapping on line $lineNum" );
+ $this->error( $line );
+ continue;
+ }
+
+ $source = hexSequenceToUtf8( $data['Code'] );
+ $dest = hexSequenceToUtf8( $m[2] );
+ $pairs[$source] = $dest;
+ }
+ }
+
+ global $IP;
+ file_put_contents( "$IP/serialized/normalize-ar.ser", serialize( $pairs ) );
+ echo "ar: " . count( $pairs ) . " pairs written.\n";
+ }
+
+ function generateMalayalam() {
+ $hexPairs = array(
+ # From http://unicode.org/versions/Unicode5.1.0/#Malayalam_Chillu_Characters
+ '0D23 0D4D 200D' => '0D7A',
+ '0D28 0D4D 200D' => '0D7B',
+ '0D30 0D4D 200D' => '0D7C',
+ '0D32 0D4D 200D' => '0D7D',
+ '0D33 0D4D 200D' => '0D7E',
+
+ # From http://permalink.gmane.org/gmane.science.linguistics.wikipedia.technical/46413
+ '0D15 0D4D 200D' => '0D7F',
+ );
+
+ $pairs = array();
+ foreach ( $hexPairs as $hexSource => $hexDest ) {
+ $source = hexSequenceToUtf8( $hexSource );
+ $dest = hexSequenceToUtf8( $hexDest );
+ $pairs[$source] = $dest;
+ }
+
+ global $IP;
+ file_put_contents( "$IP/serialized/normalize-ml.ser", serialize( $pairs ) );
+ echo "ml: " . count( $pairs ) . " pairs written.\n";
+ }
+}
+
+$maintClass = 'GenerateNormalizerData';
+require_once( DO_MAINTENANCE );
+
diff --git a/maintenance/language/lang2po.php b/maintenance/language/lang2po.php
index 1009ed6c..c7484d63 100644
--- a/maintenance/language/lang2po.php
+++ b/maintenance/language/lang2po.php
@@ -6,15 +6,27 @@
* - generate .po header
* - fix escaping of \
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup MaintenanceLanguage
*/
-$optionsWithArgs[] = 'lang';
-
/** This is a command line script */
-require_once(dirname(__FILE__).'/../commandLine.inc');
-require_once(dirname(__FILE__).'/languages.inc');
+require_once(dirname(__FILE__) . '/../Maintenance.php' );
+require_once(dirname(__FILE__) . '/languages.inc' );
define('ALL_LANGUAGES', true);
define('XGETTEXT_BIN', 'xgettext');
@@ -26,28 +38,45 @@ define('MSGMERGE_OPTIONS', ' -v ');
define('LOCALE_OUTPUT_DIR', $IP.'/locale');
+class Lang2Po extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "";
+ $this->addOption( 'lang', 'a lang code you want to generate a .po for (default: all langs)', false, true );
+ }
-if( isset($options['help']) ) { usage(); wfDie(); }
-// default output is WikiText
-if( !isset($options['lang']) ) { $options['lang'] = ALL_LANGUAGES; }
-
-function usage() {
-print <<<END
-Usage: php lang2po.php [--help] [--lang=<langcode>] [--stdout]
- --help: this message.
- --lang: a lang code you want to generate a .po for (default: all languages).
-
-END;
-}
-
+ public function execute() {
+ // Generate a template .pot based on source tree
+ $this->output( "Getting 'gettext' default messages from sources:" );
+ $this->generatePot();
+ $this->output( "done.\n" );
+
+
+ $langTool = new languages();
+ if( $this->getOption( 'lang', ALL_LANGUAGES ) === ALL_LANGUAGES ) {
+ $codes = $langTool->getLanguages();
+ } else {
+ $codes = array( $this->getOption( 'lang' ) );
+ }
+
+ // Do all languages
+ foreach ( $codes as $langcode) {
+ $this->output( "Loading messages for $langcode:\n" );
+ if( !$this->generatePo($langcode, $langTool->getMessages($langcode) ) ) {
+ $this->error( "ERROR: Failed to write file." );
+ } else {
+ $this->output( "Applying template:" );
+ $this->applyPot($langcode);
+ }
+ }
+ }
-/**
- * Return a dummy header for later edition.
- * @return string A dummy header
- */
-function poHeader() {
-return
-'# SOME DESCRIPTIVE TITLE.
+ /**
+ * Return a dummy header for later edition.
+ * @return string A dummy header
+ */
+ private function poHeader() {
+ return '# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2005 MediaWiki
# This file is distributed under the same license as the MediaWiki package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
@@ -65,94 +94,72 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
';
-}
-
-/**
- * generate and write a file in .po format.
- *
- * @param string $langcode Code of a language it will process.
- * @param array &$messages Array containing the various messages.
- * @return string Filename where stuff got saved or false.
- */
-function generatePo($langcode, $messages) {
- $data = poHeader();
-
- // Generate .po entries
- foreach($messages['all'] as $identifier => $content) {
- $data .= "msgid \"$identifier\"\n";
-
- // Escape backslashes
- $tmp = str_replace('\\', '\\\\', $content);
- // Escape doublelquotes
- $tmp = preg_replace( "/(?<!\\\\)\"/", '\"', $tmp);
- // Rewrite multilines to gettext format
- $tmp = str_replace("\n", "\"\n\"", $tmp);
-
- $data .= 'msgstr "'. $tmp . "\"\n\n";
}
- // Write the content to a file in locale/XX/messages.po
- $dir = LOCALE_OUTPUT_DIR.'/'.$langcode;
- if( !is_dir($dir) ) { mkdir( $dir, 0770 ); }
- $filename = $dir.'/fromlanguagefile.po';
-
- $file = fopen( $filename , 'wb' );
- if( fwrite( $file, $data ) ) {
- fclose( $file );
- return $filename;
- } else {
- fclose( $file );
- return false;
+ /**
+ * generate and write a file in .po format.
+ *
+ * @param string $langcode Code of a language it will process.
+ * @param array &$messages Array containing the various messages.
+ * @return string Filename where stuff got saved or false.
+ */
+ private function generatePo($langcode, $messages) {
+ $data = $this->poHeader();
+
+ // Generate .po entries
+ foreach( $messages['all'] as $identifier => $content ) {
+ $data .= "msgid \"$identifier\"\n";
+
+ // Escape backslashes
+ $tmp = str_replace('\\', '\\\\', $content);
+ // Escape doublelquotes
+ $tmp = preg_replace( "/(?<!\\\\)\"/", '\"', $tmp);
+ // Rewrite multilines to gettext format
+ $tmp = str_replace("\n", "\"\n\"", $tmp);
+
+ $data .= 'msgstr "'. $tmp . "\"\n\n";
+ }
+
+ // Write the content to a file in locale/XX/messages.po
+ $dir = LOCALE_OUTPUT_DIR.'/'.$langcode;
+ if( !is_dir($dir) ) { mkdir( $dir, 0770 ); }
+ $filename = $dir.'/fromlanguagefile.po';
+
+ $file = fopen( $filename , 'wb' );
+ if( fwrite( $file, $data ) ) {
+ fclose( $file );
+ return $filename;
+ } else {
+ fclose( $file );
+ return false;
+ }
}
-}
-
-function generatePot() {
- global $IP;
- $curdir = getcwd();
- chdir($IP);
- exec( XGETTEXT_BIN
- .' '.XGETTEXT_OPTIONS
- .' -o '.LOCALE_OUTPUT_DIR.'/wfMsg.pot'
- .' includes/*php'
- );
- chdir($curdir);
-}
-
-function applyPot($langcode) {
- $langdir = LOCALE_OUTPUT_DIR.'/'.$langcode;
-
- $from = $langdir.'/fromlanguagefile.po';
- $pot = LOCALE_OUTPUT_DIR.'/wfMsg.pot';
- $dest = $langdir.'/messages.po';
- // Merge template and generate file to get final .po
- exec(MSGMERGE_BIN.MSGMERGE_OPTIONS." $from $pot -o $dest ");
- // delete no more needed file
-// unlink($from);
-}
-
-// Generate a template .pot based on source tree
-echo "Getting 'gettext' default messages from sources:";
-generatePot();
-echo "done.\n";
-
-
-$langTool = new languages();
-
-if( $options['lang'] === ALL_LANGUAGES ) {
- $codes = $langTool->getLanguages();
-} else {
- $codes = array( $options['lang'] );
-}
-
-// Do all languages
-foreach ( $codes as $langcode) {
- echo "Loading messages for $langcode:\n";
- if( ! generatePo($langcode, $langTool->getMessages($langcode) ) ) {
- echo "ERROR: Failed to write file.\n";
- } else {
- echo "Applying template:";
- applyPot($langcode);
+ private function generatePot() {
+ global $IP;
+ $curdir = getcwd();
+ chdir($IP);
+ exec( XGETTEXT_BIN
+ .' '.XGETTEXT_OPTIONS
+ .' -o '.LOCALE_OUTPUT_DIR.'/wfMsg.pot'
+ .' includes/*php'
+ );
+ chdir($curdir);
+ }
+
+ private function applyPot($langcode) {
+ $langdir = LOCALE_OUTPUT_DIR.'/'.$langcode;
+
+ $from = $langdir.'/fromlanguagefile.po';
+ $pot = LOCALE_OUTPUT_DIR.'/wfMsg.pot';
+ $dest = $langdir.'/messages.po';
+
+ // Merge template and generate file to get final .po
+ exec(MSGMERGE_BIN.MSGMERGE_OPTIONS." $from $pot -o $dest ");
+ // delete no more needed file
+ // unlink($from);
}
}
+$maintClass = "Lang2Po";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/language/langmemusage.php b/maintenance/language/langmemusage.php
index 9bfb3cdc..71135474 100644
--- a/maintenance/language/langmemusage.php
+++ b/maintenance/language/langmemusage.php
@@ -3,31 +3,57 @@
* Dumb program that tries to get the memory usage
* for each language file.
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup MaintenanceLanguage
*/
/** This is a command line script */
-require_once(dirname(__FILE__).'/../commandLine.inc');
-require_once(dirname(__FILE__).'/languages.inc');
+require_once( dirname(__FILE__) . '/../Maintenance.php' );
+require_once( dirname(__FILE__) . '/languages.inc' );
-$langtool = new languages();
+class LangMemUsage extends Maintenance {
-if ( ! function_exists( 'memory_get_usage' ) )
- wfDie( "You must compile PHP with --enable-memory-limit\n" );
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Dumb program that tries to get the memory usage\n" .
+ "for each language file";
+ }
-$memlast = $memstart = memory_get_usage();
+ public function execute() {
+ if ( !function_exists( 'memory_get_usage' ) )
+ $this->error( "You must compile PHP with --enable-memory-limit", true );
-print 'Base memory usage: '.$memstart."\n";
+ $langtool = new languages();
+ $memlast = $memstart = memory_get_usage();
-foreach ( $langtool->getLanguages() as $langcode ) {
- Language::factory( $langcode );
- $memstep = memory_get_usage();
- printf( "%12s: %d\n", $langcode, ($memstep- $memlast) );
- $memlast = $memstep;
-}
+ $this->output( "Base memory usage: $memstart\n" );
+
+ foreach ( $langtool->getLanguages() as $langcode ) {
+ Language::factory( $langcode );
+ $memstep = memory_get_usage();
+ $this->output( sprintf( "%12s: %d\n", $langcode, ($memstep- $memlast) ) );
+ $memlast = $memstep;
+ }
-$memend = memory_get_usage();
+ $memend = memory_get_usage();
-echo ' Total Usage: '.($memend - $memstart)."\n";
+ $this->output( ' Total Usage: '.($memend - $memstart)."\n" );
+ }
+}
+$maintClass = "LangMemUsage";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/language/languages.inc b/maintenance/language/languages.inc
index 6159e844..98464292 100644
--- a/maintenance/language/languages.inc
+++ b/maintenance/language/languages.inc
@@ -14,6 +14,7 @@ class languages {
protected $mRawMessages; # Raw list of the messages in each language
protected $mMessages; # Messages in each language (except for English), divided to groups
+ protected $mFallback; # Fallback language in each language
protected $mGeneralMessages; # General messages in English, divided to groups
protected $mIgnoredMessages; # All the messages which should be exist only in the English file
protected $mOptionalMessages; # All the messages which may be translated or not, depending on the language
@@ -76,6 +77,7 @@ class languages {
*/
protected function loadFile( $code ) {
if ( isset( $this->mRawMessages[$code] ) &&
+ isset( $this->mFallback[$code] ) &&
isset( $this->mNamespaceNames[$code] ) &&
isset( $this->mNamespaceAliases[$code] ) &&
isset( $this->mMagicWords[$code] ) &&
@@ -83,6 +85,7 @@ class languages {
return;
}
$this->mRawMessages[$code] = array();
+ $this->mFallback[$code] = '';
$this->mNamespaceNames[$code] = array();
$this->mNamespaceAliases[$code] = array();
$this->mMagicWords[$code] = array();
@@ -93,6 +96,9 @@ class languages {
if ( isset( $messages ) ) {
$this->mRawMessages[$code] = $messages;
}
+ if ( isset( $fallback ) ) {
+ $this->mFallback[$code] = $fallback;
+ }
if ( isset( $namespaceNames ) ) {
$this->mNamespaceNames[$code] = $namespaceNames;
}
@@ -207,6 +213,18 @@ class languages {
}
/**
+ * Get fallback language code for a specific language.
+ *
+ * @param $code The language code.
+ *
+ * @return Fallback code.
+ */
+ public function getFallback( $code ) {
+ $this->loadFile( $code );
+ return $this->mFallback[$code];
+ }
+
+ /**
* Get namespace names for a specific language.
*
* @param $code The language code.
diff --git a/maintenance/language/makeMessageDB.php b/maintenance/language/makeMessageDB.php
deleted file mode 100644
index f853b395..00000000
--- a/maintenance/language/makeMessageDB.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-/**
- * Proof of principle script
- */
-
-require( dirname( __FILE__ ) . '/../commandLine.inc' );
-
-$obj = new MakeMessagesDB;
-$obj->run();
-
-class MakeMessagesDB {
-
- function run() {
- global $wgExtensionMessagesFiles, $wgMessageCache, $IP;
-
- $nameHash = md5( implode( "\n", array_keys( $wgExtensionMessagesFiles ) ) );
- $dir = "$IP/cache/ext-msgs";
- wfMkdirParents( $dir );
- $db = dba_open( "$dir/$nameHash.cdb", 'n', 'cdb' );
- if ( !$db ) {
- echo "Cannot open DB file\n";
- exit( 1 );
- }
-
- # Load extension messages
- foreach ( $wgExtensionMessagesFiles as $file ) {
- $messages = $magicWords = array();
- require( $file );
- foreach ( $messages as $lang => $unused ) {
- $wgMessageCache->processMessagesArray( $messages, $lang );
- }
- }
-
- # Write them to the file
- foreach ( $wgMessageCache->mExtensionMessages as $lang => $messages ) {
- foreach ( $messages as $key => $text ) {
- dba_insert( "$lang:$key", $text, $db );
- }
- }
-
- dba_close( $db );
- }
-}
-
diff --git a/maintenance/language/messageTypes.inc b/maintenance/language/messageTypes.inc
index d155db94..9b979b19 100644
--- a/maintenance/language/messageTypes.inc
+++ b/maintenance/language/messageTypes.inc
@@ -25,6 +25,7 @@ $wgIgnoredMessages = array(
'accesskey-ca-viewsource',
'accesskey-ca-history',
'accesskey-ca-protect',
+ 'accesskey-ca-unprotect',
'accesskey-ca-delete',
'accesskey-ca-undelete',
'accesskey-ca-move',
@@ -35,6 +36,7 @@ $wgIgnoredMessages = array(
'accesskey-search-fulltext',
'accesskey-p-logo',
'accesskey-n-mainpage',
+ 'accesskey-n-mainpage-description',
'accesskey-n-portal',
'accesskey-n-currentevents',
'accesskey-n-recentchanges',
@@ -65,10 +67,10 @@ $wgIgnoredMessages = array(
'accesskey-preview',
'accesskey-diff',
'accesskey-compareselectedversions',
- 'accesskey-visualcomparison',
'accesskey-watch',
'accesskey-upload',
'addsection',
+ 'talkpageheader',
'anonnotice',
'autoblock_whitelist',
'searchmenu-help',
@@ -152,8 +154,12 @@ $wgIgnoredMessages = array(
'fewestrevisions-summary',
'upload-summary',
'newuserlogentry',
- 'restrictlogpage',
'wantedtemplates-summary',
+ 'activeusers-summary',
+ 'search-summary',
+ 'editpage-tos-summary',
+ 'addsection-preload',
+ 'addsection-editintro',
);
/** Optional messages, which may be translated only if changed in the target language. */
@@ -208,6 +214,7 @@ $wgOptionalMessages = array(
'skinname-chick',
'skinname-simple',
'skinname-modern',
+ 'skinname-vector',
'common.css',
'standard.css',
'nostalgia.css',
@@ -217,6 +224,7 @@ $wgOptionalMessages = array(
'chick.css',
'simple.css',
'modern.css',
+ 'vector.css',
'print.css',
'handheld.css',
'common.js',
@@ -228,6 +236,7 @@ $wgOptionalMessages = array(
'chick.js',
'simple.js',
'modern.js',
+ 'vector.js',
'widthheight',
'exif-fnumber-format',
'exif-focallength-format',
@@ -320,6 +329,7 @@ $wgOptionalMessages = array(
'hebrew-calendar-m10-gen',
'hebrew-calendar-m11-gen',
'hebrew-calendar-m12-gen',
+ 'version-svn-revision',
'catseparator',
'semicolon-separator',
'comma-separator',
@@ -328,15 +338,21 @@ $wgOptionalMessages = array(
'word-separator',
'ellipsis',
'percent',
+ 'parentheses',
'autocomment-prefix',
'listgrouprights-right-display',
+ 'listgrouprights-right-revoked',
'timezone-utc',
'whatlinkshere-backlink',
'recentchangeslinked-backlink',
+ 'unpatrolledletter',
'diff-with-additional',
'pagetitle-view-mainpage',
'trackback',
'trackbackexcerpt',
+ 'prefs-registration-date-time',
+ 'prefs-memberingroups-type',
+ 'shared-repo-name-wikimediacommons',
);
/** EXIF messages, which may be set as optional in several checks, but are generally mandatory */
@@ -559,6 +575,8 @@ $wgEXIFMessages = array(
'exif-gpslatitude-s',
'exif-gpslongitude-e',
'exif-gpslongitude-w',
+ 'exif-gpsaltitude-0',
+ 'exif-gpsaltitude-1',
'exif-gpsstatus-a',
'exif-gpsstatus-v',
'exif-gpsmeasuremode-2',
@@ -566,6 +584,9 @@ $wgEXIFMessages = array(
'exif-gpsspeed-k',
'exif-gpsspeed-m',
'exif-gpsspeed-n',
+ 'exif-gpsdestdistance-k',
+ 'exif-gpsdestdistance-m',
+ 'exif-gpsdestdistance-n',
'exif-gpsdirection-t',
'exif-gpsdirection-m',
);
diff --git a/maintenance/language/messages.inc b/maintenance/language/messages.inc
index fb22bc9c..25bc8fd0 100644
--- a/maintenance/language/messages.inc
+++ b/maintenance/language/messages.inc
@@ -41,6 +41,7 @@ $wgMessageStructure = array(
'tog-enotifminoredits',
'tog-enotifrevealaddr',
'tog-shownumberswatching',
+ 'tog-oldsig',
'tog-fancysig',
'tog-externaleditor',
'tog-externaldiff',
@@ -65,6 +66,13 @@ $wgMessageStructure = array(
'underline-never',
'underline-default',
),
+ 'editfont' => array(
+ 'editfont-style',
+ 'editfont-default',
+ 'editfont-monospace',
+ 'editfont-sansserif',
+ 'editfont-serif',
+ ),
'dates' => array(
'sunday',
'monday',
@@ -133,6 +141,8 @@ $wgMessageStructure = array(
'category-file-count',
'category-file-count-limited',
'listingcontinuesabbrev',
+ 'index-category',
+ 'noindex-category',
),
'mainpage' => array(
'linkprefix',
@@ -144,6 +154,14 @@ $wgMessageStructure = array(
'article',
'newwindow',
'cancel',
+ 'moredotdotdot',
+ 'mypage',
+ 'mytalk',
+ 'anontalk',
+ 'navigation',
+ 'and',
+ ),
+ 'cologneblue' => array(
'qbfind',
'qbbrowse',
'qbedit',
@@ -151,15 +169,37 @@ $wgMessageStructure = array(
'qbpageinfo',
'qbmyoptions',
'qbspecialpages',
- 'moredotdotdot',
- 'mypage',
- 'mytalk',
- 'anontalk',
- 'navigation',
- 'and',
+ 'faq',
+ 'faqpage',
+ 'sitetitle',
+ 'sitesubtitle',
),
- 'metadata_help' => array(
- 'metadata_help',
+ 'vector' => array(
+ 'vector-action-addsection',
+ 'vector-action-delete',
+ 'vector-action-move',
+ 'vector-action-protect',
+ 'vector-action-undelete',
+ 'vector-action-unprotect',
+ 'vector-namespace-category',
+ 'vector-namespace-help',
+ 'vector-namespace-image',
+ 'vector-namespace-main',
+ 'vector-namespace-media',
+ 'vector-namespace-mediawiki',
+ 'vector-namespace-project',
+ 'vector-namespace-special',
+ 'vector-namespace-talk',
+ 'vector-namespace-template',
+ 'vector-namespace-user',
+ 'vector-view-create',
+ 'vector-view-edit',
+ 'vector-view-history',
+ 'vector-view-view',
+ 'vector-view-viewsource',
+ 'actions',
+ 'namespaces',
+ 'variants',
),
'miscellaneous2' => array(
'errorpagetitle',
@@ -211,18 +251,19 @@ $wgMessageStructure = array(
'otherlanguages',
'redirectedfrom',
'redirectpagesub',
+ 'talkpageheader',
'lastmodifiedat',
'viewcount',
'protectedpage',
'jumpto',
'jumptonavigation',
'jumptosearch',
+ 'view-pool-error',
),
'links' => array(
'aboutsite',
'aboutpage',
'copyright',
- 'copyrightpagename',
'copyrightpage',
'currentevents',
'currentevents-url',
@@ -230,8 +271,6 @@ $wgMessageStructure = array(
'disclaimerpage',
'edithelp',
'edithelppage',
- 'faq',
- 'faqpage',
'help',
'helppage',
'mainpage',
@@ -253,10 +292,8 @@ $wgMessageStructure = array(
),
'miscellaneous3' => array(
'ok',
- 'sitetitle',
'pagetitle',
'pagetitle-view-mainpage',
- 'sitesubtitle',
'retrievedfrom',
'youhavenewmessages',
'newmessageslink',
@@ -313,9 +350,6 @@ $wgMessageStructure = array(
'databaseerror',
'dberrortext',
'dberrortextcl',
- 'noconnect',
- 'nodb',
- 'cachederror',
'laggedslavemode',
'readonly',
'enterlockreason',
@@ -326,6 +360,8 @@ $wgMessageStructure = array(
'readonly_lag',
'internalerror',
'internalerror_info',
+ 'fileappenderrorread',
+ 'fileappenderror',
'filecopyerror',
'filerenameerror',
'filedeleteerror',
@@ -363,10 +399,8 @@ $wgMessageStructure = array(
'virus-unknownscanner',
),
'login' => array(
- 'logouttitle',
'logouttext',
'welcomecreation',
- 'loginpagetitle',
'yourname',
'yourpassword',
'yourpasswordagain',
@@ -377,6 +411,7 @@ $wgMessageStructure = array(
'nav-login-createaccount',
'loginprompt',
'userlogin',
+ 'userloginnocreate',
'logout',
'userlogout',
'notloggedin',
@@ -388,26 +423,8 @@ $wgMessageStructure = array(
'createaccountmail',
'badretype',
'userexists',
- 'youremail',
- 'username',
- 'uid',
- 'prefs-memberingroups',
- 'yourrealname',
- 'yourlanguage',
- 'yourvariant',
- 'yournick',
- 'badsig',
- 'badsiglength',
- 'yourgender',
- 'gender-unknown',
- 'gender-male',
- 'gender-female',
- 'prefs-help-gender',
- 'email',
- 'prefs-help-realname',
'loginerror',
- 'prefs-help-email',
- 'prefs-help-email-required',
+ 'createaccounterror',
'nocookiesnew',
'nocookieslogin',
'noname',
@@ -416,13 +433,16 @@ $wgMessageStructure = array(
'nosuchuser',
'nosuchusershort',
'nouserspecified',
+ 'login-userblocked',
'wrongpassword',
'wrongpasswordempty',
'passwordtooshort',
+ 'password-name-match',
'mailmypassword',
'passwordremindertitle',
'passwordremindertext',
'noemail',
+ 'noemailcreate',
'passwordsent',
'blocked-mailpassword',
'eauthentsent',
@@ -441,9 +461,11 @@ $wgMessageStructure = array(
'accountcreatedtext',
'createaccount-title',
'createaccount-text',
+ 'usernamehasherror',
'login-throttled',
'loginlanguagelabel',
'loginlanguagelinks',
+ 'suspicious-userlogout',
),
'resetpass' => array(
'resetpass',
@@ -455,16 +477,12 @@ $wgMessageStructure = array(
'retypenew',
'resetpass_submit',
'resetpass_success',
- 'resetpass_bad_temporary',
'resetpass_forbidden',
'resetpass-no-info',
'resetpass-submit-loggedin',
+ 'resetpass-submit-cancel',
'resetpass-wrong-oldpass',
'resetpass-temp-password',
- 'resetpass-log',
- 'resetpass-logtext',
- 'resetpass-logentry',
- 'resetpass-comment',
),
'toolbar' => array(
'bold_sample',
@@ -512,7 +530,6 @@ $wgMessageStructure = array(
'blockededitsource',
'whitelistedittitle',
'whitelistedittext',
- 'confirmedittitle',
'confirmedittext',
'nosuchsectiontitle',
'nosuchsectiontext',
@@ -527,10 +544,14 @@ $wgMessageStructure = array(
'talkpagetext',
'anontalkpagetext',
'noarticletext',
+ 'noarticletext-nopermission',
'noarticletextanon',
'userpage-userdoesnotexist',
+ 'userpage-userdoesnotexist-view',
+ 'blocked-notice-logextract',
'clearyourcache',
- 'usercssjsyoucanpreview',
+ 'usercssyoucanpreview',
+ 'userjsyoucanpreview',
'usercsspreview',
'userjspreview',
'userinvalidcssjstitle',
@@ -553,6 +574,7 @@ $wgMessageStructure = array(
'yourdiff',
'copyrightwarning',
'copyrightwarning2',
+ 'editpage-tos-summary',
'longpagewarning',
'longpageerror',
'readonlywarning',
@@ -570,17 +592,21 @@ $wgMessageStructure = array(
'nocreatetitle',
'nocreatetext',
'nocreate-loggedin',
+ 'sectioneditnotsupported-title',
+ 'sectioneditnotsupported-text',
'permissionserrors',
'permissionserrorstext',
'permissionserrorstext-withaction',
- 'recreate-deleted-warn',
- 'deleted-notice',
- 'deletelog-fulllog',
+ 'recreate-moveddeleted-warn',
+ 'moveddeleted-notice',
+ 'log-fulllog',
'edit-hook-aborted',
'edit-gone-missing',
'edit-conflict',
'edit-no-change',
'edit-already-exists',
+ 'addsection-preload',
+ 'addsection-editintro',
),
'parserwarnings' => array(
'expensive-parserfunction-warning',
@@ -591,6 +617,7 @@ $wgMessageStructure = array(
'post-expand-template-argument-category',
'parser-template-loop-warning',
'parser-template-recursion-depth-warning',
+ 'language-converter-depth-warning',
),
'undo' => array(
'undo-success',
@@ -622,8 +649,8 @@ $wgMessageStructure = array(
'page_last',
'histlegend',
'history-fieldset-title',
+ 'history-show-deleted',
'history_copyright',
- 'deletedrev',
'histfirst',
'histlast',
'historysize',
@@ -639,32 +666,46 @@ $wgMessageStructure = array(
'rev-deleted-comment',
'rev-deleted-user',
'rev-deleted-event',
+ 'rev-deleted-user-contribs',
'rev-deleted-text-permission',
+ 'rev-deleted-text-unhide',
+ 'rev-suppressed-text-unhide',
'rev-deleted-text-view',
+ 'rev-suppressed-text-view',
'rev-deleted-no-diff',
+ 'rev-suppressed-no-diff',
'rev-deleted-unhide-diff',
+ 'rev-suppressed-unhide-diff',
+ 'rev-deleted-diff-view',
+ 'rev-suppressed-diff-view',
'rev-delundel',
+ 'rev-showdeleted',
'revisiondelete',
'revdelete-nooldid-title',
'revdelete-nooldid-text',
'revdelete-nologtype-title',
'revdelete-nologtype-text',
- 'revdelete-toomanytargets-title',
- 'revdelete-toomanytargets-text',
'revdelete-nologid-title',
'revdelete-nologid-text',
+ 'revdelete-no-file',
+ 'revdelete-show-file-confirm',
+ 'revdelete-show-file-submit',
'revdelete-selected',
'logdelete-selected',
'revdelete-text',
+ 'revdelete-confirm',
'revdelete-suppress-text',
'revdelete-legend',
'revdelete-hide-text',
+ 'revdelete-hide-image',
'revdelete-hide-name',
'revdelete-hide-comment',
'revdelete-hide-user',
'revdelete-hide-restricted',
+ 'revdelete-radio-same',
+ 'revdelete-radio-set',
+ 'revdelete-radio-unset',
'revdelete-suppress',
- 'revdelete-hide-image',
'revdelete-unsuppress',
'revdelete-log',
'revdelete-submit',
@@ -673,7 +714,9 @@ $wgMessageStructure = array(
'revdelete-logaction',
'logdelete-logaction',
'revdelete-success',
+ 'revdelete-failure',
'logdelete-success',
+ 'logdelete-failure',
'revdel-restore',
'pagehist',
'deletedhist',
@@ -686,6 +729,18 @@ $wgMessageStructure = array(
'revdelete-unhid',
'revdelete-log-message',
'logdelete-log-message',
+ 'revdelete-hide-current',
+ 'revdelete-show-no-access',
+ 'revdelete-modify-no-access',
+ 'revdelete-modify-missing',
+ 'revdelete-no-change',
+ 'revdelete-concurrent-change',
+ 'revdelete-only-restricted',
+ 'revdelete-reason-dropdown',
+ 'revdelete-otherreason',
+ 'revdelete-reasonotherlist',
+ 'revdelete-edit-reasonlist',
+ 'revdelete-offender',
),
'suppression' => array(
'suppressionlog',
@@ -724,73 +779,17 @@ $wgMessageStructure = array(
'difference',
'lineno',
'compareselectedversions',
- 'visualcomparison',
- 'wikicodecomparison',
+ 'showhideselectedversions',
'editundo',
'diff-multi',
- 'diff-movedto',
- 'diff-styleadded',
- 'diff-added',
- 'diff-changedto',
- 'diff-movedoutof',
- 'diff-styleremoved',
- 'diff-removed',
- 'diff-changedfrom',
- 'diff-src',
- 'diff-withdestination',
- 'diff-with',
- 'diff-with-additional',
- 'diff-with-final',
- 'diff-width',
- 'diff-height',
- 'diff-p',
- 'diff-blockquote',
- 'diff-h1',
- 'diff-h2',
- 'diff-h3',
- 'diff-h4',
- 'diff-h5',
- 'diff-pre',
- 'diff-div',
- 'diff-ul',
- 'diff-ol',
- 'diff-li',
- 'diff-table',
- 'diff-tbody',
- 'diff-tr',
- 'diff-td',
- 'diff-th',
- 'diff-br',
- 'diff-hr',
- 'diff-code',
- 'diff-dl',
- 'diff-dt',
- 'diff-dd',
- 'diff-input',
- 'diff-form',
- 'diff-img',
- 'diff-span',
- 'diff-a',
- 'diff-i',
- 'diff-b',
- 'diff-strong',
- 'diff-em',
- 'diff-font',
- 'diff-big',
- 'diff-del',
- 'diff-tt',
- 'diff-sub',
- 'diff-sup',
- 'diff-strike',
),
'search' => array(
+ 'search-summary',
'searchresults',
'searchresults-title',
'searchresulttext',
'searchsubtitle',
'searchsubtitleinvalid',
- 'noexactmatch',
- 'noexactmatch-nocreate',
'toomanymatches',
'titlematches',
'notitlematches',
@@ -809,7 +808,6 @@ $wgMessageStructure = array(
'searchmenu-prefix',
'searchmenu-help',
'searchprofile-articles',
- 'searchprofile-articles-and-proj',
'searchprofile-project',
'searchprofile-images',
'searchprofile-everything',
@@ -819,8 +817,6 @@ $wgMessageStructure = array(
'searchprofile-images-tooltip',
'searchprofile-everything-tooltip',
'searchprofile-advanced-tooltip',
- 'prefs-search-nsdefault',
- 'prefs-search-nscustom',
'search-result-size',
'search-result-score',
'search-redirect',
@@ -834,11 +830,12 @@ $wgMessageStructure = array(
'search-mwsuggest-disabled',
'search-relatedarticle',
'mwsuggest-disable',
+ 'searcheverything-enable',
'searchrelated',
'searchall',
'showingresults',
'showingresultsnum',
- 'showingresultstotal',
+ 'showingresultsheader',
'nonefound',
'search-nonefound',
'powersearch',
@@ -846,6 +843,9 @@ $wgMessageStructure = array(
'powersearch-ns',
'powersearch-redir',
'powersearch-field',
+ 'powersearch-togglelabel',
+ 'powersearch-toggleall',
+ 'powersearch-togglenone',
'search-external',
'searchdisabled',
'googlesearch',
@@ -853,6 +853,14 @@ $wgMessageStructure = array(
'opensearch' => array(
'opensearch-desc',
),
+ 'quickbar' => array(
+ 'qbsettings',
+ 'qbsettings-none',
+ 'qbsettings-fixedleft',
+ 'qbsettings-fixedright',
+ 'qbsettings-floatingleft',
+ 'qbsettings-floatingright',
+ ),
'preferences' => array(
'preferences',
'preferences-summary',
@@ -860,29 +868,12 @@ $wgMessageStructure = array(
'prefs-edits',
'prefsnologin',
'prefsnologintext',
- 'prefsreset',
- 'qbsettings',
- 'qbsettings-none',
- 'qbsettings-fixedleft',
- 'qbsettings-fixedright',
- 'qbsettings-floatingleft',
- 'qbsettings-floatingright',
'changepassword',
- 'skin',
+ 'prefs-skin',
'skin-preview',
- 'math',
- 'dateformat',
+ 'prefs-math',
'datedefault',
- 'datetime',
- 'math_failure',
- 'math_unknown_error',
- 'math_unknown_function',
- 'math_lexing_error',
- 'math_syntax_error',
- 'math_image_error',
- 'math_bad_tmpdir',
- 'math_bad_output',
- 'math_notexvc',
+ 'prefs-datetime',
'prefs-personal',
'prefs-rc',
'prefs-watchlist',
@@ -890,12 +881,15 @@ $wgMessageStructure = array(
'prefs-watchlist-days-max',
'prefs-watchlist-edits',
'prefs-watchlist-edits-max',
- 'prefs-misc',
+ 'prefs-watchlist-token',
+ 'prefs-misc', // continue checking if used from here on (r49916)
'prefs-resetpass',
+ 'prefs-email',
+ 'prefs-rendering',
'saveprefs',
'resetprefs',
'restoreprefs',
- 'textboxsize',
+ 'prefs-editing',
'prefs-edit-boxsize',
'rows',
'columns',
@@ -907,11 +901,11 @@ $wgMessageStructure = array(
'recentchangesdays',
'recentchangesdays-max',
'recentchangescount',
+ 'prefs-help-recentchangescount',
+ 'prefs-help-watchlist-token',
'savedprefs',
'timezonelegend',
- 'timezonetext',
'localtime',
- 'timezoneselect',
'timezoneuseserverdefault',
'timezoneuseoffset',
'timezoneoffset',
@@ -933,9 +927,47 @@ $wgMessageStructure = array(
'defaultns',
'default',
'defaultns',
- 'files',
+ 'prefs-files',
'prefs-custom-css',
'prefs-custom-js',
+ 'prefs-reset-intro',
+ 'prefs-emailconfirm-label',
+ 'prefs-textboxsize',
+ 'youremail',
+ 'username',
+ 'uid',
+ 'prefs-memberingroups',
+ 'prefs-memberingroups-type',
+ 'prefs-registration',
+ 'prefs-registration-date-time',
+ 'yourrealname',
+ 'yourlanguage',
+ 'yourvariant',
+ 'yournick',
+ 'prefs-help-signature',
+ 'badsig',
+ 'badsiglength',
+ 'yourgender',
+ 'gender-unknown',
+ 'gender-male',
+ 'gender-female',
+ 'prefs-help-gender',
+ 'email',
+ 'prefs-help-realname',
+ 'prefs-help-email',
+ 'prefs-help-email-required',
+ 'prefs-info',
+ 'prefs-i18n',
+ 'prefs-signature',
+ 'prefs-dateformat',
+ 'prefs-timeoffset',
+ 'prefs-advancedediting',
+ 'prefs-advancedrc',
+ 'prefs-advancedrendering',
+ 'prefs-advancedsearchoptions',
+ 'prefs-advancedwatchlist',
+ 'prefs-display',
+ 'prefs-diffs',
),
'userrights' => array(
'userrights',
@@ -947,6 +979,7 @@ $wgMessageStructure = array(
'userrights-editusergroup',
'saveusergroups',
'userrights-groupsmember',
+ 'userrights-groupsmember-auto',
'userrights-groups-help',
'userrights-reason',
'userrights-no-interwiki',
@@ -1010,6 +1043,7 @@ $wgMessageStructure = array(
'right-bigdelete',
'right-deleterevision',
'right-deletedhistory',
+ 'right-deletedtext',
'right-browsearchive',
'right-undelete',
'right-suppressrevision',
@@ -1023,6 +1057,8 @@ $wgMessageStructure = array(
'right-editprotected',
'right-editinterface',
'right-editusercssjs',
+ 'right-editusercss',
+ 'right-edituserjs',
'right-rollback',
'right-markbotedits',
'right-noratelimit',
@@ -1039,6 +1075,8 @@ $wgMessageStructure = array(
'right-siteadmin',
'right-reset-passwords',
'right-override-export-depth',
+ 'right-versiondetail',
+ 'right-sendemail',
),
'rightslog' => array(
'rightslog',
@@ -1089,6 +1127,15 @@ $wgMessageStructure = array(
'recentchanges-legend',
'recentchangestext',
'recentchanges-feed-description',
+ 'recentchanges-label-legend',
+ 'recentchanges-legend-newpage',
+ 'recentchanges-label-newpage',
+ 'recentchanges-legend-minor',
+ 'recentchanges-label-minor',
+ 'recentchanges-legend-bot',
+ 'recentchanges-label-bot',
+ 'recentchanges-legend-unpatrolled',
+ 'recentchanges-label-unpatrolled',
'rcnote',
'rcnotefrom',
'rclistfrom',
@@ -1106,6 +1153,7 @@ $wgMessageStructure = array(
'minoreditletter',
'newpageletter',
'boteditletter',
+ 'unpatrolledletter',
'sectionlink',
'number_of_watching_users_RCview',
'number_of_watching_users_pageview',
@@ -1118,6 +1166,8 @@ $wgMessageStructure = array(
),
'recentchangeslinked' => array(
'recentchangeslinked',
+ 'recentchangeslinked-feed',
+ 'recentchangeslinked-toolbox',
'recentchangeslinked-title',
'recentchangeslinked-backlink',
'recentchangeslinked-noresult',
@@ -1128,8 +1178,8 @@ $wgMessageStructure = array(
'upload' => array(
'upload',
'uploadbtn',
- 'reupload',
'reuploaddesc',
+ 'upload-tryagain',
'uploadnologin',
'uploadnologintext',
'upload_directory_missing',
@@ -1156,6 +1206,7 @@ $wgMessageStructure = array(
'minlength1',
'illegalfilename',
'badfilename',
+ 'filetype-mime-mismatch',
'filetype-badmime',
'filetype-bad-ie-mime',
'filetype-unwanted-type',
@@ -1167,7 +1218,6 @@ $wgMessageStructure = array(
'fileexists',
'filepageexists',
'fileexists-extension',
- 'fileexists-thumb',
'fileexists-thumbnail-yes',
'file-thumbnail-no',
'fileexists-forbidden',
@@ -1176,6 +1226,7 @@ $wgMessageStructure = array(
'file-deleted-duplicate',
'successfulupload',
'uploadwarning',
+ 'uploadwarning-text',
'savefile',
'uploadedimage',
'overwroteimage',
@@ -1183,11 +1234,14 @@ $wgMessageStructure = array(
'uploaddisabledtext',
'php-uploaddisabledtext',
'uploadscripted',
- 'uploadcorrupt',
'uploadvirus',
+ 'upload-source',
'sourcefilename',
+ 'sourceurl',
'destfilename',
'upload-maxfilesize',
+ 'upload-description',
+ 'upload-options',
'watchthisupload',
'filewasdeleted',
'upload-wasdeleted',
@@ -1201,7 +1255,36 @@ $wgMessageStructure = array(
'upload-file-error-text',
'upload-misc-error',
'upload-misc-error-text',
+ 'upload-too-many-redirects',
+ 'upload-unknown-size',
+ 'upload-http-error',
),
+
+ 'img-auth' => array(
+ 'img-auth-accessdenied',
+ 'img-auth-desc',
+ 'img-auth-nopathinfo',
+ 'img-auth-notindir',
+ 'img-auth-badtitle',
+ 'img-auth-nologinnWL',
+ 'img-auth-nofile',
+ 'img-auth-isdir',
+ 'img-auth-streaming',
+ 'img-auth-public',
+ 'img-auth-noread',
+ ),
+
+ 'http-errors' => array(
+ 'http-invalid-url',
+ 'http-invalid-scheme',
+ 'http-request-error',
+ 'http-read-error',
+ 'http-timed-out',
+ 'http-curl-error',
+ 'http-host-unreachable',
+ 'http-bad-status',
+ ),
+
'upload-curl-errors' => array(
'upload-curl-error6',
'upload-curl-error6-text',
@@ -1210,6 +1293,7 @@ $wgMessageStructure = array(
),
'licenses' => array(
'license',
+ 'license-header',
'nolicense',
'licenses',
'license-nopreview',
@@ -1229,6 +1313,7 @@ $wgMessageStructure = array(
'listfiles_count',
),
'filedescription' => array(
+ 'file-anchor-link',
'filehist',
'filehist-help',
'filehist-deleteall',
@@ -1243,6 +1328,7 @@ $wgMessageStructure = array(
'filehist-dimensions',
'filehist-filesize',
'filehist-comment',
+ 'filehist-missing',
'imagelinks',
'linkstoimage',
'linkstoimage-more',
@@ -1251,15 +1337,15 @@ $wgMessageStructure = array(
'redirectstofile',
'duplicatesoffile',
'sharedupload',
- 'shareduploadwiki',
- 'shareduploadwiki-desc',
- 'shareduploadwiki-linktext',
+ 'sharedupload-desc-there',
+ 'sharedupload-desc-here',
'shareddescriptionfollows',
- 'noimage',
- 'noimage-linktext',
+ 'filepage-nofile',
+ 'filepage-nofile-link',
'uploadnewversion-linktext',
'shared-repo-from',
'shared-repo',
+ 'shared-repo-name-wikimediacommons',
),
'filerevert' => array(
'filerevert',
@@ -1288,6 +1374,7 @@ $wgMessageStructure = array(
'filedelete-reason-otherlist',
'filedelete-reason-dropdown',
'filedelete-edit-reasonlist',
+ 'filedelete-maintenance',
),
'mimesearch' => array(
'mimesearch',
@@ -1325,6 +1412,7 @@ $wgMessageStructure = array(
'statistics-header-edits',
'statistics-header-views',
'statistics-header-users',
+ 'statistics-header-hooks',
'statistics-articles',
'statistics-pages',
'statistics-pages-desc',
@@ -1508,6 +1596,7 @@ $wgMessageStructure = array(
'deletedcontribs' => array(
'deletedcontributions',
'deletedcontributions-title',
+ 'sp-deletedcontributions-contribs',
),
'linksearch' => array(
'linksearch',
@@ -1522,6 +1611,18 @@ $wgMessageStructure = array(
'listusersfrom',
'listusers-submit',
'listusers-noresult',
+ 'listusers-blocked',
+ ),
+ 'activeusers' => array(
+ 'activeusers',
+ 'activeusers-summary',
+ 'activeusers-intro',
+ 'activeusers-count',
+ 'activeusers-from',
+ 'activeusers-hidebots',
+ 'activeusers-hidesysops',
+ 'activeusers-submit',
+ 'activeusers-noresult',
),
'newuserlog' => array(
'newuserlogpage',
@@ -1535,15 +1636,22 @@ $wgMessageStructure = array(
'listgrouprights' => array(
'listgrouprights',
'listgrouprights-summary',
+ 'listgrouprights-key',
'listgrouprights-group',
'listgrouprights-rights',
'listgrouprights-helppage',
'listgrouprights-members',
'listgrouprights-right-display',
+ 'listgrouprights-right-revoked',
'listgrouprights-addgroup',
'listgrouprights-removegroup',
'listgrouprights-addgroup-all',
'listgrouprights-removegroup-all',
+ 'listgrouprights-addgroup-self',
+ 'listgrouprights-removegroup-self',
+ 'listgrouprights-addgroup-self-all',
+ 'listgrouprights-removegroup-self-all',
+
),
'emailuser' => array(
'mailnologin',
@@ -1629,6 +1737,7 @@ $wgMessageStructure = array(
'historywarning',
'confirmdeletetext',
'actioncomplete',
+ 'actionfailed',
'deletedtext',
'deletedarticle',
'suppressedarticle',
@@ -1653,6 +1762,7 @@ $wgMessageStructure = array(
'alreadyrolled',
'editcomment',
'revertpage',
+ 'revertpage-nouser',
'rollback-success',
'sessionfailure',
),
@@ -1671,7 +1781,7 @@ $wgMessageStructure = array(
'protectexpiry',
'protect_expiry_invalid',
'protect_expiry_old',
- 'protect-unchain',
+ 'protect-unchain-permissions',
'protect-text',
'protect-locked-blocked',
'protect-locked-dblock',
@@ -1728,6 +1838,7 @@ $wgMessageStructure = array(
'undelete-nodiff',
'undeletebtn',
'undeletelink',
+ 'undeleteviewlink',
'undeletereset',
'undeleteinvert',
'undeletecomment',
@@ -1771,7 +1882,11 @@ $wgMessageStructure = array(
'sp-contributions-newbies-sub',
'sp-contributions-newbies-title',
'sp-contributions-blocklog',
+ 'sp-contributions-deleted',
'sp-contributions-logs',
+ 'sp-contributions-talk',
+ 'sp-contributions-userrights',
+ 'sp-contributions-blocked-notice',
'sp-contributions-search',
'sp-contributions-username',
'sp-contributions-submit',
@@ -1802,6 +1917,7 @@ $wgMessageStructure = array(
),
'block' => array(
'blockip',
+ 'blockip-title',
'blockip-legend',
'blockiptext',
'ipaddress',
@@ -1845,6 +1961,8 @@ $wgMessageStructure = array(
'ipblocklist-sh-addressblocks',
'ipblocklist-summary',
'ipblocklist-submit',
+ 'ipblocklist-localblock',
+ 'ipblocklist-otherblocks',
'blocklistline',
'infiniteblock',
'expiringblock',
@@ -1861,7 +1979,8 @@ $wgMessageStructure = array(
'contribslink',
'autoblocker',
'blocklogpage',
- 'blocklog-fulllog',
+ 'blocklog-showlog',
+ 'blocklog-showsuppresslog',
'blocklogentry',
'reblock-logentry',
'blocklogtext',
@@ -1879,9 +1998,11 @@ $wgMessageStructure = array(
'ipb_hide_invalid',
'ipb_already_blocked',
'ipb-needreblock',
+ 'ipb-otherblocks-header',
'ipb_cant_unblock',
'ipb_blocked_as_range',
'ip_range_invalid',
+ 'ip_range_toolarge',
'blockme',
'proxyblocker',
'proxyblocker-disabled',
@@ -1891,6 +2012,7 @@ $wgMessageStructure = array(
'sorbsreason',
'sorbs_create_account_reason',
'cant-block-while-blocked',
+ 'cant-see-hidden-user'
),
'developertools' => array(
'lockdb',
@@ -1916,6 +2038,7 @@ $wgMessageStructure = array(
'movepagetext',
'movepagetalktext',
'movearticle',
+ 'moveuserpage-warning',
'movenologin',
'movenologintext',
'movenotallowed',
@@ -1966,6 +2089,10 @@ $wgMessageStructure = array(
'imageinvalidfilename',
'fix-double-redirects',
'move-leave-redirect',
+ 'protectedpagemovewarning',
+ 'semiprotectedpagemovewarning',
+ 'move-over-sharedrepo',
+ 'file-exists-sharedrepo',
),
'export' => array(
'export',
@@ -1988,8 +2115,14 @@ $wgMessageStructure = array(
'allmessagescurrent',
'allmessagestext',
'allmessagesnotsupportedDB',
- 'allmessagesfilter',
- 'allmessagesmodified',
+ 'allmessages-filter-legend',
+ 'allmessages-filter',
+ 'allmessages-filter-unmodified',
+ 'allmessages-filter-all',
+ 'allmessages-filter-modified',
+ 'allmessages-prefix',
+ 'allmessages-language',
+ 'allmessages-filter-submit',
),
'thumbnails' => array(
'thumbnail-more',
@@ -1999,6 +2132,9 @@ $wgMessageStructure = array(
'djvu_no_xml',
'thumbnail_invalid_params',
'thumbnail_dest_directory',
+ 'thumbnail_image-type',
+ 'thumbnail_gd-library',
+ 'thumbnail_image-missing',
),
'import' => array(
'import',
@@ -2060,6 +2196,7 @@ $wgMessageStructure = array(
'accesskey-ca-viewsource',
'accesskey-ca-history',
'accesskey-ca-protect',
+ 'accesskey-ca-unprotect',
'accesskey-ca-delete',
'accesskey-ca-undelete',
'accesskey-ca-move',
@@ -2070,6 +2207,7 @@ $wgMessageStructure = array(
'accesskey-search-fulltext',
'accesskey-p-logo',
'accesskey-n-mainpage',
+ 'accesskey-n-mainpage-description',
'accesskey-n-portal',
'accesskey-n-currentevents',
'accesskey-n-recentchanges',
@@ -2101,7 +2239,6 @@ $wgMessageStructure = array(
'accesskey-preview',
'accesskey-diff',
'accesskey-compareselectedversions',
- 'accesskey-visualcomparison',
'accesskey-watch',
'accesskey-upload',
),
@@ -2122,6 +2259,7 @@ $wgMessageStructure = array(
'tooltip-ca-viewsource',
'tooltip-ca-history',
'tooltip-ca-protect',
+ 'tooltip-ca-unprotect',
'tooltip-ca-delete',
'tooltip-ca-undelete',
'tooltip-ca-move',
@@ -2132,6 +2270,7 @@ $wgMessageStructure = array(
'tooltip-search-fulltext',
'tooltip-p-logo',
'tooltip-n-mainpage',
+ 'tooltip-n-mainpage-description',
'tooltip-n-portal',
'tooltip-n-currentevents',
'tooltip-n-recentchanges',
@@ -2179,6 +2318,7 @@ $wgMessageStructure = array(
'chick.css',
'simple.css',
'modern.css',
+ 'vector.css',
'print.css',
'handheld.css',
),
@@ -2192,6 +2332,7 @@ $wgMessageStructure = array(
'chick.js',
'simple.js',
'modern.js',
+ 'vector.js',
),
'metadata_cc' => array(
'nodublincore',
@@ -2201,10 +2342,12 @@ $wgMessageStructure = array(
'attribution' => array(
'anonymous',
'siteuser',
+ 'anonuser',
'lastmodifiedatby',
'othercontribs',
'others',
'siteusers',
+ 'anonusers',
'creditspage',
'nocredits',
),
@@ -2233,6 +2376,7 @@ $wgMessageStructure = array(
'skinname-chick',
'skinname-simple',
'skinname-modern',
+ 'skinname-vector',
),
'math' => array(
'mw_math_png',
@@ -2242,6 +2386,17 @@ $wgMessageStructure = array(
'mw_math_modern',
'mw_math_mathml',
),
+ 'matherrors' => array(
+ 'math_failure',
+ 'math_unknown_error',
+ 'math_unknown_function',
+ 'math_lexing_error',
+ 'math_syntax_error',
+ 'math_image_error',
+ 'math_bad_tmpdir',
+ 'math_bad_output',
+ 'math_notexvc',
+ ),
'patrolling' => array(
'markaspatrolleddiff',
'markaspatrolledlink',
@@ -2275,9 +2430,6 @@ $wgMessageStructure = array(
'previousdiff',
'nextdiff',
),
- 'visual-comparison' => array(
- 'visual-comparison',
- ),
'media-info' => array(
'mediawarning',
'imagemaxsize',
@@ -2290,6 +2442,8 @@ $wgMessageStructure = array(
'svg-long-desc',
'show-big-image',
'show-big-image-thumb',
+ 'file-info-gif-looped',
+ 'file-info-gif-frames',
),
'newfiles' => array(
'newimages',
@@ -2687,6 +2841,7 @@ $wgMessageStructure = array(
'watchlistall2',
'namespacesall',
'monthsall',
+ 'limitall',
),
'confirmemail' => array(
'confirmemail',
@@ -2743,6 +2898,7 @@ $wgMessageStructure = array(
'word-separator',
'ellipsis',
'percent',
+ 'parentheses',
),
'imgmulti' => array(
'imgmultipageprev',
@@ -2891,6 +3047,7 @@ $wgMessageStructure = array(
'version-hook-name',
'version-hook-subscribedby',
'version-version',
+ 'version-svn-revision',
'version-license',
'version-software',
'version-software-product',
@@ -2958,6 +3115,17 @@ $wgMessageStructure = array(
'dberr-outofdate',
'dberr-cachederror',
),
+ 'html-forms' => array(
+ 'htmlform-invalid-input',
+ 'htmlform-select-badoption',
+ 'htmlform-int-invalid',
+ 'htmlform-float-invalid',
+ 'htmlform-int-toolow',
+ 'htmlform-int-toohigh',
+ 'htmlform-submit',
+ 'htmlform-reset',
+ 'htmlform-selectorother-other',
+ ),
);
/** Comments for each block */
@@ -2970,11 +3138,13 @@ XHTML id it should only appear once and include characters that are legal
XHTML id names.",
'toggles' => 'User preference toggles',
'underline' => '',
+ 'editfont' => 'Font style option in Special:Preferences',
'dates' => 'Dates',
'categorypages' => 'Categories related messages',
'mainpage' => '',
'miscellaneous1' => '',
- 'metadata_help' => 'Metadata in edit box',
+ 'cologneblue' => 'Cologne Blue skin',
+ 'vector' => 'Vector skin',
'miscellaneous2' => '',
'links' => 'All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage) and the disambiguation template definition (see disambiguations).',
'badaccess' => '',
@@ -3000,6 +3170,7 @@ XHTML id names.",
'diffs' => 'Diffs',
'search' => 'Search results',
'opensearch' => 'OpenSearch description',
+ 'quickbar' => 'Quickbar',
'preferences' => 'Preferences page',
'userrights' => 'User rights',
'group' => 'Groups',
@@ -3012,6 +3183,8 @@ XHTML id names.",
'recentchangeslinked' => 'Recent changes linked',
'upload' => 'Upload',
'upload-errors' => '',
+ 'img-auth' => 'img_auth script messages',
+ 'http-errors' => 'HTTP errors',
'upload-curl-errors' => 'Some likely curl errors. More could be added from <http://curl.haxx.se/libcurl/c/libcurl-errors.html>',
'licenses' => '',
'filelist' => 'Special:ListFiles',
@@ -3039,6 +3212,7 @@ XHTML id names.",
'deletedcontribs' => 'Special:DeletedContributions',
'linksearch' => 'Special:LinkSearch',
'listusers' => 'Special:ListUsers',
+ 'activeusers' => 'Special:ActiveUsers',
'newuserlog' => 'Special:Log/newusers',
'listgrouprights' => 'Special:ListGroupRights',
'emailuser' => 'E-mail user',
@@ -3073,6 +3247,7 @@ XHTML id names.",
'info' => 'Info page',
'skin' => 'Skin names',
'math' => 'Math options',
+ 'matherrors' => 'Math errors',
'patrolling' => 'Patrolling',
'patrol-log' => 'Patrol log',
'imagedeletion' => 'Image deletion',
@@ -3089,7 +3264,6 @@ Variants for Chinese language",
'variantname-kk' => 'Variants for Kazakh language',
'variantname-ku' => 'Variants for Kurdish language',
'variantname-tg' => 'Variants for Tajiki language',
- 'visual-comparison' => 'Visual comparison',
'media-info' => 'Media information',
'metadata' => 'Metadata',
'exif' => 'EXIF tags',
@@ -3124,7 +3298,7 @@ Variants for Chinese language",
'exif-gpslongitude' => 'Pseudotags used for GPSLongitudeRef and GPSDestLongitudeRef',
'exif-gpsstatus' => '',
'exif-gpsmeasuremode' => '',
- 'exif-gpsspeed' => 'Pseudotags used for GPSSpeedRef and GPSDestDistanceRef',
+ 'exif-gpsspeed' => 'Pseudotags used for GPSSpeedRef',
'exif-gpsdirection' => 'Pseudotags used for GPSTrackRef, GPSImgDirectionRef and GPSDestBearingRef',
'edit-externally' => 'External editor support',
'all' => "'all' in various places, this might be different for inflected languages",
@@ -3157,35 +3331,5 @@ Variants for Chinese language",
'external_images' => 'External image whitelist',
'special-tags' => 'Special:Tags',
'db-error-messages' => 'Database error messages',
-);
-
-/** Short comments for standalone messages */
-$wgMessageComments = array(
- 'hidden-category-category' => 'Name of the category where hidden categories will be listed',
- 'lastmodifiedat' => '$1 date, $2 time',
- 'sitenotice' => 'the equivalent to wgSiteNotice',
- 'history-feed-item-nocomment' => 'user at time',
- 'sharedupload' => '$1 is the repo name, $2 is shareduploadwiki(-desc)',
- 'shared-repo-from' => '$1 is the repository name',
- 'shared-repo' => 'used when shared-repo-NAME does not exist',
- 'editcomment' => 'only shown if there is an edit comment',
- 'revertpage' => 'Additionally available: $3: revid of the revision reverted to, $4: timestamp of the revision reverted to, $5: revid of the revision reverted from, $6: timestamp of the revision reverted from',
- 'lastmodifiedatby' => '$1 date, $2 time, $3 user',
- 'exif-orientation-1' => '0th row: top; 0th column: left',
- 'exif-orientation-2' => '0th row: top; 0th column: right',
- 'exif-orientation-3' => '0th row: bottom; 0th column: right',
- 'exif-orientation-4' => '0th row: bottom; 0th column: left',
- 'exif-orientation-5' => '0th row: left; 0th column: top',
- 'exif-orientation-6' => '0th row: right; 0th column: top',
- 'exif-orientation-7' => '0th row: right; 0th column: bottom',
- 'exif-orientation-8' => '0th row: left; 0th column: bottom',
- 'movepage-moved' => 'The two titles are passed in plain text as $3 and $4 to allow additional goodies in the message.',
- 'ipboptions' => 'display1:time1,display2:time2,...',
- 'protect-expiry-options' => 'display1:time1,display2:time2,...',
- 'metadata-fields' => 'Do not translate list items',
- 'version' => 'Not used as normal message but as header for the special page itself',
- 'userrights' => 'Not used as normal message but as header for the special page itself',
- 'revision-info' => 'Additionally available: $3: revision id',
- 'revision-info-current' => 'Available parameters: $1: timestamp; $2: userlinks; $3: revision id',
- 'nocontribs' => 'Optional parameter: $1 is the user name',
+ 'html-forms' => 'HTML forms',
);
diff --git a/maintenance/language/rebuildLanguage.php b/maintenance/language/rebuildLanguage.php
index 91fda3f4..6c624ca3 100644
--- a/maintenance/language/rebuildLanguage.php
+++ b/maintenance/language/rebuildLanguage.php
@@ -17,18 +17,50 @@ require_once( 'writeMessagesArray.inc' );
* @param $code The language code.
* @param $write Write to the messages file?
* @param $listUnknown List the unknown messages?
- * @param $removeUnKnown Remove the unknown messages?
+ * @param $removeUnknown Remove the unknown messages?
+ * @param $removeDupes Remove the duplicated messages?
+ * @param $dupeMsgSource The source file intended to remove from the array.
*/
-function rebuildLanguage( $code, $write, $listUnknown, $removeUnknown ) {
+function rebuildLanguage( $code, $write, $listUnknown, $removeUnknown, $removeDupes, $dupeMsgSource ) {
global $wgLanguages;
$messages = $wgLanguages->getMessages( $code );
$messages = $messages['all'];
+ if ($removeDupes) {
+ $messages = removeDupes( $messages, $dupeMsgSource );
+ }
MessageWriter::writeMessagesToFile( $messages, $code, $write, $listUnknown, $removeUnknown );
}
+/**
+ * Remove duplicates from a message array.
+ *
+ * @param $oldMsgArray The input message array.
+ * @param $dupeMsgSource The source file path for duplicates.
+ * @return $newMsgArray The output message array, with duplicates removed.
+ */
+function removeDupes( $oldMsgArray, $dupeMsgSource ) {
+ if (file_exists($dupeMsgSource)) {
+ include($dupeMsgSource);
+ if (!isset($dupeMessages)) {
+ echo("There are no duplicated messages in the source file provided.");
+ exit(1);
+ }
+ } else {
+ echo ("The specified file $dupeMsgSource cannot be found.");
+ exit(1);
+ }
+ $newMsgArray = $oldMsgArray;
+ foreach ($oldMsgArray as $key => $value) {
+ if ( array_key_exists( $key, $dupeMessages ) ) {
+ unset($newMsgArray[$key]);
+ }
+ }
+ return $newMsgArray;
+}
+
# Show help
if ( isset( $options['help'] ) ) {
- echo <<<END
+ echo <<<TEXT
Run this script to rewrite the messages array in the files languages/messages/MessagesXX.php.
Parameters:
* lang: Language code (default: the installation default language). You can also specify "all" to check all the languages.
@@ -37,9 +69,10 @@ Options:
* dry-run: Do not write the array to the file.
* no-unknown: Do not list the unknown messages.
* remove-unknown: Remove unknown messages.
+ * remove-duplicates: Remove duplicated messages based on a PHP source file.
-END;
- exit();
+TEXT;
+ exit(1);
}
# Get the language code
@@ -49,10 +82,18 @@ if ( isset( $options['lang'] ) ) {
$wgCode = $wgContLang->getCode();
}
+# Get the duplicate message source
+if ( isset( $options['remove-duplicates'] ) && ( strcmp( $options['remove-duplicates'], '' ) ) ) {
+ $wgDupeMessageSource = $options['remove-duplicates'];
+} else {
+ $wgDupeMessageSource = '';
+}
+
# Get the options
$wgWriteToFile = !isset( $options['dry-run'] );
$wgListUnknownMessages = !isset( $options['no-unknown'] );
$wgRemoveUnknownMessages = isset( $options['remove-unknown'] );
+$wgRemoveDuplicateMessages = isset( $options['remove-duplicates'] );
# Get language objects
$wgLanguages = new languages();
@@ -60,8 +101,8 @@ $wgLanguages = new languages();
# Write all the language
if ( $wgCode == 'all' ) {
foreach ( $wgLanguages->getLanguages() as $language ) {
- rebuildLanguage( $language, $wgWriteToFile, $wgListUnknownMessages, $wgRemoveUnknownMessages );
+ rebuildLanguage( $language, $wgWriteToFile, $wgListUnknownMessages, $wgRemoveUnknownMessages, $wgRemoveDuplicateMessages, $wgDupeMessageSource );
}
} else {
- rebuildLanguage( $wgCode, $wgWriteToFile, $wgListUnknownMessages, $wgRemoveUnknownMessages );
+ rebuildLanguage( $wgCode, $wgWriteToFile, $wgListUnknownMessages, $wgRemoveUnknownMessages, $wgRemoveDuplicateMessages, $wgDupeMessageSource );
}
diff --git a/maintenance/language/transstat.php b/maintenance/language/transstat.php
index b433abb4..eeded34e 100644
--- a/maintenance/language/transstat.php
+++ b/maintenance/language/transstat.php
@@ -9,7 +9,7 @@
* @author Ashar Voultoiz <thoane@altern.org>
*
* Output is posted from time to time on:
- * http://meta.wikimedia.org/wiki/Localization_statistics
+ * http://www.mediawiki.org/wiki/Localisation_statistics
*/
$optionsWithArgs = array( 'output' );
@@ -29,18 +29,17 @@ if ( !isset( $options['output'] ) ) {
/** Print a usage message*/
function showUsage() {
- print <<<END
+ print <<<TEXT
Usage: php transstat.php [--help] [--output=csv|text|wiki]
--help : this helpful message
--output : select an output engine one of:
* 'csv' : Comma Separated Values.
* 'wiki' : MediaWiki syntax (default).
- * 'metawiki' : MediaWiki syntax used for Meta-Wiki.
* 'text' : Text with tabs.
Example: php maintenance/transstat.php --output=text
-END;
- exit();
+TEXT;
+ exit(1);
}
@@ -48,16 +47,13 @@ END;
# Select an output engine
switch ( $options['output'] ) {
case 'wiki':
- $wgOut = new wikiStatsOutput();
- break;
- case 'metawiki':
- $wgOut = new metawikiStatsOutput();
+ $output = new wikiStatsOutput();
break;
case 'text':
- $wgOut = new textStatsOutput();
+ $output = new textStatsOutput();
break;
case 'csv':
- $wgOut = new csvStatsOutput();
+ $output = new csvStatsOutput();
break;
default:
showUsage();
@@ -67,17 +63,18 @@ switch ( $options['output'] ) {
$wgLanguages = new languages();
# Header
-$wgOut->heading();
-$wgOut->blockstart();
-$wgOut->element( 'Language', true );
-$wgOut->element( 'Code', true );
-$wgOut->element( 'Translated', true );
-$wgOut->element( '%', true );
-$wgOut->element( 'Obsolete', true );
-$wgOut->element( '%', true );
-$wgOut->element( 'Problematic', true );
-$wgOut->element( '%', true );
-$wgOut->blockend();
+$output->heading();
+$output->blockstart();
+$output->element( 'Language', true );
+$output->element( 'Code', true );
+$output->element( 'Fallback', true );
+$output->element( 'Translated', true );
+$output->element( '%', true );
+$output->element( 'Obsolete', true );
+$output->element( '%', true );
+$output->element( 'Problematic', true );
+$output->element( '%', true );
+$output->blockend();
$wgGeneralMessages = $wgLanguages->getGeneralMessages();
$wgRequiredMessagesNumber = count( $wgGeneralMessages['required'] );
@@ -90,34 +87,36 @@ foreach ( $wgLanguages->getLanguages() as $code ) {
# Calculate the numbers
$language = $wgContLang->getLanguageName( $code );
+ $fallback = $wgLanguages->getFallback( $code );
$messages = $wgLanguages->getMessages( $code );
$messagesNumber = count( $messages['translated'] );
$requiredMessagesNumber = count( $messages['required'] );
- $requiredMessagesPercent = $wgOut->formatPercent( $requiredMessagesNumber, $wgRequiredMessagesNumber );
+ $requiredMessagesPercent = $output->formatPercent( $requiredMessagesNumber, $wgRequiredMessagesNumber );
$obsoleteMessagesNumber = count( $messages['obsolete'] );
- $obsoleteMessagesPercent = $wgOut->formatPercent( $obsoleteMessagesNumber, $messagesNumber, true );
+ $obsoleteMessagesPercent = $output->formatPercent( $obsoleteMessagesNumber, $messagesNumber, true );
$messagesWithMismatchVariables = $wgLanguages->getMessagesWithMismatchVariables( $code );
$emptyMessages = $wgLanguages->getEmptyMessages( $code );
$messagesWithWhitespace = $wgLanguages->getMessagesWithWhitespace( $code );
$nonXHTMLMessages = $wgLanguages->getNonXHTMLMessages( $code );
$messagesWithWrongChars = $wgLanguages->getMessagesWithWrongChars( $code );
$problematicMessagesNumber = count( array_unique( array_merge( $messagesWithMismatchVariables, $emptyMessages, $messagesWithWhitespace, $nonXHTMLMessages, $messagesWithWrongChars ) ) );
- $problematicMessagesPercent = $wgOut->formatPercent( $problematicMessagesNumber, $messagesNumber, true );
+ $problematicMessagesPercent = $output->formatPercent( $problematicMessagesNumber, $messagesNumber, true );
# Output them
- $wgOut->blockstart();
- $wgOut->element( "$language" );
- $wgOut->element( "$code" );
- $wgOut->element( "$requiredMessagesNumber/$wgRequiredMessagesNumber" );
- $wgOut->element( $requiredMessagesPercent );
- $wgOut->element( "$obsoleteMessagesNumber/$messagesNumber" );
- $wgOut->element( $obsoleteMessagesPercent );
- $wgOut->element( "$problematicMessagesNumber/$messagesNumber" );
- $wgOut->element( $problematicMessagesPercent );
- $wgOut->blockend();
+ $output->blockstart();
+ $output->element( "$language" );
+ $output->element( "$code" );
+ $output->element( "$fallback" );
+ $output->element( "$requiredMessagesNumber/$wgRequiredMessagesNumber" );
+ $output->element( $requiredMessagesPercent );
+ $output->element( "$obsoleteMessagesNumber/$messagesNumber" );
+ $output->element( $obsoleteMessagesPercent );
+ $output->element( "$problematicMessagesNumber/$messagesNumber" );
+ $output->element( $problematicMessagesPercent );
+ $output->blockend();
}
# Footer
-$wgOut->footer();
+$output->footer();
diff --git a/maintenance/language/writeMessagesArray.inc b/maintenance/language/writeMessagesArray.inc
index 3a279cb6..e28a5c04 100644
--- a/maintenance/language/writeMessagesArray.inc
+++ b/maintenance/language/writeMessagesArray.inc
@@ -15,7 +15,6 @@ class MessageWriter {
static $messageStructure;
static $blockComments;
- static $messageComments;
static $ignoredMessages;
static $optionalMessages;
@@ -79,13 +78,11 @@ class MessageWriter {
require( $dir . '/messages.inc' );
self::$messageStructure = $wgMessageStructure;
self::$blockComments = $wgBlockComments;
- self::$messageComments = $wgMessageComments;
require( $dir . '/messageTypes.inc' );
self::$ignoredMessages = $wgIgnoredMessages;
self::$optionalMessages = $wgOptionalMessages;
-
# Sort messages to blocks
$sortedMessages['unknown'] = $messages;
foreach( self::$messageStructure as $blockName => $block ) {
@@ -113,7 +110,7 @@ class MessageWriter {
$ignored = array();
$optional = array();
}
- $comments = self::makeComments( array_keys($messages), self::$messageComments, $ignored, $optional );
+ $comments = self::makeComments( array_keys( $messages ), $ignored, $optional );
# Write the block
$messagesText .= self::writeMessagesBlock( self::$blockComments[$block], $messages, $comments );
@@ -134,33 +131,19 @@ class MessageWriter {
* Generates an array of comments for messages.
*
* @param $messages Key of messages.
- * @param $comments Comments for messages, indexed by key.
* @param $ignored List of ingored message keys.
* @param $optional List of optional message keys.
*/
- public static function makeComments( $messages, $comments, $ignored, $optional ) {
+ public static function makeComments( $messages, $ignored, $optional ) {
# Comment collector
$commentArray = array();
# List of keys only
foreach( $messages as $key ) {
- $commentsForKey = array();
-
- # Add descriptive comment for this message if there is one
- if( array_key_exists( $key, $comments ) ) {
- $commentsForKey[] = $comments[$key];
- }
-
- # For translator information only
if( in_array( $key, $ignored ) ) {
- $commentsForKey[] = self::$ignoredComment;
+ $commentArray[$key] = ' # ' . self::$ignoredComment;
} elseif( in_array( $key, $optional ) ) {
- $commentsForKey[] = self::$optionalComment;
- }
-
- # Format one or more comments nicely and store in array
- if( count( $commentsForKey ) ) {
- $commentArray[$key] = ' # ' . implode( '; ', $commentsForKey );
+ $commentArray[$key] = ' # ' . self::$optionalComment;
}
}
diff --git a/maintenance/mcc.php b/maintenance/mcc.php
index 53645df8..e90266ae 100644
--- a/maintenance/mcc.php
+++ b/maintenance/mcc.php
@@ -8,64 +8,36 @@
*/
/** */
-require_once( 'commandLine.inc' );
+require_once( dirname(__FILE__) . '/commandLine.inc' );
-$mcc = new memcached( array('persistant' => true/*, 'debug' => true*/) );
+$mcc = new MWMemcached( array('persistant' => true/*, 'debug' => true*/) );
$mcc->set_servers( $wgMemCachedServers );
#$mcc->set_debug( true );
function mccShowHelp($command) {
-
- if(! $command ) { $command = 'fullhelp'; }
- $onlyone = true;
-
- switch ( $command ) {
-
- case 'fullhelp':
- // will show help for all commands
- $onlyone = false;
-
- case 'get':
- print "get: grabs something\n";
- if($onlyone) { break; }
-
- case 'getsock':
- print "getsock: lists sockets\n";
- if($onlyone) { break; }
-
- case 'set':
- print "set: changes something\n";
- if($onlyone) { break; }
-
- case 'delete':
- print "delete: deletes something\n";
- if($onlyone) { break; }
-
- case 'history':
- print "history: show command line history\n";
- if($onlyone) { break; }
-
- case 'server':
- print "server: show current memcached server\n";
- if($onlyone) { break; }
-
- case 'dumpmcc':
- print "dumpmcc: shows the whole thing\n";
- if($onlyone) { break; }
-
- case 'exit':
- case 'quit':
- print "exit or quit: exit mcc\n";
- if($onlyone) { break; }
-
- case 'help':
- print "help: help about a command\n";
- if($onlyone) { break; }
-
- default:
- if($onlyone) {
- print "$command: command does not exist or no help for it\n";
- }
+ $commandList = array(
+ 'get' => 'grabs something',
+ 'getsock' => 'lists sockets',
+ 'set' => 'changes something',
+ 'delete' => 'deletes something',
+ 'history' => 'show command line history',
+ 'server' => 'show current memcached server',
+ 'dumpmcc' => 'shows the whole thing',
+ 'exit' => 'exit mcc',
+ 'quit' => 'exit mcc',
+ 'help' => 'help about a command',
+ );
+ if( !$command ) {
+ $command = 'fullhelp';
+ }
+ if( $command === 'fullhelp' ) {
+ foreach( $commandList as $cmd => $desc ) {
+ print "$cmd: $desc\n";
+ }
+ } elseif( isset( $commandList[$command] ) ) {
+ print "$command: $commandList[$command]\n";
+ } else {
+ print "$command: command does not exist or no help for it\n";
}
}
@@ -114,6 +86,10 @@ do {
break;
case 'server':
+ if ( $mcc->_single_sock !== null ) {
+ print $mcc->_single_sock . "\n";
+ break;
+ }
$res = $mcc->get( $args[0] );
$hv = $mcc->_hashfunc( $args[0] );
for ( $i = 0; $i < 3; $i++ ) {
@@ -176,5 +152,3 @@ do {
}
}
} while ( !$quit );
-
-
diff --git a/maintenance/mctest.php b/maintenance/mctest.php
index aef6d6db..3667cb93 100644
--- a/maintenance/mctest.php
+++ b/maintenance/mctest.php
@@ -3,63 +3,81 @@
* This script makes several 'set', 'incr' and 'get' requests on every
* memcached server and shows a report.
*
- * $Id: mctest.php 37886 2008-07-21 17:06:35Z greg $
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-$optionsWithArgs = array( 'i' );
-
-require_once('commandLine.inc');
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-function microtime_float()
-{
- list($usec, $sec) = explode(" ", microtime());
- return ((float)$usec + (float)$sec);
-}
-
-
-#$wgDebugLogFile = '/dev/stdout';
+class mcTest extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Makes several 'set', 'incr' and 'get' requests on every"
+ . " memcached server and shows a report";
+ $this->addOption( 'i', 'Number of iterations', false, true );
+ $this->addArg( 'server', 'Memcached server to test', false );
+ }
-if ( isset( $args[0] ) ) {
- $wgMemCachedServers = array( $args[0] );
-}
-if ( isset( $options['i'] ) ) {
- $iterations = $options['i'];
-} else {
- $iterations = 100;
-}
+ public function execute() {
+ global $wgMemCachedServers;
-foreach ( $wgMemCachedServers as $server ) {
- print "$server ";
- $mcc = new MemCachedClientforWiki( array('persistant' => true) );
- $mcc->set_servers( array( $server ) );
- $set = 0;
- $incr = 0;
- $get = 0;
- $time_start=microtime_float();
- for ( $i=1; $i<=$iterations; $i++ ) {
- if ( !is_null( $mcc->set( "test$i", $i ) ) ) {
- $set++;
- }
- }
+ $iterations = $this->getOption( 'i', 100 );
+ if( $this->hasArg() )
+ $wgMemCachedServers = array( $this->getArg() );
- for ( $i=1; $i<=$iterations; $i++ ) {
- if ( !is_null( $mcc->incr( "test$i", $i ) ) ) {
- $incr++;
+ foreach ( $wgMemCachedServers as $server ) {
+ $this->output( $server . " " );
+ $mcc = new MemCachedClientforWiki( array('persistant' => true) );
+ $mcc->set_servers( array( $server ) );
+ $set = 0;
+ $incr = 0;
+ $get = 0;
+ $time_start = $this->microtime_float();
+ for ( $i=1; $i<=$iterations; $i++ ) {
+ if ( !is_null( $mcc->set( "test$i", $i ) ) ) {
+ $set++;
+ }
+ }
+ for ( $i=1; $i<=$iterations; $i++ ) {
+ if ( !is_null( $mcc->incr( "test$i", $i ) ) ) {
+ $incr++;
+ }
+ }
+ for ( $i=1; $i<=$iterations; $i++ ) {
+ $value = $mcc->get( "test$i" );
+ if ( $value == $i*2 ) {
+ $get++;
+ }
+ }
+ $exectime = $this->microtime_float() - $time_start;
+
+ $this->output( "set: $set incr: $incr get: $get time: $exectime\n" );
}
}
- for ( $i=1; $i<=$iterations; $i++ ) {
- $value = $mcc->get( "test$i" );
- if ( $value == $i*2 ) {
- $get++;
- }
+ /**
+ * Return microtime() as a float
+ * @return float
+ */
+ private function microtime_float() {
+ list($usec, $sec) = explode(" ", microtime());
+ return ((float)$usec + (float)$sec);
}
- $exectime=microtime_float()-$time_start;
-
- print "set: $set incr: $incr get: $get time: $exectime\n";
}
-
-
+$maintClass = "mcTest";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/mergeMessageFileList.php b/maintenance/mergeMessageFileList.php
new file mode 100644
index 00000000..57d9ebb0
--- /dev/null
+++ b/maintenance/mergeMessageFileList.php
@@ -0,0 +1,72 @@
+<?php
+
+# Start from scratch
+define( 'MW_NO_EXTENSION_MESSAGES', 1 );
+
+require_once( dirname( __FILE__ ).'/Maintenance.php' );
+$maintClass = 'MergeMessageFileList';
+$mmfl = false;
+class MergeMessageFileList extends Maintenance {
+
+ function __construct() {
+ $this->addOption( 'list-file', 'A file containing a list of extension setup files, one per line.', false, true );
+ $this->addOption( 'output', 'Send output to this file (omit for stdout)', false, true );
+ $this->mDescription = 'Merge $wgExtensionMessagesFiles from various extensions to produce a ' .
+ 'single array containing all message files.';
+ }
+
+ public function execute() {
+ global $IP, $mmfl;
+ if ( !$this->hasOption( 'list-file' ) ) {
+ $this->error( 'The --list-file option must be specified.' );
+ return;
+ }
+
+ $lines = file( $this->getOption( 'list-file' ) );
+ if ( $lines === false ) {
+ $this->error( 'Unable to open list file.' );
+ }
+ $mmfl = array( 'setupFiles' => array_map( 'trim', $lines ) );
+ if ( $this->hasOption( 'output' ) ) {
+ $mmfl['output'] = $this->getOption( 'output' );
+ }
+ }
+}
+
+require_once( DO_MAINTENANCE );
+
+foreach ( $mmfl['setupFiles'] as $fileName ) {
+ if ( strval( $fileName ) === '' ) {
+ continue;
+ }
+ $fileName = str_replace( '$IP', $IP, $fileName );
+ fwrite( STDERR, "Loading data from $fileName\n" );
+ include_once( $fileName );
+}
+fwrite( STDERR, "\n" );
+$s =
+ "<" . "?php\n" .
+ "## This file is generated by mergeMessageFileList.php. Do not edit it directly.\n\n" .
+ "if ( defined( 'MW_NO_EXTENSION_MESSAGES' ) ) return;\n\n" .
+ '$wgExtensionMessagesFiles = ' . var_export( $wgExtensionMessagesFiles, true ) . ";\n\n" .
+ '$wgExtensionAliasesFiles = ' . var_export( $wgExtensionAliasesFiles, true ) . ";\n";
+
+$dirs = array(
+ $IP,
+ dirname( dirname( __FILE__ ) ),
+ realpath( $IP )
+);
+
+foreach ( $dirs as $dir ) {
+ $s = preg_replace(
+ "/'" . preg_quote( $dir, '/' ) . "([^']*)'/",
+ '"$IP\1"',
+ $s );
+}
+
+if ( isset( $mmfl['output'] ) ) {
+ file_put_contents( $mmfl['output'], $s );
+} else {
+ echo $s;
+}
+
diff --git a/maintenance/migrateUserGroup.php b/maintenance/migrateUserGroup.php
new file mode 100644
index 00000000..6d584f7d
--- /dev/null
+++ b/maintenance/migrateUserGroup.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Re-assign users from an old group to a new one
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @ingroup Maintenance
+ */
+
+require_once( dirname(__FILE__) . '/Maintenance.php' );
+
+class MigrateUserGroup extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Re-assign users from an old group to a new one";
+ $this->addArg( 'oldgroup', 'Old user group key', true );
+ $this->addArg( 'newgroup', 'New user group key', true );
+ $this->setBatchSize( 200 );
+ }
+
+ public function execute() {
+ $count = 0;
+ $oldGroup = $this->getArg( 0 );
+ $newGroup = $this->getArg( 1 );
+ $dbw = wfGetDB( DB_MASTER );
+ $start = $dbw->selectField( 'user_groups', 'MIN(ug_user)',
+ array('ug_group' => $oldGroup), __FUNCTION__ );
+ $end = $dbw->selectField( 'user_groups', 'MAX(ug_user)',
+ array('ug_group' => $oldGroup), __FUNCTION__ );
+ if( $start === null ) {
+ $this->error( "Nothing to do - no users in the '$oldGroup' group", true );
+ }
+ # Do remaining chunk
+ $end += $this->mBatchSize - 1;
+ $blockStart = $start;
+ $blockEnd = $start + $this->mBatchSize - 1;
+ // Migrate users over in batches...
+ while( $blockEnd <= $end ) {
+ $this->output( "Doing users $blockStart to $blockEnd\n" );
+ $dbw->begin();
+ $dbw->update( 'user_groups',
+ array('ug_group' => $newGroup),
+ array('ug_group' => $oldGroup,
+ "ug_user BETWEEN $blockStart AND $blockEnd" )
+ );
+ $count += $dbw->affectedRows();
+ $dbw->commit();
+ $blockStart += $this->mBatchSize;
+ $blockEnd += $this->mBatchSize;
+ wfWaitForSlaves( 5 );
+ }
+ $this->output( "Done! $count user(s) in group '$oldGroup' are now in '$newGroup' instead.\n" );
+ }
+}
+
+$maintClass = "MigrateUserGroup";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/minify.php b/maintenance/minify.php
new file mode 100644
index 00000000..601a4d67
--- /dev/null
+++ b/maintenance/minify.php
@@ -0,0 +1,111 @@
+<?php
+/**
+ * Minify a file or set of files
+ */
+
+require_once( dirname( __FILE__ ) . '/Maintenance.php' );
+
+class MinifyScript extends Maintenance {
+ var $outDir;
+
+ public function __construct() {
+ parent::__construct();
+ $this->addOption( 'outfile',
+ 'File for output. Only a single file may be specified for input.',
+ false, true );
+ $this->addOption( 'outdir',
+ "Directory for output. If this is not specified, and neither is --outfile, then the\n" .
+ "output files will be sent to the same directories as the input files.",
+ false, true );
+ $this->mDescription = "Minify a file or set of files.\n\n" .
+ "If --outfile is not specified, then the output file names will have a .min extension\n" .
+ "added, e.g. jquery.js -> jquery.min.js.";
+
+ }
+
+ public function execute() {
+ if ( !count( $this->mArgs ) ) {
+ $this->error( "minify.php: At least one input file must be specified." );
+ exit( 1 );
+ }
+
+ if ( $this->hasOption( 'outfile' ) ) {
+ if ( count( $this->mArgs ) > 1 ) {
+ $this->error( '--outfile may only be used with a single input file.' );
+ exit( 1 );
+ }
+
+ // Minify one file
+ $this->minify( $this->getArg( 0 ), $this->getOption( 'outfile' ) );
+ return;
+ }
+
+ $outDir = $this->getOption( 'outdir', false );
+
+ foreach ( $this->mArgs as $arg ) {
+ $inPath = realpath( $arg );
+ $inName = basename( $inPath );
+ $inDir = dirname( $inPath );
+
+ if ( strpos( $inName, '.min.' ) !== false ) {
+ echo "Skipping $inName\n";
+ continue;
+ }
+
+ if ( !file_exists( $inPath ) ) {
+ $this->error( "File does not exist: $arg" );
+ exit( 1 );
+ }
+
+ $extension = $this->getExtension( $inName );
+ $outName = substr( $inName, 0, -strlen( $extension ) ) . 'min.' . $extension;
+ if ( $outDir === false ) {
+ $outPath = $inDir . '/' . $outName;
+ } else {
+ $outPath = $outDir . '/' . $outName;
+ }
+
+ $this->minify( $inPath, $outPath );
+ }
+ }
+
+ public function getExtension( $fileName ) {
+ $dotPos = strrpos( $fileName, '.' );
+ if ( $dotPos === false ) {
+ $this->error( "No file extension, cannot determine type: $arg" );
+ exit( 1 );
+ }
+ return substr( $fileName, $dotPos + 1 );
+ }
+
+ public function minify( $inPath, $outPath ) {
+ $extension = $this->getExtension( $inPath );
+ echo basename( $inPath ) . ' -> ' . basename( $outPath ) . '...';
+
+ $inText = file_get_contents( $inPath );
+ if ( $inText === false ) {
+ $this->error( "Unable to open file $inPath for reading." );
+ exit( 1 );
+ }
+ $outFile = fopen( $outPath, 'w' );
+ if ( !$outFile ) {
+ $this->error( "Unable to open file $outPath for writing." );
+ exit( 1 );
+ }
+
+ switch ( $extension ) {
+ case 'js':
+ $outText = JSMin::minify( $inText );
+ break;
+ default:
+ $this->error( "No minifier defined for extension \"$extension\"" );
+ }
+
+ fwrite( $outFile, $outText );
+ fclose( $outFile );
+ echo " ok\n";
+ }
+}
+
+$maintClass = 'MinifyScript';
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/moveBatch.php b/maintenance/moveBatch.php
index 67d513ed..d1d3193b 100644
--- a/maintenance/moveBatch.php
+++ b/maintenance/moveBatch.php
@@ -1,9 +1,22 @@
<?php
-
/**
* Maintenance script to move a batch of pages
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
* @author Tim Starling
*
@@ -20,78 +33,78 @@
* e.g. immobile_namespace for namespaces which can't be moved
*/
-$oldCwd = getcwd();
-$optionsWithArgs = array( 'u', 'r', 'i' );
-require_once( 'commandLine.inc' );
-
-chdir( $oldCwd );
-
-# Options processing
-
-$filename = 'php://stdin';
-$user = 'Move page script';
-$reason = '';
-$interval = 0;
-
-if ( isset( $args[0] ) ) {
- $filename = $args[0];
-}
-if ( isset( $options['u'] ) ) {
- $user = $options['u'];
-}
-if ( isset( $options['r'] ) ) {
- $reason = $options['r'];
-}
-if ( isset( $options['i'] ) ) {
- $interval = $options['i'];
-}
-
-$wgUser = User::newFromName( $user );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-
-# Setup complete, now start
-
-$file = fopen( $filename, 'r' );
-if ( !$file ) {
- print "Unable to read file, exiting\n";
- exit;
-}
-
-$dbw = wfGetDB( DB_MASTER );
-
-for ( $linenum = 1; !feof( $file ); $linenum++ ) {
- $line = fgets( $file );
- if ( $line === false ) {
- break;
- }
- $parts = array_map( 'trim', explode( '|', $line ) );
- if ( count( $parts ) != 2 ) {
- print "Error on line $linenum, no pipe character\n";
- continue;
+class MoveBatch extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Moves a batch of pages";
+ $this->addOption( 'u', "User to perform move", false, true );
+ $this->addOption( 'r', "Reason to move page", false, true );
+ $this->addOption( 'i', "Interval to sleep between moves" );
+ $this->addArg( 'listfile', 'List of pages to move, newline delimited', false );
}
- $source = Title::newFromText( $parts[0] );
- $dest = Title::newFromText( $parts[1] );
- if ( is_null( $source ) || is_null( $dest ) ) {
- print "Invalid title on line $linenum\n";
- continue;
+
+ public function execute() {
+ global $wgUser;
+
+ # Change to current working directory
+ $oldCwd = getcwd();
+ chdir( $oldCwd );
+
+ # Options processing
+ $user = $this->getOption( 'u', 'Move page script' );
+ $reason = $this->getOption( 'r', '' );
+ $interval = $this->getOption( 'i', 0 );
+ if( $this->hasArg() ) {
+ $file = fopen( $this->getArg(), 'r' );
+ } else {
+ $file = $this->getStdin();
+ }
+
+ # Setup
+ if( !$file ) {
+ $this->error( "Unable to read file, exiting", true );
+ }
+ $wgUser = User::newFromName( $user );
+
+ # Setup complete, now start
+ $dbw = wfGetDB( DB_MASTER );
+ for ( $linenum = 1; !feof( $file ); $linenum++ ) {
+ $line = fgets( $file );
+ if ( $line === false ) {
+ break;
+ }
+ $parts = array_map( 'trim', explode( '|', $line ) );
+ if ( count( $parts ) != 2 ) {
+ $this->error( "Error on line $linenum, no pipe character" );
+ continue;
+ }
+ $source = Title::newFromText( $parts[0] );
+ $dest = Title::newFromText( $parts[1] );
+ if ( is_null( $source ) || is_null( $dest ) ) {
+ $this->error( "Invalid title on line $linenum" );
+ continue;
+ }
+
+
+ $this->output( $source->getPrefixedText() . ' --> ' . $dest->getPrefixedText() );
+ $dbw->begin();
+ $err = $source->moveTo( $dest, false, $reason );
+ if( $err !== true ) {
+ $msg = array_shift( $err[0] );
+ $this->output( "\nFAILED: " . wfMsg( $msg, $err[0] ) );
+ }
+ $dbw->commit();
+ $this->output( "\n" );
+
+ if ( $interval ) {
+ sleep( $interval );
+ }
+ wfWaitForSlaves( 5 );
+ }
}
-
-
- print $source->getPrefixedText() . ' --> ' . $dest->getPrefixedText();
- $dbw->begin();
- $err = $source->moveTo( $dest, false, $reason );
- if( $err !== true ) {
- $msg = array_shift( $err[0] );
- print "\nFAILED: " . wfMsg( $msg, $err[0] );
- }
- $dbw->immediateCommit();
- print "\n";
-
- if ( $interval ) {
- sleep( $interval );
- }
- wfWaitForSlaves( 5 );
}
-
-
+$maintClass = "MoveBatch";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/mwdocgen.php b/maintenance/mwdocgen.php
index 378229e0..b91922c3 100644
--- a/maintenance/mwdocgen.php
+++ b/maintenance/mwdocgen.php
@@ -61,6 +61,7 @@ $mwPathS = $mwPath.'skins/';
/** Variable to get user input */
$input = '';
+$exclude = '';
#
# Functions
@@ -128,8 +129,10 @@ function getSvnRevision( $dir ) {
* @param $currentVersion String: Version number of the software
* @param $svnstat String: path to the svnstat file
* @param $input String: Path to analyze.
+ * @param $exclude String: Additionals path regex to exlcude
+ * (LocalSettings.php, AdminSettings.php and .svn directories are always excluded)
*/
-function generateConfigFile( $doxygenTemplate, $outputDirectory, $stripFromPath, $currentVersion, $svnstat, $input ){
+function generateConfigFile( $doxygenTemplate, $outputDirectory, $stripFromPath, $currentVersion, $svnstat, $input, $exclude ){
global $tmpPath;
$template = file_get_contents( $doxygenTemplate );
@@ -141,6 +144,7 @@ function generateConfigFile( $doxygenTemplate, $outputDirectory, $stripFromPath,
'{{CURRENT_VERSION}}' => $currentVersion,
'{{SVNSTAT}}' => $svnstat,
'{{INPUT}}' => $input,
+ '{{EXCLUDE}}' => $exclude,
);
$tmpCfg = str_replace( array_keys( $replacements ), array_values( $replacements ), $template );
$tmpFileName = $tmpPath . 'mwdocgen'. rand() .'.tmp';
@@ -168,6 +172,7 @@ if( is_array( $argv ) && isset( $argv[1] ) ) {
$file = $argv[2];
}
break;
+ case '--no-extensions': $input = 6; break;
}
}
@@ -182,6 +187,7 @@ Several documentation possibilities:
3 : only maintenance
4 : only skins
5 : only a given file
+ 6 : all but the extensions directory
OPTIONS;
while ( !is_numeric($input) )
{
@@ -203,6 +209,9 @@ case 5:
$file = readaline( "Enter file name $mwPath" );
}
$input = $mwPath.$file;
+case 6:
+ $input = $mwPath;
+ $exclude = 'extensions';
}
$versionNumber = getSvnRevision( $input );
@@ -213,7 +222,7 @@ if( $versionNumber === false ){ #Not using subversion ?
$version = "trunk (r$versionNumber)";
}
-$generatedConf = generateConfigFile( $doxygenTemplate, $doxyOutput, $mwPath, $version, $svnstat, $input );
+$generatedConf = generateConfigFile( $doxygenTemplate, $doxyOutput, $mwPath, $version, $svnstat, $input, $exclude );
$command = $doxygenBin . ' ' . $generatedConf;
echo <<<TEXT
@@ -233,6 +242,6 @@ echo <<<TEXT
Doxygen execution finished.
Check above for possible errors.
-You might want to deleted the temporary file $generatedConf
+You might want to delete the temporary file $generatedConf
TEXT;
diff --git a/maintenance/namespace2sql.php b/maintenance/namespace2sql.php
deleted file mode 100644
index 43a8cc64..00000000
--- a/maintenance/namespace2sql.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-/**
- * Print SQL to insert namespace names into database.
- * This source code is in the public domain.
- *
- * @file
- * @ingroup Maintenance
- */
-
-require_once( "commandLine.inc" );
-
-for ($i = -2; $i < 16; ++$i) {
- $nsname = mysql_escape_string( $wgLang->getNsText( $i ) );
- $dbname = mysql_escape_string( $wgDBname );
- print "INSERT INTO ns_name(ns_db, ns_num, ns_name) VALUES('$dbname', $i, '$nsname');\n";
-}
-
-
diff --git a/maintenance/namespaceDupes.php b/maintenance/namespaceDupes.php
index c5b3ce96..dd29558c 100644
--- a/maintenance/namespaceDupes.php
+++ b/maintenance/namespaceDupes.php
@@ -1,54 +1,71 @@
<?php
-# Copyright (C) 2005-2007 Brion Vibber <brion@pobox.com>
-# http://www.mediawiki.org/
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-# http://www.gnu.org/copyleft/gpl.html
-
/**
- * @file
+ * Check for articles to fix after adding/deleting namespaces
+ *
+ * Copyright (C) 2005-2007 Brion Vibber <brion@pobox.com>
+ * http://www.mediawiki.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
*/
-$options = array( 'fix', 'suffix', 'help' );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-/** */
-require_once( 'commandLine.inc' );
+class NamespaceConflictChecker extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "";
+ $this->addOption( 'fix', 'Attempt to automatically fix errors' );
+ $this->addOption( 'suffix', "Dupes will be renamed with correct namespace with\n" .
+ "\t\t<text> Appended after the article name", false, true );
+ $this->addOption( 'prefix', "Do an explicit check for the given title prefix\n" .
+ "\t\tappended after the article name", false, true );
+ }
-if(isset( $options['help'] ) ) {
-print <<<ENDS
-usage: namespaceDupes.php [--fix] [--suffix=<text>] [--help]
- --help : this help message
- --fix : attempt to automatically fix errors
- --suffix=<text> : dupes will be renamed with correct namespace with <text>
- appended after the article name.
- --prefix=<text> : Do an explicit check for the given title prefix
- in place of the standard namespace list.
- --verbose : Display output for checked namespaces without conflicts
+ public function execute() {
+ global $wgTitle;
-ENDS;
-die;
-}
+ $this->db = wfGetDB( DB_MASTER );
+ $wgTitle = Title::newFromText( 'Namespace title conflict cleanup script' );
-class NamespaceConflictChecker {
- function NamespaceConflictChecker( $db, $verbose=false ) {
- $this->db = $db;
- $this->verbose = $verbose;
+ $fix = $this->hasOption( 'fix' );
+ $suffix = $this->getOption( 'suffix', '' );
+ $prefix = $this->getOption( 'prefix', '' );
+ $key = intval( $this->getOption( 'key', 0 ) );
+
+ if( $prefix ) {
+ $retval = $this->checkPrefix( $key, $prefix, $fix, $suffix );
+ } else {
+ $retval = $this->checkAll( $fix, $suffix );
+ }
+
+ if( $retval ) {
+ $this->output( "\nLooks good!\n" );
+ } else {
+ $this->output( "\nOh noeees\n" );
+ }
}
- function checkAll( $fix, $suffix = '' ) {
+ /**
+ * @todo Document
+ * @param $fix bool Whether or not to fix broken entries
+ * @param $suffix String Suffix to append to renamed articles
+ */
+ private function checkAll( $fix, $suffix = '' ) {
global $wgContLang, $wgNamespaceAliases, $wgCanonicalNamespaceNames;
global $wgCapitalLinks;
@@ -76,7 +93,7 @@ class NamespaceConflictChecker {
foreach( $wgNamespaceAliases as $name => $ns ) {
$spaces[$name] = $ns;
}
- foreach( $wgContLang->namespaceAliases as $name => $ns ) {
+ foreach( $wgContLang->getNamespaceAliases() as $name => $ns ) {
$spaces[$name] = $ns;
}
@@ -112,35 +129,36 @@ class NamespaceConflictChecker {
}
return $ok;
}
-
+
+ /**
+ * Get the interwiki list
+ * @todo Needs to respect interwiki cache!
+ * @return array
+ */
private function getInterwikiList() {
$result = $this->db->select( 'interwiki', array( 'iw_prefix' ) );
- while( $row = $this->db->fetchObject( $result ) ) {
+ $prefixes = array();
+ foreach( $result as $row ) {
$prefixes[] = $row->iw_prefix;
}
$this->db->freeResult( $result );
return $prefixes;
}
- function checkNamespace( $ns, $name, $fix, $suffix = '' ) {
- if( $ns == 0 ) {
- $header = "Checking interwiki prefix: \"$name\"\n";
- } else {
- $header = "Checking namespace $ns: \"$name\"\n";
- }
-
+ /**
+ * @todo Document
+ * @param $ns int A namespace id
+ * @param $name String
+ * @param $fix bool Whether to fix broken entries
+ * @param $suffix String Suffix to append to renamed articles
+ */
+ private function checkNamespace( $ns, $name, $fix, $suffix = '' ) {
$conflicts = $this->getConflicts( $ns, $name );
$count = count( $conflicts );
if( $count == 0 ) {
- if( $this->verbose ) {
- echo $header;
- echo "... no conflicts detected!\n";
- }
return true;
}
- echo $header;
- echo "... $count conflicts detected:\n";
$ok = true;
foreach( $conflicts as $row ) {
$resolvable = $this->reportConflict( $row, $suffix );
@@ -155,23 +173,28 @@ class NamespaceConflictChecker {
/**
* @todo: do this for reals
*/
- function checkPrefix( $key, $prefix, $fix, $suffix = '' ) {
- echo "Checking prefix \"$prefix\" vs namespace $key\n";
+ private function checkPrefix( $key, $prefix, $fix, $suffix = '' ) {
+ $this->output( "Checking prefix \"$prefix\" vs namespace $key\n" );
return $this->checkNamespace( $key, $prefix, $fix, $suffix );
}
- function getConflicts( $ns, $name ) {
+ /**
+ * Find pages in mainspace that have a prefix of the new namespace
+ * so we know titles that will need migrating
+ * @param $ns int Namespace id (id for new namespace?)
+ * @param $name String Prefix that is being made a namespace
+ */
+ private function getConflicts( $ns, $name ) {
$page = 'page';
$table = $this->db->tableName( $page );
$prefix = $this->db->strencode( $name );
- $likeprefix = str_replace( '_', '\\_', $prefix);
$encNamespace = $this->db->addQuotes( $ns );
$titleSql = "TRIM(LEADING '$prefix:' FROM {$page}_title)";
if( $ns == 0 ) {
// An interwiki; try an alternate encoding with '-' for ':'
- $titleSql = "CONCAT('$prefix-',$titleSql)";
+ $titleSql = $this->db->buildConcat( array( "'$prefix-'", $titleSql ) );
}
$sql = "SELECT {$page}_id AS id,
@@ -180,12 +203,12 @@ class NamespaceConflictChecker {
$titleSql AS title
FROM {$table}
WHERE {$page}_namespace=0
- AND {$page}_title LIKE '$likeprefix:%'";
+ AND {$page}_title " . $this->db->buildLike( $name . ':', $this->db->anyString() );
- $result = $this->db->query( $sql, 'NamespaceConflictChecker::getConflicts' );
+ $result = $this->db->query( $sql, __METHOD__ );
$set = array();
- while( $row = $this->db->fetchObject( $result ) ) {
+ foreach( $result as $row ) {
$set[] = $row;
}
$this->db->freeResult( $result );
@@ -193,104 +216,89 @@ class NamespaceConflictChecker {
return $set;
}
- function reportConflict( $row, $suffix ) {
+ /**
+ * Report any conflicts we find
+ */
+ private function reportConflict( $row, $suffix ) {
$newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
if( is_null($newTitle) || !$newTitle->canExist() ) {
// Title is also an illegal title...
// For the moment we'll let these slide to cleanupTitles or whoever.
- printf( "... %d (0,\"%s\")\n",
+ $this->output( sprintf( "... %d (0,\"%s\")\n",
$row->id,
- $row->oldtitle );
- echo "... *** cannot resolve automatically; illegal title ***\n";
+ $row->oldtitle ) );
+ $this->output( "... *** cannot resolve automatically; illegal title ***\n" );
return false;
}
-
- printf( "... %d (0,\"%s\") -> (%d,\"%s\") [[%s]]\n",
+
+ $this->output( sprintf( "... %d (0,\"%s\") -> (%d,\"%s\") [[%s]]\n",
$row->id,
$row->oldtitle,
$newTitle->getNamespace(),
$newTitle->getDBkey(),
- $newTitle->getPrefixedText() );
+ $newTitle->getPrefixedText() ) );
$id = $newTitle->getArticleId();
if( $id ) {
- echo "... *** cannot resolve automatically; page exists with ID $id ***\n";
+ $this->output( "... *** cannot resolve automatically; page exists with ID $id ***\n" );
return false;
} else {
return true;
}
}
- function resolveConflict( $row, $resolvable, $suffix ) {
+ /**
+ * Resolve any conflicts
+ * @param $row Row from the page table to fix
+ * @param $resolveable bool
+ * @param $suffix String Suffix to append to the fixed page
+ */
+ private function resolveConflict( $row, $resolvable, $suffix ) {
if( !$resolvable ) {
- echo "... *** old title {$row->title}\n";
+ $this->output( "... *** old title {$row->title}\n" );
while( true ) {
$row->title .= $suffix;
- echo "... *** new title {$row->title}\n";
+ $this->output( "... *** new title {$row->title}\n" );
$title = Title::makeTitleSafe( $row->namespace, $row->title );
if ( ! $title ) {
- echo "... !!! invalid title\n";
+ $this->output( "... !!! invalid title\n" );
return false;
}
if ( $id = $title->getArticleId() ) {
- echo "... *** page exists with ID $id ***\n";
+ $this->output( "... *** page exists with ID $id ***\n" );
} else {
break;
}
}
- echo "... *** using suffixed form [[" . $title->getPrefixedText() . "]] ***\n";
- }
- $tables = array( 'page' );
- foreach( $tables as $table ) {
- $this->resolveConflictOn( $row, $table );
+ $this->output( "... *** using suffixed form [[" . $title->getPrefixedText() . "]] ***\n" );
}
+ $this->resolveConflictOn( $row, 'page', 'page' );
return true;
}
- function resolveConflictOn( $row, $table ) {
- echo "... resolving on $table... ";
+ /**
+ * Resolve a given conflict
+ * @param $row Row from the old broken entry
+ * @param $table String Table to update
+ * @param $prefix String Prefix for column name, like page or ar
+ */
+ private function resolveConflictOn( $row, $table, $prefix ) {
+ $this->output( "... resolving on $table... " );
$newTitle = Title::makeTitleSafe( $row->namespace, $row->title );
$this->db->update( $table,
array(
- "{$table}_namespace" => $newTitle->getNamespace(),
- "{$table}_title" => $newTitle->getDBkey(),
+ "{$prefix}_namespace" => $newTitle->getNamespace(),
+ "{$prefix}_title" => $newTitle->getDBkey(),
),
array(
- "{$table}_namespace" => 0,
- "{$table}_title" => $row->oldtitle,
+ "{$prefix}_namespace" => 0,
+ "{$prefix}_title" => $row->oldtitle,
),
__METHOD__ );
- echo "ok.\n";
+ $this->output( "ok.\n" );
return true;
}
}
-
-
-
-$wgTitle = Title::newFromText( 'Namespace title conflict cleanup script' );
-
-$verbose = isset( $options['verbose'] );
-$fix = isset( $options['fix'] );
-$suffix = isset( $options['suffix'] ) ? $options['suffix'] : '';
-$prefix = isset( $options['prefix'] ) ? $options['prefix'] : '';
-$key = isset( $options['key'] ) ? intval( $options['key'] ) : 0;
-
-$dbw = wfGetDB( DB_MASTER );
-$duper = new NamespaceConflictChecker( $dbw, $verbose );
-
-if( $prefix ) {
- $retval = $duper->checkPrefix( $key, $prefix, $fix, $suffix );
-} else {
- $retval = $duper->checkAll( $fix, $suffix );
-}
-
-if( $retval ) {
- echo "\nLooks good!\n";
- exit( 0 );
-} else {
- echo "\nOh noeees\n";
- exit( -1 );
-}
-
-
+$maintClass = "NamespaceConflictChecker";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/nextJobDB.php b/maintenance/nextJobDB.php
index 6af5cbec..75855bb3 100644
--- a/maintenance/nextJobDB.php
+++ b/maintenance/nextJobDB.php
@@ -2,59 +2,102 @@
/**
* Pick a database that has pending jobs
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @todo Make this work on PostgreSQL and maybe other database servers
* @ingroup Maintenance
*/
-$options = array( 'type' );
-
-require_once( 'commandLine.inc' );
-
-$type = isset($options['type'])
- ? $options['type']
- : false;
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-$mckey = $type === false
- ? "jobqueue:dbs"
- : "jobqueue:dbs:$type";
-
-$pendingDBs = $wgMemc->get( $mckey );
-if ( !$pendingDBs ) {
- $pendingDBs = array();
- # Cross-reference DBs by master DB server
- $dbsByMaster = array();
- foreach ( $wgLocalDatabases as $db ) {
- $lb = wfGetLB( $db );
- $dbsByMaster[$lb->getServerName(0)][] = $db;
+class nextJobDB extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Pick a database that has pending jobs";
+ $this->addOption( 'type', "The type of job to search for", false, true );
}
- foreach ( $dbsByMaster as $master => $dbs ) {
- $dbConn = wfGetDB( DB_MASTER, array(), $dbs[0] );
- $stype = $dbConn->addQuotes($type);
+ public function execute() {
+ global $wgMemc;
+ $type = $this->getOption( 'type', false );
+ $mckey = $type === false
+ ? "jobqueue:dbs"
+ : "jobqueue:dbs:$type";
+ $pendingDBs = $wgMemc->get( $mckey );
- # Padding row for MySQL bug
- $sql = "(SELECT '-------------------------------------------')";
- foreach ( $dbs as $dbName ) {
- if ( $sql != '' ) {
- $sql .= ' UNION ';
- }
- if ($type === false)
- $sql .= "(SELECT '$dbName' FROM `$dbName`.job LIMIT 1)";
- else
- $sql .= "(SELECT '$dbName' FROM `$dbName`.job WHERE job_cmd=$stype LIMIT 1)";
+ # If we didn't get it from the cache
+ if( !$pendingDBs ) {
+ $pendingDBs = $this->getPendingDbs( $type );
+ $wgMemc->get( $mckey, $pendingDBs, 300 );
}
- $res = $dbConn->query( $sql, 'nextJobDB.php' );
- $row = $dbConn->fetchRow( $res ); // discard padding row
- while ( $row = $dbConn->fetchRow( $res ) ) {
- $pendingDBs[] = $row[0];
+ # If we've got a pending job in a db, display it.
+ if ( $pendingDBs ) {
+ $this->output( $pendingDBs[mt_rand(0, count( $pendingDBs ) - 1)] );
}
}
- $wgMemc->set( $mckey, $pendingDBs, 300 );
-}
+ /**
+ * Get all databases that have a pending job
+ * @param $type String Job type
+ * @return array
+ */
+ private function getPendingDbs( $type ) {
+ global $wgLocalDatabases;
+ $pendingDBs = array();
+ # Cross-reference DBs by master DB server
+ $dbsByMaster = array();
+ foreach ( $wgLocalDatabases as $db ) {
+ $lb = wfGetLB( $db );
+ $dbsByMaster[$lb->getServerName(0)][] = $db;
+ }
-if ( $pendingDBs ) {
- echo $pendingDBs[mt_rand(0, count( $pendingDBs ) - 1)];
-}
+ foreach ( $dbsByMaster as $master => $dbs ) {
+ $dbConn = wfGetDB( DB_MASTER, array(), $dbs[0] );
+ $stype = $dbConn->addQuotes( $type );
+
+ # Padding row for MySQL bug
+ $sql = "(SELECT '-------------------------------------------' as db)";
+ foreach ( $dbs as $wikiId ) {
+ if ( $sql != '' ) {
+ $sql .= ' UNION ';
+ }
+ list( $dbName, $tablePrefix ) = wfSplitWikiID( $wikiId );
+ $dbConn->tablePrefix( $tablePrefix );
+ $jobTable = $dbConn->tableName( 'job' );
+
+ if ( $type === false )
+ $sql .= "(SELECT '$wikiId' as db FROM $dbName.$jobTable LIMIT 1)";
+ else
+ $sql .= "(SELECT '$wikiId' as db FROM $dbName.$jobTable WHERE job_cmd=$stype LIMIT 1)";
+ }
+ $res = $dbConn->query( $sql, __METHOD__ );
+ $first = true;
+ foreach ( $res as $row ) {
+ if ( $first ) {
+ // discard padding row
+ $first = false;
+ continue;
+ }
+ $pendingDBs[] = $row->db;
+ }
+ }
+ return $pendingDBs;
+ }
+}
+$maintClass = "nextJobDb";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/nukeNS.php b/maintenance/nukeNS.php
index 4f9fe926..21e921fe 100644
--- a/maintenance/nukeNS.php
+++ b/maintenance/nukeNS.php
@@ -13,96 +13,102 @@
* back up your DB if there's anything in the MediaWiki that is important to
* you.
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
* @author Steve Sanbeg
* based on nukePage by Rob Church
*/
-require_once( 'commandLine.inc' );
-require_once( 'nukePage.inc' );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-$ns = NS_MEDIAWIKI;
-$delete = false;
+class NukeNS extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Remove pages with only 1 revision from any namespace";
+ $this->addOption( 'delete', "Actually delete the page" );
+ $this->addOption( 'ns', 'Namespace to delete from, default NS_MEDIAWIKI', false, true );
+ }
-if (isset($options['ns']))
-{
- $ns = $options['ns'];
-}
+ public function execute() {
+ $ns = $this->getOption( 'ns', NS_MEDIAWIKI );
+ $delete = $this->getOption( 'delete', false );
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin();
-if (isset( $options['delete'] ) and $options['delete'])
-{
- $delete = true;
-}
+ $tbl_pag = $dbw->tableName( 'page' );
+ $tbl_rev = $dbw->tableName( 'revision' );
+ $res = $dbw->query( "SELECT page_title FROM $tbl_pag WHERE page_namespace = $ns" );
+ $n_deleted = 0;
-NukeNS( $ns, $delete);
+ foreach( $res as $row ) {
+ //echo "$ns_name:".$row->page_title, "\n";
+ $title = Title::makeTitle( $ns, $row->page_title );
+ $id = $title->getArticleID();
-function NukeNS($ns_no, $delete) {
+ // Get corresponding revisions
+ $res2 = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_page = $id" );
+ $revs = array();
- $dbw = wfGetDB( DB_MASTER );
- $dbw->begin();
-
- $tbl_pag = $dbw->tableName( 'page' );
- $tbl_rev = $dbw->tableName( 'revision' );
- $res = $dbw->query( "SELECT page_title FROM $tbl_pag WHERE page_namespace = $ns_no" );
+ foreach( $res2 as $row2 ) {
+ $revs[] = $row2->rev_id;
+ }
+ $count = count( $revs );
- $n_deleted = 0;
-
- while( $row = $dbw->fetchObject( $res ) ) {
- //echo "$ns_name:".$row->page_title, "\n";
- $title = Title::newFromText($row->page_title, $ns_no);
- $id = $title->getArticleID();
+ //skip anything that looks modified (i.e. multiple revs)
+ if ( $count == 1 ) {
+ #echo $title->getPrefixedText(), "\t", $count, "\n";
+ $this->output( "delete: " . $title->getPrefixedText() . "\n" );
- // Get corresponding revisions
- $res2 = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_page = $id" );
- $revs = array();
-
- while( $row2 = $dbw->fetchObject( $res2 ) ) {
- $revs[] = $row2->rev_id;
- }
- $count = count( $revs );
+ //as much as I hate to cut & paste this, it's a little different, and
+ //I already have the id & revs
+ if( $delete ) {
+ $dbw->query( "DELETE FROM $tbl_pag WHERE page_id = $id" );
+ $dbw->commit();
+ // Delete revisions as appropriate
+ $child = $this->runChild( 'NukePage', 'NukePage.php' );
+ $child->deleteRevisions( $revs );
+ $this->purgeRedundantText( true );
+ $n_deleted ++;
+ }
+ } else {
+ $this->output( "skip: " . $title->getPrefixedText() . "\n" );
+ }
+ }
+ $dbw->commit();
- //skip anything that looks modified (i.e. multiple revs)
- if (($count == 1)) {
- #echo $title->getPrefixedText(), "\t", $count, "\n";
- echo "delete: ", $title->getPrefixedText(), "\n";
-
- //as much as I hate to cut & paste this, it's a little different, and
- //I already have the id & revs
-
- if( $delete ) {
- $dbw->query( "DELETE FROM $tbl_pag WHERE page_id = $id" );
- $dbw->commit();
- // Delete revisions as appropriate
- DeleteRevisions( $revs );
- PurgeRedundantText( true );
- $n_deleted ++;
- }
- } else {
- echo "skip: ", $title->getPrefixedText(), "\n";
- }
-
-
- }
- $dbw->commit();
-
- if ($n_deleted > 0) {
- #update statistics - better to decrement existing count, or just count
- #the page table?
- $pages = $dbw->selectField('site_stats', 'ss_total_pages');
- $pages -= $n_deleted;
- $dbw->update( 'site_stats',
- array('ss_total_pages' => $pages ),
- array( 'ss_row_id' => 1),
- __METHOD__ );
-
- }
-
- if (!$delete) {
- echo( "To update the database, run the script with the --delete option.\n" );
- }
-
+ if ( $n_deleted > 0 ) {
+ #update statistics - better to decrement existing count, or just count
+ #the page table?
+ $pages = $dbw->selectField( 'site_stats', 'ss_total_pages' );
+ $pages -= $n_deleted;
+ $dbw->update(
+ 'site_stats',
+ array( 'ss_total_pages' => $pages ),
+ array( 'ss_row_id' => 1 ),
+ __METHOD__
+ );
+ }
+
+ if ( !$delete ) {
+ $this->output( "To update the database, run the script with the --delete option.\n" );
+ }
+ }
}
-
+$maintClass = "NukeNS";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/nukePage.inc b/maintenance/nukePage.inc
deleted file mode 100644
index a19c6df6..00000000
--- a/maintenance/nukePage.inc
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-
-/**
- * Support functions for the nukeArticle script
- *
- * @file
- * @ingroup Maintenance
- * @author Rob Church <robchur@gmail.com>
- */
-
-require_once( 'purgeOldText.inc' );
-
-function NukePage( $name, $delete = false ) {
-
- $dbw = wfGetDB( DB_MASTER );
- $dbw->begin();
-
- $tbl_pag = $dbw->tableName( 'page' );
- $tbl_rec = $dbw->tableName( 'recentchanges' );
- $tbl_rev = $dbw->tableName( 'revision' );
-
- # Get page ID
- echo( "Searching for \"$name\"..." );
- $title = Title::newFromText( $name );
- if( $title ) {
- $id = $title->getArticleID();
- $real = $title->getPrefixedText();
- $isGoodArticle = $title->isContentPage();
- echo( "found \"$real\" with ID $id.\n" );
-
- # Get corresponding revisions
- echo( "Searching for revisions..." );
- $res = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_page = $id" );
- while( $row = $dbw->fetchObject( $res ) ) {
- $revs[] = $row->rev_id;
- }
- $count = count( $revs );
- echo( "found $count.\n" );
-
- # Delete the page record and associated recent changes entries
- if( $delete ) {
- echo( "Deleting page record..." );
- $dbw->query( "DELETE FROM $tbl_pag WHERE page_id = $id" );
- echo( "done.\n" );
- echo( "Cleaning up recent changes..." );
- $dbw->query( "DELETE FROM $tbl_rec WHERE rc_cur_id = $id" );
- echo( "done.\n" );
- }
-
- $dbw->commit();
-
- # Delete revisions as appropriate
- if( $delete && $count ) {
- echo( "Deleting revisions..." );
- DeleteRevisions( $revs );
- echo( "done.\n" );
- PurgeRedundantText( true );
- }
-
- # Update stats as appropriate
- if ( $delete ) {
- echo( "Updating site stats..." );
- $ga = $isGoodArticle ? -1 : 0; // if it was good, decrement that too
- $stats = new SiteStatsUpdate( 0, -$count, $ga, -1 );
- $stats->doUpdate();
- echo( "done.\n" );
- }
-
-
- } else {
- echo( "not found in database.\n" );
- $dbw->commit();
- }
-
-}
-
-function DeleteRevisions( $ids ) {
-
- $dbw = wfGetDB( DB_MASTER );
- $dbw->begin();
-
- $tbl_rev = $dbw->tableName( 'revision' );
-
- $set = implode( ', ', $ids );
- $dbw->query( "DELETE FROM $tbl_rev WHERE rev_id IN ( $set )" );
-
- $dbw->commit();
-
-}
-
-?> \ No newline at end of file
diff --git a/maintenance/nukePage.php b/maintenance/nukePage.php
index b3bfc762..16ff2e40 100644
--- a/maintenance/nukePage.php
+++ b/maintenance/nukePage.php
@@ -1,29 +1,113 @@
<?php
-
/**
* Erase a page record from the database
* Irreversible (can't use standard undelete) and does not update link tables
*
- * @file
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
* @ingroup Maintenance
* @author Rob Church <robchur@gmail.com>
*/
-require_once( 'commandLine.inc' );
-require_once( 'nukePage.inc' );
+require_once( dirname(__FILE__) . '/Maintenance.php' );
-echo( "Erase Page Record\n\n" );
+class NukePage extends Maintenance {
+ public function __construct() {
+ parent::__construct();
+ $this->mDescription = "Remove a page record from the database";
+ $this->addOption( 'delete', "Actually delete the page" );
+ $this->addArg( 'title', 'Title to delete' );
+ }
-if( isset( $args[0] ) ) {
- NukePage( $args[0], true );
-} else {
- ShowUsage();
-}
+ public function execute() {
+
+ $name = $this->getArg();
+ $delete = $this->getOption( 'delete', false );
+
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin();
+
+ $tbl_pag = $dbw->tableName( 'page' );
+ $tbl_rec = $dbw->tableName( 'recentchanges' );
+ $tbl_rev = $dbw->tableName( 'revision' );
+
+ # Get page ID
+ $this->output( "Searching for \"$name\"..." );
+ $title = Title::newFromText( $name );
+ if( $title ) {
+ $id = $title->getArticleID();
+ $real = $title->getPrefixedText();
+ $isGoodArticle = $title->isContentPage();
+ $this->output( "found \"$real\" with ID $id.\n" );
+
+ # Get corresponding revisions
+ $this->output( "Searching for revisions..." );
+ $res = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_page = $id" );
+ foreach( $res as $row ) {
+ $revs[] = $row->rev_id;
+ }
+ $count = count( $revs );
+ $this->output( "found $count.\n" );
+
+ # Delete the page record and associated recent changes entries
+ if( $delete ) {
+ $this->output( "Deleting page record..." );
+ $dbw->query( "DELETE FROM $tbl_pag WHERE page_id = $id" );
+ $this->output( "done.\n" );
+ $this->output( "Cleaning up recent changes..." );
+ $dbw->query( "DELETE FROM $tbl_rec WHERE rc_cur_id = $id" );
+ $this->output( "done.\n" );
+ }
+
+ $dbw->commit();
+
+ # Delete revisions as appropriate
+ if( $delete && $count ) {
+ $this->output( "Deleting revisions..." );
+ $this->deleteRevisions( $revs );
+ $this->output( "done.\n" );
+ $this->purgeRedundantText( true );
+ }
+
+ # Update stats as appropriate
+ if ( $delete ) {
+ $this->output( "Updating site stats..." );
+ $ga = $isGoodArticle ? -1 : 0; // if it was good, decrement that too
+ $stats = new SiteStatsUpdate( 0, -$count, $ga, -1 );
+ $stats->doUpdate();
+ $this->output( "done.\n" );
+ }
+ } else {
+ $this->output( "not found in database.\n" );
+ $dbw->commit();
+ }
+ }
+
+ public function deleteRevisions( $ids ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin();
+
+ $tbl_rev = $dbw->tableName( 'revision' );
+
+ $set = implode( ', ', $ids );
+ $dbw->query( "DELETE FROM $tbl_rev WHERE rev_id IN ( $set )" );
-/** Show script usage information */
-function ShowUsage() {
- echo( "Remove a page record from the database.\n\n" );
- echo( "Usage: php nukePage.php <title>\n\n" );
- echo( " <title> : Page title; spaces escaped with underscores\n\n" );
+ $dbw->commit();
+ }
}
+$maintClass = "NukePage";
+require_once( DO_MAINTENANCE );
diff --git a/maintenance/ora/patch_seq_names_pre1.16.sql b/maintenance/ora/patch_seq_names_pre1.16.sql
new file mode 100644
index 00000000..5346b141
--- /dev/null
+++ b/maintenance/ora/patch_seq_names_pre1.16.sql
@@ -0,0 +1,8 @@
+-- script for renameing sequence names to conform with <table>_<field>_seq format
+RENAME rev_rev_id_val TO revision_rev_id_seq;
+RENAME text_old_id_val TO text_old_id_seq;
+RENAME category_id_seq TO category_cat_id_seq;
+RENAME ipblocks_ipb_id_val TO ipblocks_ipb_id_seq;
+RENAME rc_rc_id_seq TO recentchanges_rc_id_seq;
+RENAME log_log_id_seq TO logging_log_id_seq;
+RENAME pr_id_val TO page_restrictions_pr_id_seq; \ No newline at end of file
diff --git a/maintenance/ora/tables.sql b/maintenance/ora/tables.sql
index 6d4b8ed5..d2d1a21b 100644
--- a/maintenance/ora/tables.sql
+++ b/maintenance/ora/tables.sql
@@ -1,443 +1,794 @@
--- SQL to create the initial tables for the MediaWiki database.
--- This is read and executed by the install script; you should
--- not have to run it by itself unless doing a manual install.
--- This is the Oracle version (based on PostgreSQL schema).
--- For information about each table, please see the notes in maintenance/tables.sql
+-- defines must comply with ^define\s*([^\s=]*)\s*=\s?'\{\$([^\}]*)\}';
+define mw_prefix='{$wgDBprefix}';
-CREATE SEQUENCE user_user_id_seq MINVALUE 0 START WITH 0;
-CREATE TABLE mwuser ( -- replace reserved word 'user'
- user_id INTEGER NOT NULL PRIMARY KEY,
- user_name VARCHAR(255) NOT NULL UNIQUE,
- user_real_name CLOB,
- user_password CLOB,
- user_newpassword CLOB,
- user_newpass_time TIMESTAMP WITH TIME ZONE,
- user_token CHAR(32),
- user_email CLOB,
- user_email_token CHAR(32),
- user_email_token_expires TIMESTAMP WITH TIME ZONE,
- user_email_authenticated TIMESTAMP WITH TIME ZONE,
+CREATE SEQUENCE user_user_id_seq MINVALUE 0 START WITH 0;
+CREATE TABLE &mw_prefix.mwuser ( -- replace reserved word 'user'
+ user_id NUMBER NOT NULL,
+ user_name VARCHAR2(255) NOT NULL,
+ user_real_name VARCHAR2(512),
+ user_password VARCHAR2(255),
+ user_newpassword VARCHAR2(255),
+ user_newpass_time TIMESTAMP(6) WITH TIME ZONE,
+ user_token VARCHAR2(32),
+ user_email VARCHAR2(255),
+ user_email_token VARCHAR2(32),
+ user_email_token_expires TIMESTAMP(6) WITH TIME ZONE,
+ user_email_authenticated TIMESTAMP(6) WITH TIME ZONE,
user_options CLOB,
- user_touched TIMESTAMP WITH TIME ZONE,
- user_registration TIMESTAMP WITH TIME ZONE,
- user_editcount INTEGER
+ user_touched TIMESTAMP(6) WITH TIME ZONE,
+ user_registration TIMESTAMP(6) WITH TIME ZONE,
+ user_editcount NUMBER
);
-CREATE INDEX user_email_token_idx ON mwuser (user_email_token);
+ALTER TABLE &mw_prefix.mwuser ADD CONSTRAINT &mw_prefix.mwuser_pk PRIMARY KEY (user_id);
+CREATE UNIQUE INDEX &mw_prefix.mwuser_u01 ON &mw_prefix.mwuser (user_name);
+CREATE INDEX &mw_prefix.mwuser_i01 ON &mw_prefix.mwuser (user_email_token);
-- Create a dummy user to satisfy fk contraints especially with revisions
-INSERT INTO mwuser
+INSERT INTO &mw_prefix.mwuser
VALUES (user_user_id_seq.nextval,'Anonymous','',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL, '', current_timestamp, current_timestamp, 0);
-CREATE TABLE user_groups (
- ug_user INTEGER NULL REFERENCES mwuser(user_id) ON DELETE CASCADE,
- ug_group CHAR(16) NOT NULL
+CREATE TABLE &mw_prefix.user_groups (
+ ug_user NUMBER NULL REFERENCES &mw_prefix.mwuser(user_id) ON DELETE CASCADE,
+ ug_group VARCHAR2(16) NOT NULL
);
-CREATE UNIQUE INDEX user_groups_unique ON user_groups (ug_user, ug_group);
+CREATE UNIQUE INDEX &mw_prefix.user_groups_u01 ON &mw_prefix.user_groups (ug_user,ug_group);
+CREATE INDEX &mw_prefix.user_groups_i01 ON &mw_prefix.user_groups (ug_group);
-CREATE TABLE user_newtalk (
- user_id INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE,
- user_ip VARCHAR(40) NULL
+CREATE TABLE &mw_prefix.user_newtalk (
+ user_id NUMBER NOT NULL REFERENCES &mw_prefix.mwuser(user_id) ON DELETE CASCADE,
+ user_ip VARCHAR2(40) NULL,
+ user_last_timestamp TIMESTAMP(6) WITH TIME ZONE
);
-CREATE INDEX user_newtalk_id_idx ON user_newtalk (user_id);
-CREATE INDEX user_newtalk_ip_idx ON user_newtalk (user_ip);
+CREATE INDEX &mw_prefix.user_newtalk_i01 ON &mw_prefix.user_newtalk (user_id);
+CREATE INDEX &mw_prefix.user_newtalk_i02 ON &mw_prefix.user_newtalk (user_ip);
-CREATE SEQUENCE page_page_id_seq;
-CREATE TABLE page (
- page_id INTEGER NOT NULL PRIMARY KEY,
- page_namespace SMALLINT NOT NULL,
- page_title VARCHAR(255) NOT NULL,
- page_restrictions CLOB,
- page_counter INTEGER DEFAULT 0 NOT NULL,
- page_is_redirect CHAR DEFAULT 0 NOT NULL,
- page_is_new CHAR DEFAULT 0 NOT NULL,
- page_random NUMERIC(15,14) NOT NULL,
- page_touched TIMESTAMP WITH TIME ZONE,
- page_latest INTEGER NOT NULL, -- FK?
- page_len INTEGER NOT NULL
-);
-CREATE UNIQUE INDEX page_unique_name ON page (page_namespace, page_title);
-CREATE INDEX page_random_idx ON page (page_random);
-CREATE INDEX page_len_idx ON page (page_len);
-
-CREATE TRIGGER page_set_random BEFORE INSERT ON page
- FOR EACH ROW WHEN (new.page_random IS NULL)
- BEGIN
- SELECT dbms_random.value INTO :new.page_random FROM dual;
- END;
-/
-
-CREATE SEQUENCE rev_rev_id_val;
-CREATE TABLE revision (
- rev_id INTEGER NOT NULL PRIMARY KEY,
- rev_page INTEGER NULL REFERENCES page (page_id) ON DELETE CASCADE,
- rev_text_id INTEGER NULL, -- FK
- rev_comment CLOB,
- rev_user INTEGER NOT NULL REFERENCES mwuser(user_id),
- rev_user_text VARCHAR(255) NOT NULL,
- rev_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
- rev_minor_edit CHAR DEFAULT '0' NOT NULL,
- rev_deleted CHAR DEFAULT '0' NOT NULL,
- rev_len INTEGER NULL,
- rev_parent_id INTEGER DEFAULT NULL
-);
-CREATE UNIQUE INDEX revision_unique ON revision (rev_page, rev_id);
-CREATE INDEX rev_text_id_idx ON revision (rev_text_id);
-CREATE INDEX rev_timestamp_idx ON revision (rev_timestamp);
-CREATE INDEX rev_user_idx ON revision (rev_user);
-CREATE INDEX rev_user_text_idx ON revision (rev_user_text);
-
-
-CREATE SEQUENCE text_old_id_val;
-CREATE TABLE pagecontent ( -- replaces reserved word 'text'
- old_id INTEGER NOT NULL PRIMARY KEY,
- old_text CLOB,
- old_flags CLOB
+CREATE TABLE &mw_prefix.user_properties (
+ up_user NUMBER NOT NULL,
+ up_property VARCHAR2(32) NOT NULL,
+ up_value CLOB
);
+CREATE UNIQUE INDEX &mw_prefix.user_properties_u01 on &mw_prefix.user_properties (up_user,up_property);
+CREATE INDEX &mw_prefix.user_properties_i01 on &mw_prefix.user_properties (up_property);
-CREATE SEQUENCE pr_id_val;
-CREATE TABLE page_restrictions (
- pr_id INTEGER NOT NULL UNIQUE,
- pr_page INTEGER NULL REFERENCES page (page_id) ON DELETE CASCADE,
- pr_type VARCHAR(255) NOT NULL,
- pr_level VARCHAR(255) NOT NULL,
- pr_cascade SMALLINT NOT NULL,
- pr_user INTEGER NULL,
- pr_expiry TIMESTAMP WITH TIME ZONE NULL
+CREATE SEQUENCE page_page_id_seq;
+CREATE TABLE &mw_prefix.page (
+ page_id NUMBER NOT NULL,
+ page_namespace NUMBER NOT NULL,
+ page_title VARCHAR2(255) NOT NULL,
+ page_restrictions VARCHAR2(255),
+ page_counter NUMBER DEFAULT 0 NOT NULL,
+ page_is_redirect CHAR(1) DEFAULT 0 NOT NULL,
+ page_is_new CHAR(1) DEFAULT 0 NOT NULL,
+ page_random NUMBER(15,14) NOT NULL,
+ page_touched TIMESTAMP(6) WITH TIME ZONE,
+ page_latest NUMBER NOT NULL, -- FK?
+ page_len NUMBER NOT NULL
+);
+ALTER TABLE &mw_prefix.page ADD CONSTRAINT &mw_prefix.page_pk PRIMARY KEY (page_id);
+CREATE UNIQUE INDEX &mw_prefix.page_u01 ON &mw_prefix.page (page_namespace,page_title);
+CREATE INDEX &mw_prefix.page_i01 ON &mw_prefix.page (page_random);
+CREATE INDEX &mw_prefix.page_i02 ON &mw_prefix.page (page_len);
+
+/*$mw$*/
+CREATE TRIGGER &mw_prefix.page_set_random BEFORE INSERT ON &mw_prefix.page
+ FOR EACH ROW WHEN (new.page_random IS NULL)
+BEGIN
+ SELECT dbms_random.value INTO :NEW.page_random FROM dual;
+END;
+/*$mw$*/
+
+CREATE SEQUENCE revision_rev_id_seq;
+CREATE TABLE &mw_prefix.revision (
+ rev_id NUMBER NOT NULL,
+ rev_page NUMBER NULL REFERENCES &mw_prefix.page (page_id) ON DELETE CASCADE,
+ rev_text_id NUMBER NULL,
+ rev_comment VARCHAR2(255),
+ rev_user NUMBER NOT NULL REFERENCES &mw_prefix.mwuser(user_id),
+ rev_user_text VARCHAR2(255) NOT NULL,
+ rev_timestamp TIMESTAMP(6) WITH TIME ZONE NOT NULL,
+ rev_minor_edit CHAR(1) DEFAULT '0' NOT NULL,
+ rev_deleted CHAR(1) DEFAULT '0' NOT NULL,
+ rev_len NUMBER NULL,
+ rev_parent_id NUMBER DEFAULT NULL
+);
+ALTER TABLE &mw_prefix.revision ADD CONSTRAINT &mw_prefix.revision_pk PRIMARY KEY (rev_id);
+CREATE UNIQUE INDEX &mw_prefix.revision_u01 ON &mw_prefix.revision (rev_page, rev_id);
+CREATE INDEX &mw_prefix.revision_i01 ON &mw_prefix.revision (rev_timestamp);
+CREATE INDEX &mw_prefix.revision_i02 ON &mw_prefix.revision (rev_page,rev_timestamp);
+CREATE INDEX &mw_prefix.revision_i03 ON &mw_prefix.revision (rev_user,rev_timestamp);
+CREATE INDEX &mw_prefix.revision_i04 ON &mw_prefix.revision (rev_user_text,rev_timestamp);
+
+CREATE SEQUENCE text_old_id_seq;
+CREATE TABLE &mw_prefix.pagecontent ( -- replaces reserved word 'text'
+ old_id NUMBER NOT NULL,
+ old_text CLOB,
+ old_flags VARCHAR2(255)
);
-ALTER TABLE page_restrictions ADD CONSTRAINT page_restrictions_pk PRIMARY KEY (pr_page,pr_type);
+ALTER TABLE &mw_prefix.pagecontent ADD CONSTRAINT &mw_prefix.pagecontent_pk PRIMARY KEY (old_id);
-CREATE TABLE archive (
- ar_namespace SMALLINT NOT NULL,
- ar_title VARCHAR(255) NOT NULL,
+CREATE TABLE &mw_prefix.archive (
+ ar_namespace NUMBER NOT NULL,
+ ar_title VARCHAR2(255) NOT NULL,
ar_text CLOB,
- ar_comment CLOB,
- ar_user INTEGER NULL REFERENCES mwuser(user_id) ON DELETE SET NULL,
- ar_user_text CLOB NOT NULL,
- ar_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
- ar_minor_edit CHAR DEFAULT '0' NOT NULL,
- ar_flags CLOB,
- ar_rev_id INTEGER,
- ar_text_id INTEGER,
- ar_deleted INTEGER DEFAULT '0' NOT NULL
-);
-CREATE INDEX archive_name_title_timestamp ON archive (ar_namespace,ar_title,ar_timestamp);
-
-CREATE TABLE redirect (
- rd_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
- rd_namespace SMALLINT NOT NULL,
- rd_title VARCHAR(255) NOT NULL
-);
-CREATE INDEX redirect_ns_title ON redirect (rd_namespace,rd_title,rd_from);
-
-
-CREATE TABLE pagelinks (
- pl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
- pl_namespace SMALLINT NOT NULL,
- pl_title VARCHAR(255) NOT NULL
-);
-CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title);
-
-CREATE TABLE templatelinks (
- tl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
- tl_namespace INTEGER NOT NULL,
- tl_title VARCHAR(255) NOT NULL
-);
-CREATE UNIQUE INDEX templatelinks_unique ON templatelinks (tl_namespace,tl_title,tl_from);
-
-CREATE TABLE imagelinks (
- il_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
- il_to VARCHAR(255) NOT NULL
-);
-CREATE UNIQUE INDEX il_from ON imagelinks (il_to,il_from);
-
-CREATE TABLE categorylinks (
- cl_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
- cl_to VARCHAR(255) NOT NULL,
- cl_sortkey VARCHAR(86),
- cl_timestamp TIMESTAMP WITH TIME ZONE NOT NULL
-);
-CREATE UNIQUE INDEX cl_from ON categorylinks (cl_from, cl_to);
-CREATE INDEX cl_sortkey ON categorylinks (cl_to, cl_sortkey);
-
-CREATE TABLE externallinks (
- el_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE,
- el_to VARCHAR(2048) NOT NULL,
- el_index CLOB NOT NULL
-);
--- XXX CREATE INDEX externallinks_from_to ON externallinks (el_from,el_to);
--- XXX CREATE INDEX externallinks_index ON externallinks (el_index);
-
-CREATE TABLE langlinks (
- ll_from INTEGER NOT NULL REFERENCES page (page_id) ON DELETE CASCADE,
- ll_lang VARCHAR(10),
- ll_title VARCHAR(255)
-);
-CREATE UNIQUE INDEX langlinks_unique ON langlinks (ll_from,ll_lang);
-CREATE INDEX langlinks_lang_title ON langlinks (ll_lang,ll_title);
-
-
-CREATE TABLE site_stats (
- ss_row_id INTEGER NOT NULL UNIQUE,
- ss_total_views INTEGER DEFAULT 0,
- ss_total_edits INTEGER DEFAULT 0,
- ss_good_articles INTEGER DEFAULT 0,
- ss_total_pages INTEGER DEFAULT -1,
- ss_users INTEGER DEFAULT -1,
- ss_admins INTEGER DEFAULT -1,
- ss_images INTEGER DEFAULT 0
-);
-
-CREATE TABLE hitcounter (
- hc_id INTEGER NOT NULL
-);
-
-
-CREATE SEQUENCE ipblocks_ipb_id_val;
-CREATE TABLE ipblocks (
- ipb_id INTEGER NOT NULL PRIMARY KEY,
- ipb_address VARCHAR(255) NULL,
- ipb_user INTEGER NULL REFERENCES mwuser(user_id) ON DELETE SET NULL,
- ipb_by INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE,
- ipb_reason VARCHAR(255) NOT NULL,
- ipb_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
- ipb_auto CHAR DEFAULT '0' NOT NULL,
- ipb_anon_only CHAR DEFAULT '0' NOT NULL,
- ipb_create_account CHAR DEFAULT '1' NOT NULL,
- ipb_enable_autoblock CHAR DEFAULT '1' NOT NULL,
- ipb_expiry TIMESTAMP WITH TIME ZONE NOT NULL,
- ipb_range_start CHAR(8),
- ipb_range_end CHAR(8),
- ipb_deleted INTEGER DEFAULT '0' NOT NULL
-);
-CREATE INDEX ipb_address ON ipblocks (ipb_address);
-CREATE INDEX ipb_user ON ipblocks (ipb_user);
-CREATE INDEX ipb_range ON ipblocks (ipb_range_start,ipb_range_end);
-
-
-CREATE TABLE image (
- img_name VARCHAR(255) NOT NULL PRIMARY KEY,
- img_size INTEGER NOT NULL,
- img_width INTEGER NOT NULL,
- img_height INTEGER NOT NULL,
+ ar_comment VARCHAR2(255),
+ ar_user NUMBER NULL REFERENCES &mw_prefix.mwuser(user_id) ON DELETE SET NULL,
+ ar_user_text VARCHAR2(255) NOT NULL,
+ ar_timestamp TIMESTAMP(6) WITH TIME ZONE NOT NULL,
+ ar_minor_edit CHAR(1) DEFAULT '0' NOT NULL,
+ ar_flags VARCHAR2(255),
+ ar_rev_id NUMBER,
+ ar_text_id NUMBER,
+ ar_deleted NUMBER DEFAULT '0' NOT NULL,
+ ar_len NUMBER,
+ ar_page_id NUMBER,
+ ar_parent_id NUMBER
+);
+CREATE INDEX &mw_prefix.archive_i01 ON &mw_prefix.archive (ar_namespace,ar_title,ar_timestamp);
+CREATE INDEX &mw_prefix.archive_i02 ON &mw_prefix.archive (ar_user_text,ar_timestamp);
+
+
+CREATE TABLE &mw_prefix.pagelinks (
+ pl_from NUMBER NOT NULL REFERENCES &mw_prefix.page(page_id) ON DELETE CASCADE,
+ pl_namespace NUMBER NOT NULL,
+ pl_title VARCHAR2(255) NOT NULL
+);
+CREATE UNIQUE INDEX &mw_prefix.pagelinks_u01 ON &mw_prefix.pagelinks (pl_from,pl_namespace,pl_title);
+CREATE UNIQUE INDEX &mw_prefix.pagelinks_u02 ON &mw_prefix.pagelinks (pl_namespace,pl_title,pl_from);
+
+CREATE TABLE &mw_prefix.templatelinks (
+ tl_from NUMBER NOT NULL REFERENCES &mw_prefix.page(page_id) ON DELETE CASCADE,
+ tl_namespace NUMBER NOT NULL,
+ tl_title VARCHAR2(255) NOT NULL
+);
+CREATE UNIQUE INDEX &mw_prefix.templatelinks_u01 ON &mw_prefix.templatelinks (tl_from,tl_namespace,tl_title);
+CREATE UNIQUE INDEX &mw_prefix.templatelinks_u02 ON &mw_prefix.templatelinks (tl_namespace,tl_title,tl_from);
+
+CREATE TABLE &mw_prefix.imagelinks (
+ il_from NUMBER NOT NULL REFERENCES &mw_prefix.page(page_id) ON DELETE CASCADE,
+ il_to VARCHAR2(255) NOT NULL