diff options
Diffstat (limited to 'maintenance')
67 files changed, 6097 insertions, 938 deletions
diff --git a/maintenance/FiveUpgrade.inc b/maintenance/FiveUpgrade.inc index 4bbf0733..d21d8b43 100644 --- a/maintenance/FiveUpgrade.inc +++ b/maintenance/FiveUpgrade.inc @@ -98,7 +98,7 @@ class FiveUpgrade { # http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT static $cp1252 = array( 0x80 => 0x20AC, #EURO SIGN - 0x81 => UNICODE_REPLACEMENT, + 0x81 => 0xFFFD, #REPLACEMENT CHARACTER (no mapping) 0x82 => 0x201A, #SINGLE LOW-9 QUOTATION MARK 0x83 => 0x0192, #LATIN SMALL LETTER F WITH HOOK 0x84 => 0x201E, #DOUBLE LOW-9 QUOTATION MARK @@ -110,10 +110,10 @@ class FiveUpgrade { 0x8A => 0x0160, #LATIN CAPITAL LETTER S WITH CARON 0x8B => 0x2039, #SINGLE LEFT-POINTING ANGLE QUOTATION MARK 0x8C => 0x0152, #LATIN CAPITAL LIGATURE OE - 0x8D => UNICODE_REPLACEMENT, + 0x8D => 0xFFFD, #REPLACEMENT CHARACTER (no mapping) 0x8E => 0x017D, #LATIN CAPITAL LETTER Z WITH CARON - 0x8F => UNICODE_REPLACEMENT, - 0x90 => UNICODE_REPLACEMENT, + 0x8F => 0xFFFD, #REPLACEMENT CHARACTER (no mapping) + 0x90 => 0xFFFD, #REPLACEMENT CHARACTER (no mapping) 0x91 => 0x2018, #LEFT SINGLE QUOTATION MARK 0x92 => 0x2019, #RIGHT SINGLE QUOTATION MARK 0x93 => 0x201C, #LEFT DOUBLE QUOTATION MARK @@ -126,7 +126,7 @@ class FiveUpgrade { 0x9A => 0x0161, #LATIN SMALL LETTER S WITH CARON 0x9B => 0x203A, #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK 0x9C => 0x0153, #LATIN SMALL LIGATURE OE - 0x9D => UNICODE_REPLACEMENT, + 0x9D => 0xFFFD, #REPLACEMENT CHARACTER (no mapping) 0x9E => 0x017E, #LATIN SMALL LETTER Z WITH CARON 0x9F => 0x0178, #LATIN CAPITAL LETTER Y WITH DIAERESIS ); diff --git a/maintenance/archives/patch-backlinkindexes.sql b/maintenance/archives/patch-backlinkindexes.sql new file mode 100644 index 00000000..5facd9ea --- /dev/null +++ b/maintenance/archives/patch-backlinkindexes.sql @@ -0,0 +1,19 @@ +-- +-- patch-backlinkindexes.sql +-- +-- Per bug 6440 / http://bugzilla.wikimedia.org/show_bug.cgi?id=6440 +-- +-- Improve performance of the "what links here"-type queries +-- + +ALTER TABLE /*$wgDBprefix*/pagelinks + DROP INDEX pl_namespace, + ADD INDEX pl_namespace(pl_namespace, pl_title, pl_from); + +ALTER TABLE /*$wgDBprefix*/templatelinks + DROP INDEX tl_namespace, + ADD INDEX tl_namespace(tl_namespace, tl_title, tl_from); + +ALTER TABLE /*$wgDBprefix*/imagelinks + DROP INDEX il_to, + ADD INDEX il_to(il_to, il_from); diff --git a/maintenance/archives/patch-externallinks.sql b/maintenance/archives/patch-externallinks.sql index d1aa5764..52fb5bae 100644 --- a/maintenance/archives/patch-externallinks.sql +++ b/maintenance/archives/patch-externallinks.sql @@ -3,8 +3,8 @@ -- CREATE TABLE /*$wgDBprefix*/externallinks ( el_from int(8) unsigned NOT NULL default '0', - el_to blob NOT NULL default '', - el_index blob NOT NULL default '', + el_to blob NOT NULL, + el_index blob NOT NULL, KEY (el_from, el_to(40)), KEY (el_to(60), el_from), diff --git a/maintenance/archives/patch-filearchive.sql b/maintenance/archives/patch-filearchive.sql index 4bf09366..cc50f2ae 100644 --- a/maintenance/archives/patch-filearchive.sql +++ b/maintenance/archives/patch-filearchive.sql @@ -37,7 +37,7 @@ CREATE TABLE /*$wgDBprefix*/filearchive ( fa_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL, fa_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") default "unknown", fa_minor_mime varchar(32) default "unknown", - fa_description tinyblob default '', + fa_description tinyblob, fa_user int(5) unsigned default '0', fa_user_text varchar(255) binary default '', fa_timestamp char(14) binary default '', diff --git a/maintenance/archives/patch-ipb_anon_only.sql b/maintenance/archives/patch-ipb_anon_only.sql index 709308a2..b3738168 100644 --- a/maintenance/archives/patch-ipb_anon_only.sql +++ b/maintenance/archives/patch-ipb_anon_only.sql @@ -11,17 +11,17 @@ DROP TABLE IF EXISTS /*$wgDBprefix*/ipblocks_newunique; CREATE TABLE /*$wgDBprefix*/ipblocks_newunique ( ipb_id int(8) NOT NULL auto_increment, - ipb_address tinyblob NOT NULL default '', + ipb_address tinyblob NOT NULL, ipb_user int(8) unsigned NOT NULL default '0', ipb_by int(8) unsigned NOT NULL default '0', - ipb_reason tinyblob NOT NULL default '', + ipb_reason tinyblob NOT NULL, ipb_timestamp char(14) binary NOT NULL default '', ipb_auto bool NOT NULL default 0, ipb_anon_only bool NOT NULL default 0, ipb_create_account bool NOT NULL default 1, ipb_expiry char(14) binary NOT NULL default '', - ipb_range_start tinyblob NOT NULL default '', - ipb_range_end tinyblob NOT NULL default '', + ipb_range_start tinyblob NOT NULL, + ipb_range_end tinyblob NOT NULL, PRIMARY KEY ipb_id (ipb_id), UNIQUE INDEX ipb_address_unique (ipb_address(255), ipb_user, ipb_auto), diff --git a/maintenance/archives/patch-ipb_optional_autoblock.sql b/maintenance/archives/patch-ipb_optional_autoblock.sql new file mode 100644 index 00000000..f31b8359 --- /dev/null +++ b/maintenance/archives/patch-ipb_optional_autoblock.sql @@ -0,0 +1,3 @@ +-- Add an extra option field "ipb_enable_autoblock" into the ipblocks table. This allows a block to be placed that does not trigger any autoblocks. + +ALTER TABLE /*$wgDBprefix*/ipblocks ADD COLUMN ipb_enable_autoblock bool NOT NULL default '1'; diff --git a/maintenance/archives/patch-job.sql b/maintenance/archives/patch-job.sql index 89918456..d904fbeb 100644 --- a/maintenance/archives/patch-job.sql +++ b/maintenance/archives/patch-job.sql @@ -13,7 +13,7 @@ CREATE TABLE /*$wgDBprefix*/job ( -- Any other parameters to the command -- Presently unused, format undefined - job_params blob NOT NULL default '', + job_params blob NOT NULL, PRIMARY KEY job_id (job_id), KEY (job_cmd, job_namespace, job_title) diff --git a/maintenance/archives/patch-log_params.sql b/maintenance/archives/patch-log_params.sql index aa00a673..ff6527ec 100644 --- a/maintenance/archives/patch-log_params.sql +++ b/maintenance/archives/patch-log_params.sql @@ -1 +1 @@ -ALTER TABLE /*$wgDBprefix*/logging ADD log_params blob NOT NULL default ''; +ALTER TABLE /*$wgDBprefix*/logging ADD log_params blob NOT NULL; diff --git a/maintenance/archives/patch-logging.sql b/maintenance/archives/patch-logging.sql index 79bb53b5..54146fb7 100644 --- a/maintenance/archives/patch-logging.sql +++ b/maintenance/archives/patch-logging.sql @@ -23,7 +23,7 @@ CREATE TABLE /*$wgDBprefix*/logging ( log_comment varchar(255) NOT NULL default '', -- LF separated list of miscellaneous parameters - log_params blob NOT NULL default '', + log_params blob NOT NULL, KEY type_time (log_type, log_timestamp), KEY user_time (log_user, log_timestamp), diff --git a/maintenance/archives/patch-querycachetwo.sql b/maintenance/archives/patch-querycachetwo.sql new file mode 100644 index 00000000..cda5b90d --- /dev/null +++ b/maintenance/archives/patch-querycachetwo.sql @@ -0,0 +1,22 @@ +-- Used for caching expensive grouped queries that need two links (for example double-redirects) + +CREATE TABLE /*$wgDBprefix*/querycachetwo ( + -- A key name, generally the base name of of the special page. + qcc_type char(32) NOT NULL, + + -- Some sort of stored value. Sizes, counts... + qcc_value int(5) unsigned NOT NULL default '0', + + -- Target namespace+title + qcc_namespace int NOT NULL default '0', + qcc_title char(255) binary NOT NULL default '', + + -- Target namespace+title2 + qcc_namespacetwo int NOT NULL default '0', + qcc_titletwo char(255) binary NOT NULL default '', + + KEY qcc_type (qcc_type,qcc_value), + KEY qcc_title (qcc_type,qcc_namespace,qcc_title), + KEY qcc_titletwo (qcc_type,qcc_namespacetwo,qcc_titletwo) + +) TYPE=InnoDB; diff --git a/maintenance/archives/patch-rc_len.sql b/maintenance/archives/patch-rc_len.sql new file mode 100644 index 00000000..920f755b --- /dev/null +++ b/maintenance/archives/patch-rc_len.sql @@ -0,0 +1,9 @@ +-- +-- patch-rc_len.sql +-- Adds two rows to recentchanges to hold the text size befor and after the edit +-- 2006-12-03 +-- + +ALTER TABLE /*$wgDBprefix*/recentchanges + ADD COLUMN rc_old_len int(10), ADD COLUMN rc_new_len int(10); + diff --git a/maintenance/archives/patch-rc_user_text-index.sql b/maintenance/archives/patch-rc_user_text-index.sql new file mode 100644 index 00000000..f6acc992 --- /dev/null +++ b/maintenance/archives/patch-rc_user_text-index.sql @@ -0,0 +1,7 @@ +-- Add an index to recentchanges on rc_user_text +-- +-- Added 2006-11-08 +-- + + ALTER TABLE /*$wgDBprefix*/recentchanges +ADD INDEX rc_user_text(rc_user_text, rc_timestamp);
\ No newline at end of file diff --git a/maintenance/archives/patch-redirect.sql b/maintenance/archives/patch-redirect.sql new file mode 100644 index 00000000..d377f1b1 --- /dev/null +++ b/maintenance/archives/patch-redirect.sql @@ -0,0 +1,28 @@ +-- +-- Create the new redirect table. +-- For each redirect, this table contains exactly one row defining its target +-- +CREATE TABLE /*$wgDBprefix*/redirect ( + -- Key to the page_id of the redirect page + rd_from int(8) unsigned NOT NULL default '0', + + -- Key to page_namespace/page_title of the target page. + -- The target page may or may not exist, and due to renames + -- and deletions may refer to different page records as time + -- goes by. + rd_namespace int NOT NULL default '0', + rd_title varchar(255) binary NOT NULL default '', + + PRIMARY KEY rd_from (rd_from), + KEY rd_ns_title (rd_namespace,rd_title,rd_from) +) TYPE=InnoDB; + +-- Import existing redirects +-- Using ignore because some of the redirect pages contain more than one link +INSERT IGNORE + INTO /*$wgDBprefix*/redirect (rd_from,rd_namespace,rd_title) + SELECT pl_from,pl_namespace,pl_title + FROM /*$wgDBprefix*/pagelinks, /*$wgDBprefix*/page + WHERE pl_from=page_id AND page_is_redirect=1; + + diff --git a/maintenance/archives/patch-rename-user_groups-and_rights.sql b/maintenance/archives/patch-rename-user_groups-and_rights.sql index abd59319..650f2604 100644 --- a/maintenance/archives/patch-rename-user_groups-and_rights.sql +++ b/maintenance/archives/patch-rename-user_groups-and_rights.sql @@ -5,5 +5,5 @@ ALTER TABLE /*$wgDBprefix*/user_groups ALTER TABLE /*$wgDBprefix*/user_rights CHANGE user_id ur_user INT(5) UNSIGNED NOT NULL, - CHANGE user_rights ur_rights TINYBLOB NOT NULL DEFAULT ''; + CHANGE user_rights ur_rights TINYBLOB NOT NULL; diff --git a/maintenance/archives/patch-restructure.sql b/maintenance/archives/patch-restructure.sql index 53f1836b..acf306c2 100644 --- a/maintenance/archives/patch-restructure.sql +++ b/maintenance/archives/patch-restructure.sql @@ -13,7 +13,7 @@ CREATE TABLE /*$wgDBprefix*/page ( page_id int(8) unsigned NOT NULL auto_increment, page_namespace tinyint NOT NULL, page_title varchar(255) binary NOT NULL, - page_restrictions tinyblob NOT NULL default '', + page_restrictions tinyblob NOT NULL, page_counter bigint(20) unsigned NOT NULL default '0', page_is_redirect tinyint(1) unsigned NOT NULL default '0', page_is_new tinyint(1) unsigned NOT NULL default '0', @@ -31,7 +31,7 @@ CREATE TABLE /*$wgDBprefix*/page ( CREATE TABLE /*$wgDBprefix*/revision ( rev_id int(8) unsigned NOT NULL auto_increment, rev_page int(8) unsigned NOT NULL, - rev_comment tinyblob NOT NULL default '', + rev_comment tinyblob NOT NULL, rev_user int(5) unsigned NOT NULL default '0', rev_user_text varchar(255) binary NOT NULL default '', rev_timestamp char(14) binary NOT NULL default '', @@ -51,8 +51,8 @@ CREATE TABLE /*$wgDBprefix*/revision ( -- -- CREATE TABLE /*$wgDBprefix*/text ( -- old_id int(8) unsigned NOT NULL auto_increment, --- old_text mediumtext NOT NULL default '', --- old_flags tinyblob NOT NULL default '', +-- old_text mediumtext NOT NULL, +-- old_flags tinyblob NOT NULL, -- -- PRIMARY KEY old_id (old_id) -- ); diff --git a/maintenance/archives/patch-searchindex.sql b/maintenance/archives/patch-searchindex.sql index fb54dbbe..2b9b6702 100644 --- a/maintenance/archives/patch-searchindex.sql +++ b/maintenance/archives/patch-searchindex.sql @@ -15,7 +15,7 @@ CREATE TABLE /*$wgDBprefix*/searchindex ( si_title varchar(255) NOT NULL default '', -- Munged version of body text - si_text mediumtext NOT NULL default '', + si_text mediumtext NOT NULL, UNIQUE KEY (si_page) diff --git a/maintenance/archives/patch-user_editcount.sql b/maintenance/archives/patch-user_editcount.sql new file mode 100644 index 00000000..cdde36dc --- /dev/null +++ b/maintenance/archives/patch-user_editcount.sql @@ -0,0 +1,5 @@ +ALTER TABLE /*$wgDBprefix*/user + ADD COLUMN user_editcount int; + +-- Don't initialize values immediately... or should we? +-- They will be lazy-evaluated, or batch-filled via maintenance/initEditCount.php diff --git a/maintenance/archives/patch-user_newpass_time.sql b/maintenance/archives/patch-user_newpass_time.sql new file mode 100644 index 00000000..47b332ba --- /dev/null +++ b/maintenance/archives/patch-user_newpass_time.sql @@ -0,0 +1,4 @@ +-- Timestamp of the last time when a new password was +-- sent, for throttling purposes +ALTER TABLE /*$wgDBprefix*/user ADD user_newpass_time char(14) binary; + diff --git a/maintenance/archives/patch-user_rights.sql b/maintenance/archives/patch-user_rights.sql index 36f0102a..a32ef457 100644 --- a/maintenance/archives/patch-user_rights.sql +++ b/maintenance/archives/patch-user_rights.sql @@ -10,7 +10,7 @@ CREATE TABLE /*$wgDBprefix*/user_rights ( ur_user int(5) unsigned NOT NULL, -- Comma-separated list of permission keys - ur_rights tinyblob NOT NULL default '', + ur_rights tinyblob NOT NULL, UNIQUE KEY ur_user (ur_user) diff --git a/maintenance/backup.inc b/maintenance/backup.inc index 8b4b6726..1a8ff4fe 100644 --- a/maintenance/backup.inc +++ b/maintenance/backup.inc @@ -175,7 +175,7 @@ class BackupDumper { $this->initProgress( $history ); $db =& $this->backupDb(); - $exporter = new WikiExporter( $db, $history, MW_EXPORT_STREAM, $text ); + $exporter = new WikiExporter( $db, $history, WikiExporter::STREAM, $text ); $wrapper = new ExportProgressFilter( $this->sink, $this ); $exporter->setOutputSink( $wrapper ); diff --git a/maintenance/checkUsernames.php b/maintenance/checkUsernames.php index 4c0ecdce..60e52181 100644 --- a/maintenance/checkUsernames.php +++ b/maintenance/checkUsernames.php @@ -7,7 +7,6 @@ class checkUsernames { function checkUsernames() { $this->stderr = fopen( 'php://stderr', 'wt' ); - $this->log = fopen( '/home/wikipedia/logs/checkUsernames.log', 'at' ); } function main() { $fname = 'checkUsernames::main'; @@ -24,7 +23,7 @@ class checkUsernames { if ( ! User::isValidUserName( $row->user_name ) ) { $out = sprintf( "%s: %6d: '%s'\n", wfWikiID(), $row->user_id, $row->user_name ); fwrite( $this->stderr, $out ); - fwrite( $this->log, $out ); + wfDebugLog( 'checkUsernames', $out ); } } } diff --git a/maintenance/commandLine.inc b/maintenance/commandLine.inc index 2549057e..18a1d712 100644 --- a/maintenance/commandLine.inc +++ b/maintenance/commandLine.inc @@ -14,6 +14,15 @@ if ( isset( $_SERVER ) && array_key_exists( 'REQUEST_METHOD', $_SERVER ) ) { 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 @@ -179,7 +188,9 @@ if ( file_exists( '/home/wikipedia/common/langlist' ) ) { } # Turn off output buffering again, it might have been turned on in the settings files -@ob_end_flush(); +if( ob_get_level() ) { + ob_end_flush(); +} # Same with these $wgCommandLineMode = true; diff --git a/maintenance/deleteBatch.php b/maintenance/deleteBatch.php index 234744c3..14da6d84 100644 --- a/maintenance/deleteBatch.php +++ b/maintenance/deleteBatch.php @@ -3,11 +3,10 @@ # delete a batch of pages # Usage: php deleteBatch.php [-u <user>] [-r <reason>] [-i <interval>] <listfile> # where -# <listfile> is a file where each line has two titles separated by a pipe -# character. The first title is the source, the second is the destination. +# <listfile> is a file where each line contains the title of a page to be deleted. # <user> is the username -# <reason> is the move reason -# <interval> is the number of seconds to sleep for after each move +# <reason> is the delete reason +# <interval> is the number of seconds to sleep for after each delete $oldCwd = getcwd(); $optionsWithArgs = array( 'u', 'r', 'i' ); @@ -50,8 +49,8 @@ $dbw =& wfGetDB( DB_MASTER ); for ( $linenum = 1; !feof( $file ); $linenum++ ) { $line = trim( fgets( $file ) ); - if ( $line === false ) { - break; + if ( $line == '' ) { + continue; } $page = Title::newFromText( $line ); if ( is_null( $page ) ) { diff --git a/maintenance/deleteDefaultMessages.php b/maintenance/deleteDefaultMessages.php new file mode 100644 index 00000000..76924002 --- /dev/null +++ b/maintenance/deleteDefaultMessages.php @@ -0,0 +1,45 @@ +<?php + +/** + * Deletes all pages in the MediaWiki namespace which were last edited by + * "MediaWiki default". + */ + +if ( !defined( 'MEDIAWIKI' ) ) { + require_once( 'commandLine.inc' ); + deleteDefaultMessages(); +} + +function deleteDefaultMessages() { + $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 ); + + while ( $row = $dbr->fetchObject( $res ) ) { + 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(); + } +} +?> diff --git a/maintenance/dumpHTML.inc b/maintenance/dumpHTML.inc index ca2a62dc..702c7df9 100644 --- a/maintenance/dumpHTML.inc +++ b/maintenance/dumpHTML.inc @@ -38,6 +38,9 @@ class DumpHTML { # Make a copy of all images encountered var $makeSnapshot = false; + # Don't image description pages in doEverything() + var $noSharedDesc = false; + # Make links assuming the script path is in the same directory as # the destination var $alternateScriptPath = false; @@ -67,6 +70,9 @@ class DumpHTML { # Max page ID, lazy initialised var $maxPageID = false; + # UDP profiling + var $udpProfile, $udpProfileCounter = 0, $udpProfileInit = false; + function DumpHTML( $settings = array() ) { foreach ( $settings as $var => $value ) { $this->$var = $value; @@ -124,13 +130,16 @@ class DumpHTML { return; } $this->doArticles(); - $this->doLocalImageDescriptions(); - $this->doSharedImageDescriptions(); $this->doCategories(); $this->doRedirects(); if ( $this->sliceNumerator == 1 ) { $this->doSpecials(); } + $this->doLocalImageDescriptions(); + + if ( !$this->noSharedDesc ) { + $this->doSharedImageDescriptions(); + } $this->setCheckpoint( 'everything', 'done' ); } @@ -179,7 +188,8 @@ class DumpHTML { $title = Title::newFromID( $id ); if ( $title ) { $ns = $title->getNamespace() ; - if ( $ns != NS_CATEGORY && $title->getPrefixedDBkey() != $mainPage ) { + if ( $ns != NS_CATEGORY && $ns != NS_MEDIAWIKI && + $title->getPrefixedDBkey() != $mainPage ) { $this->doArticle( $title ); } } @@ -193,7 +203,7 @@ class DumpHTML { $this->setupGlobals(); print "Special:Categories..."; - $this->doArticle( Title::makeTitle( NS_SPECIAL, 'Categories' ) ); + $this->doArticle( SpecialPage::getTitleFor( 'Categories' ) ); print "\n"; } @@ -224,7 +234,9 @@ class DumpHTML { function doImageDescriptions() { $this->doLocalImageDescriptions(); - $this->doSharedImageDescriptions(); + if ( !$this->noSharedDesc ) { + $this->doSharedImageDescriptions(); + } } /** @@ -309,19 +321,23 @@ class DumpHTML { for ( $hash = $start; $hash <= $end; $hash++ ) { $this->setCheckpoint( 'shared image', $hash ); - $dir = sprintf( "%01x/%02x", intval( $hash / 16 ), $hash ); - $paths = array_merge( glob( "{$this->sharedStaticDirectory}/$dir/*" ), - glob( "{$this->sharedStaticDirectory}/thumb/$dir/*" ) ); - - foreach ( $paths as $path ) { - $file = wfBaseName( $path ); + $dir = sprintf( "%s/%01x/%02x", $this->sharedStaticDirectory, + intval( $hash / 16 ), $hash ); + $handle = @opendir( $dir ); + while ( $handle && $file = readdir( $handle ) ) { + if ( $file[0] == '.' ) { + continue; + } if ( !(++$i % REPORTING_INTERVAL ) ) { print "$i\r"; } - $title = Title::makeTitle( NS_IMAGE, $file ); + $title = Title::makeTitleSafe( NS_IMAGE, $file ); $this->doArticle( $title ); } + if ( $handle ) { + closedir( $handle ); + } } $this->setCheckpoint( 'shared image', 'done' ); print "\n"; @@ -437,6 +453,8 @@ class DumpHTML { } } + $this->profile(); + $this->rawPages = array(); $text = $this->getArticleHTML( $title ); @@ -473,11 +491,26 @@ class DumpHTML { fclose( $file ); } } + + wfIncrStats( 'dumphtml_article' ); } /** Write the given text to the file identified by the given title object */ function writeArticle( &$title, $text ) { $filename = $this->getHashedFilename( $title ); + + # Temporary hack for current dump, this should be moved to + # getFriendlyName() at the earliest opportunity. + # + # Limit filename length to 255 characters, so it works on ext3. + # Titles are in fact limited to 255 characters, but dumpHTML + # adds a suffix which may put them over the limit. + $length = strlen( $filename ); + if ( $length > 255 ) { + print "Warning: Filename too long ($length bytes). Skipping.\n"; + return; + } + $fullName = "{$this->dest}/$filename"; $fullDir = dirname( $fullName ); @@ -579,13 +612,11 @@ class DumpHTML { $wgUser->setOption( 'skin', $this->skin ); $wgUser->setOption( 'editsection', 0 ); - if ( $this->makeSnapshot ) { - $this->destUploadDirectory = "{$this->dest}/{$this->imageRel}"; - if ( realpath( $this->destUploadDirectory == $wgUploadDirectory ) ) { - $this->makeSnapshot = false; - } + $this->destUploadDirectory = "{$this->dest}/{$this->imageRel}"; + if ( realpath( $this->destUploadDirectory ) == realpath( $wgUploadDirectory ) ) { + print "Disabling image snapshot because the destination is the same as the source\n"; + $this->makeSnapshot = false; } - $this->sharedStaticDirectory = "{$this->destUploadDirectory}/shared"; $this->setupDone = true; @@ -683,9 +714,13 @@ ENDTEXT; if ( !file_exists( $destLoc ) ) { wfMkdirParents( dirname( $destLoc ), 0755 ); if ( function_exists( 'symlink' ) && !$this->forceCopy ) { - symlink( $sourceLoc, $destLoc ); + if ( !symlink( $sourceLoc, $destLoc ) ) { + print "Warning: unable to create symlink at $destLoc\n"; + } } else { - copy( $sourceLoc, $destLoc ); + if ( !copy( $sourceLoc, $destLoc ) ) { + print "Warning: unable to copy $sourceLoc to $destLoc\n"; + } } } } @@ -928,7 +963,33 @@ ENDTEXT; } return $this->maxPageID; } - + + function profile() { + global $wgProfiler; + + if ( !$this->udpProfile ) { + return; + } + if ( !$this->udpProfileInit ) { + $this->udpProfileInit = true; + } elseif ( $this->udpProfileCounter == 1 % $this->udpProfile ) { + $wgProfiler->getFunctionReport(); + $wgProfiler = new DumpHTML_ProfilerStub; + } + if ( $this->udpProfileCounter == 0 ) { + $wgProfiler = new ProfilerSimpleUDP; + $wgProfiler->setProfileID( 'dumpHTML' ); + } + $this->udpProfileCounter = ( $this->udpProfileCounter + 1 ) % $this->udpProfile; + } +} + +class DumpHTML_ProfilerStub { + function profileIn() {} + function profileOut() {} + function getOutput() {} + function close() {} + function getFunctionReport() {} } /** XML parser callback */ diff --git a/maintenance/dumpHTML.php b/maintenance/dumpHTML.php index 5e347e4b..2c0c29c4 100644 --- a/maintenance/dumpHTML.php +++ b/maintenance/dumpHTML.php @@ -17,6 +17,8 @@ * --checkpoint <file> use a checkpoint file to allow restarting of interrupted dumps * --slice <n/m> split the job into m segments and do the n'th one * --images only do image description pages + * --shared-desc only do shared (commons) image description pages + * --no-shared-desc don't do shared image description pages * --categories only do category pages * --redirects only do redirects * --special only do miscellaneous stuff @@ -24,10 +26,11 @@ * --interlang allow interlanguage links * --image-snapshot copy all images used to the destination directory * --compress generate compressed version of the html pages + * --udp-profile <N> profile 1/N rendering operations using ProfilerSimpleUDP */ -$optionsWithArgs = array( 's', 'd', 'e', 'k', 'checkpoint', 'slice' ); +$optionsWithArgs = array( 's', 'd', 'e', 'k', 'checkpoint', 'slice', 'udp-profile' ); $profiling = false; @@ -41,6 +44,10 @@ if ( $profiling ) { } } +if ( in_array( '--udp-profile', $argv ) ) { + define( 'MW_FORCE_PROFILE', 1 ); +} + require_once( "commandLine.inc" ); require_once( "dumpHTML.inc" ); @@ -93,6 +100,8 @@ $wgHTMLDump = new DumpHTML( array( 'sliceDenominator' => $sliceDenominator, 'noOverwrite' => $options['no-overwrite'], 'compress' => $options['compress'], + 'noSharedDesc' => $options['no-shared-desc'], + 'udpProfile' => $options['udp-profile'], )); @@ -104,6 +113,8 @@ if ( $options['special'] ) { $wgHTMLDump->doCategories(); } elseif ( $options['redirects'] ) { $wgHTMLDump->doRedirects(); +} elseif ( $options['shared-desc'] ) { + $wgHTMLDump->doSharedImageDescriptions(); } else { print "Creating static HTML dump in directory $dest. \n"; $dbr =& wfGetDB( DB_SLAVE ); diff --git a/maintenance/edit.php b/maintenance/edit.php new file mode 100644 index 00000000..33e0607b --- /dev/null +++ b/maintenance/edit.php @@ -0,0 +1,68 @@ +<?php + +$optionsWithArgs = array( 'u', 's' ); + +require_once( 'commandLine.inc' ); + +if ( count( $args ) == 0 || isset( $options['help'] ) ) { + print <<<EOT +Edit an article from the command line + +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(); +} + +$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... "; +$success = $wgArticle->doEdit( $text, $summary, + ( $minor ? EDIT_MINOR : 0 ) | + ( $bot ? EDIT_FORCE_BOT : 0 ) | + ( $autoSummary ? EDIT_AUTOSUMMARY : 0 ) | + ( $noRC ? EDIT_SUPPRESS_RC : 0 ) ); +if ( $success ) { + print "done\n"; +} else { + print "failed\n"; + exit( 1 ); +} +?> diff --git a/maintenance/fuzz-tester.php b/maintenance/fuzz-tester.php index 23c3cd7c..0ee052b2 100644 --- a/maintenance/fuzz-tester.php +++ b/maintenance/fuzz-tester.php @@ -694,7 +694,7 @@ class wikiFuzz { ); /** - ** @desc: Randomly returns one element of the input array. + ** Randomly returns one element of the input array. */ static public function chooseInput(array $input) { $randindex = wikiFuzz::randnum(count($input) - 1); @@ -705,14 +705,14 @@ class wikiFuzz { static private $maxparams = 10; /** - ** @desc: Returns random number between finish and start. + ** Returns random number between finish and start. */ static public function randnum($finish,$start=0) { return mt_rand($start,$finish); } /** - ** @desc: Returns a mix of random text and random wiki syntax. + ** Returns a mix of random text and random wiki syntax. */ static private function randstring() { $thestring = ""; @@ -740,7 +740,7 @@ class wikiFuzz { } /** - ** @desc: Returns either random text, or random wiki syntax, or random data from "ints", + ** Returns either random text, or random wiki syntax, or random data from "ints", ** or random data from "other". */ static private function makestring() { @@ -758,7 +758,7 @@ class wikiFuzz { /** - ** @desc: Strips out the stuff that Mediawiki balks at in a page's title. + ** Strips out the stuff that Mediawiki balks at in a page's title. ** Implementation copied/pasted from cleanupTable.inc & cleanupImages.php */ static public function makeTitleSafe($str) { @@ -775,7 +775,7 @@ class wikiFuzz { } /** - ** @desc: Returns a string of fuzz text. + ** Returns a string of fuzz text. */ static private function loop() { switch ( wikiFuzz::randnum(3) ) { @@ -803,7 +803,7 @@ class wikiFuzz { } /** - ** @desc: Returns one of the three styles of random quote: ', ", and nothing. + ** Returns one of the three styles of random quote: ', ", and nothing. */ static private function getRandQuote() { switch ( wikiFuzz::randnum(3) ) { @@ -814,7 +814,7 @@ class wikiFuzz { } /** - ** @desc: Returns fuzz text, with the parameter indicating approximately how many lines of text you want. + ** Returns fuzz text, with the parameter indicating approximately how many lines of text you want. */ static public function makeFuzz($maxtypes = 2) { $page = ""; @@ -829,7 +829,7 @@ class wikiFuzz { //////// MEDIAWIKI PAGES TO TEST, AND HOW TO TEST THEM /////// /** - ** @desc: A page test has just these things: + ** A page test has just these things: ** 1) Form parameters. ** 2) the URL we are going to test those parameters on. ** 3) Any cookies required for the test. @@ -856,7 +856,7 @@ abstract class pageTest { /** - ** @desc: a page test for the "Edit" page. Tests Parser.php and Sanitizer.php. + ** a page test for the "Edit" page. Tests Parser.php and Sanitizer.php. */ class editPageTest extends pageTest { function __construct() { @@ -888,7 +888,7 @@ class editPageTest extends pageTest { /** - ** @desc: a page test for "Special:Listusers". + ** a page test for "Special:Listusers". */ class listusersTest extends pageTest { function __construct() { @@ -907,7 +907,7 @@ class listusersTest extends pageTest { /** - ** @desc: a page test for "Special:Search". + ** a page test for "Special:Search". */ class searchTest extends pageTest { function __construct() { @@ -942,7 +942,7 @@ class searchTest extends pageTest { /** - ** @desc: a page test for "Special:Recentchanges". + ** a page test for "Special:Recentchanges". */ class recentchangesTest extends pageTest { function __construct() { @@ -971,7 +971,7 @@ class recentchangesTest extends pageTest { /** - ** @desc: a page test for "Special:Prefixindex". + ** a page test for "Special:Prefixindex". */ class prefixindexTest extends pageTest { function __construct() { @@ -997,7 +997,7 @@ class prefixindexTest extends pageTest { /** - ** @desc: a page test for "Special:MIMEsearch". + ** a page test for "Special:MIMEsearch". */ class mimeSearchTest extends pageTest { function __construct() { @@ -1014,7 +1014,7 @@ class mimeSearchTest extends pageTest { /** - ** @desc: a page test for "Special:Log". + ** a page test for "Special:Log". */ class specialLogTest extends pageTest { function __construct() { @@ -1034,7 +1034,7 @@ class specialLogTest extends pageTest { /** - ** @desc: a page test for "Special:Userlogin", with a successful login. + ** a page test for "Special:Userlogin", with a successful login. */ class successfulUserLoginTest extends pageTest { function __construct() { @@ -1053,7 +1053,7 @@ class successfulUserLoginTest extends pageTest { /** - ** @desc: a page test for "Special:Userlogin". + ** a page test for "Special:Userlogin". */ class userLoginTest extends pageTest { function __construct() { @@ -1084,7 +1084,7 @@ class userLoginTest extends pageTest { /** - ** @desc: a page test for "Special:Ipblocklist" (also includes unblocking) + ** a page test for "Special:Ipblocklist" (also includes unblocking) */ class ipblocklistTest extends pageTest { function __construct() { @@ -1117,7 +1117,7 @@ class ipblocklistTest extends pageTest { /** - ** @desc: a page test for "Special:Newimages". + ** a page test for "Special:Newimages". */ class newImagesTest extends pageTest { function __construct() { @@ -1138,7 +1138,7 @@ class newImagesTest extends pageTest { /** - ** @desc: a page test for the "Special:Imagelist" page. + ** a page test for the "Special:Imagelist" page. */ class imagelistTest extends pageTest { function __construct() { @@ -1155,7 +1155,7 @@ class imagelistTest extends pageTest { /** - ** @desc: a page test for "Special:Export". + ** a page test for "Special:Export". */ class specialExportTest extends pageTest { function __construct() { @@ -1180,7 +1180,7 @@ class specialExportTest extends pageTest { /** - ** @desc: a page test for "Special:Booksources". + ** a page test for "Special:Booksources". */ class specialBooksourcesTest extends pageTest { function __construct() { @@ -1196,7 +1196,7 @@ class specialBooksourcesTest extends pageTest { /** - ** @desc: a page test for "Special:Allpages". + ** a page test for "Special:Allpages". */ class specialAllpagesTest extends pageTest { function __construct() { @@ -1212,7 +1212,7 @@ class specialAllpagesTest extends pageTest { /** - ** @desc: a page test for the page History. + ** a page test for the page History. */ class pageHistoryTest extends pageTest { function __construct() { @@ -1232,7 +1232,7 @@ class pageHistoryTest extends pageTest { /** - ** @desc: a page test for the Special:Contributions". + ** a page test for the Special:Contributions". */ class contributionsTest extends pageTest { function __construct() { @@ -1250,7 +1250,7 @@ class contributionsTest extends pageTest { /** - ** @desc: a page test for viewing a normal page, whilst posting various params. + ** a page test for viewing a normal page, whilst posting various params. */ class viewPageTest extends pageTest { function __construct() { @@ -1317,7 +1317,7 @@ class viewPageTest extends pageTest { /** - ** @desc: a page test for "Special:Allmessages". + ** a page test for "Special:Allmessages". */ class specialAllmessagesTest extends pageTest { function __construct() { @@ -1331,7 +1331,7 @@ class specialAllmessagesTest extends pageTest { } /** - ** @desc: a page test for "Special:Newpages". + ** a page test for "Special:Newpages". */ class specialNewpages extends pageTest { function __construct() { @@ -1351,7 +1351,7 @@ class specialNewpages extends pageTest { } /** - ** @desc: a page test for "redirect.php" + ** a page test for "redirect.php" */ class redirectTest extends pageTest { function __construct() { @@ -1368,7 +1368,7 @@ class redirectTest extends pageTest { /** - ** @desc: a page test for "Special:Confirmemail" + ** a page test for "Special:Confirmemail" */ class confirmEmail extends pageTest { function __construct() { @@ -1383,7 +1383,7 @@ class confirmEmail extends pageTest { /** - ** @desc: a page test for "Special:Watchlist" + ** a page test for "Special:Watchlist" ** Note: this test would be better if we were logged in. */ class watchlistTest extends pageTest { @@ -1409,7 +1409,7 @@ class watchlistTest extends pageTest { /** - ** @desc: a page test for "Special:Blockme" + ** a page test for "Special:Blockme" */ class specialBlockmeTest extends pageTest { function __construct() { @@ -1426,7 +1426,7 @@ class specialBlockmeTest extends pageTest { /** - ** @desc: a page test for "Special:Movepage" + ** a page test for "Special:Movepage" */ class specialMovePage extends pageTest { function __construct() { @@ -1459,7 +1459,7 @@ class specialMovePage extends pageTest { /** - ** @desc: a page test for "Special:Undelete" + ** a page test for "Special:Undelete" */ class specialUndelete extends pageTest { function __construct() { @@ -1486,7 +1486,7 @@ class specialUndelete extends pageTest { /** - ** @desc: a page test for "Special:Unlockdb" + ** a page test for "Special:Unlockdb" */ class specialUnlockdb extends pageTest { function __construct() { @@ -1507,7 +1507,7 @@ class specialUnlockdb extends pageTest { /** - ** @desc: a page test for "Special:Lockdb" + ** a page test for "Special:Lockdb" */ class specialLockdb extends pageTest { function __construct() { @@ -1529,7 +1529,7 @@ class specialLockdb extends pageTest { /** - ** @desc: a page test for "Special:Userrights" + ** a page test for "Special:Userrights" */ class specialUserrights extends pageTest { function __construct() { @@ -1552,7 +1552,7 @@ class specialUserrights extends pageTest { /** - ** @desc: a test for page protection and unprotection. + ** a test for page protection and unprotection. */ class pageProtectionForm extends pageTest { function __construct() { @@ -1576,7 +1576,7 @@ class pageProtectionForm extends pageTest { /** - ** @desc: a page test for "Special:Blockip". + ** a page test for "Special:Blockip". */ class specialBlockip extends pageTest { function __construct() { @@ -1615,7 +1615,7 @@ class specialBlockip extends pageTest { /** - ** @desc: a test for the imagepage. + ** a test for the imagepage. */ class imagepageTest extends pageTest { function __construct() { @@ -1638,7 +1638,7 @@ class imagepageTest extends pageTest { /** - ** @desc: a test for page deletion form. + ** a test for page deletion form. */ class pageDeletion extends pageTest { function __construct() { @@ -1660,7 +1660,7 @@ class pageDeletion extends pageTest { /** - ** @desc: a test for Revision Deletion. + ** a test for Revision Deletion. */ class specialRevisionDelete extends pageTest { function __construct() { @@ -1691,7 +1691,7 @@ class specialRevisionDelete extends pageTest { /** - ** @desc: a test for Special:Import. + ** a test for Special:Import. */ class specialImport extends pageTest { function __construct() { @@ -1724,7 +1724,7 @@ class specialImport extends pageTest { /** - ** @desc: a test for thumb.php + ** a test for thumb.php */ class thumbTest extends pageTest { function __construct() { @@ -1745,7 +1745,7 @@ class thumbTest extends pageTest { /** - ** @desc: a test for trackback.php + ** a test for trackback.php */ class trackbackTest extends pageTest { function __construct() { @@ -1767,7 +1767,7 @@ class trackbackTest extends pageTest { /** - ** @desc: a test for profileinfo.php + ** a test for profileinfo.php */ class profileInfo extends pageTest { function __construct() { @@ -1787,7 +1787,7 @@ class profileInfo extends pageTest { /** - ** @desc: a test for Special:Cite (extension Special page). + ** a test for Special:Cite (extension Special page). */ class specialCite extends pageTest { function __construct() { @@ -1806,7 +1806,7 @@ class specialCite extends pageTest { /** - ** @desc: a test for Special:Filepath (extension Special page). + ** a test for Special:Filepath (extension Special page). */ class specialFilepath extends pageTest { function __construct() { @@ -1820,7 +1820,7 @@ class specialFilepath extends pageTest { /** - ** @desc: a test for Special:Makebot (extension Special page). + ** a test for Special:Makebot (extension Special page). */ class specialMakebot extends pageTest { function __construct() { @@ -1843,7 +1843,7 @@ class specialMakebot extends pageTest { /** - ** @desc: a test for Special:Makesysop (extension Special page). + ** a test for Special:Makesysop (extension Special page). */ class specialMakesysop extends pageTest { function __construct() { @@ -1866,7 +1866,7 @@ class specialMakesysop extends pageTest { /** - ** @desc: a test for Special:Renameuser (extension Special page). + ** a test for Special:Renameuser (extension Special page). */ class specialRenameuser extends pageTest { function __construct() { @@ -1882,7 +1882,7 @@ class specialRenameuser extends pageTest { /** - ** @desc: a test for Special:Linksearch (extension Special page). + ** a test for Special:Linksearch (extension Special page). */ class specialLinksearch extends pageTest { function __construct() { @@ -1899,7 +1899,7 @@ class specialLinksearch extends pageTest { /** - ** @desc: a test for Special:CategoryTree (extension Special page). + ** a test for Special:CategoryTree (extension Special page). */ class specialCategoryTree extends pageTest { function __construct() { @@ -1921,7 +1921,7 @@ class specialCategoryTree extends pageTest { /** - ** @desc: selects a page test to run. + ** selects a page test to run. */ function selectPageTest($count) { @@ -1987,7 +1987,7 @@ function selectPageTest($count) { /////////////////////// SAVING OUTPUT ///////////////////////// /** - ** @desc: Utility function for saving a file. Currently has no error checking. + ** Utility function for saving a file. Currently has no error checking. */ function saveFile($data, $name) { file_put_contents($name, $data); @@ -1995,7 +1995,7 @@ function saveFile($data, $name) { /** - ** @desc: Returns a test as an experimental GET-to-POST URL. + ** Returns a test as an experimental GET-to-POST URL. ** This doesn't seem to always work though, and sometimes the output is too long ** to be a valid GET URL, so we also save in other formats. */ @@ -2017,7 +2017,7 @@ function getAsURL(pageTest $test) { /** - ** @desc: Saves a plain-text human-readable version of a test. + ** Saves a plain-text human-readable version of a test. */ function saveTestAsText(pageTest $test, $filename) { $str = "Test: " . $test->getPagePath(); @@ -2030,7 +2030,7 @@ function saveTestAsText(pageTest $test, $filename) { /** - ** @desc: Saves a test as a standalone basic PHP script that shows this one problem. + ** Saves a test as a standalone basic PHP script that shows this one problem. ** Resulting script requires PHP-Curl be installed in order to work. */ function saveTestAsPHP(pageTest $test, $filename) { @@ -2051,7 +2051,7 @@ function saveTestAsPHP(pageTest $test, $filename) { /** - ** @desc: Escapes a value so that it can be used on the command line by Curl. + ** Escapes a value so that it can be used on the command line by Curl. ** Specifically, "<" and "@" need to be escaped if they are the first character, ** otherwise curl interprets these as meaning that we want to insert a file. */ @@ -2068,7 +2068,7 @@ function escapeForCurl(array $input_params) { /** - ** @desc: Saves a test as a standalone CURL shell script that shows this one problem. + ** Saves a test as a standalone CURL shell script that shows this one problem. ** Resulting script requires standalone Curl be installed in order to work. */ function saveTestAsCurl(pageTest $test, $filename) { @@ -2086,7 +2086,7 @@ function saveTestAsCurl(pageTest $test, $filename) { /** - ** @desc: Saves the internal data structure to file. + ** Saves the internal data structure to file. */ function saveTestData (pageTest $test, $filename) { saveFile(serialize($test), $filename); @@ -2094,7 +2094,7 @@ function saveTestData (pageTest $test, $filename) { /** - ** @desc: saves a test in the various formats. + ** saves a test in the various formats. */ function saveTest(pageTest $test, $testname) { $base_name = DIRECTORY . "/" . $testname; @@ -2108,7 +2108,7 @@ function saveTest(pageTest $test, $testname) { //////////////////// MEDIAWIKI OUTPUT ///////////////////////// /** - ** @desc: Asks MediaWiki for the HTML output of a test. + ** Asks MediaWiki for the HTML output of a test. */ function wikiTestOutput(pageTest $test) { @@ -2141,7 +2141,7 @@ function wikiTestOutput(pageTest $test) { //////////////////// HTML VALIDATION ///////////////////////// /* - ** @desc: Asks the validator whether this is valid HTML, or not. + ** Asks the validator whether this is valid HTML, or not. */ function validateHTML($text) { @@ -2172,7 +2172,7 @@ function validateHTML($text) { /** - ** @desc: Get tidy to check for no HTML errors in the output file (e.g. unescaped strings). + ** Get tidy to check for no HTML errors in the output file (e.g. unescaped strings). */ function tidyCheckFile($name) { $file = DIRECTORY . "/" . $name; @@ -2193,7 +2193,7 @@ function tidyCheckFile($name) { /** - ** @desc: Returns whether or not an database error log file has changed in size since + ** Returns whether or not an database error log file has changed in size since ** the last time this was run. This is used to tell if a test caused a DB error. */ function dbErrorLogged() { @@ -2222,7 +2222,7 @@ function dbErrorLogged() { ////////////////// TOP-LEVEL PROBLEM-FINDING FUNCTION //////////////////////// /** - ** @desc: takes a page test, and runs it and tests it for problems in the output. + ** takes a page test, and runs it and tests it for problems in the output. ** Returns: False on finding a problem, or True on no problems being found. */ function runWikiTest(pageTest $test, &$testname, $can_overwrite = false) { @@ -2315,7 +2315,7 @@ function runWikiTest(pageTest $test, &$testname, $can_overwrite = false) { /////////////////// RERUNNING OLD TESTS /////////////////// /** - ** @desc: We keep our failed tests so that they can be rerun. + ** We keep our failed tests so that they can be rerun. ** This function does that retesting. */ function rerunPreviousTests() { diff --git a/maintenance/generateSitemap.php b/maintenance/generateSitemap.php index a0b6979d..b8d6a5d6 100644 --- a/maintenance/generateSitemap.php +++ b/maintenance/generateSitemap.php @@ -264,6 +264,16 @@ class GenerateSitemap { $entry = $this->fileEntry( $title->getFullURL(), $date, $this->priority( $namespace ) ); $length += strlen( $entry ); $this->write( $this->file, $entry ); + // generate pages for language variants + if($wgContLang->hasVariants()){ + $variants = $wgContLang->getVariants(); + foreach($variants as $vCode){ + if($vCode==$wgContLang->getCode()) continue; // we don't want default variant + $entry = $this->fileEntry( $title->getFullURL('',$vCode), $date, $this->priority( $namespace ) ); + $length += strlen( $entry ); + $this->write( $this->file, $entry ); + } + } } if ( $this->file ) { $this->write( $this->file, $this->closeFile() ); diff --git a/maintenance/getLagTimes.php b/maintenance/getLagTimes.php new file mode 100644 index 00000000..f2c06f6a --- /dev/null +++ b/maintenance/getLagTimes.php @@ -0,0 +1,23 @@ +<?php + +require 'commandLine.inc'; + +if( empty( $wgDBservers ) ) { + 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 = $wgLoadBalancer->getLagTimes(); + foreach( $lags as $n => $lag ) { + $host = $wgDBservers[$n]["host"]; + if( IP::isValid( $host ) ) { + $ip = $host; + $host = gethostbyaddr( $host ); + } else { + $ip = gethostbyname( $host ); + } + $stars = str_repeat( '*', intval( $lag ) ); + printf( "%10s %20s %3d %s\n", $ip, $host, $lag, $stars ); + } +} + +?>
\ No newline at end of file diff --git a/maintenance/getSlaveServer.php b/maintenance/getSlaveServer.php new file mode 100644 index 00000000..ebeddc4c --- /dev/null +++ b/maintenance/getSlaveServer.php @@ -0,0 +1,7 @@ +<?php + +require_once( dirname(__FILE__).'/commandLine.inc' ); +$i = $wgLoadBalancer->getReaderIndex(); +print $wgDBservers[$i]['host'] . "\n"; + +?> diff --git a/maintenance/importDump.php b/maintenance/importDump.php index 1bca3296..22709f64 100644 --- a/maintenance/importDump.php +++ b/maintenance/importDump.php @@ -136,6 +136,8 @@ if( WikiError::isError( $result ) ) { echo $result->getMessage() . "\n"; } else { echo "Done!\n"; + echo "You might want to run rebuildrecentchanges.php to regenerate\n"; + echo "the recentchanges page."; } ?> diff --git a/maintenance/importImages.php b/maintenance/importImages.php index 2cf8bd19..abf0ec09 100644 --- a/maintenance/importImages.php +++ b/maintenance/importImages.php @@ -30,7 +30,9 @@ if( count( $args ) > 1 ) { $wgUser = User::newFromName( $options['user'] ); } else { $wgUser = User::newFromName( 'Image import script' ); - $wgUser->setLoaded( true ); + } + if ( $wgUser->isAnon() ) { + $wgUser->addToDatabase(); } # Get the upload comment @@ -119,4 +121,4 @@ END; exit(); } -?>
\ No newline at end of file +?> diff --git a/maintenance/importTextFile.php b/maintenance/importTextFile.php index 625763be..92c31fd0 100644 --- a/maintenance/importTextFile.php +++ b/maintenance/importTextFile.php @@ -1,111 +1,84 @@ <?php /** - * Maintenance script to insert an article, importing text from a file + * Maintenance script allows creating or editing pages using + * the contents of a text file * * @package MediaWiki * @subpackage Maintenance * @author Rob Church <robchur@gmail.com> */ - -$options = array( 'help', 'norc' ); + +$options = array( 'help', 'nooverwrite' ); $optionsWithArgs = array( 'title', 'user', 'comment' ); require_once( 'commandLine.inc' ); -require_once( 'importTextFile.inc' ); echo( "Import Text File\n\n" ); -if( !isset( $options['help'] ) || !$options['help'] ) { +if( isset( $options['help'] ) ) { + showHelp(); +} else { - # Check file existence $filename = $args[0]; - echo( "Using file '{$filename}'..." ); - if( file_exists( $filename ) ) { - echo( "found.\n" ); - - # Work out the title for the page - if( isset( $option['title'] ) || trim( $options['title'] ) != '' ) { - $titleText = $options['title']; - # Use the supplied title - echo( "Using title '{$titleText}'..." ); - $title = Title::newFromText( $options['title'] ); - } else { - # Attempt to make a title out of the filename - echo( "Using title from filename..." ); - $title = titleFromFilename( $filename ); - } + echo( "Using {$filename}..." ); + if( is_file( $filename ) ) { - # Check the title's valid - if( !is_null( $title ) && is_object( $title ) ) { - echo( "ok.\n" ); + $title = isset( $options['title'] ) ? $options['title'] : titleFromFilename( $filename ); + $title = Title::newFromUrl( $title ); + echo( "\nUsing title '" . $title->getPrefixedText() . "'..." ); - # Read in the text - $text = file_get_contents( $filename ); - - # Check the supplied user and fall back to a default if needed - if( isset( $options['user'] ) && trim( $options['user'] ) != '' ) { - $username = $options['user']; - } else { - $username = 'MediaWiki default'; - } - echo( "Using user '{$username}'..." ); - $user = User::newFromName( $username ); - - # Check the user's valid - if( !is_null( $user ) && is_object( $user ) ) { - echo( "ok.\n" ); - $wgUser =& $user; + if( is_object( $title ) ) { - # If a comment was supplied, use it (replace _ with spaces ) else use a default - if( isset( $options['comment'] ) || trim( $options['comment'] != '' ) ) { - $comment = str_replace( '_', ' ', $options['comment'] ); - } else { - $comment = 'Importing text file'; - } - echo( "Using edit summary '{$comment}'.\n" ); + if( !$title->exists() || !isset( $options['nooverwrite'] ) ) { - # Do we need to update recent changes? - if( isset( $options['norc'] ) && $options['norc'] ) { - $rc = false; - } else { - $rc = true; - } - - # Attempt the insertion - echo( "Attempting to insert page..." ); - $success = insertNewArticle( $title, $text, $user, $comment, $rc ); - if( $success ) { + $text = file_get_contents( $filename ); + $user = isset( $options['user'] ) ? $options['user'] : 'MediaWiki default'; + $user = User::newFromName( $user ); + echo( "\nUsing username '" . $user->getName() . "'..." ); + + if( is_object( $user ) ) { + + $wgUser =& $user; + $comment = isset( $options['comment'] ) ? $options['comment'] : 'Importing text file'; + $comment = str_replace( '_', ' ', $comment ); + + echo( "\nPerforming edit..." ); + $article = new Article( $title ); + $article->doEdit( $text, $comment ); echo( "done.\n" ); + } else { - echo( "failed. Title exists.\n" ); + echo( "invalid username.\n" ); } } else { - # Dud user - echo( "invalid username.\n" ); + echo( "page exists.\n" ); } } else { - # Dud title echo( "invalid title.\n" ); } } else { - # File not found - echo( "not found.\n" ); + echo( "does not exist.\n" ); } -} else { - # Show help - echo( "Imports the contents of a text file into a wiki page.\n\n" ); - echo( "USAGE: php importTextFile.php [--help|--title <title>|--user <user>|--comment <comment>|--norc] <filename>\n\n" ); +} + +function titleFromFilename( $filename ) { + $parts = explode( '/', $filename ); + $parts = explode( '.', $parts[ count( $parts ) - 1 ] ); + return $parts[0]; +} + +function showHelp() { + echo( "Import the contents of a text file into a wiki page.\n\n" ); + echo( "USAGE: php importTextFile.php [--help|--title <title>|--user <user>|--comment <comment>|--nooverwrite] <filename>\n\n" ); echo( " --help: Show this help information\n" ); echo( " --title <title> : Title for the new page; if not supplied, the filename is used as a base for the title\n" ); echo( " --user <user> : User to be associated with the edit; if not supplied, a default is used\n" ); echo( "--comment <comment> : Edit summary to be associated with the edit; underscores are transformed into spaces; if not supplied, a default is used\n" ); - echo( " <filename> : Path to the file containing the wikitext to import\n" ); - echo( " --norc : Do not add a page creation event to recent changes\n" ); - + echo( " --nooverwrite : Don't overwrite existing page content\n" ); + echo( " <filename> : Path to the file containing the wikitext to import\n\n" ); } -echo( "\n" ); ?>
\ No newline at end of file diff --git a/maintenance/initEditCount.php b/maintenance/initEditCount.php new file mode 100644 index 00000000..9d165cfb --- /dev/null +++ b/maintenance/initEditCount.php @@ -0,0 +1,85 @@ +<?php + +require_once "commandLine.inc"; + +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. + +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. + +"); +} +$dbw = wfGetDB( DB_MASTER ); +$user = $dbw->tableName( 'user' ); +$revision = $dbw->tableName( 'revision' ); + +$dbver = $dbw->getServerVersion(); + +// Autodetect mode... +$backgroundMode = count( $wgDBservers ) > 1 || + ($dbw instanceof DatabaseMySql && version_compare( $dbver, '4.1' ) < 0); + +if( isset( $options['background'] ) ) { + $backgroundMode = true; +} elseif( isset( $options['quick'] ) ) { + $backgroundMode = false; +} + +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->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 ); + } +} 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"; + +?> diff --git a/maintenance/initStats.php b/maintenance/initStats.php index b622c3f0..291de1ee 100644 --- a/maintenance/initStats.php +++ b/maintenance/initStats.php @@ -23,6 +23,7 @@ if( isset( $options['help'] ) ) { echo( "Counting total edits..." ); $edits = $dbr->selectField( 'revision', 'COUNT(*)', '', $fname ); +$edits += $dbr->selectField( 'archive', 'COUNT(*)', '', $fname ); echo( "{$edits}\nCounting number of articles..." ); global $wgContentNamespaces; diff --git a/maintenance/interwiki.sql b/maintenance/interwiki.sql index ca656e46..b0df5557 100644 --- a/maintenance/interwiki.sql +++ b/maintenance/interwiki.sql @@ -147,7 +147,7 @@ REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local) VALUES ('twistedwiki','http://purl.net/wiki/twisted/$1',0), ('uea','http://www.tejo.org/uea/$1',0), ('unreal','http://wiki.beyondunreal.com/wiki/$1',0), -('ursine','http://ursine.ca/$1',0), +('ursine','http://wiki.ursine.ca/$1',0), ('usej','http://www.tejo.org/usej/$1',0), ('usemod','http://www.usemod.com/cgi-bin/wiki.pl?$1',0), ('visualworks','http://wiki.cs.uiuc.edu/VisualWorks/$1',0), @@ -157,10 +157,11 @@ REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local) VALUES ('webseitzwiki','http://webseitz.fluxent.com/wiki/$1',0), ('why','http://clublet.com/c/c/why?$1',0), ('wiki','http://c2.com/cgi/wiki?$1',0), -('wikia','http://www.wikia.com/wiki/index.php/$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), ('wikif1','http://www.wikif1.org/$1',0), +('wikihow','http://www.wikihow.com/$1',0), ('wikinfo','http://www.wikinfo.org/wiki.php?title=$1',0), ('wikimedia','http://wikimediafoundation.org/wiki/$1',0), ('wikiquote','http://en.wikiquote.org/wiki/$1',1), diff --git a/maintenance/language/langmemusage.php b/maintenance/language/langmemusage.php index 974bb0d8..54b6a58c 100644 --- a/maintenance/language/langmemusage.php +++ b/maintenance/language/langmemusage.php @@ -18,7 +18,7 @@ $memlast = $memstart = memory_get_usage(); print 'Base memory usage: '.$memstart."\n"; foreach ( $langtool->getLanguages() as $langcode ) { - require_once( Language::getClassFileName( $langcode ) ); + Language::factory( $langcode ); $memstep = memory_get_usage(); printf( "%12s: %d\n", $langcode, ($memstep- $memlast) ); $memlast = $memstep; diff --git a/maintenance/language/languages.inc b/maintenance/language/languages.inc index 86cd0869..946c6cb2 100644 --- a/maintenance/language/languages.inc +++ b/maintenance/language/languages.inc @@ -6,328 +6,15 @@ * @subpackage Maintenance */ +require_once( 'messageTypes.inc' ); + class languages { private $mLanguages; # List of languages private $mRawMessages; # Raw list of the messages in each language private $mMessages; # Messages in each language (except for English), divided to groups private $mGeneralMessages; # General messages in English, divided to groups - private $mIgnoredMessages = array( - 'sidebar', - 'addsection', - 'anonnotice', - 'catseparator', - 'googlesearch', - 'exif-make-value', - 'exif-model-value', - 'exif-software-value', - 'history_copyright', - 'licenses', - 'loginend', - 'loginlanguagelinks', - 'markaspatrolledlink', - 'newarticletextanon', - 'noarticletextanon', - 'number_of_watching_users_RCview', - 'pubmedurl', - 'randompage-url', - 'recentchanges-url', - 'rfcurl', - 'shareddescriptionfollows', - 'signupend', - 'sitenotice', - 'sitesubtitle', - 'sitetitle', - 'talkpagetext', - 'trackback', - 'trackbackexcerpt', - 'widthheight', - ); # All the messages which should be exist only in the English file - private $mOptionalMessages = array( - 'imgmultigotopost', - 'linkprefix', - 'allpages-summary', - 'booksources-summary', - 'ipblocklist-summary', - 'listusers-summary', - 'longpages-summary', - 'preferences-summary', - 'specialpages-summary', - 'whatlinkshere-summary', - 'whatlinkshere-barrow', - 'imagelist-summary', - 'mimesearch-summary', - 'listredirects-summary', - 'uncategorizedpages-summary', - 'uncategorizedcategories-summary', - 'uncategorizedimages-summary', - 'popularpages-summary', - 'wantedcategories-summary', - 'wantedpages-summary', - 'mostlinked-summary', - 'mostlinkedcategories-summary', - 'mostcategories-summary', - 'mostimages-summary', - 'mostrevisions-summary', - 'prefixindex-summary', - 'shortpages-summary', - 'newpages-summary', - 'ancientpages-summary', - 'newimages-summary', - 'unwatchedpages-summary', - 'userrights-summary', - 'variantname-zh-cn', - 'variantname-zh-tw', - 'variantname-zh-hk', - 'variantname-zh-sg', - 'variantname-zh', - 'variantname-sr-ec', - 'variantname-sr-el', - 'variantname-sr-jc', - 'variantname-sr-jl', - 'variantname-sr', - 'variantname-kk-tr', - 'variantname-kk-kz', - 'variantname-kk-cn', - 'variantname-kk', - ); # All the messages which may be translated or not, depending on the language - private $mEXIFMessages = array( - 'exif-imagewidth', - 'exif-imagelength', - 'exif-bitspersample', - 'exif-compression', - 'exif-photometricinterpretation', - 'exif-orientation', - 'exif-samplesperpixel', - 'exif-planarconfiguration', - 'exif-ycbcrsubsampling', - 'exif-ycbcrpositioning', - 'exif-xresolution', - 'exif-yresolution', - 'exif-resolutionunit', - 'exif-stripoffsets', - 'exif-rowsperstrip', - 'exif-stripbytecounts', - 'exif-jpeginterchangeformat', - 'exif-jpeginterchangeformatlength', - 'exif-transferfunction', - 'exif-whitepoint', - 'exif-primarychromaticities', - 'exif-ycbcrcoefficients', - 'exif-referenceblackwhite', - 'exif-datetime', - 'exif-imagedescription', - 'exif-make', - 'exif-model', - 'exif-software', - 'exif-artist', - 'exif-copyright', - 'exif-exifversion', - 'exif-flashpixversion', - 'exif-colorspace', - 'exif-componentsconfiguration', - 'exif-compressedbitsperpixel', - 'exif-pixelydimension', - 'exif-pixelxdimension', - 'exif-makernote', - 'exif-usercomment', - 'exif-relatedsoundfile', - 'exif-datetimeoriginal', - 'exif-datetimedigitized', - 'exif-subsectime', - 'exif-subsectimeoriginal', - 'exif-subsectimedigitized', - 'exif-exposuretime', - 'exif-exposuretime-format', - 'exif-fnumber', - 'exif-fnumber-format', - 'exif-exposureprogram', - 'exif-spectralsensitivity', - 'exif-isospeedratings', - 'exif-oecf', - 'exif-shutterspeedvalue', - 'exif-aperturevalue', - 'exif-brightnessvalue', - 'exif-exposurebiasvalue', - 'exif-maxaperturevalue', - 'exif-subjectdistance', - 'exif-meteringmode', - 'exif-lightsource', - 'exif-flash', - 'exif-focallength', - 'exif-focallength-format', - 'exif-subjectarea', - 'exif-flashenergy', - 'exif-spatialfrequencyresponse', - 'exif-focalplanexresolution', - 'exif-focalplaneyresolution', - 'exif-focalplaneresolutionunit', - 'exif-subjectlocation', - 'exif-exposureindex', - 'exif-sensingmethod', - 'exif-filesource', - 'exif-scenetype', - 'exif-cfapattern', - 'exif-customrendered', - 'exif-exposuremode', - 'exif-whitebalance', - 'exif-digitalzoomratio', - 'exif-focallengthin35mmfilm', - 'exif-scenecapturetype', - 'exif-gaincontrol', - 'exif-contrast', - 'exif-saturation', - 'exif-sharpness', - 'exif-devicesettingdescription', - 'exif-subjectdistancerange', - 'exif-imageuniqueid', - 'exif-gpsversionid', - 'exif-gpslatituderef', - 'exif-gpslatitude', - 'exif-gpslongituderef', - 'exif-gpslongitude', - 'exif-gpsaltituderef', - 'exif-gpsaltitude', - 'exif-gpstimestamp', - 'exif-gpssatellites', - 'exif-gpsstatus', - 'exif-gpsmeasuremode', - 'exif-gpsdop', - 'exif-gpsspeedref', - 'exif-gpsspeed', - 'exif-gpstrackref', - 'exif-gpstrack', - 'exif-gpsimgdirectionref', - 'exif-gpsimgdirection', - 'exif-gpsmapdatum', - 'exif-gpsdestlatituderef', - 'exif-gpsdestlatitude', - 'exif-gpsdestlongituderef', - 'exif-gpsdestlongitude', - 'exif-gpsdestbearingref', - 'exif-gpsdestbearing', - 'exif-gpsdestdistanceref', - 'exif-gpsdestdistance', - 'exif-gpsprocessingmethod', - 'exif-gpsareainformation', - 'exif-gpsdatestamp', - 'exif-gpsdifferential', - 'exif-compression-1', - 'exif-compression-6', - 'exif-photometricinterpretation-2', - 'exif-photometricinterpretation-6', - 'exif-orientation-1', - 'exif-orientation-2', - 'exif-orientation-3', - 'exif-orientation-4', - 'exif-orientation-5', - 'exif-orientation-6', - 'exif-orientation-7', - 'exif-orientation-8', - 'exif-planarconfiguration-1', - 'exif-planarconfiguration-2', - 'exif-xyresolution-i', - 'exif-xyresolution-c', - 'exif-colorspace-1', - 'exif-colorspace-ffff.h', - 'exif-componentsconfiguration-0', - 'exif-componentsconfiguration-1', - 'exif-componentsconfiguration-2', - 'exif-componentsconfiguration-3', - 'exif-componentsconfiguration-4', - 'exif-componentsconfiguration-5', - 'exif-componentsconfiguration-6', - 'exif-exposureprogram-0', - 'exif-exposureprogram-1', - 'exif-exposureprogram-2', - 'exif-exposureprogram-3', - 'exif-exposureprogram-4', - 'exif-exposureprogram-5', - 'exif-exposureprogram-6', - 'exif-exposureprogram-7', - 'exif-exposureprogram-8', - 'exif-subjectdistance-value', - 'exif-meteringmode-0', - 'exif-meteringmode-1', - 'exif-meteringmode-2', - 'exif-meteringmode-3', - 'exif-meteringmode-4', - 'exif-meteringmode-5', - 'exif-meteringmode-6', - 'exif-meteringmode-255', - 'exif-lightsource-0', - 'exif-lightsource-1', - 'exif-lightsource-2', - 'exif-lightsource-3', - 'exif-lightsource-4', - 'exif-lightsource-9', - 'exif-lightsource-10', - 'exif-lightsource-11', - 'exif-lightsource-12', - 'exif-lightsource-13', - 'exif-lightsource-14', - 'exif-lightsource-15', - 'exif-lightsource-17', - 'exif-lightsource-18', - 'exif-lightsource-19', - 'exif-lightsource-20', - 'exif-lightsource-21', - 'exif-lightsource-22', - 'exif-lightsource-23', - 'exif-lightsource-24', - 'exif-lightsource-255', - 'exif-focalplaneresolutionunit-2', - 'exif-sensingmethod-1', - 'exif-sensingmethod-2', - 'exif-sensingmethod-3', - 'exif-sensingmethod-4', - 'exif-sensingmethod-5', - 'exif-sensingmethod-7', - 'exif-sensingmethod-8', - 'exif-filesource-3', - 'exif-scenetype-1', - 'exif-customrendered-0', - 'exif-customrendered-1', - 'exif-exposuremode-0', - 'exif-exposuremode-1', - 'exif-exposuremode-2', - 'exif-whitebalance-0', - 'exif-whitebalance-1', - 'exif-scenecapturetype-0', - 'exif-scenecapturetype-1', - 'exif-scenecapturetype-2', - 'exif-scenecapturetype-3', - 'exif-gaincontrol-0', - 'exif-gaincontrol-1', - 'exif-gaincontrol-2', - 'exif-gaincontrol-3', - 'exif-gaincontrol-4', - 'exif-contrast-0', - 'exif-contrast-1', - 'exif-contrast-2', - 'exif-saturation-0', - 'exif-saturation-1', - 'exif-saturation-2', - 'exif-sharpness-0', - 'exif-sharpness-1', - 'exif-sharpness-2', - 'exif-subjectdistancerange-0', - 'exif-subjectdistancerange-1', - 'exif-subjectdistancerange-2', - 'exif-subjectdistancerange-3', - 'exif-gpslatitude-n', - 'exif-gpslatitude-s', - 'exif-gpslongitude-e', - 'exif-gpslongitude-w', - 'exif-gpsstatus-a', - 'exif-gpsstatus-v', - 'exif-gpsmeasuremode-2', - 'exif-gpsmeasuremode-3', - 'exif-gpsspeed-k', - 'exif-gpsspeed-m', - 'exif-gpsspeed-n', - 'exif-gpsdirection-t', - 'exif-gpsdirection-m', - ); # All the EXIF messages, may be set as optional if defined as such + private $mIgnoredMessages; # All the messages which should be exist only in the English file + private $mOptionalMessages; # All the messages which may be translated or not, depending on the language /** * Load the list of languages: all the Messages*.php @@ -336,11 +23,16 @@ class languages { * @param $exif Treat the EXIF messages? */ function __construct( $exif = true ) { + global $wgIgnoredMessages, $wgOptionalMessages, $wgEXIFMessages; + $this->mIgnoredMessages = $wgIgnoredMessages; + if ( $exif ) { + $this->mOptionalMessages = array_merge( $wgOptionalMessages ); + } else { + $this->mOptionalMessages = array_merge( $wgOptionalMessages, $wgEXIFMessages ); + } + $this->mLanguages = array_keys( Language::getLanguageNames( true ) ); sort( $this->mLanguages ); - if ( !$exif ) { - $this->mOptionalMessages = array_merge( $this->mOptionalMessages, $this->mEXIFMessages ); - } } /** @@ -353,6 +45,24 @@ class languages { } /** + * Get the ignored messages list. + * + * @return The ignored messages list. + */ + public function getIgnoredMessages() { + return $this->mIgnoredMessages; + } + + /** + * Get the optional messages list. + * + * @return The optional messages list. + */ + public function getOptionalMessages() { + return $this->mOptionalMessages; + } + + /** * Load the raw messages for a specific langauge from the messages file. * * @param $code The langauge code. diff --git a/maintenance/language/messageTypes.inc b/maintenance/language/messageTypes.inc new file mode 100644 index 00000000..f7f1ffeb --- /dev/null +++ b/maintenance/language/messageTypes.inc @@ -0,0 +1,346 @@ +<?php +/** + * Several types of messages. + * + * @package MediaWiki + * @subpackage Maintenance + */ + +/** Ignored messages, which should be exist only in the English messages file. */ +$wgIgnoredMessages = array( + 'sidebar', + 'addsection', + 'anonnotice', + 'autoblock_whitelist', + 'catseparator', + 'googlesearch', + 'exif-make-value', + 'exif-model-value', + 'exif-software-value', + 'history_copyright', + 'licenses', + 'loginend', + 'loginlanguagelinks', + 'markaspatrolledlink', + 'newarticletextanon', + 'newtalkseperator', + 'noarticletextanon', + 'number_of_watching_users_RCview', + 'pagecategorieslink', + 'pubmedurl', + 'randompage-url', + 'rc-change-size', + 'recentchanges-url', + 'revision-nav', + 'rfcurl', + 'shareddescriptionfollows', + 'signupend', + 'sitenotice', + 'sitesubtitle', + 'sitetitle', + 'talkpagetext', + 'trackback', + 'trackbackexcerpt', + 'widthheight', +); + +/** Optional messages, which may be translated only if changed in the other language. */ +$wgOptionalMessages = array( + 'imgmultigotopost', + 'linkprefix', + 'feed-atom', + 'feed-rss', + 'allpages-summary', + 'booksources-summary', + 'ipblocklist-summary', + 'listusers-summary', + 'longpages-summary', + 'preferences-summary', + 'specialpages-summary', + 'whatlinkshere-summary', + 'whatlinkshere-barrow', + 'imagelist-summary', + 'mimesearch-summary', + 'listredirects-summary', + 'uncategorizedpages-summary', + 'uncategorizedcategories-summary', + 'uncategorizedimages-summary', + 'popularpages-summary', + 'wantedcategories-summary', + 'wantedpages-summary', + 'mostlinked-summary', + 'mostlinkedcategories-summary', + 'mostcategories-summary', + 'mostimages-summary', + 'mostrevisions-summary', + 'prefixindex-summary', + 'shortpages-summary', + 'newpages-summary', + 'ancientpages-summary', + 'newimages-summary', + 'unwatchedpages-summary', + 'userrights-summary', + 'brokenredirects-summary', + 'deadendpages-summary', + 'disambiguations-summary', + 'doubleredirects-summary', + 'lonelypages-summary', + 'unusedtemplates-summary', + 'variantname-zh-cn', + 'variantname-zh-tw', + 'variantname-zh-hk', + 'variantname-zh-sg', + 'variantname-zh', + 'variantname-sr-ec', + 'variantname-sr-el', + 'variantname-sr-jc', + 'variantname-sr-jl', + 'variantname-sr', + 'variantname-kk-tr', + 'variantname-kk-kz', + 'variantname-kk-cn', + 'variantname-kk', +); + +/** EXIF messages, which may be set as optional in several checks, but are generally mandatory */ +$wgEXIFMessages = array( + 'exif-imagewidth', + 'exif-imagelength', + 'exif-bitspersample', + 'exif-compression', + 'exif-photometricinterpretation', + 'exif-orientation', + 'exif-samplesperpixel', + 'exif-planarconfiguration', + 'exif-ycbcrsubsampling', + 'exif-ycbcrpositioning', + 'exif-xresolution', + 'exif-yresolution', + 'exif-resolutionunit', + 'exif-stripoffsets', + 'exif-rowsperstrip', + 'exif-stripbytecounts', + 'exif-jpeginterchangeformat', + 'exif-jpeginterchangeformatlength', + 'exif-transferfunction', + 'exif-whitepoint', + 'exif-primarychromaticities', + 'exif-ycbcrcoefficients', + 'exif-referenceblackwhite', + 'exif-datetime', + 'exif-imagedescription', + 'exif-make', + 'exif-model', + 'exif-software', + 'exif-artist', + 'exif-copyright', + 'exif-exifversion', + 'exif-flashpixversion', + 'exif-colorspace', + 'exif-componentsconfiguration', + 'exif-compressedbitsperpixel', + 'exif-pixelydimension', + 'exif-pixelxdimension', + 'exif-makernote', + 'exif-usercomment', + 'exif-relatedsoundfile', + 'exif-datetimeoriginal', + 'exif-datetimedigitized', + 'exif-subsectime', + 'exif-subsectimeoriginal', + 'exif-subsectimedigitized', + 'exif-exposuretime', + 'exif-exposuretime-format', + 'exif-fnumber', + 'exif-fnumber-format', + 'exif-exposureprogram', + 'exif-spectralsensitivity', + 'exif-isospeedratings', + 'exif-oecf', + 'exif-shutterspeedvalue', + 'exif-aperturevalue', + 'exif-brightnessvalue', + 'exif-exposurebiasvalue', + 'exif-maxaperturevalue', + 'exif-subjectdistance', + 'exif-meteringmode', + 'exif-lightsource', + 'exif-flash', + 'exif-focallength', + 'exif-focallength-format', + 'exif-subjectarea', + 'exif-flashenergy', + 'exif-spatialfrequencyresponse', + 'exif-focalplanexresolution', + 'exif-focalplaneyresolution', + 'exif-focalplaneresolutionunit', + 'exif-subjectlocation', + 'exif-exposureindex', + 'exif-sensingmethod', + 'exif-filesource', + 'exif-scenetype', + 'exif-cfapattern', + 'exif-customrendered', + 'exif-exposuremode', + 'exif-whitebalance', + 'exif-digitalzoomratio', + 'exif-focallengthin35mmfilm', + 'exif-scenecapturetype', + 'exif-gaincontrol', + 'exif-contrast', + 'exif-saturation', + 'exif-sharpness', + 'exif-devicesettingdescription', + 'exif-subjectdistancerange', + 'exif-imageuniqueid', + 'exif-gpsversionid', + 'exif-gpslatituderef', + 'exif-gpslatitude', + 'exif-gpslongituderef', + 'exif-gpslongitude', + 'exif-gpsaltituderef', + 'exif-gpsaltitude', + 'exif-gpstimestamp', + 'exif-gpssatellites', + 'exif-gpsstatus', + 'exif-gpsmeasuremode', + 'exif-gpsdop', + 'exif-gpsspeedref', + 'exif-gpsspeed', + 'exif-gpstrackref', + 'exif-gpstrack', + 'exif-gpsimgdirectionref', + 'exif-gpsimgdirection', + 'exif-gpsmapdatum', + 'exif-gpsdestlatituderef', + 'exif-gpsdestlatitude', + 'exif-gpsdestlongituderef', + 'exif-gpsdestlongitude', + 'exif-gpsdestbearingref', + 'exif-gpsdestbearing', + 'exif-gpsdestdistanceref', + 'exif-gpsdestdistance', + 'exif-gpsprocessingmethod', + 'exif-gpsareainformation', + 'exif-gpsdatestamp', + 'exif-gpsdifferential', + 'exif-compression-1', + 'exif-compression-6', + 'exif-unknowndate', + 'exif-photometricinterpretation-2', + 'exif-photometricinterpretation-6', + 'exif-orientation-1', + 'exif-orientation-2', + 'exif-orientation-3', + 'exif-orientation-4', + 'exif-orientation-5', + 'exif-orientation-6', + 'exif-orientation-7', + 'exif-orientation-8', + 'exif-planarconfiguration-1', + 'exif-planarconfiguration-2', + 'exif-xyresolution-i', + 'exif-xyresolution-c', + 'exif-colorspace-1', + 'exif-colorspace-ffff.h', + 'exif-componentsconfiguration-0', + 'exif-componentsconfiguration-1', + 'exif-componentsconfiguration-2', + 'exif-componentsconfiguration-3', + 'exif-componentsconfiguration-4', + 'exif-componentsconfiguration-5', + 'exif-componentsconfiguration-6', + 'exif-exposureprogram-0', + 'exif-exposureprogram-1', + 'exif-exposureprogram-2', + 'exif-exposureprogram-3', + 'exif-exposureprogram-4', + 'exif-exposureprogram-5', + 'exif-exposureprogram-6', + 'exif-exposureprogram-7', + 'exif-exposureprogram-8', + 'exif-subjectdistance-value', + 'exif-meteringmode-0', + 'exif-meteringmode-1', + 'exif-meteringmode-2', + 'exif-meteringmode-3', + 'exif-meteringmode-4', + 'exif-meteringmode-5', + 'exif-meteringmode-6', + 'exif-meteringmode-255', + 'exif-lightsource-0', + 'exif-lightsource-1', + 'exif-lightsource-2', + 'exif-lightsource-3', + 'exif-lightsource-4', + 'exif-lightsource-9', + 'exif-lightsource-10', + 'exif-lightsource-11', + 'exif-lightsource-12', + 'exif-lightsource-13', + 'exif-lightsource-14', + 'exif-lightsource-15', + 'exif-lightsource-17', + 'exif-lightsource-18', + 'exif-lightsource-19', + 'exif-lightsource-20', + 'exif-lightsource-21', + 'exif-lightsource-22', + 'exif-lightsource-23', + 'exif-lightsource-24', + 'exif-lightsource-255', + 'exif-focalplaneresolutionunit-2', + 'exif-sensingmethod-1', + 'exif-sensingmethod-2', + 'exif-sensingmethod-3', + 'exif-sensingmethod-4', + 'exif-sensingmethod-5', + 'exif-sensingmethod-7', + 'exif-sensingmethod-8', + 'exif-filesource-3', + 'exif-scenetype-1', + 'exif-customrendered-0', + 'exif-customrendered-1', + 'exif-exposuremode-0', + 'exif-exposuremode-1', + 'exif-exposuremode-2', + 'exif-whitebalance-0', + 'exif-whitebalance-1', + 'exif-scenecapturetype-0', + 'exif-scenecapturetype-1', + 'exif-scenecapturetype-2', + 'exif-scenecapturetype-3', + 'exif-gaincontrol-0', + 'exif-gaincontrol-1', + 'exif-gaincontrol-2', + 'exif-gaincontrol-3', + 'exif-gaincontrol-4', + 'exif-contrast-0', + 'exif-contrast-1', + 'exif-contrast-2', + 'exif-saturation-0', + 'exif-saturation-1', + 'exif-saturation-2', + 'exif-sharpness-0', + 'exif-sharpness-1', + 'exif-sharpness-2', + 'exif-subjectdistancerange-0', + 'exif-subjectdistancerange-1', + 'exif-subjectdistancerange-2', + 'exif-subjectdistancerange-3', + 'exif-gpslatitude-n', + 'exif-gpslatitude-s', + 'exif-gpslongitude-e', + 'exif-gpslongitude-w', + 'exif-gpsstatus-a', + 'exif-gpsstatus-v', + 'exif-gpsmeasuremode-2', + 'exif-gpsmeasuremode-3', + 'exif-gpsspeed-k', + 'exif-gpsspeed-m', + 'exif-gpsspeed-n', + 'exif-gpsdirection-t', + 'exif-gpsdirection-m', +); + +?> diff --git a/maintenance/language/messages.inc b/maintenance/language/messages.inc new file mode 100644 index 00000000..4fbe2ed1 --- /dev/null +++ b/maintenance/language/messages.inc @@ -0,0 +1,2081 @@ +<?php +/** + * Define the messages structure in the messages file, for a future automated rewriting. + * + * @package MediaWiki + * @subpackage Maintenance + */ + +/** The structure of the messages, divided to blocks */ +$wgMessageStrucutre = array( + 'sidebar' => array( + 'sidebar', + ), + 'toggles' => array( + 'tog-underline', + 'tog-highlightbroken', + 'tog-justify', + 'tog-hideminor', + 'tog-extendwatchlist', + 'tog-usenewrc', + 'tog-numberheadings', + 'tog-showtoolbar', + 'tog-editondblclick', + 'tog-editsection', + 'tog-editsectiononrightclick', + 'tog-showtoc', + 'tog-rememberpassword', + 'tog-editwidth', + 'tog-watchcreations', + 'tog-watchdefault', + 'tog-watchmoves', + 'tog-watchdeletion', + 'tog-minordefault', + 'tog-previewontop', + 'tog-previewonfirst', + 'tog-nocache', + 'tog-enotifwatchlistpages', + 'tog-enotifusertalkpages', + 'tog-enotifminoredits', + 'tog-enotifrevealaddr', + 'tog-shownumberswatching', + 'tog-fancysig', + 'tog-externaleditor', + 'tog-externaldiff', + 'tog-showjumplinks', + 'tog-uselivepreview', + 'tog-forceeditsummary', + 'tog-watchlisthideown', + 'tog-watchlisthidebots', + 'tog-watchlisthideminor', + 'tog-nolangconversion', + 'tog-ccmeonemails', + ), + 'underline' => array( + 'underline-always', + 'underline-never', + 'underline-default', + ), + 'skinpreview' => array( + 'skinpreview', + ), + 'dates' => array( + 'sunday', + 'monday', + 'tuesday', + 'wednesday', + 'thursday', + 'friday', + 'saturday', + 'sun', + 'mon', + 'tue', + 'wed', + 'thu', + 'fri', + 'sat', + 'january', + 'february', + 'march', + 'april', + 'may_long', + 'june', + 'july', + 'august', + 'september', + 'october', + 'november', + 'december', + 'january-gen', + 'february-gen', + 'march-gen', + 'april-gen', + 'may-gen', + 'june-gen', + 'july-gen', + 'august-gen', + 'september-gen', + 'october-gen', + 'november-gen', + 'december-gen', + 'jan', + 'feb', + 'mar', + 'apr', + 'may', + 'jun', + 'jul', + 'aug', + 'sep', + 'oct', + 'nov', + 'dec', + ), + 'categories' => array( + 'categories', + 'pagecategories', + 'pagecategorieslink', + 'category_header', + 'subcategories', + 'category-media-header', + ), + 'mainpage' => array( + 'linkprefix', + 'mainpage', + 'mainpagetext', + 'mainpagedocfooter', + ), + 'miscellaneous1' => array( + 'portal', + 'portal-url', + 'about', + 'aboutsite', + 'aboutpage', + 'article', + 'help', + 'helppage', + 'bugreports', + 'bugreportspage', + 'sitesupport', + 'sitesupport-url', + 'faq', + 'faqpage', + 'edithelp', + 'newwindow', + 'edithelppage', + 'cancel', + 'qbfind', + 'qbbrowse', + 'qbedit', + 'qbpageoptions', + 'qbpageinfo', + 'qbmyoptions', + 'qbspecialpages', + 'moredotdotdot', + 'mypage', + 'mytalk', + 'anontalk', + 'navigation', + ), + 'metadata_help' => array( + 'metadata_help', + ), + 'currentevents' => array( + 'currentevents', + 'currentevents-url', + ), + 'miscellaneous2' => array( + 'disclaimers', + 'disclaimerpage', + 'privacy', + 'privacypage', + 'errorpagetitle', + 'returnto', + 'tagline', + 'help', + 'search', + 'searchbutton', + 'go', + 'searcharticle', + 'history', + 'history_short', + 'updatedmarker', + 'info_short', + 'printableversion', + 'permalink', + 'print', + 'edit', + 'editthispage', + 'delete', + 'deletethispage', + 'undelete_short', + 'protect', + 'protectthispage', + 'unprotect', + 'unprotectthispage', + 'newpage', + 'talkpage', + 'specialpage', + 'personaltools', + 'postcomment', + 'addsection', + 'articlepage', + 'talk', + 'views', + 'toolbox', + 'userpage', + 'projectpage', + 'imagepage', + 'mediawikipage', + 'templatepage', + 'viewhelppage', + 'categorypage', + 'viewtalkpage', + 'otherlanguages', + 'redirectedfrom', + 'redirectpagesub', + 'lastmodifiedat', + 'viewcount', + 'copyright', + 'protectedpage', + 'jumpto', + 'jumptonavigation', + 'jumptosearch', + ), + 'badaccess' => array( + 'badaccess', + 'badaccess-group0', + 'badaccess-group1', + 'badaccess-group2', + 'badaccess-groups', + ), + 'versionrequired' => array( + 'versionrequired', + 'versionrequiredtext', + ), + 'miscellaneous3' => array( + 'widthheight', + 'ok', + 'sitetitle', + 'pagetitle', + 'sitesubtitle', + 'retrievedfrom', + 'youhavenewmessages', + 'newmessageslink', + 'newmessagesdifflink', + 'editsection', + 'editold', + 'editsectionhint', + 'toc', + 'showtoc', + 'hidetoc', + 'thisisdeleted', + 'viewdeleted', + 'restorelink', + 'feedlinks', + 'feed-invalid', + 'feed-atom', + 'feed-rss', + 'sitenotice', + 'anonnotice', + ), + 'nstab' => array( + 'nstab-main', + 'nstab-user', + 'nstab-media', + 'nstab-special', + 'nstab-project', + 'nstab-image', + 'nstab-mediawiki', + 'nstab-template', + 'nstab-help', + 'nstab-category', + ), + 'main' => array( + 'nosuchaction', + 'nosuchactiontext', + 'nosuchspecialpage', + 'nospecialpagetext', + ), + 'errors' => array( + 'error', + 'databaseerror', + 'dberrortext', + 'dberrortextcl', + 'noconnect', + 'nodb', + 'cachederror', + 'laggedslavemode', + 'readonly', + 'enterlockreason', + 'readonlytext', + 'missingarticle', + 'readonly_lag', + 'internalerror', + 'filecopyerror', + 'filerenameerror', + 'filedeleteerror', + 'filenotfound', + 'unexpected', + 'formerror', + 'badarticleerror', + 'cannotdelete', + 'badtitle', + 'badtitletext', + 'perfdisabled', + 'perfdisabledsub', + 'perfcached', + 'perfcachedts', + 'querypage-no-updates', + 'wrong_wfQuery_params', + 'viewsource', + 'viewsourcefor', + 'protectedpagetext', + 'viewsourcetext', + 'protectedinterface', + 'editinginterface', + 'sqlhidden', + ), + 'login' => array( + 'logouttitle', + 'logouttext', + 'welcomecreation', + 'loginpagetitle', + 'yourname', + 'yourpassword', + 'yourpasswordagain', + 'remembermypassword', + 'yourdomainname', + 'externaldberror', + 'loginproblem', + 'alreadyloggedin', + 'login', + 'loginprompt', + 'userlogin', + 'logout', + 'userlogout', + 'notloggedin', + 'nologin', + 'nologinlink', + 'createaccount', + 'gotaccount', + 'gotaccountlink', + 'createaccountmail', + 'badretype', + 'userexists', + 'youremail', + 'username', + 'uid', + 'yourrealname', + 'yourlanguage', + 'yourvariant', + 'yournick', + 'badsig', + 'email', + 'prefs-help-email-enotif', + 'prefs-help-realname', + 'loginerror', + 'prefs-help-email', + 'nocookiesnew', + 'nocookieslogin', + 'noname', + 'loginsuccesstitle', + 'loginsuccess', + 'nosuchuser', + 'nosuchusershort', + 'nouserspecified', + 'wrongpassword', + 'wrongpasswordempty', + 'mailmypassword', + 'passwordremindertitle', + 'passwordremindertext', + 'noemail', + 'passwordsent', + 'blocked-mailpassword', + 'eauthentsent', + 'throttled-mailpassword', + 'loginend', + 'signupend', + 'mailerror', + 'acct_creation_throttle_hit', + 'emailauthenticated', + 'emailnotauthenticated', + 'noemailprefs', + 'emailconfirmlink', + 'invalidemailaddress', + 'accountcreated', + 'accountcreatedtext', + ), + 'resetpass' => array( + 'resetpass', + 'resetpass_announce', + 'resetpass_text', + 'resetpass_header', + 'resetpass_submit', + 'resetpass_success', + 'resetpass_bad_temporary', + 'resetpass_forbidden', + 'resetpass_missing', + ), + 'toolbar' => array( + 'bold_sample', + 'bold_tip', + 'italic_sample', + 'italic_tip', + 'link_sample', + 'link_tip', + 'extlink_sample', + 'extlink_tip', + 'headline_sample', + 'headline_tip', + 'math_sample', + 'math_tip', + 'nowiki_sample', + 'nowiki_tip', + 'image_sample', + 'image_tip', + 'media_sample', + 'media_tip', + 'sig_tip', + 'hr_tip', + ), + 'edit' => array( + 'summary', + 'subject', + 'minoredit', + 'watchthis', + 'savearticle', + 'preview', + 'showpreview', + 'showlivepreview', + 'showdiff', + 'anoneditwarning', + 'missingsummary', + 'missingcommenttext', + 'missingcommentheader', + 'summary-preview', + 'subject-preview', + 'blockedtitle', + 'blockedtext', + 'blockedoriginalsource', + 'blockededitsource', + 'whitelistedittitle', + 'whitelistedittext', + 'whitelistreadtitle', + 'whitelistreadtext', + 'whitelistacctitle', + 'whitelistacctext', + 'confirmedittitle', + 'confirmedittext', + 'loginreqtitle', + 'loginreqlink', + 'loginreqpagetext', + 'accmailtitle', + 'accmailtext', + 'newarticle', + 'newarticletext', + 'newarticletextanon', + 'talkpagetext', + 'anontalkpagetext', + 'noarticletext', + 'noarticletextanon', + 'clearyourcache', + 'usercssjsyoucanpreview', + 'usercsspreview', + 'userjspreview', + 'userinvalidcssjstitle', + 'updated', + 'note', + 'previewnote', + 'previewconflict', + 'session_fail_preview', + 'session_fail_preview_html', + 'importing', + 'editing', + 'editinguser', + 'editingsection', + 'editingcomment', + 'editconflict', + 'explainconflict', + 'yourtext', + 'storedversion', + 'nonunicodebrowser', + 'editingold', + 'yourdiff', + 'copyrightwarning', + 'copyrightwarning2', + 'longpagewarning', + 'longpageerror', + 'readonlywarning', + 'protectedpagewarning', + 'semiprotectedpagewarning', + 'templatesused', + 'templatesusedpreview', + 'templatesusedsection', + 'template-protected', + 'template-semiprotected', + 'edittools', + 'nocreatetitle', + 'nocreatetext', + ), + 'undo' => array( + 'undo-success', + 'undo-failure', + 'undo-summary', + ), + 'cantcreateaccount' => array( + 'cantcreateaccounttitle', + 'cantcreateaccounttext', + ), + 'history' => array( + 'revhistory', + 'viewpagelogs', + 'nohistory', + 'revnotfound', + 'revnotfoundtext', + 'loadhist', + 'currentrev', + 'revisionasof', + 'revision-info', + 'revision-nav', + 'previousrevision', + 'nextrevision', + 'currentrevisionlink', + 'cur', + 'next', + 'last', + 'orig', + 'histlegend', + 'history_copyright', + 'deletedrev', + 'histfirst', + 'histlast', + 'rev-deleted-comment', + 'rev-deleted-user', + 'rev-deleted-text-permission', + 'rev-deleted-text-view', + 'rev-delundel', + ), + 'history-feed' => array( + 'history-feed-title', + 'history-feed-description', + 'history-feed-item-nocomment', + 'history-feed-empty', + ), + 'revdelete' => array( + 'revisiondelete', + 'revdelete-nooldid-title', + 'revdelete-nooldid-text', + 'revdelete-selected', + 'revdelete-text', + 'revdelete-legend', + 'revdelete-hide-text', + 'revdelete-hide-comment', + 'revdelete-hide-user', + 'revdelete-hide-restricted', + 'revdelete-log', + 'revdelete-submit', + 'revdelete-logentry', + ), + 'diffs' => array( + 'difference', + 'loadingrev', + 'lineno', + 'editcurrent', + 'selectnewerversionfordiff', + 'selectolderversionfordiff', + 'compareselectedversions', + 'editundo', + 'diff-multi', + ), + 'search' => array( + 'searchresults', + 'searchresulttext', + 'searchsubtitle', + 'searchsubtitleinvalid', + 'badquery', + 'badquerytext', + 'matchtotals', + 'noexactmatch', + 'titlematches', + 'notitlematches', + 'textmatches', + 'notextmatches', + 'prevn', + 'nextn', + 'viewprevnext', + 'showingresults', + 'showingresultsnum', + 'nonefound', + 'powersearch', + 'powersearchtext', + 'searchdisabled', + 'googlesearch', + 'blanknamespace', + ), + 'preferences' => array( + 'preferences', + 'preferences-summary', + 'mypreferences', + 'prefsnologin', + 'prefsnologintext', + 'prefsreset', + 'qbsettings', + 'changepassword', + 'skin', + 'math', + 'dateformat', + '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-personal', + 'prefs-rc', + 'prefs-watchlist', + 'prefs-watchlist-days', + 'prefs-watchlist-edits', + 'prefs-misc', + 'saveprefs', + 'resetprefs', + 'oldpassword', + 'newpassword', + 'retypenew', + 'textboxsize', + 'rows', + 'columns', + 'searchresultshead', + 'resultsperpage', + 'contextlines', + 'contextchars', + 'stubthreshold', + 'recentchangescount', + 'savedprefs', + 'timezonelegend', + 'timezonetext', + 'localtime', + 'timezoneoffset', + 'servertime', + 'guesstimezone', + 'allowemail', + 'defaultns', + 'default', + 'files', + ), + 'userrights' => array( + 'userrights-lookup-user', + 'userrights-user-editname', + 'editusergroup', + 'userrights-editusergroup', + 'saveusergroups', + 'userrights-groupsmember', + 'userrights-groupsavailable', + 'userrights-groupshelp', + ), + 'group' => array( + 'group', + 'group-bot', + 'group-sysop', + 'group-bureaucrat', + 'group-all', + ), + 'group-member' => array( + 'group-bot-member', + 'group-sysop-member', + 'group-bureaucrat-member', + ), + 'grouppage' => array( + 'grouppage-bot', + 'grouppage-sysop', + 'grouppage-bureaucrat', + ), + 'recentchanges' => array( + 'changes', + 'recentchanges', + 'recentchanges-url', + 'recentchangestext', + 'recentchanges-feed-description', + 'rcnote', + 'rcnotefrom', + 'rclistfrom', + 'rcshowhideminor', + 'rcshowhidebots', + 'rcshowhideliu', + 'rcshowhideanons', + 'rcshowhidepatr', + 'rcshowhidemine', + 'rclinks', + 'diff', + 'hist', + 'hide', + 'show', + 'minoreditletter', + 'newpageletter', + 'boteditletter', + 'sectionlink', + 'number_of_watching_users_RCview', + 'number_of_watching_users_pageview', + 'rc_categories', + 'rc_categories_any', + 'rc-change-size', + ), + 'upload' => array( + 'upload', + 'uploadbtn', + 'reupload', + 'reuploaddesc', + 'uploadnologin', + 'uploadnologintext', + 'upload_directory_read_only', + 'uploaderror', + 'uploadtext', + 'uploadlog', + 'uploadlogpage', + 'uploadlogpagetext', + 'filename', + 'filedesc', + 'fileuploadsummary', + 'filestatus', + 'filesource', + 'copyrightpage', + 'copyrightpagename', + 'uploadedfiles', + 'ignorewarning', + 'ignorewarnings', + 'minlength', + 'illegalfilename', + 'badfilename', + 'badfiletype', + 'large-file', + 'largefileserver', + 'emptyfile', + 'fileexists', + 'fileexists-forbidden', + 'fileexists-shared-forbidden', + 'successfulupload', + 'fileuploaded', + 'uploadwarning', + 'savefile', + 'uploadedimage', + 'uploaddisabled', + 'uploaddisabledtext', + 'uploadscripted', + 'uploadcorrupt', + 'uploadvirus', + 'sourcefilename', + 'destfilename', + 'watchthisupload', + 'filewasdeleted', + ), + 'upload-errors' => array( + 'upload-proto-error', + 'upload-proto-error-text', + 'upload-file-error', + 'upload-file-error-text', + 'upload-misc-error', + 'upload-misc-error-text', + ), + 'upload-curl-errors' => array( + 'upload-curl-error6', + 'upload-curl-error6-text', + 'upload-curl-error28', + 'upload-curl-error28-text', + ), + 'licenses' => array( + 'license', + 'nolicense', + 'licenses', + 'upload_source_url', + 'upload_source_file', + ), + 'imagelist' => array( + 'imagelist', + 'imagelist-summary', + 'imagelisttext', + 'imagelistforuser', + 'getimagelist', + 'ilsubmit', + 'showlast', + 'byname', + 'bydate', + 'bysize', + 'imgdelete', + 'imgdesc', + 'imgfile', + 'imglegend', + 'imghistory', + 'revertimg', + 'deleteimg', + 'deleteimgcompletely', + 'imghistlegend', + 'imagelinks', + 'linkstoimage', + 'nolinkstoimage', + 'sharedupload', + 'shareduploadwiki', + 'shareduploadwiki-linktext', + 'shareddescriptionfollows', + 'noimage', + 'noimage-linktext', + 'uploadnewversion-linktext', + 'imagelist_date', + 'imagelist_name', + 'imagelist_user', + 'imagelist_size', + 'imagelist_description', + 'imagelist_search_for', + ), + 'mimesearch' => array( + 'mimesearch', + 'mimesearch-summary', + 'mimetype', + 'download', + ), + 'unwatchedpages' => array( + 'unwatchedpages', + 'unwatchedpages-summary', + ), + 'listredirects' => array( + 'listredirects', + 'listredirects-summary', + ), + 'unusedtemplates' => array( + 'unusedtemplates', + 'unusedtemplates-summary', + 'unusedtemplatestext', + 'unusedtemplateswlh', + ), + 'randomredirect' => array( + 'randomredirect', + ), + 'statistics' => array( + 'statistics', + 'sitestats', + 'userstats', + 'sitestatstext', + 'userstatstext', + 'statistics-mostpopular', + ), + 'disambiguations' => array( + 'disambiguations', + 'disambiguations-summary', + 'disambiguationspage', + 'disambiguationstext', + ), + 'doubleredirects' => array( + 'doubleredirects', + 'doubleredirects-summary', + 'doubleredirectstext', + ), + 'brokenredirects' => array( + 'brokenredirects', + 'brokenredirects-summary', + 'brokenredirectstext', + ), + 'specialpages' => array( + 'nbytes', + 'ncategories', + 'nlinks', + 'nmembers', + 'nrevisions', + 'nviews', + 'lonelypages', + 'lonelypages-summary', + 'lonelypagestext', + 'uncategorizedpages', + 'uncategorizedpages-summary', + 'uncategorizedcategories', + 'uncategorizedcategories-summary', + 'uncategorizedimages', + 'uncategorizedimages-summary', + 'unusedcategories', + 'unusedimages', + 'popularpages', + 'popularpages-summary', + 'wantedcategories', + 'wantedcategories-summary', + 'wantedpages', + 'wantedpages-summary', + 'mostlinked', + 'mostlinked-summary', + 'mostlinkedcategories', + 'mostlinkedcategories-summary', + 'mostcategories', + 'mostcategories-summary', + 'mostimages', + 'mostimages-summary', + 'mostrevisions', + 'mostrevisions-summary', + 'allpages', + 'allpages-summary', + 'prefixindex', + 'prefixindex-summary', + 'randompage', + 'randompage-url', + 'shortpages', + 'shortpages-summary', + 'longpages', + 'longpages-summary', + 'deadendpages', + 'deadendpages-summary', + 'deadendpagestext', + 'listusers', + 'listusers-summary', + 'specialpages', + 'specialpages-summary', + 'spheading', + 'restrictedpheading', + 'recentchangeslinked', + 'rclsub', + 'newpages', + 'newpages-summary', + 'newpages-username', + 'ancientpages', + 'ancientpages-summary', + 'intl', + 'move', + 'movethispage', + 'unusedimagestext', + 'unusedcategoriestext', + ), + 'booksources' => array( + 'booksources', + 'booksources-summary', + 'booksources-search-legend', + 'booksources-isbn', + 'booksources-go', + 'booksources-text', + ), + 'specialpages2' => array( + 'categoriespagetext', + 'data', + 'userrights', + 'userrights-summary', + 'groups', + 'isbn', + 'rfcurl', + 'pubmedurl', + 'alphaindexline', + 'version', + 'log', + 'alllogstext', + 'logempty', + ), + 'allpages' => array( + 'nextpage', + 'prevpage', + 'allpagesfrom', + 'allarticles', + 'allinnamespace', + 'allnotinnamespace', + 'allpagesprev', + 'allpagesnext', + 'allpagessubmit', + 'allpagesprefix', + 'allpagesbadtitle', + ), + 'listusers' => array( + 'listusersfrom', + ), + 'emailuser' => array( + 'mailnologin', + 'mailnologintext', + 'emailuser', + 'emailpage', + 'emailpagetext', + 'usermailererror', + 'defemailsubject', + 'noemailtitle', + 'noemailtext', + 'emailfrom', + 'emailto', + 'emailsubject', + 'emailmessage', + 'emailsend', + 'emailccme', + 'emailccsubject', + 'emailsent', + 'emailsenttext', + ), + 'watchlist' => array( + 'watchlist', + 'watchlistfor', + 'nowatchlist', + 'watchlistanontext', + 'watchlistcount', + 'clearwatchlist', + 'watchlistcleartext', + 'watchlistclearbutton', + 'watchlistcleardone', + 'watchnologin', + 'watchnologintext', + 'addedwatch', + 'addedwatchtext', + 'removedwatch', + 'removedwatchtext', + 'watch', + 'watchthispage', + 'unwatch', + 'unwatchthispage', + 'notanarticle', + 'watchnochange', + 'watchdetails', + 'wlheader-enotif', + 'wlheader-showupdated', + 'watchmethod-recent', + 'watchmethod-list', + 'removechecked', + 'watchlistcontains', + 'watcheditlist', + 'removingchecked', + 'couldntremove', + 'iteminvalidname', + 'wlnote', + 'wlshowlast', + 'wlsaved', + 'watchlist-show-bots', + 'watchlist-hide-bots', + 'watchlist-show-own', + 'watchlist-hide-own', + 'watchlist-show-minor', + 'watchlist-hide-minor', + 'wldone', + ), + 'watching' => array( + 'watching', + 'unwatching', + ), + 'enotif' => array( + 'enotif_mailer', + 'enotif_reset', + 'enotif_newpagetext', + 'changed', + 'created', + 'enotif_subject', + 'enotif_lastvisited', + 'enotif_body', + ), + 'deleteprotectrev' => array( + 'deletepage', + 'confirm', + 'excontent', + 'excontentauthor', + 'exbeforeblank', + 'exblank', + 'confirmdelete', + 'deletesub', + 'historywarning', + 'confirmdeletetext', + 'actioncomplete', + 'deletedtext', + 'deletedarticle', + 'dellogpage', + 'dellogpagetext', + 'deletionlog', + 'reverted', + 'deletecomment', + 'imagereverted', + 'rollback', + 'rollback_short', + 'rollbacklink', + 'rollbackfailed', + 'cantrollback', + 'alreadyrolled', + 'editcomment', + 'revertpage', + 'sessionfailure', + 'protectlogpage', + 'protectlogtext', + 'protectedarticle', + 'unprotectedarticle', + 'protectsub', + 'confirmprotecttext', + 'confirmprotect', + 'protectmoveonly', + 'protectcomment', + 'unprotectsub', + 'confirmunprotecttext', + 'confirmunprotect', + 'unprotectcomment', + 'protect-unchain', + 'protect-text', + 'protect-viewtext', + 'protect-default', + 'protect-level-autoconfirmed', + 'protect-level-sysop', + ), + 'restrictions' => array( + 'restriction-edit', + 'restriction-move', + ), + 'undelete' => array( + 'undelete', + 'undeletepage', + 'viewdeletedpage', + 'undeletepagetext', + 'undeleteextrahelp', + 'undeletearticle', + 'undeleterevisions', + 'undeletehistory', + 'undeletehistorynoadmin', + 'undeleterevision', + 'undeleterevision-missing', + 'undeletebtn', + 'undeletereset', + 'undeletecomment', + 'undeletedarticle', + 'undeletedrevisions', + 'undeletedrevisions-files', + 'undeletedfiles', + 'cannotundelete', + 'undeletedpage', + ), + 'nsform' => array( + 'namespace', + 'invert', + ), + 'contributions' => array( + 'contributions', + 'mycontris', + 'contribsub', + 'nocontribs', + 'ucnote', + 'uclinks', + 'uctop', + 'newbies', + ), + 'sp-contributions' => array( + 'sp-contributions-newest', + 'sp-contributions-oldest', + 'sp-contributions-newer', + 'sp-contributions-older', + 'sp-contributions-newbies-sub', + 'sp-contributions-blocklog', + ), + 'newimages-showfrom' => array( + 'sp-newimages-showfrom', + ), + 'whatlinkshere' => array( + 'whatlinkshere', + 'whatlinkshere-summary', + 'whatlinkshere-barrow', + 'notargettitle', + 'notargettext', + 'linklistsub', + 'linkshere', + 'nolinkshere', + 'isredirect', + 'istemplate', + ), + 'block' => array( + 'blockip', + 'blockiptext', + 'ipaddress', + 'ipadressorusername', + 'ipbexpiry', + 'ipbreason', + 'ipbanononly', + 'ipbcreateaccount', + 'ipbenableautoblock', + 'ipbsubmit', + 'ipbother', + 'ipboptions', + 'ipbotheroption', + 'badipaddress', + 'blockipsuccesssub', + 'blockipsuccesstext', + 'unblockip', + 'unblockiptext', + 'ipusubmit', + 'unblocked', + 'ipblocklist', + 'ipblocklist-summary', + 'blocklistline', + 'infiniteblock', + 'expiringblock', + 'anononlyblock', + 'noautoblockblock', + 'createaccountblock', + 'ipblocklistempty', + 'blocklink', + 'unblocklink', + 'contribslink', + 'autoblocker', + 'blocklogpage', + 'blocklogentry', + 'blocklogtext', + 'unblocklogentry', + 'range_block_disabled', + 'ipb_expiry_invalid', + 'ipb_already_blocked', + 'ip_range_invalid', + 'proxyblocker', + 'ipb_cant_unblock', + 'proxyblockreason', + 'proxyblocksuccess', + 'sorbs', + 'sorbsreason', + 'sorbs_create_account_reason', + ), + 'developertools' => array( + 'lockdb', + 'unlockdb', + 'lockdbtext', + 'unlockdbtext', + 'lockconfirm', + 'unlockconfirm', + 'lockbtn', + 'unlockbtn', + 'locknoconfirm', + 'lockdbsuccesssub', + 'unlockdbsuccesssub', + 'lockdbsuccesstext', + 'unlockdbsuccesstext', + 'lockfilenotwritable', + 'databasenotlocked', + ), + 'makesysop' => array( + 'makesysoptitle', + 'makesysoptext', + 'makesysopname', + 'makesysopsubmit', + 'makesysopok', + 'makesysopfail', + 'setbureaucratflag', + 'rightslog', + 'rightslogtext', + 'rightslogentry', + 'rights', + 'set_user_rights', + 'user_rights_set', + 'set_rights_fail', + 'makesysop', + 'already_sysop', + 'already_bureaucrat', + 'rightsnone', + ), + 'movepage' => array( + 'movepage', + 'movepagetext', + 'movepagetalktext', + 'movearticle', + 'movenologin', + 'movenologintext', + 'newtitle', + 'move-watch', + 'movepagebtn', + 'pagemovedsub', + 'pagemovedtext', + 'articleexists', + 'talkexists', + 'movedto', + 'movetalk', + 'talkpagemoved', + 'talkpagenotmoved', + '1movedto2', + '1movedto2_redir', + 'movelogpage', + 'movelogpagetext', + 'movereason', + 'revertmove', + 'delete_and_move', + 'delete_and_move_text', + 'delete_and_move_confirm', + 'delete_and_move_reason', + 'selfmove', + 'immobile_namespace', + ), + 'export' => array( + 'export', + 'exporttext', + 'exportcuronly', + 'exportnohistory', + 'export-submit', + ), + 'allmessages' => array( + 'allmessages', + 'allmessagesname', + 'allmessagesdefault', + 'allmessagescurrent', + 'allmessagestext', + 'allmessagesnotsupportedUI', + 'allmessagesnotsupportedDB', + 'allmessagesfilter', + 'allmessagesmodified', + ), + 'thumbnails' => array( + 'thumbnail-more', + 'missingimage', + 'filemissing', + 'thumbnail_error', + ), + 'import' => array( + 'import', + 'importinterwiki', + 'import-interwiki-text', + 'import-interwiki-history', + 'import-interwiki-submit', + 'import-interwiki-namespace', + 'importtext', + 'importstart', + 'import-revision-count', + 'importnopages', + 'importfailed', + 'importunknownsource', + 'importcantopen', + 'importbadinterwiki', + 'importnotext', + 'importsuccess', + 'importhistoryconflict', + 'importnosources', + 'importnofile', + 'importuploaderror', + ), + 'importlog' => array( + 'importlogpage', + 'importlogpagetext', + 'import-logentry-upload', + 'import-logentry-upload-detail', + 'import-logentry-interwiki', + 'import-logentry-interwiki-detail', + ), + 'accesskeys' => array( + 'accesskey-search', + 'accesskey-minoredit', + 'accesskey-save', + 'accesskey-preview', + 'accesskey-diff', + 'accesskey-compareselectedversions', + 'accesskey-watch', + ), + 'tooltips' => array( + 'tooltip-search', + 'tooltip-minoredit', + 'tooltip-save', + 'tooltip-preview', + 'tooltip-diff', + 'tooltip-compareselectedversions', + 'tooltip-watch', + ), + 'stylesheets' => array( + 'common.css', + 'monobook.css', + ), + 'metadata_cc' => array( + 'nodublincore', + 'nocreativecommons', + 'notacceptable', + ), + 'attribution' => array( + 'anonymous', + 'siteuser', + 'lastmodifiedatby', + 'and', + 'othercontribs', + 'others', + 'siteusers', + 'creditspage', + 'nocredits', + ), + 'spamprotection' => array( + 'spamprotectiontitle', + 'spamprotectiontext', + 'spamprotectionmatch', + 'subcategorycount', + 'categoryarticlecount', + 'category-media-count', + 'listingcontinuesabbrev', + 'spambot_username', + 'spam_reverting', + 'spam_blanking', + ), + 'info' => array( + 'infosubtitle', + 'numedits', + 'numtalkedits', + 'numwatchers', + 'numauthors', + 'numtalkauthors', + ), + 'math' => array( + 'mw_math_png', + 'mw_math_simple', + 'mw_math_html', + 'mw_math_source', + 'mw_math_modern', + 'mw_math_mathml', + ), + 'patrolling' => array( + 'markaspatrolleddiff', + 'markaspatrolledlink', + 'markaspatrolledtext', + 'markedaspatrolled', + 'markedaspatrolledtext', + 'rcpatroldisabled', + 'rcpatroldisabledtext', + 'markedaspatrollederror', + 'markedaspatrollederrortext', + 'markedaspatrollederror-noautopatrol', + ), + 'monobook.js' => array( + 'monobook.js', + ), + 'common.js' => array( + 'common.js', + ), + 'imagedeletion' => array( + 'deletedrevision', + ), + 'browsediffs' => array( + 'previousdiff', + 'nextdiff', + ), + 'imagesize' => array( + 'imagemaxsize', + 'thumbsize', + 'showbigimage', + ), + 'newimages' => array( + 'newimages', + 'newimages-summary', + 'showhidebots', + 'noimages', + ), + 'variantname-zh' => array( + 'variantname-zh-cn', + 'variantname-zh-tw', + 'variantname-zh-hk', + 'variantname-zh-sg', + 'variantname-zh', + ), + 'variantname-sr' => array( + 'variantname-sr-ec', + 'variantname-sr-el', + 'variantname-sr-jc', + 'variantname-sr-jl', + 'variantname-sr', + ), + 'variantname-kk' => array( + 'variantname-kk-tr', + 'variantname-kk-kz', + 'variantname-kk-cn', + 'variantname-kk', + ), + 'specialloglabels' => array( + 'specialloguserlabel', + 'speciallogtitlelabel', + ), + 'passwordtooshort' => array( + 'passwordtooshort', + ), + 'mediawarning' => array( + 'mediawarning', + ), + 'fileinfo' => array( + 'fileinfo', + ), + 'metadata' => array( + 'metadata', + 'metadata-help', + 'metadata-expand', + 'metadata-collapse', + 'metadata-fields', + ), + 'exif' => array( + 'exif-imagewidth', + 'exif-imagelength', + 'exif-bitspersample', + 'exif-compression', + 'exif-photometricinterpretation', + 'exif-orientation', + 'exif-samplesperpixel', + 'exif-planarconfiguration', + 'exif-ycbcrsubsampling', + 'exif-ycbcrpositioning', + 'exif-xresolution', + 'exif-yresolution', + 'exif-resolutionunit', + 'exif-stripoffsets', + 'exif-rowsperstrip', + 'exif-stripbytecounts', + 'exif-jpeginterchangeformat', + 'exif-jpeginterchangeformatlength', + 'exif-transferfunction', + 'exif-whitepoint', + 'exif-primarychromaticities', + 'exif-ycbcrcoefficients', + 'exif-referenceblackwhite', + 'exif-datetime', + 'exif-imagedescription', + 'exif-make', + 'exif-model', + 'exif-software', + 'exif-artist', + 'exif-copyright', + 'exif-exifversion', + 'exif-flashpixversion', + 'exif-colorspace', + 'exif-componentsconfiguration', + 'exif-compressedbitsperpixel', + 'exif-pixelydimension', + 'exif-pixelxdimension', + 'exif-makernote', + 'exif-usercomment', + 'exif-relatedsoundfile', + 'exif-datetimeoriginal', + 'exif-datetimedigitized', + 'exif-subsectime', + 'exif-subsectimeoriginal', + 'exif-subsectimedigitized', + 'exif-exposuretime', + 'exif-exposuretime-format', + 'exif-fnumber', + 'exif-fnumber-format', + 'exif-exposureprogram', + 'exif-spectralsensitivity', + 'exif-isospeedratings', + 'exif-oecf', + 'exif-shutterspeedvalue', + 'exif-aperturevalue', + 'exif-brightnessvalue', + 'exif-exposurebiasvalue', + 'exif-maxaperturevalue', + 'exif-subjectdistance', + 'exif-meteringmode', + 'exif-lightsource', + 'exif-flash', + 'exif-focallength', + 'exif-focallength-format', + 'exif-subjectarea', + 'exif-flashenergy', + 'exif-spatialfrequencyresponse', + 'exif-focalplanexresolution', + 'exif-focalplaneyresolution', + 'exif-focalplaneresolutionunit', + 'exif-subjectlocation', + 'exif-exposureindex', + 'exif-sensingmethod', + 'exif-filesource', + 'exif-scenetype', + 'exif-cfapattern', + 'exif-customrendered', + 'exif-exposuremode', + 'exif-whitebalance', + 'exif-digitalzoomratio', + 'exif-focallengthin35mmfilm', + 'exif-scenecapturetype', + 'exif-gaincontrol', + 'exif-contrast', + 'exif-saturation', + 'exif-sharpness', + 'exif-devicesettingdescription', + 'exif-subjectdistancerange', + 'exif-imageuniqueid', + 'exif-gpsversionid', + 'exif-gpslatituderef', + 'exif-gpslatitude', + 'exif-gpslongituderef', + 'exif-gpslongitude', + 'exif-gpsaltituderef', + 'exif-gpsaltitude', + 'exif-gpstimestamp', + 'exif-gpssatellites', + 'exif-gpsstatus', + 'exif-gpsmeasuremode', + 'exif-gpsdop', + 'exif-gpsspeedref', + 'exif-gpsspeed', + 'exif-gpstrackref', + 'exif-gpstrack', + 'exif-gpsimgdirectionref', + 'exif-gpsimgdirection', + 'exif-gpsmapdatum', + 'exif-gpsdestlatituderef', + 'exif-gpsdestlatitude', + 'exif-gpsdestlongituderef', + 'exif-gpsdestlongitude', + 'exif-gpsdestbearingref', + 'exif-gpsdestbearing', + 'exif-gpsdestdistanceref', + 'exif-gpsdestdistance', + 'exif-gpsprocessingmethod', + 'exif-gpsareainformation', + 'exif-gpsdatestamp', + 'exif-gpsdifferential', + ), + 'exif-values' => array( + 'exif-make-value', + 'exif-model-value', + 'exif-software-value', + ), + 'exif-compression' => array( + 'exif-compression-1', + 'exif-compression-6', + ), + 'exif-photometricinterpretation' => array( + 'exif-photometricinterpretation-2', + 'exif-photometricinterpretation-6', + ), + 'exif-unknowndate' => array( + 'exif-unknowndate', + ), + 'exif-orientation' => array( + 'exif-orientation-1', + 'exif-orientation-2', + 'exif-orientation-3', + 'exif-orientation-4', + 'exif-orientation-5', + 'exif-orientation-6', + 'exif-orientation-7', + 'exif-orientation-8', + ), + 'exif-planarconfiguration' => array( + 'exif-planarconfiguration-1', + 'exif-planarconfiguration-2', + ), + 'exif-xyresolution' => array( + 'exif-xyresolution-i', + 'exif-xyresolution-c', + ), + 'exif-colorspace' => array( + 'exif-colorspace-1', + 'exif-colorspace-ffff.h', + ), + 'exif-componentsconfiguration' => array( + 'exif-componentsconfiguration-0', + 'exif-componentsconfiguration-1', + 'exif-componentsconfiguration-2', + 'exif-componentsconfiguration-3', + 'exif-componentsconfiguration-4', + 'exif-componentsconfiguration-5', + 'exif-componentsconfiguration-6', + ), + 'exif-exposureprogram' => array( + 'exif-exposureprogram-0', + 'exif-exposureprogram-1', + 'exif-exposureprogram-2', + 'exif-exposureprogram-3', + 'exif-exposureprogram-4', + 'exif-exposureprogram-5', + 'exif-exposureprogram-6', + 'exif-exposureprogram-7', + 'exif-exposureprogram-8', + ), + 'exif-subjectdistance-value' => array( + 'exif-subjectdistance-value', + ), + 'exif-meteringmode' => array( + 'exif-meteringmode-0', + 'exif-meteringmode-1', + 'exif-meteringmode-2', + 'exif-meteringmode-3', + 'exif-meteringmode-4', + 'exif-meteringmode-5', + 'exif-meteringmode-6', + 'exif-meteringmode-255', + ), + 'exif-lightsource' => array( + 'exif-lightsource-0', + 'exif-lightsource-1', + 'exif-lightsource-2', + 'exif-lightsource-3', + 'exif-lightsource-4', + 'exif-lightsource-9', + 'exif-lightsource-10', + 'exif-lightsource-11', + 'exif-lightsource-12', + 'exif-lightsource-13', + 'exif-lightsource-14', + 'exif-lightsource-15', + 'exif-lightsource-17', + 'exif-lightsource-18', + 'exif-lightsource-19', + 'exif-lightsource-20', + 'exif-lightsource-21', + 'exif-lightsource-22', + 'exif-lightsource-23', + 'exif-lightsource-24', + 'exif-lightsource-255', + ), + 'exif-focalplaneresolutionunit' => array( + 'exif-focalplaneresolutionunit-2', + ), + 'exif-sensingmethod' => array( + 'exif-sensingmethod-1', + 'exif-sensingmethod-2', + 'exif-sensingmethod-3', + 'exif-sensingmethod-4', + 'exif-sensingmethod-5', + 'exif-sensingmethod-7', + 'exif-sensingmethod-8', + ), + 'exif-filesource' => array( + 'exif-filesource-3', + ), + 'exif-scenetype' => array( + 'exif-scenetype-1', + ), + 'exif-customrendered' => array( + 'exif-customrendered-0', + 'exif-customrendered-1', + ), + 'exif-exposuremode' => array( + 'exif-exposuremode-0', + 'exif-exposuremode-1', + 'exif-exposuremode-2', + ), + 'exif-whitebalance' => array( + 'exif-whitebalance-0', + 'exif-whitebalance-1', + ), + 'exif-scenecapturetype' => array( + 'exif-scenecapturetype-0', + 'exif-scenecapturetype-1', + 'exif-scenecapturetype-2', + 'exif-scenecapturetype-3', + ), + 'exif-gaincontrol' => array( + 'exif-gaincontrol-0', + 'exif-gaincontrol-1', + 'exif-gaincontrol-2', + 'exif-gaincontrol-3', + 'exif-gaincontrol-4', + ), + 'exif-contrast' => array( + 'exif-contrast-0', + 'exif-contrast-1', + 'exif-contrast-2', + ), + 'exif-saturation' => array( + 'exif-saturation-0', + 'exif-saturation-1', + 'exif-saturation-2', + ), + 'exif-sharpness' => array( + 'exif-sharpness-0', + 'exif-sharpness-1', + 'exif-sharpness-2', + ), + 'exif-subjectdistancerange' => array( + 'exif-subjectdistancerange-0', + 'exif-subjectdistancerange-1', + 'exif-subjectdistancerange-2', + 'exif-subjectdistancerange-3', + ), + 'exif-gpslatitude' => array( + 'exif-gpslatitude-n', + 'exif-gpslatitude-s', + ), + 'exif-gpslongitude' => array( + 'exif-gpslongitude-e', + 'exif-gpslongitude-w', + ), + 'exif-gpsstatus' => array( + 'exif-gpsstatus-a', + 'exif-gpsstatus-v', + ), + 'exif-gpsmeasuremode' => array( + 'exif-gpsmeasuremode-2', + 'exif-gpsmeasuremode-3', + ), + 'exif-gpsspeed' => array( + 'exif-gpsspeed-k', + 'exif-gpsspeed-m', + 'exif-gpsspeed-n', + ), + 'exif-gpsdirection' => array( + 'exif-gpsdirection-t', + 'exif-gpsdirection-m', + ), + 'edit-externally' => array( + 'edit-externally', + 'edit-externally-help', + ), + 'all' => array( + 'recentchangesall', + 'imagelistall', + 'watchlistall1', + 'watchlistall2', + 'namespacesall', + ), + 'confirmemail' => array( + 'confirmemail', + 'confirmemail_noemail', + 'confirmemail_text', + 'confirmemail_pending', + 'confirmemail_send', + 'confirmemail_sent', + 'confirmemail_oncreate', + 'confirmemail_sendfailed', + 'confirmemail_invalid', + 'confirmemail_needlogin', + 'confirmemail_success', + 'confirmemail_loggedin', + 'confirmemail_error', + 'confirmemail_subject', + 'confirmemail_body', + ), + 'inputbox' => array( + 'tryexact', + 'searchfulltext', + 'createarticle', + ), + 'scarytransclusion' => array( + 'scarytranscludedisabled', + 'scarytranscludefailed', + 'scarytranscludetoolong', + ), + 'trackbacks' => array( + 'trackbackbox', + 'trackback', + 'trackbackexcerpt', + 'trackbackremove', + 'trackbacklink', + 'trackbackdeleteok', + ), + 'deleteconflict' => array( + 'deletedwhileediting', + 'confirmrecreate', + 'recreate', + 'tooltip-recreate', + ), + 'unit-pixel' => array( + 'unit-pixel', + ), + 'htmldump' => array( + 'redirectingto', + ), + 'purge' => array( + 'confirm_purge', + 'confirm_purge_button', + ), + 'newmessagesmulti' => array( + 'youhavenewmessagesmulti', + 'newtalkseperator', + ), + 'search2' => array( + 'searchcontaining', + 'searchnamed', + 'articletitles', + 'hideresults', + ), + 'displaytitle' => array( + 'displaytitle', + ), + 'catseparator' => array( + 'catseparator', + ), + 'loginlanguage' => array( + 'loginlanguagelabel', + 'loginlanguagelinks', + ), + 'imgmulti' => array( + 'imgmultipageprev', + 'imgmultipagenext', + 'imgmultigo', + 'imgmultigotopre', + 'imgmultigotopost', + ), + 'tablepager' => array( + 'ascending_abbrev', + 'descending_abbrev', + 'table_pager_next', + 'table_pager_prev', + 'table_pager_first', + 'table_pager_last', + 'table_pager_limit', + 'table_pager_limit_submit', + 'table_pager_empty', + ), + 'autosumm' => array( + 'autosumm-blank', + 'autosumm-replace', + 'autoredircomment', + 'autosumm-new', + ), + 'autoblock_whitelist' => array( + 'autoblock_whitelist', + ), + 'sizeunits' => array( + 'size-bytes', + 'size-kilobytes', + 'size-megabytes', + 'size-gigabytes', + ), +); +/** Comments for each block */ +$wgBlockComments = array( + 'sidebar' => "The sidebar for MonoBook is generated from this message, lines that do not +begin with * or ** are discarded, furthermore lines that do begin with ** and +do not contain | are also discarded, but don't depend on this behaviour for +future releases. Also note that since each list value is wrapped in a unique +XHTML id it should only appear once and include characters that are legal +XHTML id names.", + 'toggles' => 'User preference toggles', + 'underline' => '', + 'skinpreview' => '', + 'dates' => 'Dates', + 'categories' => 'Bits of text used by many pages', + 'mainpage' => '', + 'miscellaneous1' => '', + 'metadata_help' => 'Metadata in edit box', + 'currentevents' => '', + 'miscellaneous2' => '', + 'badaccess' => '', + 'versionrequired' => '', + 'miscellaneous3' => '', + 'nstab' => "Short words for each namespace, by default used in the 'article' tab in monobook", + 'main' => 'Main script and global functions', + 'errors' => 'General errors', + 'login' => 'Login and logout pages', + 'resetpass' => 'Password reset dialog', + 'toolbar' => 'Edit page toolbar', + 'edit' => 'Edit pages', + 'undo' => '"Undo" feature', + 'cantcreateaccount' => 'Account creation failure', + 'history' => 'History pages', + 'history-feed' => '', + 'revdelete' => 'Revision deletion', + 'diffs' => 'Diffs', + 'search' => 'Search results', + 'preferences' => 'Preferences page', + 'userrights' => 'User rights', + 'group' => 'Groups', + 'group-member' => '', + 'grouppage' => '', + 'recentchanges' => 'Recent changes', + 'upload' => 'Upload', + 'upload-errors' => '', + 'upload-curl-errors' => 'Some likely curl errors. More could be added from <http://curl.haxx.se/libcurl/c/libcurl-errors.html>', + 'licenses' => '', + 'imagelist' => 'Image list', + 'mimesearch' => 'MIME search', + 'unwatchedpages' => 'Unwatched pages', + 'listredirects' => 'List redirects', + 'unusedtemplates' => 'Unused templates', + 'randomredirect' => 'Random redirect', + 'statistics' => 'Statistics', + 'disambiguations' => '', + 'doubleredirects' => '', + 'brokenredirects' => '', + 'specialpages' => 'Miscellaneous special pages', + 'booksources' => 'Book sources', + 'specialpages2' => '', + 'allpages' => 'Special:Allpages', + 'listusers' => 'Special:Listusers', + 'emailuser' => 'E-mail user', + 'watchlist' => 'Watchlist', + 'watching' => 'Displayed when you click the "watch" button and it\'s in the process of watching', + 'enotif' => '', + 'deleteprotectrev' => 'Delete/protect/revert', + 'restrictions' => 'Restrictions (nouns)', + 'undelete' => 'Undelete', + 'nsform' => 'Namespace form on various pages', + 'contributions' => 'Contributions', + 'sp-contributions' => '', + 'newimages-showfrom' => '', + 'whatlinkshere' => 'What links here', + 'block' => 'Block/unblock', + 'developertools' => 'Developer tools', + 'makesysop' => 'Make sysop', + 'movepage' => 'Move page', + 'export' => 'Export', + 'allmessages' => 'Namespace 8 related', + 'thumbnails' => 'Thumbnails', + 'import' => 'Special:Import', + 'importlog' => 'Import log', + 'accesskeys' => 'Keyboard access keys for power users', + 'tooltips' => 'Tooltip help for some actions, most are in Monobook.js', + 'stylesheets' => 'Stylesheets', + 'metadata_cc' => 'Metadata', + 'attribution' => 'Attribution', + 'spamprotection' => 'Spam protection', + 'info' => 'Info page', + 'math' => 'Math options', + 'patrolling' => 'Patrolling', + 'monobook.js' => 'Monobook.js: tooltips and access keys for monobook', + 'common.js' => 'Common.js: contains nothing but a placeholder comment', + 'imagedeletion' => 'Image deletion', + 'browsediffs' => 'Browsing diffs', + 'imagesize' => '', + 'newimages' => '', + 'variantname-zh' => "Short names for language variants used for language conversion links. +To disable showing a particular link, set it to 'disable', e.g. +'variantname-zh-sg' => 'disable', +Variants for Chinese language", + 'variantname-sr' => 'Variants for Serbian language', + 'variantname-kk' => 'Variants for Kazakh language', + 'specialloglabels' => 'Labels for User: and Title: on Special:Log pages', + 'passwordtooshort' => '', + 'mediawarning' => 'Media Warning', + 'fileinfo' => '', + 'metadata' => 'Metadata', + 'exif' => 'EXIF tags', + 'exif-values' => 'Make & model, can be wikified in order to link to the camera and model name', + 'exif-compression' => 'EXIF attributes', + 'exif-unknowndate' => '', + 'exif-photometricinterpretation' => '', + 'exif-orientation' => '', + 'exif-planarconfiguration' => '', + 'exif-xyresolution' => '', + 'exif-colorspace' => '', + 'exif-componentsconfiguration' => '', + 'exif-exposureprogram' => '', + 'exif-subjectdistance-value' => '', + 'exif-meteringmode' => '', + 'exif-lightsource' => '', + 'exif-focalplaneresolutionunit' => '', + 'exif-sensingmethod' => '', + 'exif-filesource' => '', + 'exif-scenetype' => '', + 'exif-customrendered' => '', + 'exif-exposuremode' => '', + 'exif-whitebalance' => '', + 'exif-scenecapturetype' => '', + 'exif-gaincontrol' => '', + 'exif-contrast' => '', + 'exif-saturation' => '', + 'exif-sharpness' => '', + 'exif-subjectdistancerange' => '', + 'exif-gpslatitude' => 'Pseudotags used for GPSLatitudeRef and GPSDestLatitudeRef', + 'exif-gpslongitude' => 'Pseudotags used for GPSLongitudeRef and GPSDestLongitudeRef', + 'exif-gpsstatus' => '', + 'exif-gpsmeasuremode' => '', + 'exif-gpsspeed' => 'Pseudotags used for GPSSpeedRef and GPSDestDistanceRef', + '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", + 'confirmemail' => 'E-mail address confirmation', + 'inputbox' => 'Inputbox extension, may be useful in other contexts as well', + 'scarytransclusion' => 'Scary transclusion', + 'trackbacks' => 'Trackbacks', + 'deleteconflict' => 'Delete conflict', + 'unit-pixel' => '', + 'htmldump' => 'HTML dump', + 'purge' => 'action=purge', + 'newmessagesmulti' => '', + 'search2' => '', + 'displaytitle' => 'DISPLAYTITLE', + 'catseparator' => 'Separator for categories in page lists', + 'loginlanguage' => '', + 'imgmulti' => 'Multipage image navigation', + 'tablepager' => 'Table pager', + 'autosumm' => 'Auto-summaries', + 'autoblock_whitelist' => 'Autoblock whitelist', + 'sizeunits' => 'Size units', +); + +/** Short comments for standalone messages */ +$wgMessageComments = array( + 'lastmodifiedat' => '$1 date, $2 time', + 'sitenotice' => 'the equivalent to wgSiteNotice', + 'perfdisabledsub' => 'obsolete?', + 'history-feed-item-nocomment' => 'user at time', + 'editcomment' => 'only shown if there is an edit comment', + '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', + 'autoredircomment' => 'This should be changed to the new naming convention, but existed beforehand', +); + +/** Messages which contain dollar signs (which are not followed by numbers), and therefore should use a single apostrophe */ +$wgMessagseWithDollarSigns = array( + 'linkprefix', + 'enotif_subject', + 'enotif_body', + 'allmessagesnotsupportedDB', +); + +?> diff --git a/maintenance/language/rebuildLanguage.php b/maintenance/language/rebuildLanguage.php new file mode 100644 index 00000000..1643d30b --- /dev/null +++ b/maintenance/language/rebuildLanguage.php @@ -0,0 +1,82 @@ +<?php +/** + * Rewrite the messages array in the files languages/messages/MessagesXX.php. + * + * @package MediaWiki + * @subpackage Maintenance + */ + +require_once( dirname(__FILE__).'/../commandLine.inc' ); +require_once( 'languages.inc' ); +require_once( 'writeMessagesArray.inc' ); + +/** + * Rewrite a messages array. + * + * @param $code The language code. + * @param $write Write to the messages file? + */ +function rebuildLanguage( $code, $write ) { + global $wgLanguages, $wg; + + # Get messages + $messages = $wgLanguages->getMessages( $code ); + $messages = $messages['all']; + + # Rewrite messages array + $messagesText = writeMessagesArray( $messages, $code == 'en' ); + + # Write to the file + if ( $write ) { + $filename = Language::getMessagesFileName( $code ); + $contents = file_get_contents( $filename ); + if ( strpos( $contents, '$messages' ) !== false ) { + $new = explode( '$messages', $contents ); + $new = $new[0]; + $new .= $messagesText; + $new .= "\n?>\n"; + file_put_contents( $filename, $new ); + echo "Generated and wrote messages in language $code.\n"; + } + } else { + echo "Generated messages in language $code.\n"; + } +} + +# Show help +if ( isset( $options['help'] ) ) { + echo <<<END +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. + * help: Show this help. +Options: + * dry-run: Don't write the array to the file. + +END; + exit(); +} + +# Get the language code +if ( isset( $options['lang'] ) ) { + $wgCode = $options['lang']; +} else { + $wgCode = $wgContLang->getCode(); +} + +# Get the write options +$wgWriteToFile = !isset( $options['dry-run'] ); + +# Get language objects +$wgLanguages = new languages(); + +# Write all the language +if ( $wgCode == 'all' ) { + foreach ( $wgLanguages->getLanguages() as $language ) { + rebuildLanguage( $language, $wgWriteToFile ); + } +} else { + rebuildLanguage( $wgCode, $wgWriteToFile ); +} + +?> diff --git a/maintenance/language/splitLanguageFiles.inc b/maintenance/language/splitLanguageFiles.inc index c2500778..500d2cdc 100644 --- a/maintenance/language/splitLanguageFiles.inc +++ b/maintenance/language/splitLanguageFiles.inc @@ -1124,7 +1124,7 @@ $commonMsg = array ( 'thumbnail-more', 'missingimage', 'filemissing', -'Monobook.css', +'monobook.css', 'nodublincore', 'nocreativecommons', 'notacceptable', @@ -1154,7 +1154,7 @@ $commonMsg = array ( 'rcpatroldisabled', // not used ? 'rcpatroldisabledtext', // not used ? -'Monobook.js', +'monobook.js', 'newimages', 'noimages', 'variantname-zh-cn', diff --git a/maintenance/language/transstat.php b/maintenance/language/transstat.php index 590da121..e1b67274 100644 --- a/maintenance/language/transstat.php +++ b/maintenance/language/transstat.php @@ -12,7 +12,7 @@ * http://meta.wikimedia.org/wiki/Localization_statistics */ -require_once( 'maintenance/commandLine.inc' ); +require_once( dirname(__FILE__).'/../commandLine.inc' ); require_once( 'languages.inc' ); if ( isset( $options['help'] ) ) { @@ -67,7 +67,7 @@ class wikiStatsOutput extends statsOutput { 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"; - echo '{| border="2" cellpadding="4" cellspacing="0" style="background-color: #F9F9F9; border: 1px #AAAAAA solid; border-collapse: collapse;" width="100%"'."\n"; + echo '{| class="sortable wikitable" border="2" cellpadding="4" cellspacing="0" style="background-color: #F9F9F9; border: 1px #AAAAAA solid; border-collapse: collapse;" width="100%"'."\n"; } function footer() { echo "|}\n"; @@ -160,6 +160,7 @@ $wgLanguages = new languages(); $wgOut->heading(); $wgOut->blockstart(); $wgOut->element( 'Language', true ); +$wgOut->element( 'Code', true ); $wgOut->element( 'Translated', true ); $wgOut->element( '%', true ); $wgOut->element( 'Obsolete', true ); @@ -195,7 +196,8 @@ foreach ( $wgLanguages->getLanguages() as $code ) { # Output them $wgOut->blockstart(); - $wgOut->element( "$language ($code)" ); + $wgOut->element( "$language" ); + $wgOut->element( "$code" ); $wgOut->element( "$requiredMessagesNumber/$wgRequiredMessagesNumber" ); $wgOut->element( $requiredMessagesPercent ); $wgOut->element( "$obsoleteMessagesNumber/$messagesNumber" ); diff --git a/maintenance/language/writeMessagesArray.inc b/maintenance/language/writeMessagesArray.inc new file mode 100644 index 00000000..b0d17c59 --- /dev/null +++ b/maintenance/language/writeMessagesArray.inc @@ -0,0 +1,145 @@ +<?php +/** + * Write a messages array as a PHP text. + * + * @package MediaWiki + * @subpackage Maintenance + */ + +require_once( 'messages.inc' ); +require_once( 'messageTypes.inc' ); + +/** + * Write a messages array as a PHP text. + * + * @param $messages The messages array. + * @param $ignoredComments Show comments about ignored and optional messages? (For English.) + * + * @return The PHP text. + */ +function writeMessagesArray( $messages, $ignoredComments = false ) { + global $wgMessageStrucutre, $wgBlockComments, $wgMessageComments; + + # Sort messages to blocks + $sortedMessages['unknown'] = $messages; + foreach ( $wgMessageStrucutre as $blockName => $block ) { + foreach ( $block as $key ) { + if ( array_key_exists( $key, $sortedMessages['unknown'] ) ) { + $sortedMessages[$blockName][$key] = $sortedMessages['unknown'][$key]; + unset( $sortedMessages['unknown'][$key] ); + } + } + } + + # Write all the messages + $messagesText = "\$messages = array(\n"; + foreach( $sortedMessages as $block => $messages ) { + # Skip if it's the block of unknown messages - handle that in the end of file + if ( $block == 'unknown' ) { + continue; + } + + # Write the block + $messagesText .= writeMessagesBlock( $block, $wgBlockComments[$block], $messages, $ignoredComments ); + } + ksort( $sortedMessages['unknown'] ); + $messagesText .= writeMessagesBlock( 'unknown', 'Unknown messages', $sortedMessages['unknown'], $ignoredComments ); # Write the unknown messages, alphabetically sorted + $messagesText .= ");\n"; + + return $messagesText; +} + +/** + * Write a block of messages to PHP. + * + * @param $name The block name. + * @param $comment The block comment. + * @param $messages The block messages. + * @param $ignoredComments Show comments about ignored and optional messages? (For English.) + * + * @return The block, formatted in PHP. + */ +function writeMessagesBlock( $name, $comment, $messages, $ignoredComments ) { + global $wgMessageComments, $wgMessagseWithDollarSigns; + global $wgIgnoredMessages, $wgOptionalMessages; + $blockText = ''; + + # Skip the block if it includes no messages + if ( empty( $messages ) ) { + return ''; + } + + # Format the block comment (if exists); check for multiple lines comments + if ( !empty( $comment ) ) { + if ( strpos( $comment, "\n" ) === false ) { + $blockText .= "# $comment\n"; + } else { + $blockText .= "/*\n$comment\n*/\n"; + } + } + + # Get max key length + $maxKeyLength = 0; + foreach( array_keys( $messages ) as $key ) { + if ( strlen( $key ) > $maxKeyLength ) { + $maxKeyLength = strlen( $key ); + } + } + + # Format the messages + foreach( $messages as $key => $value ) { + # Add the key name + $blockText .= "'$key'"; + + # Add the appropriate block whitespace + for ( $i = 1; $i <= ( $maxKeyLength - strlen( $key ) ); $i++ ) { + $blockText .= ' '; + } + + # Refer to the value + $blockText .= ' => '; + + # Check for the appropriate apostrophe and add the value + if ( strpos( $value, "'" ) === false ) { + $blockText .= "'$value'"; + } elseif ( strpos( $value, '"' ) === false && !in_array( $key, $wgMessagseWithDollarSigns ) ) { + $blockText .= "\"$value\""; + } else { + $blockText .= "'" . str_replace( "'", "\'", $value ) . "'"; + } + + # Comma + $blockText .= ','; + + $ignoredComment = "don't translate or duplicate this message to other languages"; + $optionalComment = "only translate this message to other languages if you have to change it"; + $showIgnoredOrOptionalComment = in_array( $key, $wgIgnoredMessages ) || in_array( $key, $wgOptionalMessages ); + if ( $ignoredComments ) { + if ( array_key_exists( $key, $wgMessageComments ) ) { + $blockText .= ' # ' . $wgMessageComments[$key]; + if ( $showIgnoredOrOptionalComment ) { + $blockText .= '; '; + } + } elseif ( $showIgnoredOrOptionalComment ) { + $blockText .= ' # '; + } + if ( in_array( $key, $wgIgnoredMessages ) ) { + $blockText .= $ignoredComment; + } elseif ( in_array( $key, $wgOptionalMessages ) ) { + $blockText .= $optionalComment; + } + } elseif ( array_key_exists( $key, $wgMessageComments ) ) { + $blockText .= ' # ' . $wgMessageComments[$key]; + } + + # Newline + $blockText .= "\n"; + } + + # Newline to end the block + $blockText .= "\n"; + + return $blockText; +} + +?> diff --git a/maintenance/moveBatch.php b/maintenance/moveBatch.php index 8d7141cd..4b0abf7f 100644 --- a/maintenance/moveBatch.php +++ b/maintenance/moveBatch.php @@ -1,13 +1,23 @@ <?php -# Move a batch of pages -# Usage: php moveBatch.php [-u <user>] [-r <reason>] [-i <interval>] <listfile> -# where -# <listfile> is a file where each line has two titles separated by a pipe -# character. The first title is the source, the second is the destination. -# <user> is the username -# <reason> is the move reason -# <interval> is the number of seconds to sleep for after each move +/** + * Maintenance script to move a batch of pages + * + * @package MediaWiki + * @subpackage Maintenance + * @author Tim Starling + * + * USAGE: php moveBatch.php [-u <user>] [-r <reason>] [-i <interval>] <listfile> + * + * <listfile> - file with two titles per line, separated with pipe characters; + * the first title is the source, the second is the destination + * <user> - username to perform moves as + * <reason> - reason to be given for moves + * <interval> - number of seconds to sleep after each move + * + * This will print out error codes from Title::moveTo() if something goes wrong, + * e.g. immobile_namespace for namespaces which can't be moved + */ $oldCwd = getcwd(); $optionsWithArgs = array( 'u', 'r', 'i' ); @@ -66,7 +76,7 @@ for ( $linenum = 1; !feof( $file ); $linenum++ ) { } - print $source->getPrefixedText(); + print $source->getPrefixedText() . ' --> ' . $dest->getPrefixedText(); $dbw->begin(); $err = $source->moveTo( $dest, false, $reason ); if( $err !== true ) { diff --git a/maintenance/mysql5/tables-binary.sql b/maintenance/mysql5/tables-binary.sql new file mode 100644 index 00000000..2ab36546 --- /dev/null +++ b/maintenance/mysql5/tables-binary.sql @@ -0,0 +1,1095 @@ +-- Experimental table definitions for MySQL 4.1 and 5.0 with +-- content-holding fields switched to explicit binary charset. +-- +-- Binary is used instead of UTF-8 or UCS-2 so that all of +-- Unicode may be used as UTF-8 (MySQL still does not allow +-- use of characters outside the BMP in UTF-8 and has no UTF-16 +-- support). +-- +-- This should provide compatibility with our current MySQL 4.0 +-- behavior (safe for full UTF-8, but ugly sorting) on newer +-- versions of MySQL server, without the conversion surprises +-- you get from piggybacking on "Latin-1" fields. +-- +-- UTF-8 is used for the searchindex fields, as the fulltext index +-- doesn't seem to like the binary encoding. +-- +-- Not fully tested, may have surprises! +-- +-- TODO: Test various fields + +-- ------------------------------------------------------------ + +-- 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. + +-- +-- General notes: +-- +-- If possible, create tables as InnoDB to benefit from the +-- superior resiliency against crashes and ability to read +-- during writes (and write during reads!) +-- +-- Only the 'searchindex' table requires MyISAM due to the +-- requirement for fulltext index support, which is missing +-- from InnoDB. +-- +-- +-- The MySQL table backend for MediaWiki currently uses +-- 14-character CHAR or VARCHAR fields to store timestamps. +-- The format is YYYYMMDDHHMMSS, which is derived from the +-- text format of MySQL's TIMESTAMP fields. +-- +-- Historically TIMESTAMP fields were used, but abandoned +-- in early 2002 after a lot of trouble with the fields +-- auto-updating. +-- +-- The Postgres backend uses DATETIME fields for timestamps, +-- and we will migrate the MySQL definitions at some point as +-- well. +-- +-- +-- The /*$wgDBprefix*/ comments in this and other files are +-- replaced with the defined table prefix by the installer +-- and updater scripts. If you are installing or running +-- updates manually, you will need to manually insert the +-- table prefix if any when running these scripts. +-- + + +-- +-- The user table contains basic account information, +-- authentication keys, etc. +-- +-- Some multi-wiki sites may share a single central user table +-- between separate wikis using the $wgSharedDB setting. +-- +-- Note that when a external authentication plugin is used, +-- user table entries still need to be created to store +-- preferences and to key tracking information in the other +-- tables. +-- +CREATE TABLE /*$wgDBprefix*/user ( + user_id int(5) unsigned NOT NULL auto_increment, + + -- Usernames must be unique, must not be in the form of + -- an IP address. _Shouldn't_ allow slashes or case + -- conflicts. Spaces are allowed, and are _not_ converted + -- to underscores like titles. See the User::newFromName() for + -- the specific tests that usernames have to pass. + user_name varchar(255) binary NOT NULL default '', + + -- Optional 'real name' to be displayed in credit listings + user_real_name varchar(255) binary NOT NULL default '', + + -- Password hashes, normally hashed like so: + -- MD5(CONCAT(user_id,'-',MD5(plaintext_password))), see + -- wfEncryptPassword() in GlobalFunctions.php + user_password tinyblob NOT NULL, + + -- When using 'mail me a new password', a random + -- password is generated and the hash stored here. + -- The previous password is left in place until + -- someone actually logs in with the new password, + -- at which point the hash is moved to user_password + -- and the old password is invalidated. + user_newpassword tinyblob NOT NULL, + + -- Timestamp of the last time when a new password was + -- sent, for throttling purposes + user_newpass_time char(14) binary, + + -- Note: email should be restricted, not public info. + -- Same with passwords. + user_email tinytext NOT NULL, + + -- Newline-separated list of name=value defining the user + -- preferences + user_options blob NOT NULL, + + -- This is a timestamp which is updated when a user + -- logs in, logs out, changes preferences, or performs + -- some other action requiring HTML cache invalidation + -- to ensure that the UI is updated. + user_touched char(14) binary NOT NULL default '', + + -- A pseudorandomly generated value that is stored in + -- a cookie when the "remember password" feature is + -- used (previously, a hash of the password was used, but + -- this was vulnerable to cookie-stealing attacks) + user_token char(32) binary NOT NULL default '', + + -- Initially NULL; when a user's e-mail address has been + -- validated by returning with a mailed token, this is + -- set to the current timestamp. + user_email_authenticated char(14) binary, + + -- Randomly generated token created when the e-mail address + -- is set and a confirmation test mail sent. + user_email_token char(32) binary, + + -- Expiration date for the user_email_token + user_email_token_expires char(14) binary, + + -- Timestamp of account registration. + -- Accounts predating this schema addition may contain NULL. + user_registration char(14) binary, + + -- Count of edits and edit-like actions. + -- + -- *NOT* intended to be an accurate copy of COUNT(*) WHERE rev_user=user_id + -- May contain NULL for old accounts if batch-update scripts haven't been + -- run, as well as listing deleted edits and other myriad ways it could be + -- out of sync. + -- + -- Meant primarily for heuristic checks to give an impression of whether + -- the account has been used much. + -- + user_editcount int, + + PRIMARY KEY user_id (user_id), + UNIQUE INDEX user_name (user_name), + INDEX (user_email_token) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- +-- User permissions have been broken out to a separate table; +-- this allows sites with a shared user table to have different +-- permissions assigned to a user in each project. +-- +-- This table replaces the old user_rights field which used a +-- comma-separated blob. +-- +CREATE TABLE /*$wgDBprefix*/user_groups ( + -- Key to user_id + ug_user int(5) unsigned NOT NULL default '0', + + -- Group names are short symbolic string keys. + -- The set of group names is open-ended, though in practice + -- only some predefined ones are likely to be used. + -- + -- At runtime $wgGroupPermissions will associate group keys + -- with particular permissions. A user will have the combined + -- permissions of any group they're explicitly in, plus + -- the implicit '*' and 'user' groups. + ug_group char(16) NOT NULL default '', + + PRIMARY KEY (ug_user,ug_group), + KEY (ug_group) +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- Stores notifications of user talk page changes, for the display +-- of the "you have new messages" box +CREATE TABLE /*$wgDBprefix*/user_newtalk ( + -- Key to user.user_id + user_id int(5) NOT NULL default '0', + -- If the user is an anonymous user hir IP address is stored here + -- since the user_id of 0 is ambiguous + user_ip varchar(40) NOT NULL default '', + INDEX user_id (user_id), + INDEX user_ip (user_ip) +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + + +-- +-- Core of the wiki: each page has an entry here which identifies +-- it by title and contains some essential metadata. +-- +CREATE TABLE /*$wgDBprefix*/page ( + -- Unique identifier number. The page_id will be preserved across + -- edits and rename operations, but not deletions and recreations. + page_id int(8) unsigned NOT NULL auto_increment, + + -- A page name is broken into a namespace and a title. + -- The namespace keys are UI-language-independent constants, + -- defined in includes/Defines.php + page_namespace int NOT NULL, + + -- The rest of the title, as text. + -- Spaces are transformed into underscores in title storage. + page_title varchar(255) binary NOT NULL, + + -- Comma-separated set of permission keys indicating who + -- can move or edit the page. + page_restrictions tinyblob NOT NULL, + + -- Number of times this page has been viewed. + page_counter bigint(20) unsigned NOT NULL default '0', + + -- 1 indicates the article is a redirect. + page_is_redirect tinyint(1) unsigned NOT NULL default '0', + + -- 1 indicates this is a new entry, with only one edit. + -- Not all pages with one edit are new pages. + page_is_new tinyint(1) unsigned NOT NULL default '0', + + -- Random value between 0 and 1, used for Special:Randompage + page_random real unsigned NOT NULL, + + -- This timestamp is updated whenever the page changes in + -- a way requiring it to be re-rendered, invalidating caches. + -- Aside from editing this includes permission changes, + -- creation or deletion of linked pages, and alteration + -- of contained templates. + page_touched char(14) binary NOT NULL default '', + + -- Handy key to revision.rev_id of the current revision. + -- This may be 0 during page creation, but that shouldn't + -- happen outside of a transaction... hopefully. + page_latest int(8) unsigned NOT NULL, + + -- Uncompressed length in bytes of the page's current source text. + page_len int(8) unsigned NOT NULL, + + PRIMARY KEY page_id (page_id), + UNIQUE INDEX name_title (page_namespace,page_title), + + -- Special-purpose indexes + INDEX (page_random), + INDEX (page_len) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- +-- Every edit of a page creates also a revision row. +-- This stores metadata about the revision, and a reference +-- to the text storage backend. +-- +CREATE TABLE /*$wgDBprefix*/revision ( + rev_id int(8) unsigned NOT NULL auto_increment, + + -- Key to page_id. This should _never_ be invalid. + rev_page int(8) unsigned NOT NULL, + + -- Key to text.old_id, where the actual bulk text is stored. + -- It's possible for multiple revisions to use the same text, + -- for instance revisions where only metadata is altered + -- or a rollback to a previous version. + rev_text_id int(8) unsigned NOT NULL, + + -- Text comment summarizing the change. + -- This text is shown in the history and other changes lists, + -- rendered in a subset of wiki markup by Linker::formatComment() + rev_comment tinyblob NOT NULL, + + -- Key to user.user_id of the user who made this edit. + -- Stores 0 for anonymous edits and for some mass imports. + rev_user int(5) unsigned NOT NULL default '0', + + -- Text username or IP address of the editor. + rev_user_text varchar(255) binary NOT NULL default '', + + -- Timestamp + rev_timestamp char(14) binary NOT NULL default '', + + -- Records whether the user marked the 'minor edit' checkbox. + -- Many automated edits are marked as minor. + rev_minor_edit tinyint(1) unsigned NOT NULL default '0', + + -- Not yet used; reserved for future changes to the deletion system. + rev_deleted tinyint(1) unsigned NOT NULL default '0', + + PRIMARY KEY rev_page_id (rev_page, rev_id), + UNIQUE INDEX rev_id (rev_id), + INDEX rev_timestamp (rev_timestamp), + INDEX page_timestamp (rev_page,rev_timestamp), + INDEX user_timestamp (rev_user,rev_timestamp), + INDEX usertext_timestamp (rev_user_text,rev_timestamp) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + + +-- +-- Holds text of individual page revisions. +-- +-- Field names are a holdover from the 'old' revisions table in +-- MediaWiki 1.4 and earlier: an upgrade will transform that +-- table into the 'text' table to minimize unnecessary churning +-- and downtime. If upgrading, the other fields will be left unused. +-- +CREATE TABLE /*$wgDBprefix*/text ( + -- Unique text storage key number. + -- Note that the 'oldid' parameter used in URLs does *not* + -- refer to this number anymore, but to rev_id. + -- + -- revision.rev_text_id is a key to this column + old_id int(8) unsigned NOT NULL auto_increment, + + -- Depending on the contents of the old_flags field, the text + -- may be convenient plain text, or it may be funkily encoded. + old_text mediumblob NOT NULL, + + -- Comma-separated list of flags: + -- gzip: text is compressed with PHP's gzdeflate() function. + -- utf8: text was stored as UTF-8. + -- If $wgLegacyEncoding option is on, rows *without* this flag + -- will be converted to UTF-8 transparently at load time. + -- object: text field contained a serialized PHP object. + -- The object either contains multiple versions compressed + -- together to achieve a better compression ratio, or it refers + -- to another row where the text can be found. + old_flags tinyblob NOT NULL, + + PRIMARY KEY old_id (old_id) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- +-- Holding area for deleted articles, which may be viewed +-- or restored by admins through the Special:Undelete interface. +-- The fields generally correspond to the page, revision, and text +-- fields, with several caveats. +-- +CREATE TABLE /*$wgDBprefix*/archive ( + ar_namespace int NOT NULL default '0', + ar_title varchar(255) binary NOT NULL default '', + + -- Newly deleted pages will not store text in this table, + -- but will reference the separately existing text rows. + -- This field is retained for backwards compatibility, + -- so old archived pages will remain accessible after + -- upgrading from 1.4 to 1.5. + -- Text may be gzipped or otherwise funky. + ar_text mediumblob NOT NULL, + + -- Basic revision stuff... + ar_comment tinyblob NOT NULL, + ar_user int(5) unsigned NOT NULL default '0', + ar_user_text varchar(255) binary NOT NULL, + ar_timestamp char(14) binary NOT NULL default '', + ar_minor_edit tinyint(1) NOT NULL default '0', + + -- See ar_text note. + ar_flags tinyblob NOT NULL, + + -- When revisions are deleted, their unique rev_id is stored + -- here so it can be retained after undeletion. This is necessary + -- to retain permalinks to given revisions after accidental delete + -- cycles or messy operations like history merges. + -- + -- Old entries from 1.4 will be NULL here, and a new rev_id will + -- be created on undeletion for those revisions. + ar_rev_id int(8) unsigned, + + -- For newly deleted revisions, this is the text.old_id key to the + -- actual stored text. To avoid breaking the block-compression scheme + -- and otherwise making storage changes harder, the actual text is + -- *not* deleted from the text table, merely hidden by removal of the + -- page and revision entries. + -- + -- Old entries deleted under 1.2-1.4 will have NULL here, and their + -- ar_text and ar_flags fields will be used to create a new text + -- row upon undeletion. + ar_text_id int(8) unsigned, + + KEY name_title_timestamp (ar_namespace,ar_title,ar_timestamp) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + + +-- +-- Track page-to-page hyperlinks within the wiki. +-- +CREATE TABLE /*$wgDBprefix*/pagelinks ( + -- Key to the page_id of the page containing the link. + pl_from int(8) unsigned NOT NULL default '0', + + -- Key to page_namespace/page_title of the target page. + -- The target page may or may not exist, and due to renames + -- and deletions may refer to different page records as time + -- goes by. + pl_namespace int NOT NULL default '0', + pl_title varchar(255) binary NOT NULL default '', + + UNIQUE KEY pl_from (pl_from,pl_namespace,pl_title), + KEY (pl_namespace,pl_title,pl_from) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + + +-- +-- Track template inclusions. +-- +CREATE TABLE /*$wgDBprefix*/templatelinks ( + -- Key to the page_id of the page containing the link. + tl_from int(8) unsigned NOT NULL default '0', + + -- Key to page_namespace/page_title of the target page. + -- The target page may or may not exist, and due to renames + -- and deletions may refer to different page records as time + -- goes by. + tl_namespace int NOT NULL default '0', + tl_title varchar(255) binary NOT NULL default '', + + UNIQUE KEY tl_from (tl_from,tl_namespace,tl_title), + KEY (tl_namespace,tl_title,tl_from) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + + +-- +-- Track links to images *used inline* +-- We don't distinguish live from broken links here, so +-- they do not need to be changed on upload/removal. +-- +CREATE TABLE /*$wgDBprefix*/imagelinks ( + -- Key to page_id of the page containing the image / media link. + il_from int(8) unsigned NOT NULL default '0', + + -- Filename of target image. + -- This is also the page_title of the file's description page; + -- all such pages are in namespace 6 (NS_IMAGE). + il_to varchar(255) binary NOT NULL default '', + + UNIQUE KEY il_from (il_from,il_to), + KEY (il_to,il_from) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- +-- Track category inclusions *used inline* +-- This tracks a single level of category membership +-- (folksonomic tagging, really). +-- +CREATE TABLE /*$wgDBprefix*/categorylinks ( + -- Key to page_id of the page defined as a category member. + cl_from int(8) unsigned NOT NULL default '0', + + -- Name of the category. + -- This is also the page_title of the category's description page; + -- all such pages are in namespace 14 (NS_CATEGORY). + cl_to varchar(255) binary NOT NULL default '', + + -- The title of the linking page, or an optional override + -- to determine sort order. Sorting is by binary order, which + -- isn't always ideal, but collations seem to be an exciting + -- and dangerous new world in MySQL... + -- + -- For MySQL 4.1+ with charset set to utf8, the sort key *index* + -- needs cut to be smaller than 1024 bytes (at 3 bytes per char). + -- To sort properly on the shorter key, this field needs to be + -- the same shortness. + cl_sortkey varchar(86) binary NOT NULL default '', + + -- This isn't really used at present. Provided for an optional + -- sorting method by approximate addition time. + cl_timestamp timestamp NOT NULL, + + UNIQUE KEY cl_from (cl_from,cl_to), + + -- We always sort within a given category... + KEY cl_sortkey (cl_to,cl_sortkey), + + -- Not really used? + KEY cl_timestamp (cl_to,cl_timestamp) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- +-- Track links to external URLs +-- +CREATE TABLE /*$wgDBprefix*/externallinks ( + -- page_id of the referring page + el_from int(8) unsigned NOT NULL default '0', + + -- The URL + el_to blob NOT NULL, + + -- In the case of HTTP URLs, this is the URL with any username or password + -- removed, and with the labels in the hostname reversed and converted to + -- lower case. An extra dot is added to allow for matching of either + -- example.com or *.example.com in a single scan. + -- Example: + -- http://user:password@sub.example.com/page.html + -- becomes + -- http://com.example.sub./page.html + -- which allows for fast searching for all pages under example.com with the + -- clause: + -- WHERE el_index LIKE 'http://com.example.%' + el_index blob NOT NULL, + + KEY (el_from, el_to(40)), + KEY (el_to(60), el_from), + KEY (el_index(60)) +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- +-- Track interlanguage links +-- +CREATE TABLE /*$wgDBprefix*/langlinks ( + -- page_id of the referring page + ll_from int(8) unsigned NOT NULL default '0', + + -- Language code of the target + ll_lang varchar(10) binary NOT NULL default '', + + -- Title of the target, including namespace + ll_title varchar(255) binary NOT NULL default '', + + UNIQUE KEY (ll_from, ll_lang), + KEY (ll_lang, ll_title) +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- +-- Contains a single row with some aggregate info +-- on the state of the site. +-- +CREATE TABLE /*$wgDBprefix*/site_stats ( + -- The single row should contain 1 here. + ss_row_id int(8) unsigned NOT NULL, + + -- Total number of page views, if hit counters are enabled. + ss_total_views bigint(20) unsigned default '0', + + -- Total number of edits performed. + ss_total_edits bigint(20) unsigned default '0', + + -- An approximate count of pages matching the following criteria: + -- * in namespace 0 + -- * not a redirect + -- * contains the text '[[' + -- See Article::isCountable() in includes/Article.php + ss_good_articles bigint(20) unsigned default '0', + + -- Total pages, theoretically equal to SELECT COUNT(*) FROM page; except faster + ss_total_pages bigint(20) default '-1', + + -- Number of users, theoretically equal to SELECT COUNT(*) FROM user; + ss_users bigint(20) default '-1', + + -- Deprecated, no longer updated as of 1.5 + ss_admins int(10) default '-1', + + -- Number of images, equivalent to SELECT COUNT(*) FROM image + ss_images int(10) default '0', + + UNIQUE KEY ss_row_id (ss_row_id) + +) ENGINE=InnoDB; + +-- +-- Stores an ID for every time any article is visited; +-- depending on $wgHitcounterUpdateFreq, it is +-- periodically cleared and the page_counter column +-- in the page table updated for the all articles +-- that have been visited.) +-- +CREATE TABLE /*$wgDBprefix*/hitcounter ( + hc_id int unsigned NOT NULL +) ENGINE=HEAP MAX_ROWS=25000; + + +-- +-- The internet is full of jerks, alas. Sometimes it's handy +-- to block a vandal or troll account. +-- +CREATE TABLE /*$wgDBprefix*/ipblocks ( + -- Primary key, introduced for privacy. + ipb_id int(8) NOT NULL auto_increment, + + -- Blocked IP address in dotted-quad form or user name. + ipb_address tinyblob NOT NULL, + + -- Blocked user ID or 0 for IP blocks. + ipb_user int(8) unsigned NOT NULL default '0', + + -- User ID who made the block. + ipb_by int(8) unsigned NOT NULL default '0', + + -- Text comment made by blocker. + ipb_reason tinyblob NOT NULL, + + -- Creation (or refresh) date in standard YMDHMS form. + -- IP blocks expire automatically. + ipb_timestamp char(14) binary NOT NULL default '', + + -- Indicates that the IP address was banned because a banned + -- user accessed a page through it. If this is 1, ipb_address + -- will be hidden, and the block identified by block ID number. + ipb_auto bool NOT NULL default '0', + + -- If set to 1, block applies only to logged-out users + ipb_anon_only bool NOT NULL default 0, + + -- Block prevents account creation from matching IP addresses + ipb_create_account bool NOT NULL default 1, + + -- Block triggers autoblocks + ipb_enable_autoblock bool NOT NULL default '1', + + -- Time at which the block will expire. + ipb_expiry char(14) binary NOT NULL default '', + + -- Start and end of an address range, in hexadecimal + -- Size chosen to allow IPv6 + ipb_range_start varchar(32) NOT NULL default '', + ipb_range_end varchar(32) NOT NULL default '', + + PRIMARY KEY ipb_id (ipb_id), + + -- Unique index to support "user already blocked" messages + -- Any new options which prevent collisions should be included + UNIQUE INDEX ipb_address (ipb_address(255), ipb_user, ipb_auto, ipb_anon_only), + + INDEX ipb_user (ipb_user), + INDEX ipb_range (ipb_range_start(8), ipb_range_end(8)), + INDEX ipb_timestamp (ipb_timestamp), + INDEX ipb_expiry (ipb_expiry) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + + +-- +-- Uploaded images and other files. +-- +CREATE TABLE /*$wgDBprefix*/image ( + -- Filename. + -- This is also the title of the associated description page, + -- which will be in namespace 6 (NS_IMAGE). + img_name varchar(255) binary NOT NULL default '', + + -- File size in bytes. + img_size int(8) unsigned NOT NULL default '0', + + -- For images, size in pixels. + img_width int(5) NOT NULL default '0', + img_height int(5) NOT NULL default '0', + + -- Extracted EXIF metadata stored as a serialized PHP array. + img_metadata mediumblob NOT NULL, + + -- For images, bits per pixel if known. + img_bits int(3) NOT NULL default '0', + + -- Media type as defined by the MEDIATYPE_xxx constants + img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL, + + -- major part of a MIME media type as defined by IANA + -- see http://www.iana.org/assignments/media-types/ + img_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") NOT NULL default "unknown", + + -- minor part of a MIME media type as defined by IANA + -- the minor parts are not required to adher to any standard + -- but should be consistent throughout the database + -- see http://www.iana.org/assignments/media-types/ + img_minor_mime varchar(32) NOT NULL default "unknown", + + -- Description field as entered by the uploader. + -- This is displayed in image upload history and logs. + img_description tinyblob NOT NULL, + + -- user_id and user_name of uploader. + img_user int(5) unsigned NOT NULL default '0', + img_user_text varchar(255) binary NOT NULL default '', + + -- Time of the upload. + img_timestamp char(14) binary NOT NULL default '', + + PRIMARY KEY img_name (img_name), + + -- Used by Special:Imagelist for sort-by-size + INDEX img_size (img_size), + + -- Used by Special:Newimages and Special:Imagelist + INDEX img_timestamp (img_timestamp) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- +-- Previous revisions of uploaded files. +-- Awkwardly, image rows have to be moved into +-- this table at re-upload time. +-- +CREATE TABLE /*$wgDBprefix*/oldimage ( + -- Base filename: key to image.img_name + oi_name varchar(255) binary NOT NULL default '', + + -- Filename of the archived file. + -- This is generally a timestamp and '!' prepended to the base name. + oi_archive_name varchar(255) binary NOT NULL default '', + + -- Other fields as in image... + oi_size int(8) unsigned NOT NULL default 0, + oi_width int(5) NOT NULL default 0, + oi_height int(5) NOT NULL default 0, + oi_bits int(3) NOT NULL default 0, + oi_description tinyblob NOT NULL, + oi_user int(5) unsigned NOT NULL default '0', + oi_user_text varchar(255) binary NOT NULL default '', + oi_timestamp char(14) binary NOT NULL default '', + + INDEX oi_name (oi_name(10)) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + + +-- +-- Record of deleted file data +-- +CREATE TABLE /*$wgDBprefix*/filearchive ( + -- Unique row id + fa_id int NOT NULL auto_increment, + + -- Original base filename; key to image.img_name, page.page_title, etc + fa_name varchar(255) binary NOT NULL default '', + + -- Filename of archived file, if an old revision + fa_archive_name varchar(255) binary default '', + + -- Which storage bin (directory tree or object store) the file data + -- is stored in. Should be 'deleted' for files that have been deleted; + -- any other bin is not yet in use. + fa_storage_group varchar(16), + + -- SHA-1 of the file contents plus extension, used as a key for storage. + -- eg 8f8a562add37052a1848ff7771a2c515db94baa9.jpg + -- + -- If NULL, the file was missing at deletion time or has been purged + -- from the archival storage. + fa_storage_key varchar(64) binary default '', + + -- Deletion information, if this file is deleted. + fa_deleted_user int, + fa_deleted_timestamp char(14) binary default '', + fa_deleted_reason text, + + -- Duped fields from image + fa_size int(8) unsigned default '0', + fa_width int(5) default '0', + fa_height int(5) default '0', + fa_metadata mediumblob, + fa_bits int(3) default '0', + fa_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL, + fa_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") default "unknown", + fa_minor_mime varchar(32) default "unknown", + fa_description tinyblob, + fa_user int(5) unsigned default '0', + fa_user_text varchar(255) binary default '', + fa_timestamp char(14) binary default '', + + PRIMARY KEY (fa_id), + INDEX (fa_name, fa_timestamp), -- pick out by image name + INDEX (fa_storage_group, fa_storage_key), -- pick out dupe files + INDEX (fa_deleted_timestamp), -- sort by deletion time + INDEX (fa_deleted_user) -- sort by deleter + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- +-- Primarily a summary table for Special:Recentchanges, +-- this table contains some additional info on edits from +-- the last few days, see Article::editUpdates() +-- +CREATE TABLE /*$wgDBprefix*/recentchanges ( + rc_id int(8) NOT NULL auto_increment, + rc_timestamp varchar(14) binary NOT NULL default '', + rc_cur_time varchar(14) binary NOT NULL default '', + + -- As in revision + rc_user int(10) unsigned NOT NULL default '0', + rc_user_text varchar(255) binary NOT NULL default '', + + -- When pages are renamed, their RC entries do _not_ change. + rc_namespace int NOT NULL default '0', + rc_title varchar(255) binary NOT NULL default '', + + -- as in revision... + rc_comment varchar(255) binary NOT NULL default '', + rc_minor tinyint(3) unsigned NOT NULL default '0', + + -- Edits by user accounts with the 'bot' rights key are + -- marked with a 1 here, and will be hidden from the + -- default view. + rc_bot tinyint(3) unsigned NOT NULL default '0', + + rc_new tinyint(3) unsigned NOT NULL default '0', + + -- Key to page_id (was cur_id prior to 1.5). + -- This will keep links working after moves while + -- retaining the at-the-time name in the changes list. + rc_cur_id int(10) unsigned NOT NULL default '0', + + -- rev_id of the given revision + rc_this_oldid int(10) unsigned NOT NULL default '0', + + -- rev_id of the prior revision, for generating diff links. + rc_last_oldid int(10) unsigned NOT NULL default '0', + + -- These may no longer be used, with the new move log. + rc_type tinyint(3) unsigned NOT NULL default '0', + rc_moved_to_ns tinyint(3) unsigned NOT NULL default '0', + rc_moved_to_title varchar(255) binary NOT NULL default '', + + -- If the Recent Changes Patrol option is enabled, + -- users may mark edits as having been reviewed to + -- remove a warning flag on the RC list. + -- A value of 1 indicates the page has been reviewed. + rc_patrolled tinyint(3) unsigned NOT NULL default '0', + + -- Recorded IP address the edit was made from, if the + -- $wgPutIPinRC option is enabled. + rc_ip char(15) NOT NULL default '', + + -- Text length in characters before + -- and after the edit + rc_old_len int(10) default '0', + rc_new_len int(10) default '0', + + PRIMARY KEY rc_id (rc_id), + INDEX rc_timestamp (rc_timestamp), + INDEX rc_namespace_title (rc_namespace, rc_title), + INDEX rc_cur_id (rc_cur_id), + INDEX new_name_timestamp (rc_new,rc_namespace,rc_timestamp), + INDEX rc_ip (rc_ip), + INDEX rc_ns_usertext ( rc_namespace, rc_user_text ) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +CREATE TABLE /*$wgDBprefix*/watchlist ( + -- Key to user.user_id + wl_user int(5) unsigned NOT NULL, + + -- Key to page_namespace/page_title + -- Note that users may watch pages which do not exist yet, + -- or existed in the past but have been deleted. + wl_namespace int NOT NULL default '0', + wl_title varchar(255) binary NOT NULL default '', + + -- Timestamp when user was last sent a notification e-mail; + -- cleared when the user visits the page. + wl_notificationtimestamp varchar(14) binary, + + UNIQUE KEY (wl_user, wl_namespace, wl_title), + KEY namespace_title (wl_namespace,wl_title) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + + +-- +-- Used by the math module to keep track +-- of previously-rendered items. +-- +CREATE TABLE /*$wgDBprefix*/math ( + -- Binary MD5 hash of the latex fragment, used as an identifier key. + math_inputhash varbinary(16) NOT NULL, + + -- Not sure what this is, exactly... + math_outputhash varbinary(16) NOT NULL, + + -- texvc reports how well it thinks the HTML conversion worked; + -- if it's a low level the PNG rendering may be preferred. + math_html_conservativeness tinyint(1) NOT NULL, + + -- HTML output from texvc, if any + math_html text, + + -- MathML output from texvc, if any + math_mathml text, + + UNIQUE KEY math_inputhash (math_inputhash) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- +-- When using the default MySQL search backend, page titles +-- and text are munged to strip markup, do Unicode case folding, +-- and prepare the result for MySQL's fulltext index. +-- +-- This table must be MyISAM; InnoDB does not support the needed +-- fulltext index. +-- +CREATE TABLE /*$wgDBprefix*/searchindex ( + -- Key to page_id + si_page int(8) unsigned NOT NULL, + + -- Munged version of title + si_title varchar(255) NOT NULL default '', + + -- Munged version of body text + si_text mediumtext NOT NULL, + + UNIQUE KEY (si_page), + FULLTEXT si_title (si_title), + FULLTEXT si_text (si_text) + +) ENGINE=MyISAM, DEFAULT CHARSET=utf8; + +-- +-- Recognized interwiki link prefixes +-- +CREATE TABLE /*$wgDBprefix*/interwiki ( + -- The interwiki prefix, (e.g. "Meatball", or the language prefix "de") + iw_prefix char(32) NOT NULL, + + -- The URL of the wiki, with "$1" as a placeholder for an article name. + -- Any spaces in the name will be transformed to underscores before + -- insertion. + iw_url char(127) NOT NULL, + + -- A boolean value indicating whether the wiki is in this project + -- (used, for example, to detect redirect loops) + iw_local bool NOT NULL, + + -- Boolean value indicating whether interwiki transclusions are allowed. + iw_trans tinyint(1) NOT NULL default 0, + + UNIQUE KEY iw_prefix (iw_prefix) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- +-- Used for caching expensive grouped queries +-- +CREATE TABLE /*$wgDBprefix*/querycache ( + -- A key name, generally the base name of of the special page. + qc_type char(32) NOT NULL, + + -- Some sort of stored value. Sizes, counts... + qc_value int(5) unsigned NOT NULL default '0', + + -- Target namespace+title + qc_namespace int NOT NULL default '0', + qc_title char(255) binary NOT NULL default '', + + KEY (qc_type,qc_value) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- +-- For a few generic cache operations if not using Memcached +-- +CREATE TABLE /*$wgDBprefix*/objectcache ( + keyname char(255) binary NOT NULL default '', + value mediumblob, + exptime datetime, + UNIQUE KEY (keyname), + KEY (exptime) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- +-- Cache of interwiki transclusion +-- +CREATE TABLE /*$wgDBprefix*/transcache ( + tc_url varchar(255) NOT NULL, + tc_contents text, + tc_time int NOT NULL, + UNIQUE INDEX tc_url_idx (tc_url) +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +CREATE TABLE /*$wgDBprefix*/logging ( + -- Symbolic keys for the general log type and the action type + -- within the log. The output format will be controlled by the + -- action field, but only the type controls categorization. + log_type char(10) NOT NULL default '', + log_action char(10) NOT NULL default '', + + -- Timestamp. Duh. + log_timestamp char(14) NOT NULL default '19700101000000', + + -- The user who performed this action; key to user_id + log_user int unsigned NOT NULL default 0, + + -- Key to the page affected. Where a user is the target, + -- this will point to the user page. + log_namespace int NOT NULL default 0, + log_title varchar(255) binary NOT NULL default '', + + -- Freeform text. Interpreted as edit history comments. + log_comment varchar(255) NOT NULL default '', + + -- LF separated list of miscellaneous parameters + log_params blob NOT NULL, + + KEY type_time (log_type, log_timestamp), + KEY user_time (log_user, log_timestamp), + KEY page_time (log_namespace, log_title, log_timestamp) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +CREATE TABLE /*$wgDBprefix*/trackbacks ( + tb_id int auto_increment, + tb_page int REFERENCES page(page_id) ON DELETE CASCADE, + tb_title varchar(255) NOT NULL, + tb_url varchar(255) NOT NULL, + tb_ex text, + tb_name varchar(255), + + PRIMARY KEY (tb_id), + INDEX (tb_page) +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- Jobs performed by parallel apache threads or a command-line daemon +CREATE TABLE /*$wgDBprefix*/job ( + job_id int(9) unsigned NOT NULL auto_increment, + + -- Command name, currently only refreshLinks is defined + job_cmd varchar(255) NOT NULL default '', + + -- Namespace and title to act on + -- Should be 0 and '' if the command does not operate on a title + job_namespace int NOT NULL, + 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) +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- Details of updates to cached special pages +CREATE TABLE /*$wgDBprefix*/querycache_info ( + + -- Special page name + -- Corresponds to a qc_type value + qci_type varchar(32) NOT NULL default '', + + -- Timestamp of last update + qci_timestamp char(14) NOT NULL default '19700101000000', + + UNIQUE KEY ( qci_type ) + +) ENGINE=InnoDB; + +-- For each redirect, this table contains exactly one row defining its target +CREATE TABLE /*$wgDBprefix*/redirect ( + -- Key to the page_id of the redirect page + rd_from int(8) unsigned NOT NULL default '0', + + -- Key to page_namespace/page_title of the target page. + -- The target page may or may not exist, and due to renames + -- and deletions may refer to different page records as time + -- goes by. + rd_namespace int NOT NULL default '0', + rd_title varchar(255) binary NOT NULL default '', + + PRIMARY KEY rd_from (rd_from), + KEY rd_ns_title (rd_namespace,rd_title,rd_from) +) ENGINE=InnoDB, DEFAULT CHARSET=binary; + +-- Used for caching expensive grouped queries that need two links (for example double-redirects) + +CREATE TABLE /*$wgDBprefix*/querycachetwo ( + -- A key name, generally the base name of of the special page. + qcc_type char(32) NOT NULL, + + -- Some sort of stored value. Sizes, counts... + qcc_value int(5) unsigned NOT NULL default '0', + + -- Target namespace+title + qcc_namespace int NOT NULL default '0', + qcc_title char(255) binary NOT NULL default '', + + -- Target namespace+title2 + qcc_namespacetwo int NOT NULL default '0', + qcc_titletwo char(255) binary NOT NULL default '', + + KEY qcc_type (qcc_type,qcc_value), + KEY qcc_title (qcc_type,qcc_namespace,qcc_title), + KEY qcc_titletwo (qcc_type,qcc_namespacetwo,qcc_titletwo) + +) ENGINE=InnoDB, DEFAULT CHARSET=binary; diff --git a/maintenance/mysql5/tables.sql b/maintenance/mysql5/tables.sql index 81a4690a..356f3bbf 100644 --- a/maintenance/mysql5/tables.sql +++ b/maintenance/mysql5/tables.sql @@ -36,7 +36,7 @@ -- in early 2002 after a lot of trouble with the fields -- auto-updating. -- --- The PostgreSQL backend uses DATETIME fields for timestamps, +-- The Postgres backend uses DATETIME fields for timestamps, -- and we will migrate the MySQL definitions at some point as -- well. -- @@ -77,7 +77,7 @@ CREATE TABLE /*$wgDBprefix*/user ( -- Password hashes, normally hashed like so: -- MD5(CONCAT(user_id,'-',MD5(plaintext_password))), see -- wfEncryptPassword() in GlobalFunctions.php - user_password tinyblob NOT NULL default '', + user_password tinyblob NOT NULL, -- When using 'mail me a new password', a random -- password is generated and the hash stored here. @@ -85,15 +85,19 @@ CREATE TABLE /*$wgDBprefix*/user ( -- someone actually logs in with the new password, -- at which point the hash is moved to user_password -- and the old password is invalidated. - user_newpassword tinyblob NOT NULL default '', + user_newpassword tinyblob NOT NULL, + + -- Timestamp of the last time when a new password was + -- sent, for throttling purposes + user_newpass_time char(14) binary, -- Note: email should be restricted, not public info. -- Same with passwords. - user_email tinytext NOT NULL default '', + user_email tinytext NOT NULL, -- Newline-separated list of name=value defining the user -- preferences - user_options blob NOT NULL default '', + user_options blob NOT NULL, -- This is a timestamp which is updated when a user -- logs in, logs out, changes preferences, or performs @@ -110,24 +114,36 @@ CREATE TABLE /*$wgDBprefix*/user ( -- Initially NULL; when a user's e-mail address has been -- validated by returning with a mailed token, this is -- set to the current timestamp. - user_email_authenticated CHAR(14) BINARY, + user_email_authenticated char(14) binary, -- Randomly generated token created when the e-mail address -- is set and a confirmation test mail sent. - user_email_token CHAR(32) BINARY, + user_email_token char(32) binary, -- Expiration date for the user_email_token - user_email_token_expires CHAR(14) BINARY, + user_email_token_expires char(14) binary, -- Timestamp of account registration. -- Accounts predating this schema addition may contain NULL. - user_registration CHAR(14) BINARY, + user_registration char(14) binary, + + -- Count of edits and edit-like actions. + -- + -- *NOT* intended to be an accurate copy of COUNT(*) WHERE rev_user=user_id + -- May contain NULL for old accounts if batch-update scripts haven't been + -- run, as well as listing deleted edits and other myriad ways it could be + -- out of sync. + -- + -- Meant primarily for heuristic checks to give an impression of whether + -- the account has been used much. + -- + user_editcount int, PRIMARY KEY user_id (user_id), UNIQUE INDEX user_name (user_name), INDEX (user_email_token) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- -- User permissions have been broken out to a separate table; @@ -153,7 +169,7 @@ CREATE TABLE /*$wgDBprefix*/user_groups ( PRIMARY KEY (ug_user,ug_group), KEY (ug_group) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- Stores notifications of user talk page changes, for the display -- of the "you have new messages" box @@ -165,7 +181,7 @@ CREATE TABLE /*$wgDBprefix*/user_newtalk ( user_ip varchar(40) NOT NULL default '', INDEX user_id (user_id), INDEX user_ip (user_ip) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- @@ -188,7 +204,7 @@ CREATE TABLE /*$wgDBprefix*/page ( -- Comma-separated set of permission keys indicating who -- can move or edit the page. - page_restrictions tinyblob NOT NULL default '', + page_restrictions tinyblob NOT NULL, -- Number of times this page has been viewed. page_counter bigint(20) unsigned NOT NULL default '0', @@ -225,7 +241,7 @@ CREATE TABLE /*$wgDBprefix*/page ( INDEX (page_random), INDEX (page_len) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- -- Every edit of a page creates also a revision row. @@ -247,7 +263,7 @@ CREATE TABLE /*$wgDBprefix*/revision ( -- Text comment summarizing the change. -- This text is shown in the history and other changes lists, -- rendered in a subset of wiki markup by Linker::formatComment() - rev_comment tinyblob NOT NULL default '', + rev_comment tinyblob NOT NULL, -- Key to user.user_id of the user who made this edit. -- Stores 0 for anonymous edits and for some mass imports. @@ -273,7 +289,7 @@ CREATE TABLE /*$wgDBprefix*/revision ( INDEX user_timestamp (rev_user,rev_timestamp), INDEX usertext_timestamp (rev_user_text,rev_timestamp) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- @@ -294,7 +310,7 @@ CREATE TABLE /*$wgDBprefix*/text ( -- Depending on the contents of the old_flags field, the text -- may be convenient plain text, or it may be funkily encoded. - old_text mediumblob NOT NULL default '', + old_text mediumblob NOT NULL, -- Comma-separated list of flags: -- gzip: text is compressed with PHP's gzdeflate() function. @@ -305,11 +321,11 @@ CREATE TABLE /*$wgDBprefix*/text ( -- The object either contains multiple versions compressed -- together to achieve a better compression ratio, or it refers -- to another row where the text can be found. - old_flags tinyblob NOT NULL default '', + old_flags tinyblob NOT NULL, PRIMARY KEY old_id (old_id) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- -- Holding area for deleted articles, which may be viewed @@ -327,17 +343,17 @@ CREATE TABLE /*$wgDBprefix*/archive ( -- so old archived pages will remain accessible after -- upgrading from 1.4 to 1.5. -- Text may be gzipped or otherwise funky. - ar_text mediumblob NOT NULL default '', + ar_text mediumblob NOT NULL, -- Basic revision stuff... - ar_comment tinyblob NOT NULL default '', + ar_comment tinyblob NOT NULL, ar_user int(5) unsigned NOT NULL default '0', ar_user_text varchar(255) binary NOT NULL, ar_timestamp char(14) binary NOT NULL default '', ar_minor_edit tinyint(1) NOT NULL default '0', -- See ar_text note. - ar_flags tinyblob NOT NULL default '', + ar_flags tinyblob NOT NULL, -- When revisions are deleted, their unique rev_id is stored -- here so it can be retained after undeletion. This is necessary @@ -361,7 +377,7 @@ CREATE TABLE /*$wgDBprefix*/archive ( KEY name_title_timestamp (ar_namespace,ar_title,ar_timestamp) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- @@ -378,10 +394,10 @@ CREATE TABLE /*$wgDBprefix*/pagelinks ( pl_namespace int NOT NULL default '0', pl_title varchar(255) binary NOT NULL default '', - UNIQUE KEY pl_from(pl_from,pl_namespace,pl_title), - KEY (pl_namespace,pl_title) + UNIQUE KEY pl_from (pl_from,pl_namespace,pl_title), + KEY (pl_namespace,pl_title,pl_from) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- @@ -398,10 +414,10 @@ CREATE TABLE /*$wgDBprefix*/templatelinks ( tl_namespace int NOT NULL default '0', tl_title varchar(255) binary NOT NULL default '', - UNIQUE KEY tl_from(tl_from,tl_namespace,tl_title), - KEY (tl_namespace,tl_title) + UNIQUE KEY tl_from (tl_from,tl_namespace,tl_title), + KEY (tl_namespace,tl_title,tl_from) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- @@ -418,10 +434,10 @@ CREATE TABLE /*$wgDBprefix*/imagelinks ( -- all such pages are in namespace 6 (NS_IMAGE). il_to varchar(255) binary NOT NULL default '', - UNIQUE KEY il_from(il_from,il_to), - KEY (il_to) + UNIQUE KEY il_from (il_from,il_to), + KEY (il_to,il_from) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- -- Track category inclusions *used inline* @@ -452,15 +468,15 @@ CREATE TABLE /*$wgDBprefix*/categorylinks ( -- sorting method by approximate addition time. cl_timestamp timestamp NOT NULL, - UNIQUE KEY cl_from(cl_from,cl_to), + UNIQUE KEY cl_from (cl_from,cl_to), -- We always sort within a given category... - KEY cl_sortkey(cl_to,cl_sortkey), + KEY cl_sortkey (cl_to,cl_sortkey), -- Not really used? - KEY cl_timestamp(cl_to,cl_timestamp) + KEY cl_timestamp (cl_to,cl_timestamp) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- -- Track links to external URLs @@ -470,7 +486,7 @@ CREATE TABLE /*$wgDBprefix*/externallinks ( el_from int(8) unsigned NOT NULL default '0', -- The URL - el_to blob NOT NULL default '', + el_to blob NOT NULL, -- In the case of HTTP URLs, this is the URL with any username or password -- removed, and with the labels in the hostname reversed and converted to @@ -483,12 +499,12 @@ CREATE TABLE /*$wgDBprefix*/externallinks ( -- which allows for fast searching for all pages under example.com with the -- clause: -- WHERE el_index LIKE 'http://com.example.%' - el_index blob NOT NULL default '', + el_index blob NOT NULL, KEY (el_from, el_to(40)), KEY (el_to(60), el_from), KEY (el_index(60)) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- -- Track interlanguage links @@ -542,7 +558,7 @@ CREATE TABLE /*$wgDBprefix*/site_stats ( UNIQUE KEY ss_row_id (ss_row_id) -) TYPE=InnoDB; +) ENGINE=InnoDB; -- -- Stores an ID for every time any article is visited; @@ -552,8 +568,8 @@ CREATE TABLE /*$wgDBprefix*/site_stats ( -- that have been visited.) -- CREATE TABLE /*$wgDBprefix*/hitcounter ( - hc_id INTEGER UNSIGNED NOT NULL -) TYPE=HEAP MAX_ROWS=25000; + hc_id int unsigned NOT NULL +) ENGINE=HEAP MAX_ROWS=25000; -- @@ -565,7 +581,7 @@ CREATE TABLE /*$wgDBprefix*/ipblocks ( ipb_id int(8) NOT NULL auto_increment, -- Blocked IP address in dotted-quad form or user name. - ipb_address varchar(40) binary NOT NULL default '', + ipb_address tinyblob NOT NULL, -- Blocked user ID or 0 for IP blocks. ipb_user int(8) unsigned NOT NULL default '0', @@ -574,7 +590,7 @@ CREATE TABLE /*$wgDBprefix*/ipblocks ( ipb_by int(8) unsigned NOT NULL default '0', -- Text comment made by blocker. - ipb_reason tinyblob NOT NULL default '', + ipb_reason tinyblob NOT NULL, -- Creation (or refresh) date in standard YMDHMS form. -- IP blocks expire automatically. @@ -590,6 +606,9 @@ CREATE TABLE /*$wgDBprefix*/ipblocks ( -- Block prevents account creation from matching IP addresses ipb_create_account bool NOT NULL default 1, + + -- Block triggers autoblocks + ipb_enable_autoblock bool NOT NULL default '1', -- Time at which the block will expire. ipb_expiry char(14) binary NOT NULL default '', @@ -610,7 +629,7 @@ CREATE TABLE /*$wgDBprefix*/ipblocks ( INDEX ipb_timestamp (ipb_timestamp), INDEX ipb_expiry (ipb_expiry) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- @@ -626,14 +645,14 @@ CREATE TABLE /*$wgDBprefix*/image ( img_size int(8) unsigned NOT NULL default '0', -- For images, size in pixels. - img_width int(5) NOT NULL default '0', - img_height int(5) NOT NULL default '0', + img_width int(5) NOT NULL default '0', + img_height int(5) NOT NULL default '0', -- Extracted EXIF metadata stored as a serialized PHP array. img_metadata mediumblob NOT NULL, -- For images, bits per pixel if known. - img_bits int(3) NOT NULL default '0', + img_bits int(3) NOT NULL default '0', -- Media type as defined by the MEDIATYPE_xxx constants img_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL, @@ -650,7 +669,7 @@ CREATE TABLE /*$wgDBprefix*/image ( -- Description field as entered by the uploader. -- This is displayed in image upload history and logs. - img_description tinyblob NOT NULL default '', + img_description tinyblob NOT NULL, -- user_id and user_name of uploader. img_user int(5) unsigned NOT NULL default '0', @@ -667,7 +686,7 @@ CREATE TABLE /*$wgDBprefix*/image ( -- Used by Special:Newimages and Special:Imagelist INDEX img_timestamp (img_timestamp) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- -- Previous revisions of uploaded files. @@ -687,14 +706,14 @@ CREATE TABLE /*$wgDBprefix*/oldimage ( oi_width int(5) NOT NULL default 0, oi_height int(5) NOT NULL default 0, oi_bits int(3) NOT NULL default 0, - oi_description tinyblob NOT NULL default '', + oi_description tinyblob NOT NULL, oi_user int(5) unsigned NOT NULL default '0', oi_user_text varchar(255) binary NOT NULL default '', oi_timestamp char(14) binary NOT NULL default '', INDEX oi_name (oi_name(10)) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- @@ -702,7 +721,7 @@ CREATE TABLE /*$wgDBprefix*/oldimage ( -- CREATE TABLE /*$wgDBprefix*/filearchive ( -- Unique row id - fa_id int not null auto_increment, + fa_id int NOT NULL auto_increment, -- Original base filename; key to image.img_name, page.page_title, etc fa_name varchar(255) binary NOT NULL default '', @@ -729,14 +748,14 @@ CREATE TABLE /*$wgDBprefix*/filearchive ( -- Duped fields from image fa_size int(8) unsigned default '0', - fa_width int(5) default '0', - fa_height int(5) default '0', + fa_width int(5) default '0', + fa_height int(5) default '0', fa_metadata mediumblob, - fa_bits int(3) default '0', + fa_bits int(3) default '0', fa_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL, fa_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") default "unknown", fa_minor_mime varchar(32) default "unknown", - fa_description tinyblob default '', + fa_description tinyblob, fa_user int(5) unsigned default '0', fa_user_text varchar(255) binary default '', fa_timestamp char(14) binary default '', @@ -747,7 +766,7 @@ CREATE TABLE /*$wgDBprefix*/filearchive ( INDEX (fa_deleted_timestamp), -- sort by deletion time INDEX (fa_deleted_user) -- sort by deleter -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- -- Primarily a summary table for Special:Recentchanges, @@ -804,15 +823,20 @@ CREATE TABLE /*$wgDBprefix*/recentchanges ( -- $wgPutIPinRC option is enabled. rc_ip char(15) NOT NULL default '', + -- Text length in characters before + -- and after the edit + rc_old_len int(10) default '0', + rc_new_len int(10) default '0', + PRIMARY KEY rc_id (rc_id), INDEX rc_timestamp (rc_timestamp), INDEX rc_namespace_title (rc_namespace, rc_title), INDEX rc_cur_id (rc_cur_id), - INDEX new_name_timestamp(rc_new,rc_namespace,rc_timestamp), + INDEX new_name_timestamp (rc_new,rc_namespace,rc_timestamp), INDEX rc_ip (rc_ip), INDEX rc_ns_usertext ( rc_namespace, rc_user_text ) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; CREATE TABLE /*$wgDBprefix*/watchlist ( -- Key to user.user_id @@ -831,7 +855,7 @@ CREATE TABLE /*$wgDBprefix*/watchlist ( UNIQUE KEY (wl_user, wl_namespace, wl_title), KEY namespace_title (wl_namespace,wl_title) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- @@ -857,7 +881,7 @@ CREATE TABLE /*$wgDBprefix*/math ( UNIQUE KEY math_inputhash (math_inputhash) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- -- When using the default MySQL search backend, page titles @@ -875,13 +899,13 @@ CREATE TABLE /*$wgDBprefix*/searchindex ( si_title varchar(255) NOT NULL default '', -- Munged version of body text - si_text mediumtext NOT NULL default '', + si_text mediumtext NOT NULL, UNIQUE KEY (si_page), FULLTEXT si_title (si_title), FULLTEXT si_text (si_text) -) TYPE=MyISAM, DEFAULT CHARSET=utf8; +) ENGINE=MyISAM, DEFAULT CHARSET=utf8; -- -- Recognized interwiki link prefixes @@ -897,14 +921,14 @@ CREATE TABLE /*$wgDBprefix*/interwiki ( -- A boolean value indicating whether the wiki is in this project -- (used, for example, to detect redirect loops) - iw_local BOOL NOT NULL, + iw_local bool NOT NULL, -- Boolean value indicating whether interwiki transclusions are allowed. - iw_trans TINYINT(1) NOT NULL DEFAULT 0, + iw_trans tinyint(1) NOT NULL default 0, UNIQUE KEY iw_prefix (iw_prefix) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- -- Used for caching expensive grouped queries @@ -922,29 +946,29 @@ CREATE TABLE /*$wgDBprefix*/querycache ( KEY (qc_type,qc_value) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- -- For a few generic cache operations if not using Memcached -- CREATE TABLE /*$wgDBprefix*/objectcache ( - keyname char(255) binary not null default '', + keyname char(255) binary NOT NULL default '', value mediumblob, exptime datetime, - unique key (keyname), - key (exptime) + UNIQUE KEY (keyname), + KEY (exptime) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- -- Cache of interwiki transclusion -- CREATE TABLE /*$wgDBprefix*/transcache ( - tc_url VARCHAR(255) NOT NULL, - tc_contents TEXT, - tc_time INT NOT NULL, - UNIQUE INDEX tc_url_idx(tc_url) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; + tc_url varchar(255) NOT NULL, + tc_contents text, + tc_time int NOT NULL, + UNIQUE INDEX tc_url_idx (tc_url) +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; CREATE TABLE /*$wgDBprefix*/logging ( -- Symbolic keys for the general log type and the action type @@ -968,24 +992,25 @@ CREATE TABLE /*$wgDBprefix*/logging ( log_comment varchar(255) NOT NULL default '', -- LF separated list of miscellaneous parameters - log_params blob NOT NULL default '', + log_params blob NOT NULL, KEY type_time (log_type, log_timestamp), KEY user_time (log_user, log_timestamp), KEY page_time (log_namespace, log_title, log_timestamp) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; CREATE TABLE /*$wgDBprefix*/trackbacks ( - tb_id integer AUTO_INCREMENT PRIMARY KEY, - tb_page integer REFERENCES page(page_id) ON DELETE CASCADE, - tb_title varchar(255) NOT NULL, - tb_url varchar(255) NOT NULL, - tb_ex text, - tb_name varchar(255), - - INDEX (tb_page) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; + tb_id int auto_increment, + tb_page int REFERENCES page(page_id) ON DELETE CASCADE, + tb_title varchar(255) NOT NULL, + tb_url varchar(255) NOT NULL, + tb_ex text, + tb_name varchar(255), + + PRIMARY KEY (tb_id), + INDEX (tb_page) +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- Jobs performed by parallel apache threads or a command-line daemon CREATE TABLE /*$wgDBprefix*/job ( @@ -1001,22 +1026,61 @@ CREATE TABLE /*$wgDBprefix*/job ( -- Any other parameters to the command -- Presently unused, format undefined - job_params blob NOT NULL default '', + job_params blob NOT NULL, PRIMARY KEY job_id (job_id), KEY (job_cmd, job_namespace, job_title) -) TYPE=InnoDB, DEFAULT CHARSET=utf8; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; -- Details of updates to cached special pages CREATE TABLE /*$wgDBprefix*/querycache_info ( - -- Special page name - -- Corresponds to a qc_type value - qci_type varchar(32) NOT NULL default '', + -- Special page name + -- Corresponds to a qc_type value + qci_type varchar(32) NOT NULL default '', + + -- Timestamp of last update + qci_timestamp char(14) NOT NULL default '19700101000000', + + UNIQUE KEY ( qci_type ) + +) ENGINE=InnoDB; + +-- For each redirect, this table contains exactly one row defining its target +CREATE TABLE /*$wgDBprefix*/redirect ( + -- Key to the page_id of the redirect page + rd_from int(8) unsigned NOT NULL default '0', + + -- Key to page_namespace/page_title of the target page. + -- The target page may or may not exist, and due to renames + -- and deletions may refer to different page records as time + -- goes by. + rd_namespace int NOT NULL default '0', + rd_title varchar(255) binary NOT NULL default '', + + PRIMARY KEY rd_from (rd_from), + KEY rd_ns_title (rd_namespace,rd_title,rd_from) +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; - -- Timestamp of last update - qci_timestamp char(14) NOT NULL default '19700101000000', +-- Used for caching expensive grouped queries that need two links (for example double-redirects) - UNIQUE KEY ( qci_type ) +CREATE TABLE /*$wgDBprefix*/querycachetwo ( + -- A key name, generally the base name of of the special page. + qcc_type char(32) NOT NULL, + + -- Some sort of stored value. Sizes, counts... + qcc_value int(5) unsigned NOT NULL default '0', + + -- Target namespace+title + qcc_namespace int NOT NULL default '0', + qcc_title char(255) binary NOT NULL default '', + + -- Target namespace+title2 + qcc_namespacetwo int NOT NULL default '0', + qcc_titletwo char(255) binary NOT NULL default '', + + KEY qcc_type (qcc_type,qcc_value), + KEY qcc_title (qcc_type,qcc_namespace,qcc_title), + KEY qcc_titletwo (qcc_type,qcc_namespacetwo,qcc_titletwo) -) TYPE=InnoDB; +) ENGINE=InnoDB, DEFAULT CHARSET=utf8; diff --git a/maintenance/namespace2sql.php b/maintenance/namespace2sql.php index 8084bfec..081f6099 100644 --- a/maintenance/namespace2sql.php +++ b/maintenance/namespace2sql.php @@ -6,8 +6,8 @@ require_once( "commandLine.inc" ); for ($i = -2; $i < 16; ++$i) { - $nsname = wfStrencode( $wgLang->getNsText( $i ) ); - $dbname = wfStrencode( $wgDBname ); + $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/oracle/tables.sql b/maintenance/oracle/tables.sql index 6733f950..030f427f 100644 --- a/maintenance/oracle/tables.sql +++ b/maintenance/oracle/tables.sql @@ -109,14 +109,14 @@ CREATE TABLE pagelinks ( pl_title VARCHAR2(255) NOT NULL ); CREATE UNIQUE INDEX pl_from ON pagelinks(pl_from, pl_namespace, pl_title); -CREATE INDEX pl_namespace ON pagelinks(pl_namespace, pl_title); +CREATE INDEX pl_namespace ON pagelinks(pl_namespace, pl_title, pl_from); CREATE TABLE imagelinks ( il_from NUMBER(8) NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, il_to VARCHAR2(255) NOT NULL ); CREATE UNIQUE INDEX il_from ON imagelinks(il_from, il_to); -CREATE INDEX il_to ON imagelinks(il_to); +CREATE INDEX il_to ON imagelinks(il_to, il_from); CREATE TABLE categorylinks ( cl_from NUMBER(8) NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, @@ -229,6 +229,8 @@ CREATE TABLE recentchanges ( rc_moved_to_title VARCHAR2(255), rc_patrolled NUMBER(3) DEFAULT 0 NOT NULL, rc_ip VARCHAR2(40), + rc_old_len NUMBER(10) DEFAULT 0, + rc_new_len NUMBER(10) DEFAULT 0, CONSTRAINT rc_pk PRIMARY KEY (rc_id) ); CREATE INDEX rc_timestamp ON recentchanges (rc_timestamp); diff --git a/maintenance/ourusers.php b/maintenance/ourusers.php index b50519d2..9b7af605 100644 --- a/maintenance/ourusers.php +++ b/maintenance/ourusers.php @@ -59,28 +59,6 @@ foreach( $hosts as $host ) { print "GRANT SELECT, INSERT, UPDATE, DELETE ON `$db`.* TO 'wikiuser'@'$host' IDENTIFIED BY '$wikiuser_pass';\n"; } -/* - print "\n-- wikisql\n\n"; - foreach ( $databases as $db ) { -print <<<EOS -GRANT SELECT ON `$db`.`old` TO 'wikisql'@'$host' IDENTIFIED BY '$wikisql_pass'; -GRANT SELECT ON `$db`.`imagelinks` TO 'wikisql'@'$host' IDENTIFIED BY '$wikisql_pass'; -GRANT SELECT ON `$db`.`image` TO 'wikisql'@'$host' IDENTIFIED BY '$wikisql_pass'; -GRANT SELECT ON `$db`.`watchlist` TO 'wikisql'@'$host' IDENTIFIED BY '$wikisql_pass'; -GRANT SELECT ON `$db`.`site_stats` TO 'wikisql'@'$host' IDENTIFIED BY '$wikisql_pass'; -GRANT SELECT ON `$db`.`archive` TO 'wikisql'@'$host' IDENTIFIED BY '$wikisql_pass'; -GRANT SELECT ON `$db`.`links` TO 'wikisql'@'$host' IDENTIFIED BY '$wikisql_pass'; -GRANT SELECT ON `$db`.`ipblocks` TO 'wikisql'@'$host' IDENTIFIED BY '$wikisql_pass'; -GRANT SELECT ON `$db`.`cur` TO 'wikisql'@'$host' IDENTIFIED BY '$wikisql_pass'; -GRANT SELECT (user_rights, user_id, user_name, user_options) ON `$db`.`user` TO 'wikisql'@'$host' IDENTIFIED BY '$wikisql_pass'; -GRANT SELECT ON `$db`.`oldimage` TO 'wikisql'@'$host' IDENTIFIED BY '$wikisql_pass'; -GRANT SELECT ON `$db`.`recentchanges` TO 'wikisql'@'$host' IDENTIFIED BY '$wikisql_pass'; -GRANT SELECT ON `$db`.`math` TO 'wikisql'@'$host' IDENTIFIED BY '$wikisql_pass'; -GRANT SELECT ON `$db`.`brokenlinks` TO 'wikisql'@'$host' IDENTIFIED BY '$wikisql_pass'; - -EOS; - }*/ - print "\n-- wikiadmin\n\n"; print "GRANT PROCESS, REPLICATION CLIENT ON *.* TO 'wikiadmin'@'$host' IDENTIFIED BY '$wikiadmin_pass';\n"; print "GRANT ALL PRIVILEGES ON `boardvote%`.* TO wikiadmin@'$host' IDENTIFIED BY '$wikiadmin_pass';\n"; diff --git a/maintenance/parserTests.inc b/maintenance/parserTests.inc index 0aabd27b..c85220d0 100644 --- a/maintenance/parserTests.inc +++ b/maintenance/parserTests.inc @@ -25,7 +25,7 @@ */ /** */ -$options = array( 'quick', 'color', 'quiet', 'help' ); +$options = array( 'quick', 'color', 'quiet', 'help', 'show-output', 'record' ); $optionsWithArgs = array( 'regex' ); require_once( 'commandLine.inc' ); @@ -54,6 +54,11 @@ class ParserTest { var $lightcolor; /** + * boolean $showOutput Show test output + */ + var $showOutput; + + /** * Sets terminal colorization and diff/quick modes depending on OS and * command-line options (--color and --quick). * @@ -80,10 +85,14 @@ class ParserTest { break; } } + $this->term = $this->color + ? new AnsiTermColorer( $this->lightcolor ) + : new DummyTermColorer(); $this->showDiffs = !isset( $options['quick'] ); - $this->quiet = isset( $options['quiet'] ); + $this->showOutput = isset( $options['show-output'] ); + if (isset($options['regex'])) { $this->regex = $options['regex']; @@ -92,7 +101,14 @@ class ParserTest { $this->regex = ''; } + if( isset( $options['record'] ) ) { + $this->recorder = new DbTestRecorder( $this->term ); + } else { + $this->recorder = new TestRecorder( $this->term ); + } + $this->hooks = array(); + $this->functionHooks = array(); } /** @@ -109,38 +125,53 @@ class ParserTest { } /** - * Run a series of tests listed in the given text file. + * Run a series of tests listed in the given text files. * Each test consists of a brief description, wikitext input, * and the expected HTML output. * * Prints status updates on stdout and counts up the total * number and percentage of passed tests. * - * @param string $filename + * @param array of strings $filenames * @return bool True if passed all tests, false if any tests failed. * @public */ - function runTestsFromFile( $filename ) { + function runTestsFromFiles( $filenames ) { + $this->recorder->start(); + $ok = true; + foreach( $filenames as $filename ) { + $ok = $this->runFile( $filename ) && $ok; + } + $this->recorder->end(); + $this->recorder->report(); + return $ok; + } + + private function runFile( $filename ) { $infile = fopen( $filename, 'rt' ); if( !$infile ) { wfDie( "Couldn't open $filename\n" ); + } else { + print $this->term->color( 1 ) . + "Reading tests from \"$filename\"..." . + $this->term->reset() . + "\n"; } $data = array(); $section = null; - $success = 0; - $total = 0; $n = 0; + $ok = true; while( false !== ($line = fgets( $infile ) ) ) { $n++; if( preg_match( '/^!!\s*(\w+)/', $line, $matches ) ) { $section = strtolower( $matches[1] ); if( $section == 'endarticle') { if( !isset( $data['text'] ) ) { - wfDie( "'endarticle' without 'text' at line $n\n" ); + wfDie( "'endarticle' without 'text' at line $n of $filename\n" ); } if( !isset( $data['article'] ) ) { - wfDie( "'endarticle' without 'article' at line $n\n" ); + wfDie( "'endarticle' without 'article' at line $n of $filename\n" ); } $this->addArticle($this->chomp($data['article']), $this->chomp($data['text']), $n); $data = array(); @@ -149,7 +180,7 @@ class ParserTest { } if( $section == 'endhooks' ) { if( !isset( $data['hooks'] ) ) { - wfDie( "'endhooks' without 'hooks' at line $n\n" ); + wfDie( "'endhooks' without 'hooks' at line $n of $filename\n" ); } foreach( explode( "\n", $data['hooks'] ) as $line ) { $line = trim( $line ); @@ -161,15 +192,29 @@ class ParserTest { $section = null; continue; } + if( $section == 'endfunctionhooks' ) { + if( !isset( $data['functionhooks'] ) ) { + wfDie( "'endfunctionhooks' without 'functionhooks' at line $n of $filename\n" ); + } + foreach( explode( "\n", $data['functionhooks'] ) as $line ) { + $line = trim( $line ); + if( $line ) { + $this->requireFunctionHook( $line ); + } + } + $data = array(); + $section = null; + continue; + } if( $section == 'end' ) { if( !isset( $data['test'] ) ) { - wfDie( "'end' without 'test' at line $n\n" ); + wfDie( "'end' without 'test' at line $n of $filename\n" ); } if( !isset( $data['input'] ) ) { - wfDie( "'end' without 'input' at line $n\n" ); + wfDie( "'end' without 'input' at line $n of $filename\n" ); } if( !isset( $data['result'] ) ) { - wfDie( "'end' without 'result' at line $n\n" ); + wfDie( "'end' without 'result' at line $n of $filename\n" ); } if( !isset( $data['options'] ) ) { $data['options'] = ''; @@ -184,20 +229,19 @@ class ParserTest { $section = null; continue; } - if( $this->runTest( + $result = $this->runTest( $this->chomp( $data['test'] ), $this->chomp( $data['input'] ), $this->chomp( $data['result'] ), - $this->chomp( $data['options'] ) ) ) { - $success++; - } - $total++; + $this->chomp( $data['options'] ) ); + $ok = $ok && $result; + $this->recorder->record( $this->chomp( $data['test'] ), $result ); $data = array(); $section = null; continue; } if ( isset ($data[$section] ) ) { - wfDie( "duplicate section '$section' at line $n\n" ); + wfDie( "duplicate section '$section' at line $n of $filename\n" ); } $data[$section] = ''; continue; @@ -206,19 +250,8 @@ class ParserTest { $data[$section] .= $line; } } - if( $total > 0 ) { - $ratio = wfPercent( 100 * $success / $total ); - print $this->termColor( 1 ) . "\nPassed $success of $total tests ($ratio) "; - if( $success == $total ) { - print $this->termColor( 32 ) . "PASSED!"; - } else { - print $this->termColor( 31 ) . "FAILED!"; - } - print $this->termReset() . "\n"; - return ($success == $total); - } else { - wfDie( "No tests found.\n" ); - } + print "\n"; + return $ok; } /** @@ -258,6 +291,9 @@ class ParserTest { foreach( $this->hooks as $tag => $callback ) { $parser->setHook( $tag, $callback ); } + foreach( $this->functionHooks as $tag => $callback ) { + $parser->setFunctionHook( $tag, $callback ); + } wfRunHooks( 'ParserTestParser', array( &$parser ) ); $title =& Title::makeTitle( NS_MAIN, $titleText ); @@ -320,6 +356,12 @@ class ParserTest { $lang = 'en'; } + if( preg_match( '/variant=([a-z]+(?:-[a-z]+)?)/', $opts, $m ) ) + $variant = $m[1]; + else + $variant = false; + + $settings = array( 'wgServer' => 'http://localhost', 'wgScript' => '/index.php', @@ -334,7 +376,7 @@ class ParserTest { 'wgLanguageCode' => $lang, 'wgContLanguageCode' => $lang, 'wgDBprefix' => 'parsertest_', - + 'wgRawHtml' => preg_match('/\\brawhtml\\b/i', $opts), 'wgLang' => null, 'wgContLang' => null, 'wgNamespacesWithSubpages' => array( 0 => preg_match('/\\bsubpage\\b/i', $opts)), @@ -345,6 +387,9 @@ class ParserTest { 'wgUseTeX' => false, 'wgLocaltimezone' => 'UTC', 'wgAllowExternalImages' => true, + 'wgUseTidy' => false, + 'wgDefaultLanguageVariant' => $variant, + 'wgVariantArticlePath' => false, ); $this->savedGlobals = array(); foreach( $settings as $var => $val ) { @@ -374,7 +419,8 @@ class ParserTest { 'recentchanges', 'watchlist', 'math', 'searchindex', 'interwiki', 'querycache', - 'objectcache', 'job' + 'objectcache', 'job', 'redirect', + 'querycachetwo' ); // FIXME manually adding additional table for the tasks extension @@ -477,6 +523,7 @@ class ParserTest { 'img_media_type' => MEDIATYPE_BITMAP, 'img_major_mime' => "image", 'img_minor_mime' => "jpeg", + 'img_metadata' => serialize( array() ), ) ); # Update certain things in site_stats @@ -532,22 +579,57 @@ class ParserTest { * @private */ function teardownUploadDir( $dir ) { - unlink( "$dir/3/3a/Foobar.jpg" ); - rmdir( "$dir/3/3a" ); - rmdir( "$dir/3" ); - @rmdir( "$dir/thumb/6/65" ); - @rmdir( "$dir/thumb/6" ); - - @unlink( "$dir/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" ); - @rmdir( "$dir/thumb/3/3a/Foobar.jpg" ); - @rmdir( "$dir/thumb/3/3a" ); - @rmdir( "$dir/thumb/3/39" ); # wtf? - @rmdir( "$dir/thumb/3" ); - @rmdir( "$dir/thumb" ); - @rmdir( "$dir" ); + // delete the files first, then the dirs. + self::deleteFiles( + array ( + "$dir/3/3a/Foobar.jpg", + "$dir/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg", + "$dir/thumb/3/3a/Foobar.jpg/200px-Foobar.jpg", + "$dir/thumb/3/3a/Foobar.jpg/640px-Foobar.jpg", + "$dir/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg", + ) + ); + + self::deleteDirs( + array ( + "$dir/3/3a", + "$dir/3", + "$dir/thumb/6/65", + "$dir/thumb/6", + "$dir/thumb/3/3a/Foobar.jpg", + "$dir/thumb/3/3a", + "$dir/thumb/3", + "$dir/thumb", + "$dir", + ) + ); } /** + * @desc delete the specified files, if they exist. + * @param array $files full paths to files to delete. + */ + private static function deleteFiles( $files ) { + foreach( $files as $file ) { + if( file_exists( $file ) ) { + unlink( $file ); + } + } + } + + /** + * @desc delete the specified directories, if they exist. Must be empty. + * @param array $dirs full paths to directories to delete. + */ + private static function deleteDirs( $dirs ) { + foreach( $dirs as $dir ) { + if( is_dir( $dir ) ) { + rmdir( $dir ); + } + } + } + + /** * "Running test $desc..." * @private */ @@ -564,7 +646,7 @@ class ParserTest { */ function showSuccess( $desc ) { if( !$this->quiet ) { - print $this->termColor( '1;32' ) . 'PASSED' . $this->termReset() . "\n"; + print $this->term->color( '1;32' ) . 'PASSED' . $this->term->reset() . "\n"; } return true; } @@ -585,7 +667,10 @@ class ParserTest { # test, in case it succeeded. Show it now: $this->showTesting( $desc ); } - print $this->termColor( '1;31' ) . 'FAILED!' . $this->termReset() . "\n"; + print $this->term->color( '1;31' ) . 'FAILED!' . $this->term->reset() . "\n"; + if ( $this->showOutput ) { + print "--- Expected ---\n$result\n--- Actual ---\n$html\n"; + } if( $this->showDiffs ) { print $this->quickDiff( $result, $html ); if( !$this->wellFormed( $html ) ) { @@ -636,33 +721,6 @@ class ParserTest { } /** - * Return ANSI terminal escape code for changing text attribs/color, - * or empty string if color output is disabled. - * - * @param string $color Semicolon-separated list of attribute/color codes - * @return string - * @private - */ - function termColor( $color ) { - if($this->lightcolor) { - return $this->color ? "\x1b[1;{$color}m" : ''; - } else { - return $this->color ? "\x1b[{$color}m" : ''; - } - } - - /** - * Return ANSI terminal escape code for restoring default text attributes, - * or empty string if color output is disabled. - * - * @return string - * @private - */ - function termReset() { - return $this->color ? "\x1b[0m" : ''; - } - - /** * Colorize unified diff output if set for ANSI color output. * Subtractions are colored blue, additions red. * @@ -673,8 +731,8 @@ class ParserTest { function colorDiff( $text ) { return preg_replace( array( '/^(-.*)$/m', '/^(\+.*)$/m' ), - array( $this->termColor( 34 ) . '$1' . $this->termReset(), - $this->termColor( 31 ) . '$1' . $this->termReset() ), + array( $this->term->color( 34 ) . '$1' . $this->term->reset(), + $this->term->color( 31 ) . '$1' . $this->term->reset() ), $text ); } @@ -717,6 +775,22 @@ class ParserTest { } } + + /** + * Steal a callback function from the primary parser, save it for + * application to our scary parser. If the hook is not installed, + * die a painful dead to warn the others. + * @param string $name + */ + private function requireFunctionHook( $name ) { + global $wgParser; + if( isset( $wgParser->mFunctionHooks[$name] ) ) { + $this->functionHooks[$name] = $wgParser->mFunctionHooks[$name]; + } else { + wfDie( "This test suite requires the '$name' function hook extension.\n" ); + } + } + /* * Run the "tidy" command on text if the $wgUseTidy * global is true @@ -762,25 +836,241 @@ class ParserTest { $start = max( 0, $position - 10 ); $before = $position - $start; $fragment = '...' . - $this->termColor( 34 ) . + $this->term->color( 34 ) . substr( $text, $start, $before ) . - $this->termColor( 0 ) . - $this->termColor( 31 ) . - $this->termColor( 1 ) . + $this->term->color( 0 ) . + $this->term->color( 31 ) . + $this->term->color( 1 ) . substr( $text, $position, 1 ) . - $this->termColor( 0 ) . - $this->termColor( 34 ) . + $this->term->color( 0 ) . + $this->term->color( 34 ) . substr( $text, $position + 1, 9 ) . - $this->termColor( 0 ) . + $this->term->color( 0 ) . '...'; $display = str_replace( "\n", ' ', $fragment ); $caret = ' ' . str_repeat( ' ', $before ) . - $this->termColor( 31 ) . + $this->term->color( 31 ) . '^' . - $this->termColor( 0 ); + $this->term->color( 0 ); return "$display\n$caret"; } + +} + +class AnsiTermColorer { + function __construct( $light ) { + $this->light = $light; + } + + /** + * Return ANSI terminal escape code for changing text attribs/color + * + * @param string $color Semicolon-separated list of attribute/color codes + * @return string + * @private + */ + function color( $color ) { + $light = $this->light ? "1;" : ""; + return "\x1b[{$light}{$color}m"; + } + + /** + * Return ANSI terminal escape code for restoring default text attributes + * + * @return string + * @private + */ + function reset() { + return "\x1b[0m"; + } +} + +/* A colour-less terminal */ +class DummyTermColorer { + function color( $color ) { + return ''; + } + + function reset() { + return ''; + } +} + +class TestRecorder { + function __construct( $term ) { + $this->term = $term; + } + + function start() { + $this->total = 0; + $this->success = 0; + } + + function record( $test, $result ) { + $this->total++; + $this->success += ($result ? 1 : 0); + } + + function end() { + // dummy + } + + function report() { + if( $this->total > 0 ) { + $this->reportPercentage( $this->success, $this->total ); + } else { + wfDie( "No tests found.\n" ); + } + } + + function reportPercentage( $success, $total ) { + $ratio = wfPercent( 100 * $success / $total ); + print $this->term->color( 1 ) . "Passed $success of $total tests ($ratio)... "; + if( $success == $total ) { + print $this->term->color( 32 ) . "PASSED!"; + } else { + print $this->term->color( 31 ) . "FAILED!"; + } + print $this->term->reset() . "\n"; + return ($success == $total); + } +} + +class DbTestRecorder extends TestRecorder { + private $db; ///< Database connection to the main DB + private $curRun; ///< run ID number for the current run + private $prevRun; ///< run ID number for the previous run, if any + + function __construct( $term ) { + parent::__construct( $term ); + $this->db = wfGetDB( DB_MASTER ); + } + + /** + * Set up result recording; insert a record for the run with the date + * and all that fun stuff + */ + function start() { + parent::start(); + + $this->db->begin(); + + // We'll make comparisons against the previous run later... + $this->prevRun = $this->db->selectField( 'testrun', 'MAX(tr_id)' ); + + $this->db->insert( 'testrun', + array( + 'tr_date' => $this->db->timestamp(), + 'tr_mw_version' => SpecialVersion::getVersion(), + 'tr_php_version' => phpversion(), + 'tr_db_version' => $this->db->getServerVersion(), + 'tr_uname' => php_uname() + ), + __METHOD__ ); + $this->curRun = $this->db->insertId(); + } + + /** + * Record an individual test item's success or failure to the db + * @param string $test + * @param bool $result + */ + function record( $test, $result ) { + parent::record( $test, $result ); + $this->db->insert( 'testitem', + array( + 'ti_run' => $this->curRun, + 'ti_name' => $test, + 'ti_success' => $result ? 1 : 0, + ), + __METHOD__ ); + } + + /** + * Commit transaction and clean up for result recording + */ + function end() { + $this->db->commit(); + parent::end(); + } + + function report() { + if( $this->prevRun ) { + $table = array( + array( 'previously failing test(s) now PASSING! :)', 0, 1 ), + array( 'previously PASSING test(s) removed o_O', 1, null ), + array( 'new PASSING test(s) :)', null, 1 ), + + array( 'previously passing test(s) now FAILING! :(', 1, 0 ), + array( 'previously FAILING test(s) removed O_o', 0, null ), + array( 'new FAILING test(s) :(', null, 0 ), + ); + foreach( $table as $criteria ) { + list( $label, $before, $after ) = $criteria; + $differences = $this->compareResult( $before, $after ); + if( $differences ) { + $count = count($differences); + printf( "%4d %s\n", $count, $label ); + foreach ($differences as $differing_test_name) { + print " * $differing_test_name\n"; + } + } + } + } else { + print "No previous test runs to compare against.\n"; + } + parent::report(); + } + + /** + ** @desc: Returns an array of the test names with changed results, based on the specified + ** before/after criteria. + */ + private function compareResult( $before, $after ) { + $testitem = $this->db->tableName( 'testitem' ); + $prevRun = intval( $this->prevRun ); + $curRun = intval( $this->curRun ); + $prevStatus = $this->condition( $before ); + $curStatus = $this->condition( $after ); + + // note: requires mysql >= ver 4.1 for subselects + if( is_null( $after ) ) { + $sql = " + select prev.ti_name as t from $testitem as prev + where prev.ti_run=$prevRun and + prev.ti_success $prevStatus and + (select current.ti_success from $testitem as current + where current.ti_run=$curRun + and prev.ti_name=current.ti_name) $curStatus"; + } else { + $sql = " + select current.ti_name as t from $testitem as current + where current.ti_run=$curRun and + current.ti_success $curStatus and + (select prev.ti_success from $testitem as prev + where prev.ti_run=$prevRun + and prev.ti_name=current.ti_name) $prevStatus"; + } + $result = $this->db->query( $sql, __METHOD__ ); + $retval = array(); + while ($row = $this->db->fetchObject( $result )) { + $retval[] = $row->t; + } + $this->db->freeResult( $result ); + return $retval; + } + + /** + ** @desc: Helper function for compareResult() database querying. + */ + private function condition( $value ) { + if( is_null( $value ) ) { + return 'IS NULL'; + } else { + return '=' . intval( $value ); + } + } } diff --git a/maintenance/parserTests.php b/maintenance/parserTests.php index 309bf2e0..bd147788 100644 --- a/maintenance/parserTests.php +++ b/maintenance/parserTests.php @@ -26,22 +26,24 @@ require('parserTests.inc'); if( isset( $options['help'] ) ) { - echo <<<END + echo <<<ENDS MediaWiki $wgVersion parser test suite -Usage: php parserTests.php [--quick] [--quiet] [--color[=(yes|no|light)]] +Usage: php parserTests.php [--quick] [--quiet] [--show-output] + [--color[=(yes|no|light)]] [--regex=<expression>] [--file=<testfile>] [--help] Options: - --quick Suppress diff output of failed tests - --quiet Suppress notification of passed tests (shows only failed tests) - --color Override terminal detection and force color output on or off - 'light' option is similar to 'yes' but with color for dark backgrounds - --regex Only run tests whose descriptions which match given regex - --file Run test cases from a custom file instead of parserTests.txt - --help Show this help message + --quick Suppress diff output of failed tests + --quiet Suppress notification of passed tests (shows only failed tests) + --show-output Show expected and actual output + --color Override terminal detection and force color output on or off + 'light' option is similar to 'yes' but with color for dark backgrounds + --regex Only run tests whose descriptions which match given regex + --file Run test cases from a custom file instead of parserTests.txt + --help Show this help message -END; +ENDS; exit( 0 ); } @@ -52,13 +54,16 @@ $wgTitle = Title::newFromText( 'Parser test script do not use' ); $tester = new ParserTest(); if( isset( $options['file'] ) ) { - $file = $options['file']; + $files = array( $options['file'] ); } else { - # Note: the command line setup changes the current working directory - # to the parent, which is why we have to put the subdir here: - $file = $IP.'/maintenance/parserTests.txt'; + // Default parser tests and any set from extensions or local config + $files = $wgParserTestFiles; } -$ok = $tester->runTestsFromFile( $file ); + +# Print out software version to assist with locating regressions +$version = SpecialVersion::getVersion(); +echo( "This is MediaWiki version {$version}.\n" ); +$ok = $tester->runTestsFromFiles( $files ); exit ($ok ? 0 : -1); ?> diff --git a/maintenance/parserTests.txt b/maintenance/parserTests.txt index 66b46a53..3d748aef 100644 --- a/maintenance/parserTests.txt +++ b/maintenance/parserTests.txt @@ -18,7 +18,8 @@ # subpage enable subpages (disabled by default) # noxml don't check for XML well formdness # title=[[XXX]] run test using article title XXX -# language=XXX set content language to XXX for this test +# language=XXX set content language to XXX for this test +# variant=XXX set the variant of language for this test (eg zh-tw) # disabled do not run test # # For testing purposes, temporary articles can created: @@ -377,7 +378,7 @@ Simple definition !! end !! test -Simple definition +Definition list for indentation only !! input : Indented text !! result @@ -428,7 +429,7 @@ Definition list with wikilink containing colon # At Brion's and JeLuF's insistence... :) !! test -Definition list with wikilink containing colon +Definition list with news link containing colon !! input ; news:alt.wikipedia.rox: This isn't even a real newsgroup! !! result @@ -2170,12 +2171,12 @@ Template with complex template as argument !! end !! test -TODO: Template with thumb image (with link in description) +Template with thumb image (with link in description) !! input {{paramtest| param =[[Image:noimage.png|thumb|[[no link|link]] [[no link|caption]]]]}} !! result -This is a test template with parameter <div class="thumb tright"><div style="width:182px;"><a href="/index.php?title=Special:Upload&wpDestFile=Noimage.png" class="new" title="Image:Noimage.png">Image:Noimage.png</a> <div class="thumbcaption"><a href="/index.php?title=No_link&action=edit" class="new" title="No link">link</a> <a href="/index.php?title=No_link&action=edit" class="new" title="No link">caption</a></div></div></div> +This is a test template with parameter <div class="thumb tright"><div class="thumbinner" style="width:122px;"><a href="/index.php?title=Special:Upload&wpDestFile=Noimage.png" class="new" title="Image:Noimage.png">Image:Noimage.png</a> <div class="thumbcaption"><a href="/index.php?title=No_link&action=edit" class="new" title="No link">link</a> <a href="/index.php?title=No_link&action=edit" class="new" title="No link">caption</a></div></div></div> !! end @@ -2284,7 +2285,7 @@ Template infinite loop !! input {{loop1}} !! result -<p>{{loop1}}<!-- WARNING: template loop detected --> +<p><a href="/index.php?title=Loop1&action=edit" class="new" title="Loop1">loop1</a><!-- WARNING: template loop detected --> </p> !! end @@ -2826,6 +2827,25 @@ msg Foo !! end +!! test +{{#special:}} page name, known +!! options +msg +!! input +{{#special:Recentchanges}} +!! result +Special:Recentchanges +!! end + +!! test +{{#special:}} page name, unknown +!! options +msg +!! input +{{#special:foobarnonexistent}} +!! result +No such special page +!! end ### ### Images @@ -2862,7 +2882,7 @@ Image with frame and link !! input [[Image:Foobar.jpg|frame|left|This is a test image [[Main Page]]]] !! result -<div class="thumb tleft"><div style="width:1943px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="This is a test image Main Page"><img src="http://example.com/images/3/3a/Foobar.jpg" alt="This is a test image Main Page" width="1941" height="220" longdesc="/wiki/Image:Foobar.jpg" /></a> <div class="thumbcaption">This is a test image <a href="/wiki/Main_Page" title="Main Page">Main Page</a></div></div></div> +<div class="thumb tleft"><div class="thumbinner" style="width:1943px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="This is a test image Main Page"><img src="http://example.com/images/3/3a/Foobar.jpg" alt="This is a test image Main Page" width="1941" height="220" longdesc="/wiki/Image:Foobar.jpg" class="thumbimage" /></a> <div class="thumbcaption">This is a test image <a href="/wiki/Main_Page" title="Main Page">Main Page</a></div></div></div> !! end @@ -2891,16 +2911,16 @@ Thumbnail image caption with a free URL !! input [[Image:foobar.jpg|thumb|http://example.com]] !! result -<div class="thumb tright"><div style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="http://example.com"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="http://example.com" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="Enlarge" /></a></div><a href="http://example.com" class="external free" title="http://example.com" rel="nofollow">http://example.com</a></div></div></div> +<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="http://example.com"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="http://example.com" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div><a href="http://example.com" class="external free" title="http://example.com" rel="nofollow">http://example.com</a></div></div></div> !! end !! test BUG 1887: A ISBN with a thumbnail !! input -[[Image:foobar.jpg|thumb|ISBN 12354]] +[[Image:foobar.jpg|thumb|ISBN 1235467890]] !! result -<div class="thumb tright"><div style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="ISBN 12354"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="ISBN 12354" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="Enlarge" /></a></div><a href="/index.php?title=Special:Booksources&isbn=12354" class="internal">ISBN 12354</a></div></div></div> +<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="ISBN 1235467890"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="ISBN 1235467890" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div><a href="/index.php?title=Special:Booksources&isbn=1235467890" class="internal">ISBN 1235467890</a></div></div></div> !! end @@ -2909,7 +2929,7 @@ BUG 1887: A RFC with a thumbnail !! input [[Image:foobar.jpg|thumb|This is RFC 12354]] !! result -<div class="thumb tright"><div style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="This is RFC 12354"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="This is RFC 12354" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="Enlarge" /></a></div>This is <a href="http://www.ietf.org/rfc/rfc12354.txt" class="external" title="http://www.ietf.org/rfc/rfc12354.txt">RFC 12354</a></div></div></div> +<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="This is RFC 12354"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="This is RFC 12354" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>This is <a href="http://www.ietf.org/rfc/rfc12354.txt" class="external" title="http://www.ietf.org/rfc/rfc12354.txt">RFC 12354</a></div></div></div> !! end @@ -2918,7 +2938,7 @@ BUG 1887: A mailto link with a thumbnail !! input [[Image:foobar.jpg|thumb|Please mailto:nobody@example.com]] !! result -<div class="thumb tright"><div style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Please mailto:nobody@example.com"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="Please mailto:nobody@example.com" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="Enlarge" /></a></div>Please <a href="mailto:nobody@example.com" class="external free" title="mailto:nobody@example.com" rel="nofollow">mailto:nobody@example.com</a></div></div></div> +<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Please mailto:nobody@example.com"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="Please mailto:nobody@example.com" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>Please <a href="mailto:nobody@example.com" class="external free" title="mailto:nobody@example.com" rel="nofollow">mailto:nobody@example.com</a></div></div></div> !! end @@ -2928,7 +2948,7 @@ so math is not stripped and turns up as escaped <math> tags. !! input [[Image:foobar.jpg|thumb|<math>2+2</math>]] !! result -<div class="thumb tright"><div style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="<math>2+2</math>"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="<math>2+2</math>" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="Enlarge" /></a></div><math>2+2</math></div></div></div> +<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="<math>2+2</math>"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="<math>2+2</math>" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div><math>2+2</math></div></div></div> !! end @@ -2939,7 +2959,7 @@ math !! input [[Image:foobar.jpg|thumb|<math>2+2</math>]] !! result -<div class="thumb tright"><div style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="2 + 2"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="2 + 2" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="Enlarge" /></a></div><span class="texhtml">2 + 2</span></div></div></div> +<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="2 + 2"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="2 + 2" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div><span class="texhtml">2 + 2</span></div></div></div> !! end @@ -3012,7 +3032,7 @@ Image caption containing another image !! input [[Image:Foobar.jpg|thumb|This is a caption with another [[Image:icon.png|image]] inside it!]] !! result -<div class="thumb tright"><div style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="This is a caption with another Image:Icon.png inside it!"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="This is a caption with another Image:Icon.png inside it!" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="Enlarge" /></a></div>This is a caption with another <a href="/index.php?title=Special:Upload&wpDestFile=Icon.png" class="new" title="Image:Icon.png">Image:Icon.png</a> inside it!</div></div></div> +<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="This is a caption with another Image:Icon.png inside it!"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="This is a caption with another Image:Icon.png inside it!" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>This is a caption with another <a href="/index.php?title=Special:Upload&wpDestFile=Icon.png" class="new" title="Image:Icon.png">Image:Icon.png</a> inside it!</div></div></div> !! end @@ -3032,7 +3052,7 @@ Bug 3090: External links other than http: in image captions !! input [[Image:Foobar.jpg|thumb|200px|This caption has [irc://example.net irc] and [https://example.com Secure] ext links in it.]] !! result -<div class="thumb tright"><div style="width:202px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="This caption has irc and Secure ext links in it."><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/200px-Foobar.jpg" alt="This caption has irc and Secure ext links in it." width="200" height="23" longdesc="/wiki/Image:Foobar.jpg" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="Enlarge" /></a></div>This caption has <a href="irc://example.net" class="external text" title="irc://example.net" rel="nofollow">irc</a> and <a href="https://example.com" class="external text" title="https://example.com" rel="nofollow">Secure</a> ext links in it.</div></div></div> +<div class="thumb tright"><div class="thumbinner" style="width:202px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="This caption has irc and Secure ext links in it."><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/200px-Foobar.jpg" alt="This caption has irc and Secure ext links in it." width="200" height="23" longdesc="/wiki/Image:Foobar.jpg" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>This caption has <a href="irc://example.net" class="external text" title="irc://example.net" rel="nofollow">irc</a> and <a href="https://example.com" class="external text" title="https://example.com" rel="nofollow">Secure</a> ext links in it.</div></div></div> !! end @@ -3136,8 +3156,6 @@ es:Alimento fr:Nourriture zh:食品 ### !! test Basic section headings -!! options -title=[[Parser test script]] !! input == Headline 1 == Some text @@ -3147,21 +3165,19 @@ More ===Smaller headline=== Blah blah !! result -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test_script&action=edit&section=1" title="Edit section: Headline 1">edit</a>]</div><a name="Headline_1"></a><h2> Headline 1 </h2> +<a name="Headline_1"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: Headline 1">edit</a>]</span> <span class="mw-headline"> Headline 1 </span></h2> <p>Some text </p> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test_script&action=edit&section=2" title="Edit section: Headline 2">edit</a>]</div><a name="Headline_2"></a><h2>Headline 2</h2> +<a name="Headline_2"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: Headline 2">edit</a>]</span> <span class="mw-headline">Headline 2</span></h2> <p>More </p> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test_script&action=edit&section=3" title="Edit section: Smaller headline">edit</a>]</div><a name="Smaller_headline"></a><h3>Smaller headline</h3> +<a name="Smaller_headline"></a><h3><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=3" title="Edit section: Smaller headline">edit</a>]</span> <span class="mw-headline">Smaller headline</span></h3> <p>Blah blah </p> !! end !! test Section headings with TOC -!! options -title=[[Parser test script]] !! input == Headline 1 == === Subheadline 1 === @@ -3194,14 +3210,14 @@ Some text </li> </ul> </td></tr></table><script type="text/javascript"> if (window.showTocToggle) { var tocShowText = "show"; var tocHideText = "hide"; showTocToggle(); } </script> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test_script&action=edit&section=1" title="Edit section: Headline 1">edit</a>]</div><a name="Headline_1"></a><h2> Headline 1 </h2> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test_script&action=edit&section=2" title="Edit section: Subheadline 1">edit</a>]</div><a name="Subheadline_1"></a><h3> Subheadline 1 </h3> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test_script&action=edit&section=3" title="Edit section: Skipping a level">edit</a>]</div><a name="Skipping_a_level"></a><h5> Skipping a level </h5> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test_script&action=edit&section=4" title="Edit section: Skipping a level">edit</a>]</div><a name="Skipping_a_level_2"></a><h6> Skipping a level </h6> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test_script&action=edit&section=5" title="Edit section: Headline 2">edit</a>]</div><a name="Headline_2"></a><h2> Headline 2 </h2> +<a name="Headline_1"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: Headline 1">edit</a>]</span> <span class="mw-headline"> Headline 1 </span></h2> +<a name="Subheadline_1"></a><h3><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: Subheadline 1">edit</a>]</span> <span class="mw-headline"> Subheadline 1 </span></h3> +<a name="Skipping_a_level"></a><h5><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=3" title="Edit section: Skipping a level">edit</a>]</span> <span class="mw-headline"> Skipping a level </span></h5> +<a name="Skipping_a_level_2"></a><h6><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=4" title="Edit section: Skipping a level">edit</a>]</span> <span class="mw-headline"> Skipping a level </span></h6> +<a name="Headline_2"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=5" title="Edit section: Headline 2">edit</a>]</span> <span class="mw-headline"> Headline 2 </span></h2> <p>Some text </p> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test_script&action=edit&section=6" title="Edit section: Another headline">edit</a>]</div><a name="Another_headline"></a><h3>Another headline</h3> +<a name="Another_headline"></a><h3><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=6" title="Edit section: Another headline">edit</a>]</span> <span class="mw-headline">Another headline</span></h3> !! end @@ -3249,29 +3265,27 @@ Handling of sections up to level 6 and beyond </li> </ul> </td></tr></table><script type="text/javascript"> if (window.showTocToggle) { var tocShowText = "show"; var tocHideText = "hide"; showTocToggle(); } </script> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: Level 1 Heading">edit</a>]</div><a name="Level_1_Heading"></a><h1> Level 1 Heading</h1> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: Level 2 Heading">edit</a>]</div><a name="Level_2_Heading"></a><h2> Level 2 Heading</h2> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test&action=edit&section=3" title="Edit section: Level 3 Heading">edit</a>]</div><a name="Level_3_Heading"></a><h3> Level 3 Heading</h3> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test&action=edit&section=4" title="Edit section: Level 4 Heading">edit</a>]</div><a name="Level_4_Heading"></a><h4> Level 4 Heading</h4> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test&action=edit&section=5" title="Edit section: Level 5 Heading">edit</a>]</div><a name="Level_5_Heading"></a><h5> Level 5 Heading</h5> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test&action=edit&section=6" title="Edit section: Level 6 Heading">edit</a>]</div><a name="Level_6_Heading"></a><h6> Level 6 Heading</h6> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test&action=edit&section=7" title="Edit section: = Level 7 Heading=">edit</a>]</div><a name=".3D_Level_7_Heading.3D"></a><h6>= Level 7 Heading=</h6> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test&action=edit&section=8" title="Edit section: == Level 8 Heading==">edit</a>]</div><a name=".3D.3D_Level_8_Heading.3D.3D"></a><h6>== Level 8 Heading==</h6> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test&action=edit&section=9" title="Edit section: === Level 9 Heading===">edit</a>]</div><a name=".3D.3D.3D_Level_9_Heading.3D.3D.3D"></a><h6>=== Level 9 Heading===</h6> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test&action=edit&section=10" title="Edit section: ==== Level 10 Heading====">edit</a>]</div><a name=".3D.3D.3D.3D_Level_10_Heading.3D.3D.3D.3D"></a><h6>==== Level 10 Heading====</h6> +<a name="Level_1_Heading"></a><h1><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: Level 1 Heading">edit</a>]</span> <span class="mw-headline"> Level 1 Heading</span></h1> +<a name="Level_2_Heading"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: Level 2 Heading">edit</a>]</span> <span class="mw-headline"> Level 2 Heading</span></h2> +<a name="Level_3_Heading"></a><h3><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=3" title="Edit section: Level 3 Heading">edit</a>]</span> <span class="mw-headline"> Level 3 Heading</span></h3> +<a name="Level_4_Heading"></a><h4><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=4" title="Edit section: Level 4 Heading">edit</a>]</span> <span class="mw-headline"> Level 4 Heading</span></h4> +<a name="Level_5_Heading"></a><h5><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=5" title="Edit section: Level 5 Heading">edit</a>]</span> <span class="mw-headline"> Level 5 Heading</span></h5> +<a name="Level_6_Heading"></a><h6><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=6" title="Edit section: Level 6 Heading">edit</a>]</span> <span class="mw-headline"> Level 6 Heading</span></h6> +<a name=".3D_Level_7_Heading.3D"></a><h6><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=7" title="Edit section: = Level 7 Heading=">edit</a>]</span> <span class="mw-headline">= Level 7 Heading=</span></h6> +<a name=".3D.3D_Level_8_Heading.3D.3D"></a><h6><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=8" title="Edit section: == Level 8 Heading==">edit</a>]</span> <span class="mw-headline">== Level 8 Heading==</span></h6> +<a name=".3D.3D.3D_Level_9_Heading.3D.3D.3D"></a><h6><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=9" title="Edit section: === Level 9 Heading===">edit</a>]</span> <span class="mw-headline">=== Level 9 Heading===</span></h6> +<a name=".3D.3D.3D.3D_Level_10_Heading.3D.3D.3D.3D"></a><h6><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=10" title="Edit section: ==== Level 10 Heading====">edit</a>]</span> <span class="mw-headline">==== Level 10 Heading====</span></h6> !! end !! test Resolving duplicate section names -!! options -title=[[Parser test script]] !! input == Foo bar == == Foo bar == !! result -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test_script&action=edit&section=1" title="Edit section: Foo bar">edit</a>]</div><a name="Foo_bar"></a><h2> Foo bar </h2> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test_script&action=edit&section=2" title="Edit section: Foo bar">edit</a>]</div><a name="Foo_bar_2"></a><h2> Foo bar </h2> +<a name="Foo_bar"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: Foo bar">edit</a>]</span> <span class="mw-headline"> Foo bar </span></h2> +<a name="Foo_bar_2"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: Foo bar">edit</a>]</span> <span class="mw-headline"> Foo bar </span></h2> !! end @@ -3284,18 +3298,16 @@ Template:sections !! test Template with sections, __NOTOC__ -!! options -title=[[Parser test script]] !! input __NOTOC__ ==Section 0== {{sections}} ==Section 4== !! result -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test_script&action=edit&section=1" title="Edit section: Section 0">edit</a>]</div><a name="Section_0"></a><h2>Section 0</h2> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Template:Sections&action=edit&section=1" title="Template:Sections">edit</a>]</div><a name="Section_1"></a><h3>Section 1</h3> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Template:Sections&action=edit&section=2" title="Template:Sections">edit</a>]</div><a name="Section_2"></a><h2>Section 2</h2> -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test_script&action=edit&section=2" title="Edit section: Section 4">edit</a>]</div><a name="Section_4"></a><h2>Section 4</h2> +<a name="Section_0"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: Section 0">edit</a>]</span> <span class="mw-headline">Section 0</span></h2> +<a name="Section_1"></a><h3><span class="editsection">[<a href="/index.php?title=Template:Sections&action=edit&section=1" title="Template:Sections">edit</a>]</span> <span class="mw-headline">Section 1</span></h3> +<a name="Section_2"></a><h2><span class="editsection">[<a href="/index.php?title=Template:Sections&action=edit&section=2" title="Template:Sections">edit</a>]</span> <span class="mw-headline">Section 2</span></h2> +<a name="Section_4"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: Section 4">edit</a>]</span> <span class="mw-headline">Section 4</span></h2> !! end @@ -3306,19 +3318,17 @@ __NOEDITSECTION__ ==Section 1== ==Section 2== !! result -<a name="Section_1"></a><h2>Section 1</h2> -<a name="Section_2"></a><h2>Section 2</h2> +<a name="Section_1"></a><h2> <span class="mw-headline">Section 1</span></h2> +<a name="Section_2"></a><h2> <span class="mw-headline">Section 2</span></h2> !! end !! test Link inside a section heading -!! options -title=[[Parser test script]] !! input ==Section with a [[Main Page|link]] in it== !! result -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test_script&action=edit&section=1" title="Edit section: Section with a link in it">edit</a>]</div><a name="Section_with_a_link_in_it"></a><h2>Section with a <a href="/wiki/Main_Page" title="Main Page">link</a> in it</h2> +<a name="Section_with_a_link_in_it"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: Section with a link in it">edit</a>]</span> <span class="mw-headline">Section with a <a href="/wiki/Main_Page" title="Main Page">link</a> in it</span></h2> !! end @@ -3909,7 +3919,7 @@ Expansion of multi-line templates in attribute values (bug 6255 sanity check) !! end !! test -Expansion of multi-line templates in attribute values (bug 6255 sanity check) +Expansion of multi-line templates in attribute values (bug 6255 sanity check 2) !! input <div style="background: #00FF00">-</div> !! result @@ -4459,7 +4469,7 @@ Fuzz testing: Parser14 == onmouseover= == http://__TOC__ !! result -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: onmouseover=">edit</a>]</div><a name="onmouseover.3D"></a><h2> onmouseover= </h2> +<a name="onmouseover.3D"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: onmouseover=">edit</a>]</span> <span class="mw-headline"> onmouseover= </span></h2> http://<table id="toc" class="toc" summary="Contents"><tr><td><div id="toctitle"><h2>Contents</h2></div> <ul> <li class="toclevel-1"><a href="#onmouseover.3D"><span class="tocnumber">1</span> <span class="toctext">onmouseover=</span></a></li> @@ -4474,7 +4484,7 @@ Fuzz testing: Parser14-table ==a== {| STYLE=__TOC__ !! result -<div class="editsection" style="float:right;margin-left:5px;">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: a">edit</a>]</div><a name="a"></a><h2>a</h2> +<a name="a"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: a">edit</a>]</span> <span class="mw-headline">a</span></h2> <table style="__TOC__"> <tr><td></td></tr> </table> @@ -4612,7 +4622,7 @@ Fuzz testing: image with bogus manual thumbnail !!input [[Image:foobar.jpg|thumbnail= ]] !!result -<div class="thumb tright"><div style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title=""><img src="http://example.com/images/3/3a/Foobar.jpg" alt="" width="180" height="-1" longdesc="/wiki/Image:Foobar.jpg" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="Enlarge" /></a></div></div></div></div> +<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title=""><img src="http://example.com/images/3/3a/Foobar.jpg" alt="" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div></div></div></div> !!end @@ -4750,6 +4760,42 @@ Special page transclusion twice (bug 5021) !! end !! test +Transclusion of default MediaWiki message +!! input +{{MediaWiki:Mainpage}} +!!result +<p>Main Page +</p> +!! end + +!! test +Transclusion of nonexistent MediaWiki message +!! input +{{MediaWiki:Mainpagexxx}} +!!result +<p><a href="/index.php?title=MediaWiki:Mainpagexxx&action=edit" class="new" title="MediaWiki:Mainpagexxx">MediaWiki:Mainpagexxx</a> +</p> +!! end + +!! test +Transclusion of MediaWiki message with underscore +!! input +{{MediaWiki:history_short}} +!! result +<p>History +</p> +!! end + +!! test +Transclusion of MediaWiki message with space +!! input +{{MediaWiki:history short}} +!! result +<p>History +</p> +!! end + +!! test Invalid header with following text !! input = x = y @@ -5723,11 +5769,11 @@ image4 |300px| centre !! end !! test -TODO: HTML Hex character encoding. +HTML Hex character encoding (spells the word "JavaScript") !! input JavaScript !! result -<p>JavaScript +<p>JavaScript </p> !! end @@ -5744,9 +5790,9 @@ __FORCETOC__ !! test ISBN code coverage !! input -ISBN 983 987 +ISBN 978-0-1234-56 789 !! result -<p><a href="/index.php?title=Special:Booksources&isbn=983" class="internal">ISBN 983</a> 987 +<p><a href="/index.php?title=Special:Booksources&isbn=9780123456" class="internal">ISBN 978-0-1234-56</a> 789 </p> !! end @@ -5761,12 +5807,10 @@ ISBN !! test Double ISBN -!! options -disabled # Disabled until Bug 6560 resolved !! input -ISBN ISBN 1234 +ISBN ISBN 1234567890 !! result -<p>ISBN <a href="/wiki/index.php?title=Special:Booksources&isbn=1234" class="internal">ISBN 1234</a> +<p>ISBN <a href="/index.php?title=Special:Booksources&isbn=1234567890" class="internal">ISBN 1234567890</a> </p> !! end @@ -5851,6 +5895,521 @@ TODO: dt/dd/dl test !!end + +# Images with the "|" character in external URLs in comment tags; Eats half the comment, leaves unmatched "</a>" tag. +!! test +TODO: Images with the "|" character in the comment +!! input +[[image:Foobar.jpg|thumb|An [http://test/?param1=|left|¶m2=|x external] URL]] +!! result +<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/Image:Foobar.jpg" class="internal" title="An external URL"><img src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" alt="An external URL" width="180" height="20" longdesc="/wiki/Image:Foobar.jpg" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify" style="float:right"><a href="/wiki/Image:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>An <a href="http://test/?param1=|left|&param2=|x" class="external text" title="http://test/?param1=|left|&param2=|x" rel="nofollow">external</a> URL</div></div></div> + +!!end + +!! test +[Before] HTML without raw HTML enabled ($wgRawHtml==false) +!! input +<html><script>alert(1);</script></html> +!! result +<p><html><script>alert(1);</script></html> +</p> +!! end + +!! test +HTML with raw HTML ($wgRawHtml==true) +!! options +rawhtml +!! input +<html><script>alert(1);</script></html> +!! result +<p><script>alert(1);</script> +</p> +!! end + +!! test +Parents of subpages, one level up +!! options +subpage title=[[Subpage test/L1/L2/L3]] +!! input +[[../|L2]] +!! result +<p><a href="/index.php?title=Subpage_test/L1/L2&action=edit" class="new" title="Subpage test/L1/L2">L2</a> +</p> +!! end + + +!! test +Parents of subpages, one level up, not named +!! options +subpage title=[[Subpage test/L1/L2/L3]] +!! input +[[../]] +!! result +<p><a href="/index.php?title=Subpage_test/L1/L2&action=edit" class="new" title="Subpage test/L1/L2">Subpage test/L1/L2</a> +</p> +!! end + + + +!! test +Parents of subpages, two levels up +!! options +disabled +subpage title=[[Subpage test/L1/L2/L3]] +!! input +[[../../|L1]]2 +!! result +<p><a href="/index.php?title=Subpage_test/L1&action=edit" class="new" title="Subpage test/L1">L1</a> +</p> +!! end + + +# Question: should result be "/index.php?title=Subpage_test/L1&action=edit" instead? +!! test +TODO: Parents of subpages, two levels up, without trailing slash or name. +!! options +subpage title=[[Subpage test/L1/L2/L3]] +!! input +[[../..]] +!! result +<p><a href="/index.php?title=Subpage_test/L1/L2/..&action=edit" class="new" title="Subpage test/L1">../..</a> +</p> +!! end + +# Question: Why should the link text in the above test be "../..", yet in this test the "../.." part is silently dropped? +# Current result: <p><a href="/index.php?title=Subpage_test/L1////&action=edit" class="new" title="Subpage test/L1////">/// +!! test +TODO: Parents of subpages, two levels up, with lots of extra trailing slashes. +!! options +subpage title=[[Subpage test/L1/L2/L3]] +!! input +[[../../////]] +!! result +<p><a href="/index.php?title=Subpage_test/L1&action=edit" class="new" title="Subpage test/L1">Subpage test/L1</a> +</p> +!! end + +!! test +Definition list code coverage +!! input +; title : def +; title : def +;title: def +!! result +<dl><dt> title </dt><dd> def +</dd><dt> title </dt><dd> def +</dd><dt>title</dt><dd> def +</dd></dl> + +!! end + +!! test +TODO: Don't fall for the self-closing div +!! input +<div>hello world</div/> +!! result +<div>hello world</div> + +!! end + +!! test +MSGNW magic word +!! input +{{MSGNW:msg}} +!! result +<p>[[:Template:Msg]] +</p> +!! end + +!! test +RAW magic word +!! input +{{RAW:QUERTY}} +!! result +<p><a href="/index.php?title=Template:QUERTY&action=edit" class="new" title="Template:QUERTY">Template:QUERTY</a> +</p> +!! end + +# This isn't needed for XHTML conformance, but would be handy as a fallback security measure +!! test +TODO: Always escape literal '>' in output, not just after '<' +!! input +><> +!! result +<p>><> +</p> +!! end + +!! test +Template caching +!! input +{{Test}} +{{Test}} +!! result +<p>This is a test template +This is a test template +</p> +!! end + + +!! article +MediaWiki:Fake +!! text +==header== +!! endarticle + +!! test +Inclusion of !userCanEdit() content +!! input +{{MediaWiki:Fake}} +!! result +<a name="header"></a><h2><span class="editsection">[<a href="/index.php?title=MediaWiki:Fake&action=edit&section=1" title="MediaWiki:Fake">edit</a>]</span> <span class="mw-headline">header</span></h2> + +!! end + + +!! test +Out-of-order TOC heading levels +!! input +==2== +======6====== +===3=== +=1= +=====5===== +==2== +!! result +<table id="toc" class="toc" summary="Contents"><tr><td><div id="toctitle"><h2>Contents</h2></div> +<ul> +<li class="toclevel-1"><a href="#2"><span class="tocnumber">1</span> <span class="toctext">2</span></a> +<ul> +<li class="toclevel-2"><a href="#6"><span class="tocnumber">1.1</span> <span class="toctext">6</span></a></li> +<li class="toclevel-2"><a href="#3"><span class="tocnumber">1.2</span> <span class="toctext">3</span></a></li> +</ul> +</li> +<li class="toclevel-1"><a href="#1_7"><span class="tocnumber">2</span> <span class="toctext">1</span></a> +<ul> +<li class="toclevel-2"><a href="#5"><span class="tocnumber">2.1</span> <span class="toctext">5</span></a></li> +<li class="toclevel-2"><a href="#2_4"><span class="tocnumber">2.2</span> <span class="toctext">2</span></a></li> +</ul> +</li> +</ul> +</td></tr></table><script type="text/javascript"> if (window.showTocToggle) { var tocShowText = "show"; var tocHideText = "hide"; showTocToggle(); } </script> +<a name="2"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: 2">edit</a>]</span> <span class="mw-headline">2</span></h2> +<a name="6"></a><h6><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: 6">edit</a>]</span> <span class="mw-headline">6</span></h6> +<a name="3"></a><h3><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=3" title="Edit section: 3">edit</a>]</span> <span class="mw-headline">3</span></h3> +<a name="1_7"></a><h1><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=4" title="Edit section: 1">edit</a>]</span> <span class="mw-headline">1</span></h1> +<a name="5"></a><h5><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=5" title="Edit section: 5">edit</a>]</span> <span class="mw-headline">5</span></h5> +<a name="2_4"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=6" title="Edit section: 2">edit</a>]</span> <span class="mw-headline">2</span></h2> + +!! end + + +!! test +ISBN with a dummy number +!! input +ISBN --- +!! result +<p>ISBN --- +</p> +!! end + + +!! test +ISBN with space-delimited number +!! input +ISBN 92 9017 032 8 +!! result +<p><a href="/index.php?title=Special:Booksources&isbn=9290170328" class="internal">ISBN 92 9017 032 8</a> +</p> +!! end + + +!! test +ISBN with multiple spaces, no number +!! input +ISBN foo +!! result +<p>ISBN foo +</p> +!! end + + +!! test +ISBN length +!! input +ISBN 123456789 + +ISBN 1234567890 + +ISBN 12345678901 +!! result +<p>ISBN 123456789 +</p><p><a href="/index.php?title=Special:Booksources&isbn=1234567890" class="internal">ISBN 1234567890</a> +</p><p>ISBN 12345678901 +</p> +!! end + + +!! test +ISBN with trailing year (bug 8110) +!! input +ISBN 1-234-56789-0 - 2006 + +ISBN 1 234 56789 0 - 2006 +!! result +<p><a href="/index.php?title=Special:Booksources&isbn=1234567890" class="internal">ISBN 1-234-56789-0</a> - 2006 +</p><p><a href="/index.php?title=Special:Booksources&isbn=1234567890" class="internal">ISBN 1 234 56789 0</a> - 2006 +</p> +!! end + + +!! test +Pages in namespace (Magic word disabled currently) +!! input +{{PAGESINNAMESPACE:}} +!! result + +!! end + + +!! test +anchorencode +!! input +{{anchorencode:foo bar©#%n}} +!! result +<p>foo_bar.C2.A9.23.25n +</p> +!! end + + +!! test +Bug 8293: Use of center tag ruins paragraph formatting +!! input +<center> +foo +</center> + +bar + + baz +!! result +<center> +<p>foo +</p> +</center> +<p>bar +</p> +<pre>baz +</pre> +!! end + + +### +### Language variants related tests +### +!! test +Self-link in language variants +!! options +title=[[Dunav]] language=sr +!! input +Both [[Dunav]] and [[Дунав]] are names for this river. +!! result +<p>Both <strong class="selflink">Dunav</strong> and <strong class="selflink">Дунав</strong> are names for this river. +</p> +!!end + + +!! test +Link to pages in language variants +!! options +language=sr +!! input +Main Page can be written as [[Маин Паге]] +!! result +<p>Main Page can be written as <a href="/wiki/Main_Page" title="Main Page">Маин Паге</a> +</p> +!!end + + +!! test +Multiple links to pages in language variants +!! options +language=sr +!! input +[[Main Page]] can be written as [[Маин Паге]] same as [[Маин Паге]]. +!! result +<p><a href="/wiki/Main_Page" title="Main Page">Main Page</a> can be written as <a href="/wiki/Main_Page" title="Main Page">Маин Паге</a> same as <a href="/wiki/Main_Page" title="Main Page">Маин Паге</a>. +</p> +!!end + + +!! test +Simple template in language variants +!! options +language=sr +!! input +{{тест}} +!! result +<p>This is a test template +</p> +!! end + + +!! test +Template with explicit namespace in language variants +!! options +language=sr +!! input +{{Template:тест}} +!! result +<p>This is a test template +</p> +!! end + + +!! test +Basic test for template parameter in language variants +!! options +language=sr +!! input +{{парамтест|param=foo}} +!! result +<p>This is a test template with parameter foo +</p> +!! end + + +!! test +Simple category in language variants +!! options +language=sr cat +!! input +[[:Category:МедиаWики Усер'с Гуиде]] +!! result +<a href="/wiki/Category:MediaWiki_User%27s_Guide" title="Category:MediaWiki User's Guide">MediaWiki User's Guide</a> +!! end + + +!! test +Stripping -{}- tags (language variants) +!! options +language=sr +!! input +Latin proverb: -{Ne nuntium necare}- +!! result +<p>Latin proverb: Ne nuntium necare +</p> +!! end + + +!! test +Prevent conversion with -{}- tags (language variants) +!! options +language=sr variant=sr-ec +!! input +Latinski: -{Ne nuntium necare}- +!! result +<p>Латински: Ne nuntium necare +</p> +!! end + + +!! test +Prevent conversion of text with -{}- tags (language variants) +!! options +language=sr variant=sr-ec +!! input +Latinski: -{Ne nuntium necare}- +!! result +<p>Латински: Ne nuntium necare +</p> +!! end + + +!! test +Prevent conversion of links with -{}- tags (language variants) +!! options +language=sr variant=sr-ec +!! input +-{[[Main Page]]}- +!! result +<p><a href="/index.php?title=Main_Page&variant=sr-ec" title="Main Page">Main Page</a> +</p> +!! end + + +!! test +-{}- tags within headlines (within html for parserConvert()) +!! options +language=sr variant=sr-ec +!! input +== -{Naslov}- == +!! result +<a name="-.7BNaslov.7D-"></a><h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Уреди део: Naslov">уреди</a>]</span> <span class="mw-headline"> Naslov </span></h2> + +!! end + + +!! test +Explicit definition of language variant alternatives +!! options +language=zh variant=zh-tw +!! input +-{zh:China;zh-tw:Taiwan}-, not China +!! result +<p>Taiwan, not China +</p> +!! end + + +!! test +Adding explicit session-wise language variant mapping (A flag) +!! options +language=zh variant=zh-tw +!! input +-{A|zh:China;zh-tw:Taiwan}- is China +!! result +<p>Taiwan is Taiwan +</p> +!! end + + +!! test +Adding explicit conversion rule for title (T flag) +!! options +language=zh variant=zh-tw +!! input +Should be stripped-{T|zh:China;zh-tw:Taiwan}-! +!! result +<p>Should be stripped! +</p> +!! end + + +!! test +Raw output of variant escape tags (R flag) +!! options +language=zh variant=zh-tw +!! input +Raw: -{R|zh:China;zh-tw:Taiwan}- +!! result +<p>Raw: zh:China;zh-tw:Taiwan +</p> +!! end + + +!! test +Do not convert roman numbers to language variants +!! options +language=sr variant=sr-ec +!! input +Fridrih IV je car. +!! result +<p>Фридрих IV је цар. +</p> +!! end + + # # # diff --git a/maintenance/postgres/compare_schemas.pl b/maintenance/postgres/compare_schemas.pl index 4a76b270..cdbbdf41 100644 --- a/maintenance/postgres/compare_schemas.pl +++ b/maintenance/postgres/compare_schemas.pl @@ -7,8 +7,9 @@ use strict; use warnings; use Data::Dumper; -my @old = ("../tables.sql"); +my @old = ("../tables.sql", "../mysql5/tables.sql", "../mysql5/tables-binary.sql"); my $new = "tables.sql"; +my @xfile; ## Read in exceptions and other metadata my %ok; @@ -23,7 +24,7 @@ while (<DATA>) { next; } if ($name eq 'XFILE') { - push @old, $val; + push @xfile, $val; next; } for (split(/\s+/ => $val)) { @@ -31,12 +32,10 @@ while (<DATA>) { } } -open my $newfh, "<", $new or die qq{Could not open $new: $!\n}; - my $datatype = join '|' => qw( bool tinyint int bigint real float -tinytext mediumtext text char varchar +tinytext mediumtext text char varchar varbinary timestamp datetime tinyblob mediumblob blob ); @@ -50,12 +49,43 @@ my $typeval2 = qr{ unsigned| binary| NOT NULL| NULL| auto_increment| default ['\ my $indextype = join '|' => qw(INDEX KEY FULLTEXT), "PRIMARY KEY", "UNIQUE INDEX", "UNIQUE KEY"; $indextype = qr{$indextype}; +my $engine = qr{TYPE|ENGINE}; + my $tabletype = qr{InnoDB|MyISAM|HEAP|HEAP MAX_ROWS=\d+}; +my $charset = qr{utf8|binary}; + +open my $newfh, "<", $new or die qq{Could not open $new: $!\n}; + + my ($table,%old); -for my $old (@old) { - open my $oldfh, "<", $old or die qq{Could not open $old: $!\n}; +## Read in the xfiles +my %xinfo; +for my $xfile (@xfile) { + print "Loading $xfile\n"; + my $info = &parse_sql($xfile); + for (keys %$info) { + $xinfo{$_} = $info->{$_}; + } +} + +for my $oldfile (@old) { + print "Loading $oldfile\n"; + my $info = &parse_sql($oldfile); + for (keys %xinfo) { + $info->{$_} = $xinfo{$_}; + } + $old{$oldfile} = $info; +} + +sub parse_sql { + + my $oldfile = shift; + + open my $oldfh, "<", $oldfile or die qq{Could not open $oldfile: $!\n}; + + my %info; while (<$oldfh>) { next if /^\s*\-\-/ or /^\s+$/; s/\s*\-\- [\w ]+$//; @@ -63,37 +93,66 @@ for my $old (@old) { if (/CREATE\s*TABLE/i) { m{^CREATE TABLE /\*\$wgDBprefix\*/(\w+) \($} - or die qq{Invalid CREATE TABLE at line $. of $old\n}; + or die qq{Invalid CREATE TABLE at line $. of $oldfile\n}; $table = $1; - $old{$table}{name}=$table; + $info{$table}{name}=$table; + } + elsif (/^\) ($engine)=($tabletype);$/) { + $info{$table}{engine}=$1; + $info{$table}{type}=$2; } - elsif (/^\) TYPE=($tabletype);$/) { - $old{$table}{type}=$1; + elsif (/^\) ($engine)=($tabletype), DEFAULT CHARSET=($charset);$/) { + $info{$table}{engine}=$1; + $info{$table}{type}=$2; + $info{$table}{charset}=$3; } elsif (/^ (\w+) $datatype$typeval$typeval2{0,3},?$/) { - $old{$table}{column}{$1} = $2; + $info{$table}{column}{$1} = $2; } elsif (/^ ($indextype)(?: (\w+))? \(([\w, \(\)]+)\),?$/) { - $old{$table}{lc $1."_name"} = $2 ? $2 : ""; - $old{$table}{lc $1."pk_target"} = $3; + $info{$table}{lc $1."_name"} = $2 ? $2 : ""; + $info{$table}{lc $1."pk_target"} = $3; } else { - die "Cannot parse line $. of $old:\n$_\n"; + die "Cannot parse line $. of $oldfile:\n$_\n"; } + } close $oldfh; + + return \%info; + +} ## end of parse_sql + +for my $oldfile (@old) { + +## Begin non-standard indent + +## MySQL sanity checks +for my $table (sort keys %{$old{$oldfile}}) { + my $t = $old{$oldfile}{$table}; + if (($oldfile =~ /5/ and $t->{engine} ne 'ENGINE') + or + ($oldfile !~ /5/ and $t->{engine} ne 'TYPE')) { + die "Invalid engine for $oldfile: $t->{engine}\n" unless $t->{name} eq 'profiling'; + } + my $charset = $t->{charset} || ''; + if ($oldfile !~ /binary/ and $charset eq 'binary') { + die "Invalid charset for $oldfile: $charset\n"; + } } -$datatype = join '|' => qw( +my $dtype = join '|' => qw( SMALLINT INTEGER BIGINT NUMERIC SERIAL TEXT CHAR VARCHAR BYTEA TIMESTAMPTZ CIDR ); -$datatype = qr{($datatype)}; +$dtype = qr{($dtype)}; my %new; my ($infunction,$inview,$inrule) = (0,0,0); +seek $newfh, 0, 0; while (<$newfh>) { next if /^\s*\-\-/ or /^\s*$/; s/\s*\-\- [\w ']+$//; @@ -130,24 +189,23 @@ while (<$newfh>) { } elsif (/^\);$/) { } - elsif (/^ (\w+) +$datatype/) { + elsif (/^ (\w+) +$dtype/) { $new{$table}{column}{$1} = $2; } else { die "Cannot parse line $. of $new:\n$_\n"; } } -close $newfh; ## Old but not new -for my $t (sort keys %old) { +for my $t (sort keys %{$old{$oldfile}}) { if (!exists $new{$t} and !exists $ok{OLD}{$t}) { print "Table not in $new: $t\n"; next; } next if exists $ok{OLD}{$t} and !$ok{OLD}{$t}; my $newt = exists $ok{OLD}{$t} ? $ok{OLD}{$t} : $t; - my $oldcol = $old{$t}{column}; + my $oldcol = $old{$oldfile}{$t}{column}; my $newcol = $new{$newt}{column}; for my $c (keys %$oldcol) { if (!exists $newcol->{$c}) { @@ -164,12 +222,16 @@ for my $t (sort keys %old) { } ## New but not old: for (sort keys %new) { - if (!exists $old{$_} and !exists $ok{NEW}{$_}) { + if (!exists $old{$oldfile}{$_} and !exists $ok{NEW}{$_}) { print "Not in old: $_\n"; next; } } + +} ## end each file to be parsed + + __DATA__ ## Known exceptions OLD: searchindex ## We use tsearch2 directly on the page table instead diff --git a/maintenance/postgres/tables.sql b/maintenance/postgres/tables.sql index 9ac329d8..e6cbbe2a 100644 --- a/maintenance/postgres/tables.sql +++ b/maintenance/postgres/tables.sql @@ -17,6 +17,7 @@ CREATE TABLE mwuser ( -- replace reserved word 'user' user_real_name TEXT, user_password TEXT, user_newpassword TEXT, + user_newpass_time TIMESTAMPTZ, user_token CHAR(32), user_email TEXT, user_email_token CHAR(32), @@ -24,7 +25,8 @@ CREATE TABLE mwuser ( -- replace reserved word 'user' user_email_authenticated TIMESTAMPTZ, user_options TEXT, user_touched TIMESTAMPTZ, - user_registration TIMESTAMPTZ + user_registration TIMESTAMPTZ, + user_editcount INTEGER ); CREATE INDEX user_email_token_idx ON mwuser (user_email_token); @@ -86,7 +88,7 @@ CREATE TABLE revision ( rev_page INTEGER NULL REFERENCES page (page_id) ON DELETE CASCADE, rev_text_id INTEGER NULL, -- FK rev_comment TEXT, - rev_user INTEGER NOT NULL REFERENCES mwuser(user_id), + rev_user INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE RESTRICT, rev_user_text TEXT NOT NULL, rev_timestamp TIMESTAMPTZ NOT NULL, rev_minor_edit CHAR NOT NULL DEFAULT '0', @@ -137,6 +139,14 @@ DO INSTEAD INSERT INTO archive2 VALUES ( ); +CREATE TABLE redirect ( + rd_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, + rd_namespace SMALLINT NOT NULL, + rd_title TEXT 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, @@ -201,18 +211,19 @@ CREATE TABLE hitcounter ( CREATE SEQUENCE ipblocks_ipb_id_val; CREATE TABLE ipblocks ( - ipb_id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('ipblocks_ipb_id_val'), - ipb_address CIDR 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 TEXT NOT NULL, - ipb_timestamp TIMESTAMPTZ NOT NULL, - ipb_auto CHAR NOT NULL DEFAULT '0', - ipb_anon_only CHAR NOT NULL DEFAULT '0', - ipb_create_account CHAR NOT NULL DEFAULT '1', - ipb_expiry TIMESTAMPTZ NOT NULL, - ipb_range_start TEXT, - ipb_range_end TEXT + ipb_id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('ipblocks_ipb_id_val'), + ipb_address TEXT 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 TEXT NOT NULL, + ipb_timestamp TIMESTAMPTZ NOT NULL, + ipb_auto CHAR NOT NULL DEFAULT '0', + ipb_anon_only CHAR NOT NULL DEFAULT '0', + ipb_create_account CHAR NOT NULL DEFAULT '1', + ipb_enable_autoblock CHAR NOT NULL DEFAULT '1', + ipb_expiry TIMESTAMPTZ NOT NULL, + ipb_range_start TEXT, + ipb_range_end TEXT ); CREATE INDEX ipb_address ON ipblocks (ipb_address); CREATE INDEX ipb_user ON ipblocks (ipb_user); @@ -300,7 +311,9 @@ CREATE TABLE recentchanges ( rc_moved_to_ns SMALLINT, rc_moved_to_title TEXT, rc_patrolled CHAR NOT NULL DEFAULT '0', - rc_ip CIDR + rc_ip CIDR, + rc_old_len INTEGER, + rc_new_len INTEGER ); CREATE INDEX rc_timestamp ON recentchanges (rc_timestamp); CREATE INDEX rc_namespace_title ON recentchanges (rc_namespace, rc_title); @@ -348,6 +361,19 @@ CREATE TABLE querycache_info ( qci_timestamp TIMESTAMPTZ NULL ); +CREATE TABLE querycachetwo ( + qcc_type TEXT NOT NULL, + qcc_value SMALLINT NOT NULL DEFAULT 0, + qcc_namespace INTEGER NOT NULL DEFAULT 0, + qcc_title TEXT NOT NULL DEFAULT '', + qcc_namespacetwo INTEGER NOT NULL DEFAULT 0, + qcc_titletwo TEXT NOT NULL DEFAULT '' +); +CREATE INDEX querycachetwo_type_value ON querycachetwo (qcc_type, qcc_value); +CREATE INDEX querycachetwo_title ON querycachetwo (qcc_type,qcc_namespace,qcc_title); +CREATE INDEX querycachetwo_titletwo ON querycachetwo (qcc_type,qcc_namespacetwo,qcc_titletwo); + + CREATE TABLE objectcache ( keyname CHAR(255) UNIQUE, value BYTEA NOT NULL DEFAULT '', @@ -470,7 +496,7 @@ CREATE TABLE mediawiki_version ( ); INSERT INTO mediawiki_version (type,mw_version,sql_version,sql_date) - VALUES ('Creation','??','$LastChangedRevision: 16747 $','$LastChangedDate: 2006-10-02 17:55:26 -0700 (Mon, 02 Oct 2006) $'); + VALUES ('Creation','??','$LastChangedRevision: 18326 $','$LastChangedDate: 2006-12-14 07:34:56 -0800 (Thu, 14 Dec 2006) $'); COMMIT; diff --git a/maintenance/postgres/wp_mysql2postgres.pl b/maintenance/postgres/wp_mysql2postgres.pl index 788d9e0b..981d99f3 100644 --- a/maintenance/postgres/wp_mysql2postgres.pl +++ b/maintenance/postgres/wp_mysql2postgres.pl @@ -1,7 +1,7 @@ #!/usr/bin/perl ## Convert data from a MySQL mediawiki database into a Postgres mediawiki database -## svn: $Id: wp_mysql2postgres.pl 16088 2006-08-16 01:12:20Z greg $ +## svn: $Id: wp_mysql2postgres.pl 18836 2007-01-05 03:37:19Z brion $ use strict; use warnings; @@ -160,7 +160,7 @@ $MYSQLSOCKET and $conninfo .= "\n-- socket $MYSQLSOCKET"; print qq{ -- Dump of MySQL Mediawiki tables for import into a Postgres Mediawiki schema -- Performed by the program: $0 --- Version: $VERSION (subversion }.q{$LastChangedRevision: 16088 $}.qq{) +-- Version: $VERSION (subversion }.q{$LastChangedRevision: 18836 $}.qq{) -- Author: Greg Sabino Mullane <greg\@turnstep.com> Comments welcome -- -- This file was created: $now diff --git a/maintenance/purgeList.php b/maintenance/purgeList.php new file mode 100644 index 00000000..9bf7c1bf --- /dev/null +++ b/maintenance/purgeList.php @@ -0,0 +1,34 @@ +<?php + +/** + * Send purge requests for listed pages to squid + */ + +require_once( "commandLine.inc" ); + +$stdin = fopen( "php://stdin", "rt" ); +$urls = array(); + +while( !feof( $stdin ) ) { + $page = trim( fgets( $stdin ) ); + if ( substr( $page, 0, 7 ) == 'http://' ) { + $urls[] = $page; + } elseif( $page !== '' ) { + $title = Title::newFromText( $page ); + if( $title ) { + $url = $title->getFullUrl(); + echo "$url\n"; + $urls[] = $url; + } else { + echo "(Invalid title '$page')\n"; + } + } +} + +echo "Purging " . count( $urls ) . " urls...\n"; +$u = new SquidUpdate( $urls ); +$u->doUpdate(); + +echo "Done!\n"; + +?> diff --git a/maintenance/showStats.php b/maintenance/showStats.php new file mode 100644 index 00000000..27f9be6b --- /dev/null +++ b/maintenance/showStats.php @@ -0,0 +1,46 @@ +<?php + +/** + * Maintenance script to show the cached statistics. + * Give out the same output as [[Special:Statistics]] + * + * @author Ashar Voultoiz <hashar@altern.org> + * Based on initStats.php by: + * @author Brion Vibber + * @author Rob Church <robchur@gmail.com> + * + * @licence GNU General Public License 2.0 or later + */ + +require_once( 'commandLine.inc' ); + +# +# Configuration +# +$fields = array( + 'ss_total_views' => 'Total views', + 'ss_total_edits' => 'Total edits', + 'ss_good_articles' => 'Number of articles', + 'ss_total_pages' => 'Total pages', + 'ss_users' => 'Number of users', + 'ss_admins' => 'Number of admins', + 'ss_images' => 'Number of images', +); + +// Get cached stats from slave database +$dbr =& wfGetDB( DB_SLAVE ); +$fname = 'showStats'; +$stats = $dbr->selectRow( 'site_stats', '*', '' ); + +// Get maximum size for each column +$max_length_value = $max_length_desc = 0; +foreach( $fields as $field => $desc ) { + $max_length_value = max( $max_length_value, strlen( $stats->$field ) ); + $max_length_desc = max( $max_length_desc , strlen( $desc )) ; +} + +// Show them +foreach( $fields as $field => $desc ) { + printf( "%-{$max_length_desc}s: %{$max_length_value}d\n", $desc, $stats->$field ); +} +?> diff --git a/maintenance/stats.php b/maintenance/stats.php index bb19e671..25bb9cc7 100644 --- a/maintenance/stats.php +++ b/maintenance/stats.php @@ -1,6 +1,10 @@ <?php require_once('commandLine.inc'); +if( get_class( $wgMemc ) == 'FakeMemCachedClient' ) { + die("You are running FakeMemCachedClient, I can not provide any statistics.\n"); +} + print "Requests\n"; $session = intval($wgMemc->get(wfMemcKey('stats','request_with_session'))); $noSession = intval($wgMemc->get(wfMemcKey('stats','request_without_session'))); diff --git a/maintenance/tables.sql b/maintenance/tables.sql index 3ffa5e5f..188ca63e 100644 --- a/maintenance/tables.sql +++ b/maintenance/tables.sql @@ -64,7 +64,7 @@ CREATE TABLE /*$wgDBprefix*/user ( -- Password hashes, normally hashed like so: -- MD5(CONCAT(user_id,'-',MD5(plaintext_password))), see -- wfEncryptPassword() in GlobalFunctions.php - user_password tinyblob NOT NULL default '', + user_password tinyblob NOT NULL, -- When using 'mail me a new password', a random -- password is generated and the hash stored here. @@ -72,15 +72,19 @@ CREATE TABLE /*$wgDBprefix*/user ( -- someone actually logs in with the new password, -- at which point the hash is moved to user_password -- and the old password is invalidated. - user_newpassword tinyblob NOT NULL default '', + user_newpassword tinyblob NOT NULL, + -- Timestamp of the last time when a new password was + -- sent, for throttling purposes + user_newpass_time char(14) binary, + -- Note: email should be restricted, not public info. -- Same with passwords. - user_email tinytext NOT NULL default '', + user_email tinytext NOT NULL, -- Newline-separated list of name=value defining the user -- preferences - user_options blob NOT NULL default '', + user_options blob NOT NULL, -- This is a timestamp which is updated when a user -- logs in, logs out, changes preferences, or performs @@ -109,6 +113,18 @@ CREATE TABLE /*$wgDBprefix*/user ( -- Timestamp of account registration. -- Accounts predating this schema addition may contain NULL. user_registration char(14) binary, + + -- Count of edits and edit-like actions. + -- + -- *NOT* intended to be an accurate copy of COUNT(*) WHERE rev_user=user_id + -- May contain NULL for old accounts if batch-update scripts haven't been + -- run, as well as listing deleted edits and other myriad ways it could be + -- out of sync. + -- + -- Meant primarily for heuristic checks to give an impression of whether + -- the account has been used much. + -- + user_editcount int, PRIMARY KEY user_id (user_id), UNIQUE INDEX user_name (user_name), @@ -176,7 +192,7 @@ CREATE TABLE /*$wgDBprefix*/page ( -- Comma-separated set of permission keys indicating who -- can move or edit the page. - page_restrictions tinyblob NOT NULL default '', + page_restrictions tinyblob NOT NULL, -- Number of times this page has been viewed. page_counter bigint(20) unsigned NOT NULL default '0', @@ -235,7 +251,7 @@ CREATE TABLE /*$wgDBprefix*/revision ( -- Text comment summarizing the change. -- This text is shown in the history and other changes lists, -- rendered in a subset of wiki markup by Linker::formatComment() - rev_comment tinyblob NOT NULL default '', + rev_comment tinyblob NOT NULL, -- Key to user.user_id of the user who made this edit. -- Stores 0 for anonymous edits and for some mass imports. @@ -282,7 +298,7 @@ CREATE TABLE /*$wgDBprefix*/text ( -- Depending on the contents of the old_flags field, the text -- may be convenient plain text, or it may be funkily encoded. - old_text mediumblob NOT NULL default '', + old_text mediumblob NOT NULL, -- Comma-separated list of flags: -- gzip: text is compressed with PHP's gzdeflate() function. @@ -293,7 +309,7 @@ CREATE TABLE /*$wgDBprefix*/text ( -- The object either contains multiple versions compressed -- together to achieve a better compression ratio, or it refers -- to another row where the text can be found. - old_flags tinyblob NOT NULL default '', + old_flags tinyblob NOT NULL, PRIMARY KEY old_id (old_id) @@ -315,17 +331,17 @@ CREATE TABLE /*$wgDBprefix*/archive ( -- so old archived pages will remain accessible after -- upgrading from 1.4 to 1.5. -- Text may be gzipped or otherwise funky. - ar_text mediumblob NOT NULL default '', + ar_text mediumblob NOT NULL, -- Basic revision stuff... - ar_comment tinyblob NOT NULL default '', + ar_comment tinyblob NOT NULL, ar_user int(5) unsigned NOT NULL default '0', ar_user_text varchar(255) binary NOT NULL, ar_timestamp char(14) binary NOT NULL default '', ar_minor_edit tinyint(1) NOT NULL default '0', -- See ar_text note. - ar_flags tinyblob NOT NULL default '', + ar_flags tinyblob NOT NULL, -- When revisions are deleted, their unique rev_id is stored -- here so it can be retained after undeletion. This is necessary @@ -367,7 +383,7 @@ CREATE TABLE /*$wgDBprefix*/pagelinks ( pl_title varchar(255) binary NOT NULL default '', UNIQUE KEY pl_from (pl_from,pl_namespace,pl_title), - KEY (pl_namespace,pl_title) + KEY (pl_namespace,pl_title,pl_from) ) TYPE=InnoDB; @@ -387,7 +403,7 @@ CREATE TABLE /*$wgDBprefix*/templatelinks ( tl_title varchar(255) binary NOT NULL default '', UNIQUE KEY tl_from (tl_from,tl_namespace,tl_title), - KEY (tl_namespace,tl_title) + KEY (tl_namespace,tl_title,tl_from) ) TYPE=InnoDB; @@ -406,7 +422,7 @@ CREATE TABLE /*$wgDBprefix*/imagelinks ( il_to varchar(255) binary NOT NULL default '', UNIQUE KEY il_from (il_from,il_to), - KEY (il_to) + KEY (il_to,il_from) ) TYPE=InnoDB; @@ -458,7 +474,7 @@ CREATE TABLE /*$wgDBprefix*/externallinks ( el_from int(8) unsigned NOT NULL default '0', -- The URL - el_to blob NOT NULL default '', + el_to blob NOT NULL, -- In the case of HTTP URLs, this is the URL with any username or password -- removed, and with the labels in the hostname reversed and converted to @@ -471,7 +487,7 @@ CREATE TABLE /*$wgDBprefix*/externallinks ( -- which allows for fast searching for all pages under example.com with the -- clause: -- WHERE el_index LIKE 'http://com.example.%' - el_index blob NOT NULL default '', + el_index blob NOT NULL, KEY (el_from, el_to(40)), KEY (el_to(60), el_from), @@ -553,7 +569,7 @@ CREATE TABLE /*$wgDBprefix*/ipblocks ( ipb_id int(8) NOT NULL auto_increment, -- Blocked IP address in dotted-quad form or user name. - ipb_address tinyblob NOT NULL default '', + ipb_address tinyblob NOT NULL, -- Blocked user ID or 0 for IP blocks. ipb_user int(8) unsigned NOT NULL default '0', @@ -562,7 +578,7 @@ CREATE TABLE /*$wgDBprefix*/ipblocks ( ipb_by int(8) unsigned NOT NULL default '0', -- Text comment made by blocker. - ipb_reason tinyblob NOT NULL default '', + ipb_reason tinyblob NOT NULL, -- Creation (or refresh) date in standard YMDHMS form. -- IP blocks expire automatically. @@ -578,14 +594,17 @@ CREATE TABLE /*$wgDBprefix*/ipblocks ( -- Block prevents account creation from matching IP addresses ipb_create_account bool NOT NULL default 1, + + -- Block triggers autoblocks + ipb_enable_autoblock bool NOT NULL default '1', -- Time at which the block will expire. ipb_expiry char(14) binary NOT NULL default '', -- Start and end of an address range, in hexadecimal -- Size chosen to allow IPv6 - ipb_range_start tinyblob NOT NULL default '', - ipb_range_end tinyblob NOT NULL default '', + ipb_range_start tinyblob NOT NULL, + ipb_range_end tinyblob NOT NULL, PRIMARY KEY ipb_id (ipb_id), @@ -638,11 +657,11 @@ CREATE TABLE /*$wgDBprefix*/image ( -- Description field as entered by the uploader. -- This is displayed in image upload history and logs. - img_description tinyblob NOT NULL default '', + img_description tinyblob NOT NULL, -- user_id and user_name of uploader. img_user int(5) unsigned NOT NULL default '0', - img_user_text varchar(255) binary NOT NULL default '', + img_user_text varchar(255) binary NOT NULL, -- Time of the upload. img_timestamp char(14) binary NOT NULL default '', @@ -675,9 +694,9 @@ CREATE TABLE /*$wgDBprefix*/oldimage ( oi_width int(5) NOT NULL default 0, oi_height int(5) NOT NULL default 0, oi_bits int(3) NOT NULL default 0, - oi_description tinyblob NOT NULL default '', + oi_description tinyblob NOT NULL, oi_user int(5) unsigned NOT NULL default '0', - oi_user_text varchar(255) binary NOT NULL default '', + oi_user_text varchar(255) binary NOT NULL, oi_timestamp char(14) binary NOT NULL default '', INDEX oi_name (oi_name(10)) @@ -723,9 +742,9 @@ CREATE TABLE /*$wgDBprefix*/filearchive ( fa_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL, fa_major_mime ENUM("unknown", "application", "audio", "image", "text", "video", "message", "model", "multipart") default "unknown", fa_minor_mime varchar(32) default "unknown", - fa_description tinyblob default '', + fa_description tinyblob, fa_user int(5) unsigned default '0', - fa_user_text varchar(255) binary default '', + fa_user_text varchar(255) binary, fa_timestamp char(14) binary default '', PRIMARY KEY (fa_id), @@ -748,7 +767,7 @@ CREATE TABLE /*$wgDBprefix*/recentchanges ( -- As in revision rc_user int(10) unsigned NOT NULL default '0', - rc_user_text varchar(255) binary NOT NULL default '', + rc_user_text varchar(255) binary NOT NULL, -- When pages are renamed, their RC entries do _not_ change. rc_namespace int NOT NULL default '0', @@ -791,13 +810,19 @@ CREATE TABLE /*$wgDBprefix*/recentchanges ( -- $wgPutIPinRC option is enabled. rc_ip char(15) NOT NULL default '', + -- Text length in characters before + -- and after the edit + rc_old_len int(10), + rc_new_len int(10), + PRIMARY KEY rc_id (rc_id), INDEX rc_timestamp (rc_timestamp), INDEX rc_namespace_title (rc_namespace, rc_title), INDEX rc_cur_id (rc_cur_id), INDEX new_name_timestamp (rc_new,rc_namespace,rc_timestamp), INDEX rc_ip (rc_ip), - INDEX rc_ns_usertext (rc_namespace, rc_user_text) + INDEX rc_ns_usertext (rc_namespace, rc_user_text), + INDEX rc_user_text (rc_user_text, rc_timestamp) ) TYPE=InnoDB; @@ -862,7 +887,7 @@ CREATE TABLE /*$wgDBprefix*/searchindex ( si_title varchar(255) NOT NULL default '', -- Munged version of body text - si_text mediumtext NOT NULL default '', + si_text mediumtext NOT NULL, UNIQUE KEY (si_page), FULLTEXT si_title (si_title), @@ -955,7 +980,7 @@ CREATE TABLE /*$wgDBprefix*/logging ( log_comment varchar(255) NOT NULL default '', -- LF separated list of miscellaneous parameters - log_params blob NOT NULL default '', + log_params blob NOT NULL, KEY type_time (log_type, log_timestamp), KEY user_time (log_user, log_timestamp), @@ -991,7 +1016,7 @@ CREATE TABLE /*$wgDBprefix*/job ( -- Any other parameters to the command -- Presently unused, format undefined - job_params blob NOT NULL default '', + job_params blob NOT NULL, PRIMARY KEY job_id (job_id), KEY (job_cmd, job_namespace, job_title) @@ -1012,4 +1037,42 @@ CREATE TABLE /*$wgDBprefix*/querycache_info ( ) TYPE=InnoDB; +-- For each redirect, this table contains exactly one row defining its target +CREATE TABLE /*$wgDBprefix*/redirect ( + -- Key to the page_id of the redirect page + rd_from int(8) unsigned NOT NULL default '0', + + -- Key to page_namespace/page_title of the target page. + -- The target page may or may not exist, and due to renames + -- and deletions may refer to different page records as time + -- goes by. + rd_namespace int NOT NULL default '0', + rd_title varchar(255) binary NOT NULL default '', + + PRIMARY KEY rd_from (rd_from), + KEY rd_ns_title (rd_namespace,rd_title,rd_from) +) TYPE=InnoDB; + +-- Used for caching expensive grouped queries that need two links (for example double-redirects) +CREATE TABLE /*$wgDBprefix*/querycachetwo ( + -- A key name, generally the base name of of the special page. + qcc_type char(32) NOT NULL, + + -- Some sort of stored value. Sizes, counts... + qcc_value int(5) unsigned NOT NULL default '0', + + -- Target namespace+title + qcc_namespace int NOT NULL default '0', + qcc_title char(255) binary NOT NULL default '', + + -- Target namespace+title2 + qcc_namespacetwo int NOT NULL default '0', + qcc_titletwo char(255) binary NOT NULL default '', + + KEY qcc_type (qcc_type,qcc_value), + KEY qcc_title (qcc_type,qcc_namespace,qcc_title), + KEY qcc_titletwo (qcc_type,qcc_namespacetwo,qcc_titletwo) + +) TYPE=InnoDB; + -- vim: sw=2 sts=2 et diff --git a/maintenance/testRunner.sql b/maintenance/testRunner.sql new file mode 100644 index 00000000..8591d81d --- /dev/null +++ b/maintenance/testRunner.sql @@ -0,0 +1,35 @@ +-- +-- Optional tables for parserTests recording mode +-- With --record option, success data will be saved to these tables, +-- and comparisons of what's changed from the previous run will be +-- displayed at the end of each run. +-- +-- These tables currently require MySQL 5 (or maybe 4.1?) for subselects. +-- + +drop table if exists /*$wgDBprefix*/testitem; +drop table if exists /*$wgDBprefix*/testrun; + +create table /*$wgDBprefix*/testrun ( + tr_id int not null auto_increment, + + tr_date char(14) binary, + tr_mw_version blob, + tr_php_version blob, + tr_db_version blob, + tr_uname blob, + + primary key (tr_id) +) engine=InnoDB; + +create table /*$wgDBprefix*/testitem ( + ti_run int not null, + ti_name varchar(255), + ti_success bool, + + unique key (ti_run, ti_name), + key (ti_run, ti_success), + + foreign key (ti_run) references /*$wgDBprefix*/testrun(tr_id) + on delete cascade +) engine=InnoDB; diff --git a/maintenance/update.php b/maintenance/update.php index 402818ce..490c3f63 100644 --- a/maintenance/update.php +++ b/maintenance/update.php @@ -10,7 +10,7 @@ require_once 'counter.php'; /** */ $wgUseMasterForMaintenance = true; -$options = array( 'quick' ); +$options = array( 'quick', 'nopurge' ); require_once( "commandLine.inc" ); require_once( "updaters.inc" ); $wgTitle = Title::newFromText( "MediaWiki database updater" ); @@ -54,13 +54,10 @@ if( !isset( $options['quick'] ) ) { echo "\n"; } -if ( isset( $options['doshared'] ) ) { - $doShared = true; -} else { - $doShared = false; -} +$shared = isset( $options['doshared'] ); +$purge = !isset( $options['nopurge'] ); -do_all_updates( $doShared ); +do_all_updates( $shared, $purge ); print "Done.\n"; diff --git a/maintenance/updateArticleCount.inc.php b/maintenance/updateArticleCount.inc.php index 20546a78..7eaea749 100644 --- a/maintenance/updateArticleCount.inc.php +++ b/maintenance/updateArticleCount.inc.php @@ -39,12 +39,9 @@ class ArticleCounter { function makeSql() { extract( $this->dbr->tableNames( 'page', 'pagelinks' ) ); $nsset = $this->makeNsSet(); - return "SELECT COUNT(*) AS count FROM {$page} - LEFT JOIN {$pagelinks} ON pl_from = page_id - WHERE page_namespace IN ( $nsset ) - AND page_is_redirect = 0 - AND page_len > 0 - AND pl_namespace IS NOT NULL"; + return "SELECT DISTINCT page_namespace,page_title FROM $page,$pagelinks " . + "WHERE pl_from=page_id and page_namespace IN ( $nsset ) " . + "AND page_is_redirect = 0 AND page_len > 0"; } /** @@ -55,14 +52,16 @@ class ArticleCounter { function count() { $res = $this->dbr->query( $this->makeSql(), __METHOD__ ); if( $res ) { - $row = $this->dbr->fetchObject( $res ); + $count = $this->dbr->numRows( $res ); $this->dbr->freeResult( $res ); - return (int)$row->count; + return $count; } else { - return false; # Look out for this when handling the result + # Look out for this when handling the result + # - Actually it's unreachable, !$res throws an exception -- TS + return false; } } } -?>
\ No newline at end of file +?> diff --git a/maintenance/updateSpecialPages.php b/maintenance/updateSpecialPages.php index a7a72b58..89b5aa94 100644 --- a/maintenance/updateSpecialPages.php +++ b/maintenance/updateSpecialPages.php @@ -28,6 +28,11 @@ foreach ( $wgQueryPages as $page ) { continue; } + if ( in_array( $special, $wgDisableQueryPageUpdate ) ) { + printf("%-30s disabled\n", $special); + continue; + } + $specialObj = SpecialPage::getPage( $special ); if ( !$specialObj ) { print "No such special page: $special\n"; @@ -40,12 +45,12 @@ foreach ( $wgQueryPages as $page ) { $queryPage = new $class; if( !(isset($options['only'])) or ($options['only'] == $queryPage->getName()) ) { - printf( '%-30s', $special ); + printf( '%-30s ', $special ); if ( $queryPage->isExpensive() ) { $t1 = explode( ' ', microtime() ); # Do the query - $num = $queryPage->recache( $limit === null ? 1000 : $limit ); + $num = $queryPage->recache( $limit === null ? $wgQueryCacheLimit : $limit ); $t2 = explode( ' ', microtime() ); if ( $num === false ) { @@ -80,12 +85,15 @@ foreach ( $wgQueryPages as $page ) { } # Wait for the slave to catch up + /* $slaveDB =& wfGetDB( DB_SLAVE, array('QueryPage::recache', 'vslow' ) ); while( $slaveDB->getLag() > 600 ) { print "Slave lagged, waiting...\n"; sleep(30); } + */ + wfWaitForSlaves( 5 ); } else { print "cheap, skipped\n"; diff --git a/maintenance/updaters.inc b/maintenance/updaters.inc index d334660e..7909b13d 100644 --- a/maintenance/updaters.inc +++ b/maintenance/updaters.inc @@ -6,9 +6,14 @@ /** */ +if ( !defined( 'MEDIAWIKI' ) ) { + echo "This file is not a valid entry point\n"; + exit( 1 ); +} + require_once 'convertLinks.inc'; -require_once 'InitialiseMessages.inc'; require_once 'userDupes.inc'; +require_once 'deleteDefaultMessages.php'; $wgRenamedTables = array( # from to patch file @@ -30,16 +35,20 @@ $wgNewTables = array( array( 'langlinks', 'patch-langlinks.sql' ), array( 'querycache_info', 'patch-querycacheinfo.sql' ), array( 'filearchive', 'patch-filearchive.sql' ), + array( 'redirect', 'patch-redirect.sql' ), + array( 'querycachetwo', 'patch-querycachetwo.sql' ), ); $wgNewFields = array( # table field patch file (in maintenance/archives) array( 'ipblocks', 'ipb_id', 'patch-ipblocks.sql' ), array( 'ipblocks', 'ipb_expiry', 'patch-ipb_expiry.sql' ), + array( 'ipblocks', 'ipb_enable_autoblock', 'patch-ipb_optional_autoblock.sql' ), array( 'recentchanges', 'rc_type', 'patch-rc_type.sql' ), array( 'recentchanges', 'rc_ip', 'patch-rc_ip.sql' ), array( 'recentchanges', 'rc_id', 'patch-rc_id.sql' ), array( 'recentchanges', 'rc_patrolled', 'patch-rc-patrol.sql' ), + array( 'recentchanges', 'rc_old_len', 'patch-rc_len.sql' ), array( 'user', 'user_real_name', 'patch-user-realname.sql' ), array( 'user', 'user_token', 'patch-user_token.sql' ), array( 'user', 'user_email_token', 'patch-user_email_token.sql' ), @@ -57,6 +66,8 @@ $wgNewFields = array( array( 'ipblocks', 'ipb_range_start', 'patch-ipb_range_start.sql' ), array( 'site_stats', 'ss_images', 'patch-ss_images.sql' ), array( 'ipblocks', 'ipb_anon_only', 'patch-ipb_anon_only.sql' ), + array( 'user', 'user_newpass_time','patch-user_newpass_time.sql' ), + array( 'user', 'user_editcount', 'patch-user_editcount.sql' ), ); function rename_table( $from, $to, $patch ) { @@ -379,7 +390,7 @@ function do_schema_restructuring() { page_id int(8) unsigned NOT NULL auto_increment, page_namespace int NOT NULL, page_title varchar(255) binary NOT NULL, - page_restrictions tinyblob NOT NULL default '', + page_restrictions tinyblob NOT NULL, page_counter bigint(20) unsigned NOT NULL default '0', page_is_redirect tinyint(1) unsigned NOT NULL default '0', page_is_new tinyint(1) unsigned NOT NULL default '0', @@ -396,7 +407,7 @@ function do_schema_restructuring() { $wgDatabase->query("CREATE TABLE $revision ( rev_id int(8) unsigned NOT NULL auto_increment, rev_page int(8) unsigned NOT NULL, - rev_comment tinyblob NOT NULL default '', + rev_comment tinyblob NOT NULL, rev_user int(5) unsigned NOT NULL default '0', rev_user_text varchar(255) binary NOT NULL default '', rev_timestamp char(14) binary NOT NULL default '', @@ -775,12 +786,59 @@ function do_rc_indices_update() { dbsource( archive( 'patch-recentchanges-utindex.sql' ) ); } else { # Index seems to exist - echo( "...seems to be ok\n" ); + echo( "...index on ( rc_namespace, rc_user_text ) seems to be ok\n" ); + } + + #Add (rc_user_text, rc_timestamp) index [A. Garrett], November 2006 + # See if we can find the index we want + $info = $wgDatabase->indexInfo( 'recentchanges', 'rc_user_text', __METHOD__ ); + if( !$info ) { + # None, so create + echo( "...index on ( rc_user_text, rc_timestamp ) not found; creating\n" ); + dbsource( archive( 'patch-rc_user_text-index.sql' ) ); + } else { + # Index seems to exist + echo( "...index on ( rc_user_text, rc_timestamp ) seems to be ok\n" ); + } +} + +function index_has_field($table, $index, $field) { + global $wgDatabase; + echo( "Checking if $table index $index includes field $field...\n" ); + $info = $wgDatabase->indexInfo( $table, $index, __METHOD__ ); + if( $info ) { + foreach($info as $row) { + if($row->Column_name == $field) { + echo( "...index $index on table $table seems to be ok\n" ); + return true; + } + } } + echo( "...index $index on table $table has no field $field; adding\n" ); + return false; } -function do_all_updates( $doShared = false ) { - global $wgNewTables, $wgNewFields, $wgRenamedTables, $wgSharedDB, $wgDatabase, $wgDBtype; +function do_backlinking_indices_update() { + echo( "Checking for backlinking indices...\n" ); + if (!index_has_field('pagelinks', 'pl_namespace', 'pl_from') || + !index_has_field('templatelinks', 'tl_namespace', 'tl_from') || + !index_has_field('imagelinks', 'il_to', 'il_from')) + { + dbsource( archive( 'patch-backlinkindexes.sql' ) ); + } +} + +function purge_cache() { + global $wgDatabase; + # We can't guarantee that the user will be able to use TRUNCATE, + # but we know that DELETE is available to us + echo( "Purging caches..." ); + $wgDatabase->delete( 'objectcache', '*', __METHOD__ ); + echo( "done.\n" ); +} + +function do_all_updates( $shared = false, $purge = true ) { + global $wgNewTables, $wgNewFields, $wgRenamedTables, $wgSharedDB, $wgDatabase, $wgDBtype, $IP; $doUser = !$wgSharedDB || $doShared; @@ -844,8 +902,17 @@ function do_all_updates( $doShared = false ) { do_page_random_update(); flush(); do_rc_indices_update(); flush(); + + do_backlinking_indices_update(); flush(); - initialiseMessages(); flush(); + echo "Deleting old default messages..."; flush(); + deleteDefaultMessages(); + echo "Done\n"; flush(); + + if( $purge ) { + purge_cache(); + flush(); + } } function archive($name) { @@ -861,20 +928,28 @@ function archive($name) { function do_postgres_updates() { global $wgDatabase, $wgVersion, $wgDBmwschema; - $version = "1.7.1"; - # Just in case their LocalSetings.php does not have this: if ( !isset( $wgDBmwschema )) $wgDBmwschema = 'mediawiki'; + ## Default to the oldest supported version + $version = 1.7; + if ($wgDatabase->tableExists("mediawiki_version")) { $version = "1.8"; + $sql = "SELECT mw_version FROM mediawiki_version ORDER BY cdate DESC LIMIT 1"; + $tempversion = pg_fetch_result($wgDatabase->doQuery($sql),0,0); + $thisver = array(); + if (preg_match('/(\d+\.\d+)/', $tempversion, $thisver)) { + $version = $thisver[1]; + } } - if ($version == '1.7.1') { - $upgrade = <<<PGEND + print " Detected version: $version "; + $upgrade = ''; -BEGIN; + if ($version <= 1.7) { + $upgrade = <<<PGEND -- Type tweaking: ALTER TABLE oldimage ALTER oi_size TYPE INTEGER; @@ -927,9 +1002,6 @@ CREATE TABLE mediawiki_version ( cdate TIMESTAMPTZ NOT NULL DEFAULT now() ); -INSERT INTO mediawiki_version (type,mw_version,notes) -VALUES ('Upgrade','MWVERSION','Upgrade from older version 1.7.1'); - -- Special modifications ALTER TABLE archive RENAME to archive2; CREATE VIEW archive AS @@ -957,20 +1029,69 @@ END; CREATE TRIGGER page_deleted AFTER DELETE ON page FOR EACH ROW EXECUTE PROCEDURE page_deleted(); -COMMIT; - PGEND; - $upgrade = str_replace( 'MWVERSION', $wgVersion, $upgrade ); + } ## end version 1.7 + + else if ($version <= 1.8) { + $upgrade = <<<PGEND + +-- Tighten up restrictions on the revision table so we don't lose data: +ALTER TABLE revision DROP CONSTRAINT revision_rev_user_fkey; +ALTER TABLE revision ADD CONSTRAINT revision_rev_user_fkey + FOREIGN KEY (rev_user) REFERENCES mwuser(user_id) ON DELETE RESTRICT; + +-- New columns for better password tracking: +ALTER TABLE mwuser ADD user_newpass_time TIMESTAMPTZ; +ALTER TABLE mwuser ADD user_editcount INTEGER; - $res = $wgDatabase->query($upgrade); +-- New column for autoblocking problem users +ALTER TABLE ipblocks ADD ipb_enable_autoblock CHAR NOT NULL DEFAULT '1'; - } ## end version 1.7.1 upgrade +-- Despite it's name, ipb_address does not necessarily contain IP addresses :) +ALTER TABLE ipblocks ALTER ipb_address TYPE TEXT USING ipb_address::TEXT; - else { - print "No updates needed\n"; +-- New tables: +CREATE TABLE redirect ( + rd_from INTEGER NOT NULL REFERENCES page(page_id) ON DELETE CASCADE, + rd_namespace SMALLINT NOT NULL, + rd_title TEXT NOT NULL +); +CREATE INDEX redirect_ns_title ON redirect (rd_namespace,rd_title,rd_from); + +CREATE TABLE querycachetwo ( + qcc_type TEXT NOT NULL, + qcc_value SMALLINT NOT NULL DEFAULT 0, + qcc_namespace INTEGER NOT NULL DEFAULT 0, + qcc_title TEXT NOT NULL DEFAULT '', + qcc_namespacetwo INTEGER NOT NULL DEFAULT 0, + qcc_titletwo TEXT NOT NULL DEFAULT '' +); +CREATE INDEX querycachetwo_type_value ON querycachetwo (qcc_type, qcc_value); +CREATE INDEX querycachetwo_title ON querycachetwo (qcc_type,qcc_namespace,qcc_title); +CREATE INDEX querycachetwo_titletwo ON querycachetwo (qcc_type,qcc_namespacetwo,qcc_titletwo); + +-- New columns for fancy recentchanges display +ALTER TABLE recentchanges ADD rc_old_len INT; +ALTER TABLE recentchanges ADD rc_new_len INT; + +-- Note this upgrade +INSERT INTO mediawiki_version (type,mw_version,notes) +VALUES ('Upgrade','MWVERSION','Upgrade from older version THISVERSION'); + +PGEND; + + } + + if ( !strlen($upgrade)) { + print "No updates needed for version $version\n"; + return; } + $upgrade = str_replace( 'MWVERSION', $wgVersion, $upgrade ); + $upgrade = str_replace( 'THISVERSION', $version, $upgrade ); + $res = $wgDatabase->query("BEGIN;\n\n $upgrade\n\nCOMMIT;\n"); + return; } diff --git a/maintenance/userDupes.inc b/maintenance/userDupes.inc index e632f737..9af66f11 100644 --- a/maintenance/userDupes.inc +++ b/maintenance/userDupes.inc @@ -46,7 +46,7 @@ class UserDupes { # Confusingly, 'Non_unique' is 0 for *unique* indexes, # and 1 for *non-unique* indexes. Pass the crack, MySQL, # it's obviously some good stuff! - return ( $info->Non_unique == 0 ); + return ( $info[0]->Non_unique == 0 ); } /** |