diff options
Diffstat (limited to 'maintenance')
251 files changed, 7354 insertions, 14720 deletions
diff --git a/maintenance/7zip.inc b/maintenance/7zip.inc index 4ac480ed..6bb06668 100644 --- a/maintenance/7zip.inc +++ b/maintenance/7zip.inc @@ -2,6 +2,24 @@ /** * 7z stream wrapper * + * Copyright © 2005 Brion Vibber <brion@pobox.com> + * http://www.mediawiki.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance */ diff --git a/maintenance/Doxyfile b/maintenance/Doxyfile index 3d037651..7d7849c8 100644 --- a/maintenance/Doxyfile +++ b/maintenance/Doxyfile @@ -113,7 +113,7 @@ FILE_PATTERNS = *.c \ *.odl \ *.cs \ *.php \ - *.php3 \ + *.php5 \ *.inc \ *.m \ *.mm \ @@ -129,14 +129,14 @@ FILE_PATTERNS = *.c \ *.H++ \ *.CS \ *.PHP \ - *.PHP3 \ + *.PHP5 \ *.M \ *.MM \ *.PY RECURSIVE = YES -EXCLUDE = +EXCLUDE = {{EXCLUDE}} EXCLUDE_SYMLINKS = YES -EXCLUDE_PATTERNS = LocalSettings.php AdminSettings.php StartProfiler.php .svn */.git/* {{EXCLUDE}} +EXCLUDE_PATTERNS = LocalSettings.php AdminSettings.php StartProfiler.php .svn */.git/* {{EXCLUDE_PATTERNS}} EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO @@ -255,7 +255,7 @@ PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- CLASS_DIAGRAMS = NO HIDE_UNDOC_RELATIONS = YES -HAVE_DOT = NO +HAVE_DOT = {{HAVE_DOT}} CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES @@ -264,6 +264,7 @@ TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = YES +CALLER_GRAPH = YES GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png diff --git a/maintenance/Maintenance.php b/maintenance/Maintenance.php index 24bb7809..9ea57f4e 100644 --- a/maintenance/Maintenance.php +++ b/maintenance/Maintenance.php @@ -1,5 +1,20 @@ <?php /** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance * @defgroup Maintenance Maintenance @@ -12,12 +27,9 @@ define( 'DO_MAINTENANCE', RUN_MAINTENANCE_IF_MAIN ); // original name, harmless $maintClass = false; // Make sure we're on PHP5 or better -if ( version_compare( PHP_VERSION, '5.2.3' ) < 0 ) { - die ( "Sorry! This version of MediaWiki requires PHP 5.2.3; you are running " . - PHP_VERSION . ".\n\n" . - "If you are sure you already have PHP 5.2.3 or higher installed, it may be\n" . - "installed in a different path from PHP " . PHP_VERSION . ". Check with your system\n" . - "administrator.\n" ); +if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.2.3' ) < 0 ) { + require_once( dirname( __FILE__ ) . '/../includes/PHPVersionError.php' ); + wfPHPVersionError( 'cli' ); } // Wrapper for posix_isatty() @@ -35,21 +47,6 @@ if ( !function_exists( 'posix_isatty' ) ) { * is the execute() method. See docs/maintenance.txt for more info * and a quick demo of how to use it. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * * @author Chad Horohoe <chad@anyonecanedit.org> * @since 1.16 * @ingroup Maintenance @@ -70,6 +67,9 @@ abstract class Maintenance { // This is the desired params protected $mParams = array(); + // Array of mapping short parameters to long ones + protected $mShortParamsMap = array(); + // Array of desired args protected $mArgList = array(); @@ -92,10 +92,22 @@ abstract class Maintenance { // Have we already loaded our user input? protected $mInputLoaded = false; - // Batch size. If a script supports this, they should set - // a default with setBatchSize() + /** + * Batch size. If a script supports this, they should set + * a default with setBatchSize() + * + * @var int + */ protected $mBatchSize = null; + // Generic options added by addDefaultParams() + private $mGenericParameters = array(); + // Generic options which might or not be supported by the script + private $mDependantParameters = array(); + + // Used by getDD() / setDB() + private $mDb = null; + /** * List of all the core maintenance scripts. This is added * to scripts added by extensions in $wgMaintenanceScripts @@ -121,18 +133,25 @@ abstract class Maintenance { /** * Should we execute the maintenance script, or just allow it to be included * as a standalone class? It checks that the call stack only includes this - * function and a require (meaning was called from the file scope) + * function and "requires" (meaning was called from the file scope) * * @return Boolean */ public static function shouldExecute() { $bt = debug_backtrace(); - if( count( $bt ) !== 2 ) { - return false; + if ( count( $bt ) < 2 ) { + return false; // sanity + } + if ( $bt[0]['class'] !== 'Maintenance' || $bt[0]['function'] !== 'shouldExecute' ) { + return false; // last call should be to this function + } + $includeFuncs = array( 'require_once', 'require', 'include' ); + for( $i=1; $i < count( $bt ); $i++ ) { + if ( !in_array( $bt[$i]['function'], $includeFuncs ) ) { + return false; // previous calls should all be "requires" + } } - return $bt[1]['function'] == 'require_once' && - $bt[0]['class'] == 'Maintenance' && - $bt[0]['function'] == 'shouldExecute'; + return true; } /** @@ -148,9 +167,13 @@ abstract class Maintenance { * @param $description String: the description of the param to show on --help * @param $required Boolean: is the param required? * @param $withArg Boolean: is an argument required with this option? + * @param $shortName String: character to use as short name */ - protected function addOption( $name, $description, $required = false, $withArg = false ) { - $this->mParams[$name] = array( 'desc' => $description, 'require' => $required, 'withArg' => $withArg ); + protected function addOption( $name, $description, $required = false, $withArg = false, $shortName = false ) { + $this->mParams[$name] = array( 'desc' => $description, 'require' => $required, 'withArg' => $withArg, 'shortName' => $shortName ); + if ( $shortName !== false ) { + $this->mShortParamsMap[$shortName] = $name; + } } /** @@ -280,10 +303,11 @@ abstract class Maintenance { } if ( $channel === null ) { $this->cleanupChanneled(); - - $f = fopen( 'php://stdout', 'w' ); - fwrite( $f, $out ); - fclose( $f ); + if( php_sapi_name() == 'cli' ) { + fwrite( STDOUT, $out ); + } else { + print( $out ); + } } else { $out = preg_replace( '/\n\z/', '', $out ); @@ -295,19 +319,18 @@ abstract class Maintenance { * Throw an error to the user. Doesn't respect --quiet, so don't use * this for non-error output * @param $err String: the error to display - * @param $die Boolean: If true, go ahead and die out. + * @param $die Int: if > 0, go ahead and die out using this int as the code */ - protected function error( $err, $die = false ) { + protected function error( $err, $die = 0 ) { $this->outputChanneled( false ); if ( php_sapi_name() == 'cli' ) { fwrite( STDERR, $err . "\n" ); } else { - $f = fopen( 'php://stderr', 'w' ); - fwrite( $f, $err . "\n" ); - fclose( $f ); + print $err; } - if ( $die ) { - die(); + $die = intval( $die ); + if ( $die > 0 ) { + die( $die ); } } @@ -319,9 +342,11 @@ abstract class Maintenance { */ public function cleanupChanneled() { if ( !$this->atLineStart ) { - $handle = fopen( 'php://stdout', 'w' ); - fwrite( $handle, "\n" ); - fclose( $handle ); + if( php_sapi_name() == 'cli' ) { + fwrite( STDOUT, "\n" ); + } else { + print "\n"; + } $this->atLineStart = true; } } @@ -340,25 +365,34 @@ abstract class Maintenance { return; } - $handle = fopen( 'php://stdout', 'w' ); + $cli = php_sapi_name() == 'cli'; // End the current line if necessary if ( !$this->atLineStart && $channel !== $this->lastChannel ) { - fwrite( $handle, "\n" ); + if( $cli ) { + fwrite( STDOUT, "\n" ); + } else { + print "\n"; + } } - fwrite( $handle, $msg ); + if( $cli ) { + fwrite( STDOUT, $msg ); + } else { + print $msg; + } $this->atLineStart = false; if ( $channel === null ) { // For unchanneled messages, output trailing newline immediately - fwrite( $handle, "\n" ); + if( $cli ) { + fwrite( STDOUT, "\n" ); + } else { + print "\n"; + } $this->atLineStart = true; } $this->lastChannel = $channel; - - // Cleanup handle - fclose( $handle ); } /** @@ -379,8 +413,11 @@ abstract class Maintenance { * Add the default parameters to the scripts */ protected function addDefaultParams() { - $this->addOption( 'help', 'Display this help message' ); - $this->addOption( 'quiet', 'Whether to supress non-error output' ); + + # Generic (non script dependant) options: + + $this->addOption( 'help', 'Display this help message', false, false, 'h' ); + $this->addOption( 'quiet', 'Whether to supress non-error output', false, false, 'q' ); $this->addOption( 'conf', 'Location of LocalSettings.php, if not default', false, true ); $this->addOption( 'wiki', 'For specifying the wiki ID', false, true ); $this->addOption( 'globals', 'Output globals at the end of processing for debugging' ); @@ -388,6 +425,12 @@ abstract class Maintenance { $this->addOption( 'server', "The protocol and server name to use in URLs, e.g. " . "http://en.wikipedia.org. This is sometimes necessary because " . "server name detection may fail in command line scripts.", false, true ); + + # Save generic options to display them separately in help + $this->mGenericParameters = $this->mParams ; + + # Script dependant options: + // If we support a DB, show the options if ( $this->getDbType() > 0 ) { $this->addOption( 'dbuser', 'The DB user to use for this script', false, true ); @@ -398,6 +441,9 @@ abstract class Maintenance { $this->addOption( 'batch-size', 'Run this many operations ' . 'per batch, default: ' . $this->mBatchSize, false, true ); } + # Save additional script dependant options to display + # them separately in help + $this->mDependantParameters = array_diff_key( $this->mParams, $this->mGenericParameters ); } /** @@ -409,17 +455,20 @@ abstract class Maintenance { */ public function runChild( $maintClass, $classFile = null ) { // Make sure the class is loaded first - if ( !class_exists( $maintClass ) ) { + if ( !MWInit::classExists( $maintClass ) ) { if ( $classFile ) { require_once( $classFile ); } - if ( !class_exists( $maintClass ) ) { + if ( !MWInit::classExists( $maintClass ) ) { $this->error( "Cannot spawn child: $maintClass" ); } } $child = new $maintClass(); $child->loadParamsAndArgs( $this->mSelf, $this->mOptions, $this->mArgs ); + if ( !is_null( $this->mDb ) ) { + $child->setDB( $this->mDb ); + } return $child; } @@ -435,7 +484,7 @@ abstract class Maintenance { } # Make sure we can handle script parameters - if ( !ini_get( 'register_argc_argv' ) ) { + if ( !function_exists( 'hphp_thread_set_warmup_enabled' ) && !ini_get( 'register_argc_argv' ) ) { $this->error( 'Cannot get command line arguments, register_argc_argv is set to false', true ); } @@ -590,6 +639,9 @@ abstract class Maintenance { # Short options for ( $p = 1; $p < strlen( $arg ); $p++ ) { $option = $arg { $p } ; + if ( !isset( $this->mParams[$option] ) && isset( $this->mShortParamsMap[$option] ) ) { + $option = $this->mShortParamsMap[$option]; + } if ( array_key_exists( $option, $options ) ) { $this->error( "\nERROR: $option parameter given twice\n" ); $this->maybeHelp( true ); @@ -655,7 +707,7 @@ abstract class Maintenance { $this->mQuiet = true; } if ( $this->hasOption( 'batch-size' ) ) { - $this->mBatchSize = $this->getOption( 'batch-size' ); + $this->mBatchSize = intval( $this->getOption( 'batch-size' ) ); } } @@ -701,22 +753,75 @@ abstract class Maintenance { } $this->output( "$output\n\n" ); - // Parameters description - foreach ( $this->mParams as $par => $info ) { + # TODO abstract some repetitive code below + + // Generic parameters + $this->output( "Generic maintenance parameters:\n" ); + foreach ( $this->mGenericParameters as $par => $info ) { + if ( $info['shortName'] !== false ) { + $par .= " (-{$info['shortName']})"; + } $this->output( wordwrap( "$tab--$par: " . $info['desc'], $descWidth, "\n$tab$tab" ) . "\n" ); } + $this->output( "\n" ); - // Arguments description - foreach ( $this->mArgList as $info ) { - $openChar = $info['require'] ? '<' : '['; - $closeChar = $info['require'] ? '>' : ']'; - $this->output( - wordwrap( "$tab$openChar" . $info['name'] . "$closeChar: " . - $info['desc'], $descWidth, "\n$tab$tab" ) . "\n" - ); + $scriptDependantParams = $this->mDependantParameters; + if( count($scriptDependantParams) > 0 ) { + $this->output( "Script dependant parameters:\n" ); + // Parameters description + foreach ( $scriptDependantParams as $par => $info ) { + if ( $info['shortName'] !== false ) { + $par .= " (-{$info['shortName']})"; + } + $this->output( + wordwrap( "$tab--$par: " . $info['desc'], $descWidth, + "\n$tab$tab" ) . "\n" + ); + } + $this->output( "\n" ); + } + + + // Script specific parameters not defined on construction by + // Maintenance::addDefaultParams() + $scriptSpecificParams = array_diff_key( + # all script parameters: + $this->mParams, + # remove the Maintenance default parameters: + $this->mGenericParameters, + $this->mDependantParameters + ); + if( count($scriptSpecificParams) > 0 ) { + $this->output( "Script specific parameters:\n" ); + // Parameters description + foreach ( $scriptSpecificParams as $par => $info ) { + if ( $info['shortName'] !== false ) { + $par .= " (-{$info['shortName']})"; + } + $this->output( + wordwrap( "$tab--$par: " . $info['desc'], $descWidth, + "\n$tab$tab" ) . "\n" + ); + } + $this->output( "\n" ); + } + + // Print arguments + if( count( $this->mArgList ) > 0 ) { + $this->output( "Arguments:\n" ); + // Arguments description + foreach ( $this->mArgList as $info ) { + $openChar = $info['require'] ? '<' : '['; + $closeChar = $info['require'] ? '>' : ']'; + $this->output( + wordwrap( "$tab$openChar" . $info['name'] . "$closeChar: " . + $info['desc'], $descWidth, "\n$tab$tab" ) . "\n" + ); + } + $this->output( "\n" ); } die( 1 ); @@ -727,7 +832,7 @@ abstract class Maintenance { */ public function finalSetup() { global $wgCommandLineMode, $wgShowSQLErrors, $wgServer; - global $wgProfiling, $wgDBadminuser, $wgDBadminpassword; + global $wgDBadminuser, $wgDBadminpassword; global $wgDBuser, $wgDBpassword, $wgDBservers, $wgLBFactoryConf; # Turn off output buffering again, it might have been turned on in the settings files @@ -772,8 +877,6 @@ abstract class Maintenance { $wgShowSQLErrors = true; @set_time_limit( 0 ); $this->adjustMemoryLimit(); - - $wgProfiling = false; // only for Profiler.php mode; avoids OOM errors } /** @@ -840,7 +943,9 @@ abstract class Maintenance { ini_set( 'include_path', ".:$IP:$IP/includes:$IP/languages:$IP/maintenance" ); if ( $lang == 'test' && $site == 'wikipedia' ) { - define( 'TESTWIKI', 1 ); + if ( !defined( 'TESTWIKI' ) ) { + define( 'TESTWIKI', 1 ); + } } } @@ -849,12 +954,11 @@ abstract class Maintenance { * @return String */ public function loadSettings() { - global $wgWikiFarm, $wgCommandLineMode, $IP; + global $wgCommandLineMode, $IP; - $wgWikiFarm = false; if ( isset( $this->mOptions['conf'] ) ) { $settingsFile = $this->mOptions['conf']; - } else if ( defined("MW_CONFIG_FILE") ) { + } elseif ( defined("MW_CONFIG_FILE") ) { $settingsFile = MW_CONFIG_FILE; } else { $settingsFile = "$IP/LocalSettings.php"; @@ -884,7 +988,7 @@ abstract class Maintenance { */ public function purgeRedundantText( $delete = true ) { # Data should come off the master, wrapped in a transaction - $dbw = wfGetDB( DB_MASTER ); + $dbw = $this->getDB( DB_MASTER ); $dbw->begin(); $tbl_arc = $dbw->tableName( 'archive' ); @@ -988,6 +1092,30 @@ abstract class Maintenance { } /** + * Returns a database to be used by current maintenance script. It can be set by setDB(). + * If not set, wfGetDB() will be used. + * This function has the same parameters as wfGetDB() + * + * @return DatabaseBase + */ + protected function &getDB( $db, $groups = array(), $wiki = false ) { + if ( is_null( $this->mDb ) ) { + return wfGetDB( $db, $groups, $wiki ); + } else { + return $this->mDb; + } + } + + /** + * Sets database object to be returned by getDB(). + * + * @param $db DatabaseBase: Database object to be used + */ + public function setDB( &$db ) { + $this->mDb = $db; + } + + /** * Lock the search index * @param &$db Database object */ @@ -1019,7 +1147,7 @@ abstract class Maintenance { * Perform a search index update with locking * @param $maxLockTime Integer: the maximum time to keep the search index locked. * @param $callback callback String: the function that will update the function. - * @param $dbw Database object + * @param $dbw DatabaseBase object * @param $results */ public function updateSearchIndex( $maxLockTime, $callback, $dbw, $results ) { @@ -1083,11 +1211,7 @@ abstract class Maintenance { public static function readconsole( $prompt = '> ' ) { static $isatty = null; if ( is_null( $isatty ) ) { - if ( posix_isatty( 0 /*STDIN*/ ) ) { - $isatty = true; - } else { - $isatty = false; - } + $isatty = posix_isatty( 0 /*STDIN*/ ); } if ( $isatty && function_exists( 'readline' ) ) { @@ -1150,3 +1274,70 @@ class FakeMaintenance extends Maintenance { } } +/** + * Class for scripts that perform database maintenance and want to log the + * update in `updatelog` so we can later skip it + */ +abstract class LoggedUpdateMaintenance extends Maintenance { + public function __construct() { + parent::__construct(); + $this->addOption( 'force', 'Run the update even if it was completed already' ); + $this->setBatchSize( 200 ); + } + + public function execute() { + $db = $this->getDB( DB_MASTER ); + $key = $this->getUpdateKey(); + + if ( !$this->hasOption( 'force' ) && + $db->selectRow( 'updatelog', '1', array( 'ul_key' => $key ), __METHOD__ ) ) + { + $this->output( "..." . $this->updateSkippedMessage() . "\n" ); + return true; + } + + if ( !$this->doDBUpdates() ) { + return false; + } + + if ( + $db->insert( 'updatelog', array( 'ul_key' => $key ), __METHOD__, 'IGNORE' ) ) + { + return true; + } else { + $this->output( $this->updatelogFailedMessage() . "\n" ); + return false; + } + } + + /** + * Message to show that the update was done already and was just skipped + * @return String + */ + protected function updateSkippedMessage() { + $key = $this->getUpdateKey(); + return "Update '{$key}' already logged as completed."; + } + + /** + * Message to show the the update log was unable to log the completion of this update + * @return String + */ + protected function updatelogFailedMessage() { + $key = $this->getUpdateKey(); + return "Unable to log update '{$key}' as completed."; + } + + /** + * Do the actual work. All child classes will need to implement this. + * Return true to log the update as done or false (usually on failure). + * @return Bool + */ + abstract protected function doDBUpdates(); + + /** + * Get the update key name to go in the update log table + * @return String + */ + abstract protected function getUpdateKey(); +}
\ No newline at end of file diff --git a/maintenance/Site.php b/maintenance/Site.php new file mode 100644 index 00000000..87d637b4 --- /dev/null +++ b/maintenance/Site.php @@ -0,0 +1,19 @@ +<?php +/** + * @todo document + * @ingroup Maintenance + */ +class Site { + var $suffix, $lateral, $url; + + function __construct( $s, $l, $u ) { + $this->suffix = $s; + $this->lateral = $l; + $this->url = $u; + } + + function getURL( $lang, $urlprotocol ) { + $xlang = str_replace( '_', '-', $lang ); + return "$urlprotocol//$xlang.{$this->url}/wiki/\$1"; + } +} diff --git a/maintenance/addwiki.php b/maintenance/addwiki.php index e86a8c5d..43f42be5 100644 --- a/maintenance/addwiki.php +++ b/maintenance/addwiki.php @@ -31,12 +31,16 @@ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); class AddWiki extends Maintenance { public function __construct() { + global $wgNoDBParam; + parent::__construct(); $this->mDescription = "Add a new wiki to the family. Wikimedia specific!"; $this->addArg( 'language', 'Language code of new site, e.g. en' ); $this->addArg( 'site', 'Type of site, e.g. wikipedia' ); $this->addArg( 'dbname', 'Name of database to create, e.g. enwiki' ); $this->addArg( 'domain', 'Domain name of the wiki, e.g. en.wikipedia.org' ); + + $wgNoDBParam = true; } public function getDbType() { @@ -44,9 +48,11 @@ class AddWiki extends Maintenance { } public function execute() { - global $IP, $wgDefaultExternalStore, $wgNoDBParam; + global $IP, $wgDefaultExternalStore, $wgVersionNumber; + if ( !$wgVersionNumber ) { // set in CommonSettings.php + $this->error( '$wgVersionNumber is not set, please use MWScript.php wrapper.', true ); + } - $wgNoDBParam = true; $lang = $this->getArg( 0 ); $site = $this->getArg( 1 ); $dbName = $this->getArg( 2 ); @@ -54,7 +60,7 @@ class AddWiki extends Maintenance { $languageNames = Language::getLanguageNames(); if ( !isset( $languageNames[$lang] ) ) { - $this->error( "Language $lang not found in \$wgLanguageNames", true ); + $this->error( "Language $lang not found in Names.php", true ); } $name = $languageNames[$lang]; @@ -78,12 +84,11 @@ class AddWiki extends Maintenance { $dbw->sourceFile( "$IP/extensions/Oversight/hidden.sql" ); $dbw->sourceFile( "$IP/extensions/GlobalBlocking/localdb_patches/setup-global_block_whitelist.sql" ); $dbw->sourceFile( "$IP/extensions/AbuseFilter/abusefilter.tables.sql" ); - $dbw->sourceFile( "$IP/extensions/UsabilityInitiative/PrefStats/PrefStats.sql" ); + $dbw->sourceFile( "$IP/extensions/PrefStats/patches/PrefStats.sql" ); $dbw->sourceFile( "$IP/extensions/ProofreadPage/ProofreadPage.sql" ); - $dbw->sourceFile( "$IP/extensions/UsabilityInitiative/ClickTracking/ClickTrackingEvents.sql" ); - $dbw->sourceFile( "$IP/extensions/UsabilityInitiative/ClickTracking/ClickTracking.sql" ); - $dbw->sourceFile( "$IP/extensions/UsabilityInitiative/UserDailyContribs/UserDailyContribs.sql" ); - $dbw->sourceFile( "$IP/extensions/UsabilityInitiative/OptIn/OptIn.sql" ); + $dbw->sourceFile( "$IP/extensions/ClickTracking/patches/ClickTrackingEvents.sql" ); + $dbw->sourceFile( "$IP/extensions/ClickTracking/patches/ClickTracking.sql" ); + $dbw->sourceFile( "$IP/extensions/UserDailyContribs/patches/UserDailyContribs.sql" ); $dbw->query( "INSERT INTO site_stats(ss_row_id) VALUES (1)" ); @@ -126,13 +131,12 @@ class AddWiki extends Maintenance { } } - global $wgTitle, $wgArticle; - $wgTitle = Title::newFromText( wfMsgWeirdKey( "mainpage/$lang" ) ); - $this->output( "Writing main page to " . $wgTitle->getPrefixedDBkey() . "\n" ); - $wgArticle = new Article( $wgTitle ); + $title = Title::newFromText( wfMessage( 'mainpage' )->inLanguage( $lang )->useDatabase( false )->plain() ); + $this->output( "Writing main page to " . $title->getPrefixedDBkey() . "\n" ); + $article = new Article( $title ); $ucsite = ucfirst( $site ); - $wgArticle->insertNewArticle( $this->getFirstArticle( $ucsite, $name ), '', false, false ); + $article->doEdit( $this->getFirstArticle( $ucsite, $name ), '', EDIT_NEW | EDIT_AUTOSUMMARY ); $this->output( "Adding to dblists\n" ); @@ -144,6 +148,13 @@ class AddWiki extends Maintenance { # Update the sublists shell_exec( "cd $common && ./refresh-dblist" ); + # Add to wikiversions.dat + $file = fopen( "$common/wikiversions.dat", "a" ); + fwrite( $file, "$dbName php-$wgVersionNumber\n" ); + fclose( $file ); + # Rebuild wikiversions.cdb + shell_exec( "cd $common/multiversion && ./refreshWikiversionsCDB" ); + # print "Constructing interwiki SQL\n"; # Rebuild interwiki tables # passthru( '/home/wikipedia/conf/interwiki/update' ); @@ -387,9 +398,9 @@ See Wikimedia's [[m:|Meta-Wiki]] for the coordination of these projects. [[rmy:]] [[rn:]] [[ro:]] -[[roa-rup:]] [[roa-tara:]] [[ru:]] +[[rup:]] [[rw:]] [[sa:]] [[sah:]] diff --git a/maintenance/archives/patch-archive_ar_revid.sql b/maintenance/archives/patch-archive_ar_revid.sql index 67ee97b1..3b3fdee6 100644 --- a/maintenance/archives/patch-archive_ar_revid.sql +++ b/maintenance/archives/patch-archive_ar_revid.sql @@ -1,4 +1,3 @@ -- Hopefully temporary index. -- For https://bugzilla.wikimedia.org/show_bug.cgi?id=21279 -ALTER TABLE /*$wgDBprefix*/archive - ADD INDEX ar_revid ( ar_rev_id );
\ No newline at end of file +CREATE INDEX /*i*/ar_revid ON /*$wgDBprefix*/archive ( ar_rev_id );
\ No newline at end of file diff --git a/maintenance/archives/patch-image_reditects.sql b/maintenance/archives/patch-image_reditects.sql deleted file mode 100644 index e69de29b..00000000 --- a/maintenance/archives/patch-image_reditects.sql +++ /dev/null diff --git a/maintenance/archives/patch-math.sql b/maintenance/archives/patch-math.sql deleted file mode 100644 index 1d4b90e1..00000000 --- a/maintenance/archives/patch-math.sql +++ /dev/null @@ -1,28 +0,0 @@ --- Creates table math used for caching TeX blocks. Needs to be run --- on old installations when adding TeX support (2002-12-26) --- Or, TeX can be disabled via $wgUseTeX=false in LocalSettings.php - --- Note: math table has changed, and this script needs to be run again --- to create it. (2003-02-02) - -DROP TABLE IF EXISTS /*$wgDBprefix*/math; -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 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) - -) /*$wgDBTableOptions*/; diff --git a/maintenance/archives/patch-page_no_title_convert.sql b/maintenance/archives/patch-page_no_title_convert.sql deleted file mode 100644 index e69de29b..00000000 --- a/maintenance/archives/patch-page_no_title_convert.sql +++ /dev/null diff --git a/maintenance/archives/patch-profiling.sql b/maintenance/archives/patch-profiling.sql index 29663341..0a0e4e1a 100644 --- a/maintenance/archives/patch-profiling.sql +++ b/maintenance/archives/patch-profiling.sql @@ -9,4 +9,4 @@ CREATE TABLE /*_*/profiling ( pf_server varchar(30) NOT NULL default '' ) ENGINE=HEAP; -CREATE UNIQUE INDEX /*i*/pf_name_server ON /*_*/profiling (pf_name, pf_server)
\ No newline at end of file +CREATE UNIQUE INDEX /*i*/pf_name_server ON /*_*/profiling (pf_name, pf_server);
\ No newline at end of file diff --git a/maintenance/archives/patch-up_property.sql b/maintenance/archives/patch-up_property.sql new file mode 100644 index 00000000..742841e4 --- /dev/null +++ b/maintenance/archives/patch-up_property.sql @@ -0,0 +1,4 @@ +-- Increase the length of up_property from 32 -> 255 bytes. Bug 19408 + +ALTER TABLE /*_*/user_properties + MODIFY up_property varbinary(255); diff --git a/maintenance/archives/patch-uploadstash.sql b/maintenance/archives/patch-uploadstash.sql new file mode 100644 index 00000000..2512076f --- /dev/null +++ b/maintenance/archives/patch-uploadstash.sql @@ -0,0 +1,49 @@ +-- +-- Store information about newly uploaded files before they're +-- moved into the actual filestore +-- +CREATE TABLE /*_*/uploadstash ( + us_id int unsigned NOT NULL PRIMARY KEY auto_increment, + + -- the user who uploaded the file. + us_user int unsigned NOT NULL, + + -- file key. this is how applications actually search for the file. + -- this might go away, or become the primary key. + us_key varchar(255) NOT NULL, + + -- the original path + us_orig_path varchar(255) NOT NULL, + + -- the temporary path at which the file is actually stored + us_path varchar(255) NOT NULL, + + -- which type of upload the file came from (sometimes) + us_source_type varchar(50), + + -- the date/time on which the file was added + us_timestamp varbinary(14) not null, + + us_status varchar(50) not null, + + -- file properties from File::getPropsFromPath. these may prove unnecessary. + -- + us_size int unsigned NOT NULL, + -- this hash comes from File::sha1Base36(), and is 31 characters + us_sha1 varchar(31) NOT NULL, + us_mime varchar(255), + -- Media type as defined by the MEDIATYPE_xxx constants, should duplicate definition in the image table + us_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL, + -- image-specific properties + us_image_width int unsigned, + us_image_height int unsigned, + us_image_bits smallint unsigned + +) /*$wgDBTableOptions*/; + +-- sometimes there's a delete for all of a user's stuff. +CREATE INDEX /*i*/us_user ON /*_*/uploadstash (us_user); +-- pick out files by key, enforce key uniqueness +CREATE UNIQUE INDEX /*i*/us_key ON /*_*/uploadstash (us_key); +-- the abandoned upload cleanup script needs this +CREATE INDEX /*i*/us_timestamp ON /*_*/uploadstash (us_timestamp); diff --git a/maintenance/archives/patch-user-newtalk-timestamp-null.sql b/maintenance/archives/patch-user-newtalk-timestamp-null.sql new file mode 100644 index 00000000..7234362d --- /dev/null +++ b/maintenance/archives/patch-user-newtalk-timestamp-null.sql @@ -0,0 +1 @@ +ALTER TABLE /*_*/user_newtalk MODIFY user_last_timestamp varbinary(14) NULL default NULL; diff --git a/maintenance/archives/patch-user_email_index.sql b/maintenance/archives/patch-user_email_index.sql new file mode 100644 index 00000000..6a3d6208 --- /dev/null +++ b/maintenance/archives/patch-user_email_index.sql @@ -0,0 +1 @@ +CREATE INDEX /*i*/user_email ON /*_*/user (user_email(50)); diff --git a/maintenance/archives/patch-user_former_groups.sql b/maintenance/archives/patch-user_former_groups.sql new file mode 100644 index 00000000..ed18b2b6 --- /dev/null +++ b/maintenance/archives/patch-user_former_groups.sql @@ -0,0 +1,9 @@ +-- Stores the groups the user has once belonged to. +-- The user may still belong these groups. Check user_groups. +CREATE TABLE /*_*/user_former_groups ( + -- Key to user_id + ufg_user int unsigned NOT NULL default 0, + ufg_group varbinary(16) NOT NULL default '' +) /*$wgDBTableOptions*/; + +CREATE UNIQUE INDEX /*i*/ufg_user_group ON /*_*/user_former_groups (ufg_user,ufg_group); diff --git a/maintenance/archives/patch-user_last_timestamp.sql b/maintenance/archives/patch-user_last_timestamp.sql index b6d5f0f3..e5f85bf1 100644 --- a/maintenance/archives/patch-user_last_timestamp.sql +++ b/maintenance/archives/patch-user_last_timestamp.sql @@ -1,3 +1,3 @@ -- For getting diff since last view ALTER TABLE /*$wgDBprefix*/user_newtalk - ADD user_last_timestamp binary(14) NOT NULL default ''; + ADD user_last_timestamp varbinary(14) NULL default NULL; diff --git a/maintenance/archives/upgradeLogging.php b/maintenance/archives/upgradeLogging.php index 54a82c09..1765bd9f 100644 --- a/maintenance/archives/upgradeLogging.php +++ b/maintenance/archives/upgradeLogging.php @@ -2,6 +2,21 @@ /** * Replication-safe online upgrade script for log_id/log_deleted * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup MaintenanceArchive */ @@ -9,6 +24,10 @@ require( dirname( __FILE__ ) . '/../commandLine.inc' ); class UpdateLogging { + + /** + * @var DatabaseBase + */ var $dbw; var $batchSize = 1000; var $minTs = false; @@ -145,7 +164,7 @@ EOT; $this->dbw->insert( $dstTable, $batch, __METHOD__ ); $numRowsCopied += count( $batch ); - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } echo "Copied $numRowsCopied rows\n"; } diff --git a/maintenance/backup.inc b/maintenance/backup.inc index 9ed463c9..4cb9c58a 100644 --- a/maintenance/backup.inc +++ b/maintenance/backup.inc @@ -50,6 +50,11 @@ class BackupDumper { var $sink = null; // Output filters var $stubText = false; // include rev_text_id instead of text; for 2-pass dump var $dumpUploads = false; + var $dumpUploadFileContents = false; + var $lastTime = 0; + var $pageCountLast = 0; + var $revCountLast = 0; + var $ID = 0; function BackupDumper( $args ) { $this->stderr = fopen( "php://stderr", "wt" ); @@ -119,18 +124,17 @@ class BackupDumper { $sinks[] = $sink; } if ( !isset( $this->outputTypes[$val] ) ) { - wfDie( "Unrecognized output sink type '$val'\n" ); + $this->fatalError( "Unrecognized output sink type '$val'" ); } $type = $this->outputTypes[$val]; $sink = new $type( $param ); break; case "filter": if ( is_null( $sink ) ) { - $this->progress( "Warning: assuming stdout for filter output\n" ); $sink = new DumpOutput(); } if ( !isset( $this->filterTypes[$val] ) ) { - wfDie( "Unrecognized filter type '$val'\n" ); + $this->fatalError( "Unrecognized filter type '$val'" ); } $type = $this->filterTypes[$val]; $filter = new $type( $sink, $param ); @@ -150,8 +154,8 @@ class BackupDumper { if ( !function_exists( 'utf8_normalize' ) ) { wfDl( "php_utfnormal.so" ); if ( !function_exists( 'utf8_normalize' ) ) { - wfDie( "Failed to load UTF-8 normalization extension. " . - "Install or remove --force-normal parameter to use slower code.\n" ); + $this->fatalError( "Failed to load UTF-8 normalization extension. " . + "Install or remove --force-normal parameter to use slower code." ); } } break; @@ -188,6 +192,7 @@ class BackupDumper { $db = $this->backupDb(); $exporter = new WikiExporter( $db, $history, WikiExporter::STREAM, $text ); $exporter->dumpUploads = $this->dumpUploads; + $exporter->dumpUploadFileContents = $this->dumpUploadFileContents; $wrapper = new ExportProgressFilter( $this->sink, $this ); $exporter->setOutputSink( $wrapper ); @@ -205,6 +210,8 @@ class BackupDumper { } else if ( is_null( $this->pages ) ) { if ( $this->startId || $this->endId ) { $exporter->pagesByRange( $this->startId, $this->endId ); + } elseif ( $this->revStartId || $this->revEndId ) { + $exporter->revsByRange( $this->revStartId, $this->revEndId ); } else { $exporter->allPages(); } @@ -232,6 +239,8 @@ class BackupDumper { $dbr = wfGetDB( DB_SLAVE ); $this->maxCount = $dbr->selectField( $table, "MAX($field)", '', __METHOD__ ); $this->startTime = wfTime(); + $this->lastTime = $this->startTime; + $this->ID = getmypid(); } /** @@ -280,27 +289,46 @@ class BackupDumper { function showReport() { if ( $this->reporting ) { - $delta = wfTime() - $this->startTime; $now = wfTimestamp( TS_DB ); - if ( $delta ) { - $rate = $this->pageCount / $delta; - $revrate = $this->revCount / $delta; + $nowts = wfTime(); + $deltaAll = wfTime() - $this->startTime; + $deltaPart = wfTime() - $this->lastTime; + $this->pageCountPart = $this->pageCount - $this->pageCountLast; + $this->revCountPart = $this->revCount - $this->revCountLast; + + if ( $deltaAll ) { $portion = $this->revCount / $this->maxCount; - $eta = $this->startTime + $delta / $portion; + $eta = $this->startTime + $deltaAll / $portion; $etats = wfTimestamp( TS_DB, intval( $eta ) ); + $pageRate = $this->pageCount / $deltaAll; + $revRate = $this->revCount / $deltaAll; } else { - $rate = '-'; - $revrate = '-'; + $pageRate = '-'; + $revRate = '-'; $etats = '-'; } - $this->progress( sprintf( "%s: %s %d pages (%0.3f/sec), %d revs (%0.3f/sec), ETA %s [max %d]", - $now, wfWikiID(), $this->pageCount, $rate, $this->revCount, $revrate, $etats, $this->maxCount ) ); + if ( $deltaPart ) { + $pageRatePart = $this->pageCountPart / $deltaPart; + $revRatePart = $this->revCountPart / $deltaPart; + } else { + $pageRatePart = '-'; + $revRatePart = '-'; + } + $this->progress( sprintf( "%s: %s (ID %d) %d pages (%0.1f|%0.1f/sec all|curr), %d revs (%0.1f|%0.1f/sec all|curr), ETA %s [max %d]", + $now, wfWikiID(), $this->ID, $this->pageCount, $pageRate, $pageRatePart, $this->revCount, $revRate, $revRatePart, $etats, $this->maxCount ) ); + $this->lastTime = $nowts; + $this->revCountLast = $this->revCount; } } function progress( $string ) { fwrite( $this->stderr, $string . "\n" ); } + + function fatalError( $msg ) { + $this->progress( "$msg\n" ); + die(1); + } } class ExportProgressFilter extends DumpFilter { diff --git a/maintenance/backupPrefetch.inc b/maintenance/backupPrefetch.inc index 9d743137..93f75c65 100644 --- a/maintenance/backupPrefetch.inc +++ b/maintenance/backupPrefetch.inc @@ -2,6 +2,24 @@ /** * Helper class for the --prefetch option of dumpTextPass.php * + * Copyright © 2005 Brion Vibber <brion@pobox.com> + * http://www.mediawiki.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance */ @@ -33,7 +51,12 @@ class BaseDump { $this->infiles = explode(';',$infile); $this->reader = new XMLReader(); $infile = array_shift($this->infiles); - $this->reader->open( $infile ); + if (defined( 'LIBXML_PARSEHUGE' ) ) { + $this->reader->open( $infile, null, LIBXML_PARSEHUGE ); + } + else { + $this->reader->open( $infile ); + } } /** diff --git a/maintenance/benchmarks/Benchmarker.php b/maintenance/benchmarks/Benchmarker.php index 66789ea4..57fb8759 100644 --- a/maintenance/benchmarks/Benchmarker.php +++ b/maintenance/benchmarks/Benchmarker.php @@ -1,14 +1,29 @@ <?php /** - * Create a doxygen subgroup of Maintenance for benchmarks * @defgroup Benchmark Benchmark - * @ingroup Maintenance */ /** - * TODO: report PHP version, OS .. + * Create a doxygen subgroup of Maintenance for benchmarks + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @todo Report PHP version, OS .. * @file - * @ingroup Benchmark + * @ingroup Maintenance Benchmark */ require_once( dirname( __FILE__ ) . '/../Maintenance.php' ); diff --git a/maintenance/benchmarks/bench_HTTP_HTTPS.php b/maintenance/benchmarks/bench_HTTP_HTTPS.php index 13d15fce..0038b2d1 100644 --- a/maintenance/benchmarks/bench_HTTP_HTTPS.php +++ b/maintenance/benchmarks/bench_HTTP_HTTPS.php @@ -1,6 +1,24 @@ <?php /** * This come from r75429 message + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance * @author Platonides */ @@ -8,7 +26,8 @@ require_once( dirname( __FILE__ ) . '/Benchmarker.php' ); class bench_HTTP_HTTPS extends Benchmarker { public function __construct() { - parent::__construct(); + parent::__construct(); + $this->mDescription = "Benchmark HTTP request vs HTTPS request."; } public function execute() { diff --git a/maintenance/benchmarks/bench_delete_truncate.php b/maintenance/benchmarks/bench_delete_truncate.php new file mode 100644 index 00000000..9fe9bea9 --- /dev/null +++ b/maintenance/benchmarks/bench_delete_truncate.php @@ -0,0 +1,78 @@ +<?php + +require_once( dirname( __FILE__ ) . '/Benchmarker.php' ); + +class BenchmarkDeleteTruncate extends Benchmarker { + + public function __construct() { + parent::__construct(); + $this->mDescription = "Benchmarks SQL DELETE vs SQL TRUNCATE."; + } + + public function execute() { + $dbw = wfGetDB( DB_MASTER ); + + $test = $dbw->tableName( 'test' ); + $dbw->query( "CREATE TABLE IF NOT EXISTS /*_*/$test ( + test_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT, + text varbinary(255) NOT NULL +);" ); + + $this->insertData( $dbw ); + + $start = wfTime(); + + $this->delete( $dbw ); + + $end = wfTime(); + + echo "Delete: " . $end - $start; + echo "\r\n"; + + $this->insertData( $dbw ); + + $start = wfTime(); + + $this->truncate( $dbw ); + + $end = wfTime(); + + echo "Truncate: " . $end - $start; + echo "\r\n"; + + $dbw->dropTable( 'test' ); + } + + /** + * @param $dbw DatabaseBase + * @return void + */ + private function insertData( $dbw ) { + $range = range( 0, 1024 ); + $data = array(); + foreach( $range as $r ) { + $data[] = array( 'text' => $r ); + } + $dbw->insert( 'test', $data, __METHOD__ ); + } + + /** + * @param $dbw DatabaseBase + * @return void + */ + private function delete( $dbw ) { + $dbw->delete( 'text', '*', __METHOD__ ); + } + + /** + * @param $dbw DatabaseBase + * @return void + */ + private function truncate( $dbw ) { + $test = $dbw->tableName( 'test' ); + $dbw->query( "TRUNCATE TABLE $test" ); + } +} + +$maintClass = "BenchmarkDeleteTruncate"; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/benchmarks/bench_if_switch.php b/maintenance/benchmarks/bench_if_switch.php new file mode 100644 index 00000000..11c00b3c --- /dev/null +++ b/maintenance/benchmarks/bench_if_switch.php @@ -0,0 +1,88 @@ +<?php +/** + * This come from r75429 message + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance + * @author Platonides + */ + +require_once( dirname( __FILE__ ) . '/Benchmarker.php' ); +class bench_if_switch extends Benchmarker { + + public function __construct() { + parent::__construct(); + $this->mDescription = "Benchmark if elseif... versus switch case."; + } + + public function execute() { + $this->bench( array( + array( 'function' => array( $this, 'doElseIf' ) ), + array( 'function' => array( $this, 'doSwitch' ) ), + )); + print $this->getFormattedResults(); + } + + // bench function 1 + function doElseIf() { + $a = 'z'; + if( $a == 'a') {} + elseif( $a == 'b') {} + elseif( $a == 'c') {} + elseif( $a == 'd') {} + elseif( $a == 'e') {} + elseif( $a == 'f') {} + elseif( $a == 'g') {} + elseif( $a == 'h') {} + elseif( $a == 'i') {} + elseif( $a == 'j') {} + elseif( $a == 'k') {} + elseif( $a == 'l') {} + elseif( $a == 'm') {} + elseif( $a == 'n') {} + elseif( $a == 'o') {} + elseif( $a == 'p') {} + else {} + } + + // bench function 2 + function doSwitch() { + $a = 'z'; + switch( $a ) { + case 'b': break; + case 'c': break; + case 'd': break; + case 'e': break; + case 'f': break; + case 'g': break; + case 'h': break; + case 'i': break; + case 'j': break; + case 'k': break; + case 'l': break; + case 'm': break; + case 'n': break; + case 'o': break; + case 'p': break; + default: + } + } +} + +$maintClass = 'bench_if_switch'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/benchmarks/bench_strtr_str_replace.php b/maintenance/benchmarks/bench_strtr_str_replace.php new file mode 100644 index 00000000..ae576981 --- /dev/null +++ b/maintenance/benchmarks/bench_strtr_str_replace.php @@ -0,0 +1,50 @@ +<?php + +require_once( dirname( __FILE__ ) . '/Benchmarker.php' ); + +function bfNormalizeTitleStrTr( $str ) { + return strtr( $str, '_', ' ' ); +} + +function bfNormalizeTitleStrReplace( $str ) { + return str_replace( '_', ' ', $str ); +} + +class bench_strtr_str_replace extends Benchmarker { + + public function __construct() { + parent::__construct(); + $this->mDescription = "Benchmark for strtr() vs str_replace()."; + } + + public function execute() { + $this->bench( array( + array( 'function' => array( $this, 'benchstrtr' ) ), + array( 'function' => array( $this, 'benchstr_replace' ) ), + array( 'function' => array( $this, 'benchstrtr_indirect' ) ), + array( 'function' => array( $this, 'benchstr_replace_indirect' ) ), + )); + print $this->getFormattedResults(); + } + + function benchstrtr() { + strtr( "[[MediaWiki:Some_random_test_page]]", "_", " " ); + } + + function benchstr_replace() { + str_replace( "_", " ", "[[MediaWiki:Some_random_test_page]]"); + } + + + function benchstrtr_indirect() { + bfNormalizeTitleStrTr( "[[MediaWiki:Some_random_test_page]]" ); + } + + function benchstr_replace_indirect() { + bfNormalizeTitleStrReplace( "[[MediaWiki:Some_random_test_page]]" ); + } + +} + +$maintClass = 'bench_strtr_str_replace'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/benchmarks/bench_wfIsWindows.php b/maintenance/benchmarks/bench_wfIsWindows.php index 2f759e07..4c35221d 100644 --- a/maintenance/benchmarks/bench_wfIsWindows.php +++ b/maintenance/benchmarks/bench_wfIsWindows.php @@ -1,6 +1,24 @@ <?php /** * This come from r75429 message + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance * @author Platonides */ @@ -8,7 +26,8 @@ require_once( dirname( __FILE__ ) . '/Benchmarker.php' ); class bench_wfIsWindows extends Benchmarker { public function __construct() { - parent::__construct(); + parent::__construct(); + $this->mDescription = "Benchmark for wfIsWindows."; } public function execute() { diff --git a/maintenance/benchmarkPurge.php b/maintenance/benchmarks/benchmarkPurge.php index 8360ef85..4ab7aa10 100644 --- a/maintenance/benchmarkPurge.php +++ b/maintenance/benchmarks/benchmarkPurge.php @@ -21,13 +21,12 @@ * @ingroup Maintenance */ -require_once( dirname( __FILE__ ) . '/Maintenance.php' ); +require_once( dirname( __FILE__ ) . '/Benchmarker.php' ); -class BenchmarkPurge extends Maintenance { +class BenchmarkPurge extends Benchmarker { public function __construct() { parent::__construct(); - $this->addOption( "count", "How many URLs to feed to Squid for purging", false, true ); $this->mDescription = "Benchmark the Squid purge functions."; } @@ -104,4 +103,4 @@ class BenchmarkPurge extends Maintenance { } $maintClass = "BenchmarkPurge"; -require_once( DO_MAINTENANCE ); +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/cdb.php b/maintenance/cdb.php new file mode 100644 index 00000000..270f7a60 --- /dev/null +++ b/maintenance/cdb.php @@ -0,0 +1,119 @@ +<?php +/** + * cdb inspector tool + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @todo document + * @ingroup Maintenance + */ + +/** */ +require_once( dirname( __FILE__ ) . '/commandLine.inc' ); + +function cdbShowHelp( $command ) { + $commandList = array( + 'load' => 'load a cdb file for reading', + 'get' => 'get a value for a key', + 'exit' => 'exit cdb', + 'quit' => 'exit cdb', + 'help' => 'help about a command', + ); + if ( !$command ) { + $command = 'fullhelp'; + } + if ( $command === 'fullhelp' ) { + $max_cmd_len = max( array_map( 'strlen', array_keys( $commandList ) ) ); + foreach ( $commandList as $cmd => $desc ) { + printf( "%-{$max_cmd_len}s: %s\n", $cmd, $desc ); + } + } elseif ( isset( $commandList[$command] ) ) { + print "$command: $commandList[$command]\n"; + } else { + print "$command: command does not exist or no help for it\n"; + } +} + +do { + $bad = false; + $showhelp = false; + $quit = false; + static $fileHandle; + + $line = Maintenance::readconsole(); + if ( $line === false ) exit; + + $args = explode( ' ', $line ); + $command = array_shift( $args ); + + // process command + switch ( $command ) { + case 'help': + // show an help message + cdbShowHelp( array_shift( $args ) ); + break; + case 'load': + if( !isset( $args[0] ) ) { + print "Need a filename there buddy\n"; + break; + } + $file = $args[0]; + print "Loading cdb file $file..."; + $fileHandle = CdbReader::open( $file ); + if( !$fileHandle ) { + print "not a cdb file or unable to read it\n"; + } else { + print "ok\n"; + } + break; + case 'get': + if( !$fileHandle ) { + print "Need to load a cdb file first\n"; + break; + } + if( !isset( $args[0] ) ) { + print "Need to specify a key, Luke\n"; + break; + } + $res = $fileHandle->get( $args[0] ); + if ( $res === false ) { + print "No such key/value pair\n"; + } elseif ( is_string( $res ) ) { + print "$res\n"; + } else { + var_dump( $res ); + } + break; + case 'quit': + case 'exit': + $quit = true; + break; + + default: + $bad = true; + } // switch() end + + if ( $bad ) { + if ( $command ) { + print "Bad command\n"; + } + } else { + if ( function_exists( 'readline_add_history' ) ) { + readline_add_history( $line ); + } + } +} while ( !$quit ); diff --git a/maintenance/changePassword.php b/maintenance/changePassword.php index 568952b9..ef87dfbd 100644 --- a/maintenance/changePassword.php +++ b/maintenance/changePassword.php @@ -29,13 +29,20 @@ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); class ChangePassword extends Maintenance { public function __construct() { parent::__construct(); - $this->addOption( "user", "The username to operate on", true, true ); + $this->addOption( "user", "The username to operate on", false, true ); + $this->addOption( "userid", "The user id to operate on", false, true ); $this->addOption( "password", "The password to use", true, true ); $this->mDescription = "Change a user's password"; } public function execute() { - $user = User::newFromName( $this->getOption( 'user' ) ); + if ( $this->hasOption( "user" ) ) { + $user = User::newFromName( $this->getOption( 'user' ) ); + } elseif ( $this->hasOption( "userid" ) ) { + $user = User::newFromId( $this->getOption( 'userid' ) ); + } else { + $this->error( "A \"user\" or \"userid\" must be set to change the password for" , true ); + } if ( !$user->getId() ) { $this->error( "No such user: " . $this->getOption( 'user' ), true ); } diff --git a/maintenance/checkBadRedirects.php b/maintenance/checkBadRedirects.php index 52bfa65a..bac2ff69 100644 --- a/maintenance/checkBadRedirects.php +++ b/maintenance/checkBadRedirects.php @@ -27,7 +27,7 @@ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); class CheckBadRedirects extends Maintenance { public function __construct() { parent::__construct(); - $this->mDescription = "Look for bad redirects"; + $this->mDescription = "Check for bad redirects"; } public function execute() { @@ -39,8 +39,8 @@ class CheckBadRedirects extends Maintenance { array( 'page_is_redirect' => 1 ) ); $count = $result->numRows(); - $this->output( "Found $count total redirects.\n" . - "Looking for bad redirects:\n\n" ); + $this->output( "Found $count redirects.\n" . + "Checking for bad redirects:\n\n" ); foreach ( $result as $row ) { $title = Title::makeTitle( $row->page_namespace, $row->page_title ); @@ -52,7 +52,7 @@ class CheckBadRedirects extends Maintenance { } } } - $this->output( "\ndone.\n" ); + $this->output( "\nDone.\n" ); } } diff --git a/maintenance/checkSyntax.php b/maintenance/checkSyntax.php index 396cac5f..83f73be5 100644 --- a/maintenance/checkSyntax.php +++ b/maintenance/checkSyntax.php @@ -101,7 +101,9 @@ class CheckSyntax extends Maintenance { return; // process only this path } elseif ( $this->hasOption( 'list-file' ) ) { $file = $this->getOption( 'list-file' ); - $f = @fopen( $file, 'r' ); + wfSuppressWarnings(); + $f = fopen( $file, 'r' ); + wfRestoreWarnings(); if ( !$f ) { $this->error( "Can't open file $file\n", true ); } @@ -137,7 +139,7 @@ class CheckSyntax extends Maintenance { // Don't just put $IP, because the recursive dir thingie goes into all subdirs $dirs = array( $IP . '/includes', - $IP . '/config', + $IP . '/mw-config', $IP . '/languages', $IP . '/maintenance', $IP . '/skins', @@ -275,7 +277,9 @@ class CheckSyntax extends Maintenance { } $text = file_get_contents( $file ); + $tokens = token_get_all( $text ); + $this->checkEvilToken( $file, $tokens, '@', 'Error supression operator (@)'); $this->checkRegex( $file, $text, '/^[\s\r\n]+<\?/', 'leading whitespace' ); $this->checkRegex( $file, $text, '/\?>[\s\r\n]*$/', 'trailing ?>' ); $this->checkRegex( $file, $text, '/^[\xFF\xFE\xEF]/', 'byte-order mark' ); @@ -292,6 +296,18 @@ class CheckSyntax extends Maintenance { $this->mWarnings[$file][] = $desc; $this->output( "Warning in file $file: $desc found.\n" ); } + + private function checkEvilToken( $file, $tokens, $evilToken, $desc ) { + if ( !in_array( $evilToken, $tokens ) ) { + return; + } + + if ( !isset( $this->mWarnings[$file] ) ) { + $this->mWarnings[$file] = array(); + } + $this->mWarnings[$file][] = $desc; + $this->output( "Warning in file $file: $desc found.\n" ); + } } $maintClass = "CheckSyntax"; diff --git a/maintenance/cleanupImages.php b/maintenance/cleanupImages.php index b25b9bbe..c8e90958 100644 --- a/maintenance/cleanupImages.php +++ b/maintenance/cleanupImages.php @@ -150,7 +150,7 @@ class ImageCleanup extends TableCleanup { $this->output( "DRY RUN: would rename $path to $finalPath\n" ); } else { $this->output( "renaming $path to $finalPath\n" ); - // XXX: should this use File::move()? FIXME? + // @todo FIXME: Should this use File::move()? $db->begin(); $db->update( 'image', array( 'img_name' => $final ), diff --git a/maintenance/cleanupSpam.php b/maintenance/cleanupSpam.php index 39abe4c5..8561281d 100644 --- a/maintenance/cleanupSpam.php +++ b/maintenance/cleanupSpam.php @@ -123,7 +123,6 @@ class CleanupSpam extends Maintenance { $article->doEdit( $rev->getText(), wfMsg( 'spam_reverting', $domain ), EDIT_UPDATE ); } $dbw->commit(); - wfDoUpdates(); } } } diff --git a/maintenance/cleanupTable.inc b/maintenance/cleanupTable.inc index 67a32510..f63c6d74 100644 --- a/maintenance/cleanupTable.inc +++ b/maintenance/cleanupTable.inc @@ -101,7 +101,8 @@ class TableCleanup extends Maintenance { } $table = $params['table']; - $count = $dbr->selectField( $table, 'count(*)', $params['conds'], __METHOD__ ); + // count(*) would melt the DB for huge tables, we can estimate here + $count = $dbr->estimateRowCount( $table, '*', '', __METHOD__ ); $this->init( $count, $table ); $this->output( "Processing $table...\n" ); diff --git a/maintenance/cleanupUploadStash.php b/maintenance/cleanupUploadStash.php new file mode 100644 index 00000000..4b47f397 --- /dev/null +++ b/maintenance/cleanupUploadStash.php @@ -0,0 +1,75 @@ +<?php +/** + * Remove old or broken uploads from temporary uploaded file storage, + * clean up associated database records + * + * Copyright © 2011, Wikimedia Foundation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author Ian Baker <ibaker@wikimedia.org> + * @ingroup Maintenance + */ + +require_once( dirname( __FILE__ ) . '/Maintenance.php' ); + +class UploadStashCleanup extends Maintenance { + + public function __construct() { + parent::__construct(); + $this->mDescription = "Clean up abandoned files in temporary uploaded file stash"; + } + + public function execute() { + $repo = RepoGroup::singleton()->getLocalRepo(); + + $dbr = $repo->getSlaveDb(); + + $this->output( "Getting list of files to clean up...\n" ); + $res = $dbr->select( + 'uploadstash', + 'us_key', + 'us_timestamp < ' . $dbr->timestamp( time() - UploadStash::REPO_AGE * 3600 ), + __METHOD__ + ); + + if( !is_object( $res ) || $res->numRows() == 0 ) { + // nothing to do. + return false; + } + + // finish the read before starting writes. + $keys = array(); + foreach($res as $row) { + array_push( $keys, $row->us_key ); + } + + $this->output( 'Removing ' . count($keys) . " file(s)...\n" ); + // this could be done some other, more direct/efficient way, but using + // UploadStash's own methods means it's less likely to fall accidentally + // out-of-date someday + $stash = new UploadStash( $repo ); + + foreach( $keys as $key ) { + $stash->getFile( $key, true ); + $stash->removeFileNoAuth( $key ); + } + } +} + +$maintClass = "UploadStashCleanup"; +require_once( RUN_MAINTENANCE_IF_MAIN );
\ No newline at end of file diff --git a/maintenance/commandLine.inc b/maintenance/commandLine.inc index 4ae753ba..f57c0b67 100644 --- a/maintenance/commandLine.inc +++ b/maintenance/commandLine.inc @@ -1,8 +1,26 @@ <?php - /** - * Backwards-compatibility wrapper for old-style maintenance scripts + * Backwards-compatibility wrapper for old-style maintenance scripts. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance */ + require( dirname( __FILE__ ) . '/Maintenance.php' ); global $optionsWithArgs; diff --git a/maintenance/compareParsers.php b/maintenance/compareParsers.php new file mode 100644 index 00000000..4fe4e4d0 --- /dev/null +++ b/maintenance/compareParsers.php @@ -0,0 +1,142 @@ +<?php +/** + * Take page text out of an XML dump file and render basic HTML out to files. + * This is *NOT* suitable for publishing or offline use; it's intended for + * running comparative tests of parsing behavior using real-world data. + * + * Templates etc are pulled from the local wiki database, not from the dump. + * + * Copyright (C) 2011 Platonides - http://www.mediawiki.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance + */ + +require_once( dirname( __FILE__ ) . '/dumpIterator.php' ); + +class CompareParsers extends DumpIterator { + + private $count = 0; + + public function __construct() { + parent::__construct(); + $this->saveFailed = false; + $this->mDescription = "Run a file or dump with several parsers"; + $this->addOption( 'parser1', 'The first parser to compare.', true, true ); + $this->addOption( 'parser2', 'The second parser to compare.', true, true ); + $this->addOption( 'tidy', 'Run tidy on the articles.', false, false ); + $this->addOption( 'save-failed', 'Folder in which articles which differ will be stored.', false, true ); + $this->addOption( 'show-diff', 'Show a diff of the two renderings.', false, false ); + $this->addOption( 'diff-bin', 'Binary to use for diffing (can also be provided by DIFF env var).', false, false ); + $this->addOption( 'strip-parameters', 'Remove parameters of html tags to increase readability.', false, false ); + $this->addOption( 'show-parsed-output', 'Show the parsed html if both Parsers give the same output.', false, false ); + } + + public function checkOptions() { + if ( $this->hasOption('save-failed') ) { + $this->saveFailed = $this->getOption('save-failed'); + } + + $this->stripParametersEnabled = $this->hasOption( 'strip-parameters' ); + $this->showParsedOutput = $this->hasOption( 'show-parsed-output' ); + + $this->showDiff = $this->hasOption( 'show-diff' ); + if ( $this->showDiff ) { + $bin = $this->getOption( 'diff-bin', getenv( 'DIFF' ) ); + if ( $bin != '' ) { + global $wgDiff; + $wgDiff = $bin; + } + } + + $user = new User(); + $this->options = ParserOptions::newFromUser( $user ); + + if ( $this->hasOption( 'tidy' ) ) { + global $wgUseTidy; + if ( !$wgUseTidy ) { + $this->error( 'Tidy was requested but $wgUseTidy is not set in LocalSettings.php', true ); + } + $this->options->setTidy( true ); + } + + $this->failed = 0; + } + + public function conclusions() { + $this->error( "{$this->failed} failed revisions out of {$this->count}" ); + if ($this->count > 0) + $this->output( " (" . ( $this->failed / $this->count ) . "%)\n" ); + } + + function stripParameters( $text ) { + if ( !$this->stripParametersEnabled ) { + return $text; + } + return preg_replace( '/(<a) [^>]+>/', '$1>', $text ); + } + + /** + * Callback function for each revision, parse with both parsers and compare + * @param $rev Revision + */ + public function processRevision( $rev ) { + $title = $rev->getTitle(); + + $parser1Name = $this->getOption( 'parser1' ); + $parser2Name = $this->getOption( 'parser2' ); + + self::checkParserLocally( $parser1Name ); + self::checkParserLocally( $parser2Name ); + + $parser1 = new $parser1Name(); + $parser2 = new $parser2Name(); + + $output1 = $parser1->parse( $rev->getText(), $title, $this->options ); + $output2 = $parser2->parse( $rev->getText(), $title, $this->options ); + + if ( $output1->getText() != $output2->getText() ) { + $this->failed++; + $this->error( "Parsing for {$title->getPrefixedText()} differs\n" ); + + if ( $this->saveFailed ) { + file_put_contents( $this->saveFailed . '/' . rawurlencode( $title->getPrefixedText() ) . ".txt", $rev->getText()); + } + if ( $this->showDiff ) { + $this->output( wfDiff( $this->stripParameters( $output1->getText() ), $this->stripParameters( $output2->getText() ), '' ) ); + } + } else { + $this->output( $title->getPrefixedText() . "\tOK\n" ); + if ( $this->showParsedOutput ) { + $this->output( $this->stripParameters( $output1->getText() ) ); + } + } + } + + private static function checkParserLocally( $parserName ) { + /* Look for the parser in a file appropiately named in the current folder */ + if ( !class_exists( $parserName ) && file_exists( "$parserName.php" ) ) { + global $wgAutoloadClasses; + $wgAutoloadClasses[ $parserName ] = realpath( '.' ) . "/$parserName.php"; + } + } + +} + +$maintClass = "CompareParsers"; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/convertLinks.php b/maintenance/convertLinks.php index b7a55d57..85ef5c8c 100644 --- a/maintenance/convertLinks.php +++ b/maintenance/convertLinks.php @@ -193,12 +193,12 @@ This gives a huge speed improvement for very large links tables which are MyISAM if ( $overwriteLinksTable ) { # Check for existing links_backup, and delete it if it exists. $this->output( "Dropping backup links table if it exists..." ); - $dbw->query( "DROP TABLE IF EXISTS $links_backup", DB_MASTER ); + $dbw->query( "DROP TABLE IF EXISTS $links_backup", __METHOD__ ); $this->output( " done.\n" ); # Swap in the new table, and move old links table to links_backup $this->output( "Swapping tables '$links' to '$links_backup'; '$links_temp' to '$links'..." ); - $dbw->query( "RENAME TABLE links TO $links_backup, $links_temp TO $links", DB_MASTER ); + $dbw->query( "RENAME TABLE links TO $links_backup, $links_temp TO $links", __METHOD__ ); $this->output( " done.\n\n" ); $dbw->close(); diff --git a/maintenance/convertUserOptions.php b/maintenance/convertUserOptions.php index 278d40ff..f46f710d 100644 --- a/maintenance/convertUserOptions.php +++ b/maintenance/convertUserOptions.php @@ -46,7 +46,7 @@ class ConvertUserOptions extends Maintenance { $id = $this->convertOptionBatch( $res, $dbw ); $dbw->commit(); - wfWaitForSlaves( 1 ); + wfWaitForSlaves(); if ( $id ) $this->output( "--Converted to ID $id\n" ); diff --git a/maintenance/createAndPromote.php b/maintenance/createAndPromote.php index 8bff284a..0d7de9a9 100644 --- a/maintenance/createAndPromote.php +++ b/maintenance/createAndPromote.php @@ -28,7 +28,8 @@ class CreateAndPromote extends Maintenance { public function __construct() { parent::__construct(); - $this->mDescription = "Create a new user account with administrator rights"; + $this->mDescription = "Create a new user account"; + $this->addOption( "sysop", "Grant the account sysop rights" ); $this->addOption( "bureaucrat", "Grant the account bureaucrat rights" ); $this->addArg( "username", "Username of new user" ); $this->addArg( "password", "Password to set" ); @@ -59,9 +60,12 @@ class CreateAndPromote extends Maintenance { $user->saveSettings(); # Promote user - $user->addGroup( 'sysop' ); - if ( $this->hasOption( 'bureaucrat' ) ) + if ( $this->hasOption( 'sysop' ) ) { + $user->addGroup( 'sysop' ); + } + if ( $this->hasOption( 'bureaucrat' ) ) { $user->addGroup( 'bureaucrat' ); + } # Increment site_stats.ss_users $ssu = new SiteStatsUpdate( 0, 0, 0, 0, 1 ); diff --git a/maintenance/deleteArchivedFiles.inc b/maintenance/deleteArchivedFiles.inc index e0ac225e..68394b4a 100644 --- a/maintenance/deleteArchivedFiles.inc +++ b/maintenance/deleteArchivedFiles.inc @@ -45,9 +45,12 @@ class DeleteArchivedFilesImplementation { array( 'FOR UPDATE' ) ); if ( $path && file_exists( $path ) && !$inuse ) { - unlink( $path ); // delete - $count++; - $dbw->query( "DELETE FROM $tbl_arch WHERE fa_id = $id" ); + if( unlink( $path ) ) { // delete + $count++; + $dbw->query( "DELETE FROM $tbl_arch WHERE fa_id = $id" ); + } else { + $output->handleOutput( "Unable to remove file $path, skipping\n" ); + } } else { $output->handleOutput( "Notice - file '$key' not found in group '$group'\n" ); if ( $force ) { diff --git a/maintenance/deleteArchivedRevisions.inc b/maintenance/deleteArchivedRevisions.inc index 10bd4cff..7628985c 100644 --- a/maintenance/deleteArchivedRevisions.inc +++ b/maintenance/deleteArchivedRevisions.inc @@ -54,4 +54,4 @@ class DeleteArchivedRevisionsImplementation { $maint->purgeRedundantText( true ); } } -}
\ No newline at end of file +} diff --git a/maintenance/deleteBatch.php b/maintenance/deleteBatch.php index c8bb4803..eb2e1f34 100644 --- a/maintenance/deleteBatch.php +++ b/maintenance/deleteBatch.php @@ -87,8 +87,10 @@ class DeleteBatch extends Maintenance { if ( $page->getNamespace() == NS_FILE ) { $art = new ImagePage( $page ); $img = wfFindFile( $art->mTitle ); - if ( !$img || !$img->delete( $reason ) ) { - $this->output( "FAILED to delete image file... " ); + if ( !$img + || !$img->isLocal() + || !$img->delete( $reason ) ) { + $this->output( " FAILED to delete image file... " ); } } else { $art = new Article( $page ); @@ -96,7 +98,7 @@ class DeleteBatch extends Maintenance { $success = $art->doDeleteArticle( $reason ); $dbw->commit(); if ( $success ) { - $this->output( "\n" ); + $this->output( " Deleted!\n" ); } else { $this->output( " FAILED to delete article\n" ); } @@ -104,7 +106,7 @@ class DeleteBatch extends Maintenance { if ( $interval ) { sleep( $interval ); } - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } } } diff --git a/maintenance/deleteDefaultMessages.php b/maintenance/deleteDefaultMessages.php index fc482ac0..a33921b1 100644 --- a/maintenance/deleteDefaultMessages.php +++ b/maintenance/deleteDefaultMessages.php @@ -63,9 +63,7 @@ class DeleteDefaultMessages extends Maintenance { $dbw = wfGetDB( DB_MASTER ); foreach ( $res as $row ) { - if ( function_exists( 'wfWaitForSlaves' ) ) { - wfWaitForSlaves( 5 ); - } + wfWaitForSlaves(); $dbw->ping(); $title = Title::makeTitle( $row->page_namespace, $row->page_title ); $article = new Article( $title ); diff --git a/maintenance/deleteOldRevisions.php b/maintenance/deleteOldRevisions.php index ba76e9e9..2cb347fc 100644 --- a/maintenance/deleteOldRevisions.php +++ b/maintenance/deleteOldRevisions.php @@ -60,6 +60,7 @@ class DeleteOldRevisions extends Maintenance { # Get "active" revisions from the page table $this->output( "Searching for active revisions..." ); $res = $dbw->query( "SELECT page_latest FROM $tbl_pag{$pageIdClause}" ); + $cur = array(); foreach ( $res as $row ) { $cur[] = $row->page_latest; } diff --git a/maintenance/deleteOrphanedRevisions.php b/maintenance/deleteOrphanedRevisions.php index e972d1fa..c322320b 100644 --- a/maintenance/deleteOrphanedRevisions.php +++ b/maintenance/deleteOrphanedRevisions.php @@ -75,7 +75,7 @@ class DeleteOrphanedRevisions extends Maintenance { * Do this inside a transaction * * @param $id Array of revision id values - * @param $dbw Database class (needs to be a master) + * @param $dbw DatabaseBase class (needs to be a master) */ private function deleteRevs( $id, &$dbw ) { if ( !is_array( $id ) ) diff --git a/maintenance/deleteSelfExternals.php b/maintenance/deleteSelfExternals.php index db23e92c..4e9f54f2 100644 --- a/maintenance/deleteSelfExternals.php +++ b/maintenance/deleteSelfExternals.php @@ -39,7 +39,7 @@ class DeleteSelfExternals extends Maintenance { $this->output( "Deleting self externals from $wgServer\n" ); $db = wfGetDB( DB_MASTER ); while ( 1 ) { - wfWaitForSlaves( 2 ); + wfWaitForSlaves(); $db->commit(); $q = $db->limitResult( "DELETE /* deleteSelfExternals */ FROM externallinks WHERE el_to" . $db->buildLike( $wgServer . '/', $db->anyString() ), $this->mBatchSize ); diff --git a/maintenance/doMaintenance.php b/maintenance/doMaintenance.php index a9f5fae7..44e00032 100644 --- a/maintenance/doMaintenance.php +++ b/maintenance/doMaintenance.php @@ -53,52 +53,49 @@ $maintenance->setup(); // to $maintenance->mSelf. Keep that here for b/c $self = $maintenance->getName(); -# Setup the profiler -global $IP; -if ( file_exists( "$IP/StartProfiler.php" ) ) { - require_once( "$IP/StartProfiler.php" ); +// Detect compiled mode +if ( isset( $_SERVER['MW_COMPILED'] ) ) { + define( 'MW_COMPILED', 1 ); } else { - require_once( "$IP/includes/ProfilerStub.php" ); + # Get the MWInit class + require_once( "$IP/includes/Init.php" ); + require_once( "$IP/includes/AutoLoader.php" ); } +# Stub the profiler +require_once( MWInit::compiledPath( 'includes/profiler/Profiler.php' ) ); + // Some other requires -require_once( "$IP/includes/AutoLoader.php" ); -require_once( "$IP/includes/Defines.php" ); -require_once( "$IP/includes/DefaultSettings.php" ); +if ( !defined( 'MW_COMPILED' ) ) { + require_once( "$IP/includes/Defines.php" ); +} +require_once( MWInit::compiledPath( 'includes/DefaultSettings.php' ) ); if ( defined( 'MW_CONFIG_CALLBACK' ) ) { # Use a callback function to configure MediaWiki - $callback = MW_CONFIG_CALLBACK; - # PHP 5.1 doesn't support "class::method" for call_user_func, so split it - if ( strpos( $callback, '::' ) !== false ) { - $callback = explode( '::', $callback, 2 ); - } - call_user_func( $callback ); -} elseif ( file_exists( "$IP/wmf-config/wikimedia-mode" ) ) { + MWFunction::call( MW_CONFIG_CALLBACK ); +} elseif ( file_exists( "$IP/../wmf-config/wikimedia-mode" ) ) { // Load settings, using wikimedia-mode if needed - // Fixme: replace this hack with general farm-friendly code - # TODO FIXME! Wikimedia-specific stuff needs to go away to an ext + // @todo FIXME: Replace this hack with general farm-friendly code + # @todo FIXME: Wikimedia-specific stuff needs to go away to an ext # Maybe a hook? global $cluster; - $wgWikiFarm = true; $cluster = 'pmtpa'; - require_once( "$IP/includes/SiteConfiguration.php" ); - require( "$IP/wmf-config/wgConf.php" ); + require( MWInit::interpretedPath( '../wmf-config/wgConf.php' ) ); $maintenance->loadWikimediaSettings(); - require( $IP . '/wmf-config/CommonSettings.php' ); + require( MWInit::interpretedPath( '../wmf-config/CommonSettings.php' ) ); } else { require_once( $maintenance->loadSettings() ); } if ( $maintenance->getDbType() === Maintenance::DB_ADMIN && - is_readable( "$IP/AdminSettings.php" ) ) + is_readable( "$IP/AdminSettings.php" ) ) { - require( "$IP/AdminSettings.php" ); + require( MWInit::interpretedPath( 'AdminSettings.php' ) ); } $maintenance->finalSetup(); // Some last includes -require_once( "$IP/includes/Setup.php" ); -require_once( "$IP/maintenance/install-utils.inc" ); +require_once( MWInit::compiledPath( 'includes/Setup.php' ) ); // Much much faster startup than creating a title object $wgTitle = null; diff --git a/maintenance/dumpBackup.php b/maintenance/dumpBackup.php index 90e8f72f..15189261 100644 --- a/maintenance/dumpBackup.php +++ b/maintenance/dumpBackup.php @@ -27,7 +27,7 @@ $originalDir = getcwd(); -$optionsWithArgs = array( 'pagelist', 'start', 'end' ); +$optionsWithArgs = array( 'pagelist', 'start', 'end', 'revstart', 'revend'); require_once( dirname( __FILE__ ) . '/commandLine.inc' ); require_once( 'backup.inc' ); @@ -44,7 +44,8 @@ if ( isset( $options['pagelist'] ) ) { $pages = file( $options['pagelist'] ); chdir( $olddir ); if ( $pages === false ) { - wfDie( "Unable to open file {$options['pagelist']}\n" ); + echo( "Unable to open file {$options['pagelist']}\n" ); + die(1); } $pages = array_map( 'trim', $pages ); $dumper->pages = array_filter( $pages, create_function( '$x', 'return $x !== "";' ) ); @@ -56,9 +57,17 @@ if ( isset( $options['start'] ) ) { if ( isset( $options['end'] ) ) { $dumper->endId = intval( $options['end'] ); } + +if ( isset( $options['revstart'] ) ) { + $dumper->revStartId = intval( $options['revstart'] ); +} +if ( isset( $options['revend'] ) ) { + $dumper->revEndId = intval( $options['revend'] ); +} $dumper->skipHeader = isset( $options['skip-header'] ); $dumper->skipFooter = isset( $options['skip-footer'] ); $dumper->dumpUploads = isset( $options['uploads'] ); +$dumper->dumpUploadFileContents = isset( $options['include-files'] ); $textMode = isset( $options['stub'] ) ? WikiExporter::STUB : WikiExporter::TEXT; @@ -70,6 +79,8 @@ if ( isset( $options['full'] ) ) { $dumper->dump( WikiExporter::STABLE, $textMode ); } elseif ( isset( $options['logs'] ) ) { $dumper->dump( WikiExporter::LOGS ); +} elseif ( isset($options['revrange'] ) ) { + $dumper->dump( WikiExporter::RANGE, $textMode ); } else { $dumper->progress( <<<ENDS This script dumps the wiki page or logging database into an @@ -85,7 +96,8 @@ Actions: --stable Stable versions of pages? --pagelist=<file> Where <file> is a list of page titles to be dumped - + --revrange Dump specified range of revisions, requires + revstart and revend options. Options: --quiet Don't dump status reports to stderr. --report=n Report position and speed after every n pages processed. @@ -93,10 +105,13 @@ Options: --server=h Force reading from MySQL server h --start=n Start from page_id or log_id n --end=n Stop before page_id or log_id n (exclusive) + --revstart=n Start from rev_id n + --revend=n Stop before rev_id n (exclusive) --skip-header Don't output the <mediawiki> header --skip-footer Don't output the </mediawiki> footer --stub Don't perform old_text lookups; for 2-pass dump - --uploads Include upload records (experimental) + --uploads Include upload records without files + --include-files Include files within the XML stream --conf=<file> Use the specified configuration file (LocalSettings.php) --wiki=<wiki> Only back up the specified <wiki> diff --git a/maintenance/dumpInterwiki.php b/maintenance/dumpInterwiki.php index 4a4b6791..217afd88 100644 --- a/maintenance/dumpInterwiki.php +++ b/maintenance/dumpInterwiki.php @@ -3,30 +3,28 @@ * Build constant slightly compact database of interwiki prefixes * Wikimedia specific! * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @todo document * @ingroup Maintenance * @ingroup Wikimedia */ -/** - * @todo document - * @ingroup Maintenance - */ -class Site { - var $suffix, $lateral, $url; - - function __construct( $s, $l, $u ) { - $this->suffix = $s; - $this->lateral = $l; - $this->url = $u; - } - - function getURL( $lang ) { - $xlang = str_replace( '_', '-', $lang ); - return "http://$xlang.{$this->url}/wiki/\$1"; - } -} +require_once( dirname( __FILE__ ) . '/Site.php' ); require_once( dirname( __FILE__ ) . '/Maintenance.php' ); @@ -39,6 +37,7 @@ class DumpInterwiki extends Maintenance { $this->addOption( 'dblist', 'File with one db per line', false, true ); $this->addOption( 'specialdbs', "File with one 'special' db per line", false, true ); $this->addOption( 'o', 'Cdb output file', false, true ); + $this->addOption( 'protocolrelative', 'Output wikimedia interwiki urls as protocol relative', false, false ); } function execute() { @@ -57,6 +56,12 @@ class DumpInterwiki extends Maintenance { $this->dbFile = false; } + if ( $this->hasOption( 'protocolrelative' ) ) { + $this->urlprotocol = ''; + } else { + $this->urlprotocol = 'http:'; + } + $this->getRebuildInterwikiDump(); } @@ -78,9 +83,9 @@ class DumpInterwiki extends Maintenance { # Extra interwiki links that can't be in the intermap for some reason $extraLinks = array( - array( 'm', 'http://meta.wikimedia.org/wiki/$1', 1 ), - array( 'meta', 'http://meta.wikimedia.org/wiki/$1', 1 ), - array( 'sep11', 'http://sep11.wikipedia.org/wiki/$1', 1 ), + array( 'm', $this->urlprotocol . '//meta.wikimedia.org/wiki/$1', 1 ), + array( 'meta', $this->urlprotocol . '//meta.wikimedia.org/wiki/$1', 1 ), + array( 'sep11', $this->urlprotocol . '//sep11.wikipedia.org/wiki/$1', 1 ), ); # Language aliases, usually configured as redirects to the real wiki in apache @@ -128,6 +133,13 @@ class DumpInterwiki extends Maintenance { $url = $matches[2]; if ( preg_match( '/(wikipedia|wiktionary|wikisource|wikiquote|wikibooks|wikimedia)\.org/', $url ) ) { + if ( $this->hasOption( 'protocolrelative' ) ) { + if ( substr( $url, 0, 5 ) == 'http:' ) { + $url = substr( $url, 5 ); + } else if ( substr( $url, 0, 6 ) == 'https:' ) { + $url = substr( $url, 6 ); + } + } $local = 1; } else { $local = 0; @@ -157,7 +169,7 @@ class DumpInterwiki extends Maintenance { # Links to multilanguage sites foreach ( $sites as $targetSite ) { $this->makeLink( array( 'iw_prefix' => $targetSite->lateral, - 'iw_url' => $targetSite->getURL( 'en' ), + 'iw_url' => $targetSite->getURL( 'en', $this->urlprotocol ), 'iw_local' => 1 ), $db ); } } else { @@ -181,14 +193,14 @@ class DumpInterwiki extends Maintenance { foreach ( $sites as $targetSite ) { if ( $targetSite->suffix != $site->suffix ) { $this->makeLink( array( 'iw_prefix' => $targetSite->lateral, - 'iw_url' => $targetSite->getURL( $lang ), + 'iw_url' => $targetSite->getURL( $lang, $this->urlprotocol ), 'iw_local' => 1 ), $db ); } } if ( $site->suffix == "wiki" ) { $this->makeLink( array( 'iw_prefix' => 'w', - 'iw_url' => "http://en.wikipedia.org/wiki/$1", + 'iw_url' => $this->urlprotocol . "//en.wikipedia.org/wiki/$1", 'iw_local' => 1 ), $db ); } @@ -205,12 +217,12 @@ class DumpInterwiki extends Maintenance { function makeLanguageLinks( &$site, $source ) { # Actual languages with their own databases foreach ( $this->langlist as $targetLang ) { - $this->makeLink( array( $targetLang, $site->getURL( $targetLang ), 1 ), $source ); + $this->makeLink( array( $targetLang, $site->getURL( $targetLang, $this->urlprotocol ), 1 ), $source ); } # Language aliases foreach ( $this->languageAliases as $alias => $lang ) { - $this->makeLink( array( $alias, $site->getURL( $lang ), 1 ), $source ); + $this->makeLink( array( $alias, $site->getURL( $lang, $this->urlprotocol ), 1 ), $source ); } } diff --git a/maintenance/dumpIterator.php b/maintenance/dumpIterator.php new file mode 100644 index 00000000..470bc56e --- /dev/null +++ b/maintenance/dumpIterator.php @@ -0,0 +1,167 @@ +<?php +/** + * Take page text out of an XML dump file and perform some operation on it. + * Used as a base class for CompareParsers and PreprocessDump. + * We implement below the simple task of searching inside a dump. + * + * Copyright (C) 2011 Platonides - http://www.mediawiki.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance + */ + +require_once( dirname( __FILE__ ) . '/Maintenance.php' ); + +abstract class DumpIterator extends Maintenance { + + private $count = 0; + private $startTime; + + public function __construct() { + parent::__construct(); + $this->mDescription = "Does something with a dump"; + $this->addOption( 'file', 'File with text to run.', false, true ); + $this->addOption( 'dump', 'XML dump to execute all revisions.', false, true ); + $this->addOption( 'from', 'Article from XML dump to start from.', false, true ); + } + + public function execute() { + if (! ( $this->hasOption('file') ^ $this->hasOption('dump') ) ) { + $this->error("You must provide a file or dump", true); + } + + $this->checkOptions(); + + if ( $this->hasOption('file') ) { + $revision = new WikiRevision; + + $revision->setText( file_get_contents( $this->getOption( 'file' ) ) ); + $revision->setTitle( Title::newFromText( rawurldecode( basename( $this->getOption( 'file' ), '.txt' ) ) ) ); + $this->handleRevision( $revision ); + return; + } + + $this->startTime = wfTime(); + + if ( $this->getOption('dump') == '-' ) { + $source = new ImportStreamSource( $this->getStdin() ); + } else { + $this->error("Sorry, I don't support dump filenames yet. Use - and provide it on stdin on the meantime.", true); + } + $importer = new WikiImporter( $source ); + + $importer->setRevisionCallback( + array( &$this, 'handleRevision' ) ); + + $this->from = $this->getOption( 'from', null ); + $this->count = 0; + $importer->doImport(); + + $this->conclusions(); + + $delta = wfTime() - $this->startTime; + $this->error( "Done {$this->count} revisions in " . round($delta, 2) . " seconds " ); + if ($delta > 0) + $this->error( round($this->count / $delta, 2) . " pages/sec" ); + + # Perform the memory_get_peak_usage() when all the other data has been output so there's no damage if it dies. + # It is only available since 5.2.0 (since 5.2.1 if you haven't compiled with --enable-memory-limit) + $this->error( "Memory peak usage of " . memory_get_peak_usage() . " bytes\n" ); + } + + public function finalSetup() { + parent::finalSetup(); + + if ( $this->getDbType() == Maintenance::DB_NONE ) { + global $wgUseDatabaseMessages, $wgLocalisationCacheConf, $wgHooks; + $wgUseDatabaseMessages = false; + $wgLocalisationCacheConf['storeClass'] = 'LCStore_Null'; + $wgHooks['InterwikiLoadPrefix'][] = 'DumpIterator::disableInterwikis'; + } + } + + static function disableInterwikis( $prefix, &$data ) { + # Title::newFromText will check on each namespaced article if it's an interwiki. + # We always answer that it is not. + + return false; + } + + /** + * Callback function for each revision, child classes should override + * processRevision instead. + * @param $rev Revision + */ + public function handleRevision( $rev ) { + $title = $rev->getTitle(); + if ( !$title ) { + $this->error( "Got bogus revision with null title!" ); + return; + } + + $this->count++; + if ( isset( $this->from ) ) { + if ( $this->from != $title ) + return; + $this->output( "Skipped " . ($this->count - 1) . " pages\n" ); + + $this->count = 1; + $this->from = null; + } + + $this->processRevision( $rev ); + } + + /* Stub function for processing additional options */ + public function checkOptions() { + return; + } + + /* Stub function for giving data about what was computed */ + public function conclusions() { + return; + } + + /* Core function which does whatever the maintenance script is designed to do */ + abstract public function processRevision( $rev ); +} + +class SearchDump extends DumpIterator { + + public function __construct() { + parent::__construct(); + $this->mDescription = "Runs a regex in the revisions from a dump"; + $this->addOption( 'regex', 'Searching regex', true, true ); + } + + public function getDbType() { + return Maintenance::DB_NONE; + } + + /** + * @param $rev Revision + */ + public function processRevision( $rev ) { + if ( preg_match( $this->getOption( 'regex' ), $rev->getText() ) ) { + $this->output( $rev->getTitle() . " matches at edit from " . $rev->getTimestamp() . "\n" ); + } + } +} + +$maintClass = "SearchDump"; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/dumpTextPass.php b/maintenance/dumpTextPass.php index 98d4af0e..4e85e64e 100644 --- a/maintenance/dumpTextPass.php +++ b/maintenance/dumpTextPass.php @@ -2,7 +2,7 @@ /** * Script that postprocesses XML dumps from dumpBackup.php to add page text * - * Copyright © 2005 Brion Vibber <brion@pobox.com>, 2010 Alexandre Emsenhuber + * Copyright 2005 Brion Vibber <brion@pobox.com>, 2010 Alexandre Emsenhuber * http://www.mediawiki.org/ * * This program is free software; you can redistribute it and/or modify @@ -35,8 +35,11 @@ require_once( 'backup.inc' ); class TextPassDumper extends BackupDumper { var $prefetch = null; var $input = "php://stdin"; + var $history = WikiExporter::FULL; var $fetchCount = 0; var $prefetchCount = 0; + var $prefetchCountLast = 0; + var $fetchCountLast = 0; var $failures = 0; var $maxFailures = 5; @@ -51,20 +54,50 @@ class TextPassDumper extends BackupDumper { var $spawnRead = false; var $spawnErr = false; + var $xmlwriterobj = false; + + // when we spend more than maxTimeAllowed seconds on this run, we continue + // processing until we write out the next complete page, then save output file(s), + // rename it/them and open new one(s) + var $maxTimeAllowed = 0; // 0 = no limit + var $timeExceeded = false; + var $firstPageWritten = false; + var $lastPageWritten = false; + var $checkpointJustWritten = false; + var $checkpointFiles = array(); + + function initProgress( $history ) { + parent::initProgress(); + $this->timeOfCheckpoint = $this->startTime; + } + function dump( $history, $text = WikiExporter::TEXT ) { - # This shouldn't happen if on console... ;) + // This shouldn't happen if on console... ;) header( 'Content-type: text/html; charset=UTF-8' ); - # Notice messages will foul up your XML output even if they're - # relatively harmless. + // Notice messages will foul up your XML output even if they're + // relatively harmless. if ( ini_get( 'display_errors' ) ) ini_set( 'display_errors', 'stderr' ); - $this->initProgress( $history ); + $this->initProgress( $this->history ); $this->db = $this->backupDb(); - $this->readDump(); + $this->egress = new ExportProgressFilter( $this->sink, $this ); + + // it would be nice to do it in the constructor, oh well. need egress set + $this->finalOptionCheck(); + + // we only want this so we know how to close a stream :-P + $this->xmlwriterobj = new XmlDumpWriter(); + + $input = fopen( $this->input, "rt" ); + $result = $this->readDump( $input ); + + if ( WikiError::isError( $result ) ) { + throw new MWException( $result->getMessage() ); + } if ( $this->spawnProc ) { $this->closeSpawn(); @@ -85,6 +118,18 @@ class TextPassDumper extends BackupDumper { case 'stub': $this->input = $url; break; + case 'maxtime': + $this->maxTimeAllowed = intval($val)*60; + break; + case 'checkpointfile': + $this->checkpointFiles[] = $val; + break; + case 'current': + $this->history = WikiExporter::CURRENT; + break; + case 'full': + $this->history = WikiExporter::FULL; + break; case 'spawn': $this->spawn = true; if ( $val ) { @@ -128,96 +173,143 @@ class TextPassDumper extends BackupDumper { } if ( $this->reporting ) { - $delta = wfTime() - $this->startTime; $now = wfTimestamp( TS_DB ); - if ( $delta ) { - $rate = $this->pageCount / $delta; - $revrate = $this->revCount / $delta; + $nowts = wfTime(); + $deltaAll = wfTime() - $this->startTime; + $deltaPart = wfTime() - $this->lastTime; + $this->pageCountPart = $this->pageCount - $this->pageCountLast; + $this->revCountPart = $this->revCount - $this->revCountLast; + + if ( $deltaAll ) { $portion = $this->revCount / $this->maxCount; - $eta = $this->startTime + $delta / $portion; + $eta = $this->startTime + $deltaAll / $portion; $etats = wfTimestamp( TS_DB, intval( $eta ) ); - $fetchrate = 100.0 * $this->prefetchCount / $this->fetchCount; + if ( $this->fetchCount ) { + $fetchRate = 100.0 * $this->prefetchCount / $this->fetchCount; + } + else { + $fetchRate = '-'; + } + $pageRate = $this->pageCount / $deltaAll; + $revRate = $this->revCount / $deltaAll; } else { - $rate = '-'; - $revrate = '-'; + $pageRate = '-'; + $revRate = '-'; $etats = '-'; - $fetchrate = '-'; + $fetchRate = '-'; + } + if ( $deltaPart ) { + if ( $this->fetchCountLast ) { + $fetchRatePart = 100.0 * $this->prefetchCountLast / $this->fetchCountLast; + } + else { + $fetchRatePart = '-'; + } + $pageRatePart = $this->pageCountPart / $deltaPart; + $revRatePart = $this->revCountPart / $deltaPart; + + } else { + $fetchRatePart = '-'; + $pageRatePart = '-'; + $revRatePart = '-'; } - $this->progress( sprintf( "%s: %s %d pages (%0.3f/sec), %d revs (%0.3f/sec), %0.1f%% prefetched, ETA %s [max %d]", - $now, wfWikiID(), $this->pageCount, $rate, $this->revCount, $revrate, $fetchrate, $etats, $this->maxCount ) ); + $this->progress( sprintf( "%s: %s (ID %d) %d pages (%0.1f|%0.1f/sec all|curr), %d revs (%0.1f|%0.1f/sec all|curr), %0.1f%%|%0.1f%% prefetched (all|curr), ETA %s [max %d]", + $now, wfWikiID(), $this->ID, $this->pageCount, $pageRate, $pageRatePart, $this->revCount, $revRate, $revRatePart, $fetchRate, $fetchRatePart, $etats, $this->maxCount ) ); + $this->lastTime = $nowts; + $this->revCountLast = $this->revCount; + $this->prefetchCountLast = $this->prefetchCount; + $this->fetchCountLast = $this->fetchCount; } } - function readDump() { - $state = ''; - $lastName = ''; - $this->thisPage = 0; - $this->thisRev = 0; + function setTimeExceeded() { + $this->timeExceeded = True; + } - $reader = new XMLReader(); - $reader->open( $this->input ); - $writer = new XMLWriter(); - $writer->openMemory(); + function checkIfTimeExceeded() { + if ( $this->maxTimeAllowed && ( $this->lastTime - $this->timeOfCheckpoint > $this->maxTimeAllowed ) ) { + return True; + } + return False; + } + function finalOptionCheck() { + if ( ( $this->checkpointFiles && ! $this->maxTimeAllowed ) || + ( $this->maxTimeAllowed && !$this->checkpointFiles ) ) { + throw new MWException("Options checkpointfile and maxtime must be specified together.\n"); + } + foreach ($this->checkpointFiles as $checkpointFile) { + $count = substr_count ( $checkpointFile,"%s" ); + if ( $count != 2 ) { + throw new MWException("Option checkpointfile must contain two '%s' for substitution of first and last pageids, count is $count instead, file is $checkpointFile.\n"); + } + } - while ( $reader->read() ) { - $tag = $reader->name; - $type = $reader->nodeType; + if ( $this->checkpointFiles ) { + $filenameList = (array)$this->egress->getFilenames(); + if ( count( $filenameList ) != count( $this->checkpointFiles ) ) { + throw new MWException("One checkpointfile must be specified for each output option, if maxtime is used.\n"); + } + } + } - if ( $type == XmlReader::END_ELEMENT ) { - $writer->endElement(); + function readDump( $input ) { + $this->buffer = ""; + $this->openElement = false; + $this->atStart = true; + $this->state = ""; + $this->lastName = ""; + $this->thisPage = 0; + $this->thisRev = 0; - if ( $tag == 'revision' ) { - $this->revCount(); - $this->thisRev = ''; - } elseif ( $tag == 'page' ) { - $this->reportPage(); - $this->thisPage = ''; - } - } elseif ( $type == XmlReader::ELEMENT ) { - $attribs = array(); - if ( $reader->hasAttributes ) { - for ( $i = 0; $reader->moveToAttributeNo( $i ); $i++ ) { - $attribs[$reader->name] = $reader->value; - } - } + $parser = xml_parser_create( "UTF-8" ); + xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false ); - if ( $reader->isEmptyElement && $tag == 'text' && isset( $attribs['id'] ) ) { - $writer->startElement( 'text' ); - $writer->writeAttribute( 'xml:space', 'preserve' ); - $text = $this->getText( $attribs['id'] ); - if ( strlen( $text ) ) { - $writer->text( $text ); - } - $writer->endElement(); - } else { - $writer->startElement( $tag ); - foreach( $attribs as $name => $val ) { - $writer->writeAttribute( $name, $val ); - } - if ( $reader->isEmptyElement ) { - $writer->endElement(); - } - } + xml_set_element_handler( $parser, array( &$this, 'startElement' ), array( &$this, 'endElement' ) ); + xml_set_character_data_handler( $parser, array( &$this, 'characterData' ) ); - $lastName = $tag; - if ( $tag == 'revision' ) { - $state = 'revision'; - } elseif ( $tag == 'page' ) { - $state = 'page'; + $offset = 0; // for context extraction on error reporting + $bufferSize = 512 * 1024; + do { + if ($this->checkIfTimeExceeded()) { + $this->setTimeExceeded(); + } + $chunk = fread( $input, $bufferSize ); + if ( !xml_parse( $parser, $chunk, feof( $input ) ) ) { + wfDebug( "TextDumpPass::readDump encountered XML parsing error\n" ); + return new WikiXmlError( $parser, 'XML import parse failure', $chunk, $offset ); + } + $offset += strlen( $chunk ); + } while ( $chunk !== false && !feof( $input ) ); + if ($this->maxTimeAllowed) { + $filenameList = (array)$this->egress->getFilenames(); + // we wrote some stuff after last checkpoint that needs renamed + if (file_exists($filenameList[0])) { + $newFilenames = array(); + # we might have just written the header and footer and had no + # pages or revisions written... perhaps they were all deleted + # there's no pageID 0 so we use that. the caller is responsible + # for deciding what to do with a file containing only the + # siteinfo information and the mw tags. + if (! $this->firstPageWritten) { + $firstPageID = str_pad(0,9,"0",STR_PAD_LEFT); + $lastPageID = str_pad(0,9,"0",STR_PAD_LEFT); } - } elseif ( $type == XMLReader::SIGNIFICANT_WHITESPACE || $type = XMLReader::TEXT ) { - if ( $lastName == 'id' ) { - if ( $state == 'revision' ) { - $this->thisRev .= $reader->value; - } elseif ( $state == 'page' ) { - $this->thisPage .= $reader->value; - } + else { + $firstPageID = str_pad($this->firstPageWritten,9,"0",STR_PAD_LEFT); + $lastPageID = str_pad($this->lastPageWritten,9,"0",STR_PAD_LEFT); + } + for ( $i = 0; $i < count( $filenameList ); $i++ ) { + $checkpointNameFilledIn = sprintf( $this->checkpointFiles[$i], $firstPageID, $lastPageID ); + $fileinfo = pathinfo($filenameList[$i]); + $newFilenames[] = $fileinfo['dirname'] . '/' . $checkpointNameFilledIn; } - $writer->text( $reader->value ); + $this->egress->closeAndRename( $newFilenames ); } - $this->sink->write( $writer->outputMemory() ); } + xml_parser_free( $parser ); + + return true; } function getText( $id ) { @@ -240,6 +332,7 @@ class TextPassDumper extends BackupDumper { } private function doGetText( $id ) { + $id = intval( $id ); $this->failures = 0; $ex = new MWException( "Graceful storage failure" ); @@ -334,12 +427,23 @@ class TextPassDumper extends BackupDumper { function openSpawn() { global $IP; - $cmd = implode( " ", - array_map( 'wfEscapeShellArg', - array( - $this->php, - "$IP/maintenance/fetchText.php", - '--wiki', wfWikiID() ) ) ); + if ( file_exists( "$IP/../multiversion/MWScript.php" ) ) { + $cmd = implode( " ", + array_map( 'wfEscapeShellArg', + array( + $this->php, + "$IP/../multiversion/MWScript.php", + "fetchText.php", + '--wiki', wfWikiID() ) ) ); + } + else { + $cmd = implode( " ", + array_map( 'wfEscapeShellArg', + array( + $this->php, + "$IP/maintenance/fetchText.php", + '--wiki', wfWikiID() ) ) ); + } $spec = array( 0 => array( "pipe", "r" ), 1 => array( "pipe", "w" ), @@ -427,13 +531,129 @@ class TextPassDumper extends BackupDumper { $normalized = $wgContLang->normalize( $stripped ); return $normalized; } + + function startElement( $parser, $name, $attribs ) { + $this->checkpointJustWritten = false; + + $this->clearOpenElement( null ); + $this->lastName = $name; + + if ( $name == 'revision' ) { + $this->state = $name; + $this->egress->writeOpenPage( null, $this->buffer ); + $this->buffer = ""; + } elseif ( $name == 'page' ) { + $this->state = $name; + if ( $this->atStart ) { + $this->egress->writeOpenStream( $this->buffer ); + $this->buffer = ""; + $this->atStart = false; + } + } + + if ( $name == "text" && isset( $attribs['id'] ) ) { + $text = $this->getText( $attribs['id'] ); + $this->openElement = array( $name, array( 'xml:space' => 'preserve' ) ); + if ( strlen( $text ) > 0 ) { + $this->characterData( $parser, $text ); + } + } else { + $this->openElement = array( $name, $attribs ); + } + } + + function endElement( $parser, $name ) { + $this->checkpointJustWritten = false; + + if ( $this->openElement ) { + $this->clearOpenElement( "" ); + } else { + $this->buffer .= "</$name>"; + } + + if ( $name == 'revision' ) { + $this->egress->writeRevision( null, $this->buffer ); + $this->buffer = ""; + $this->thisRev = ""; + } elseif ( $name == 'page' ) { + if (! $this->firstPageWritten) { + $this->firstPageWritten = trim($this->thisPage); + } + $this->lastPageWritten = trim($this->thisPage); + if ($this->timeExceeded) { + $this->egress->writeClosePage( $this->buffer ); + // nasty hack, we can't just write the chardata after the + // page tag, it will include leading blanks from the next line + $this->egress->sink->write("\n"); + + $this->buffer = $this->xmlwriterobj->closeStream(); + $this->egress->writeCloseStream( $this->buffer ); + + $this->buffer = ""; + $this->thisPage = ""; + // this could be more than one file if we had more than one output arg + $checkpointFilenames = array(); + $filenameList = (array)$this->egress->getFilenames(); + $newFilenames = array(); + $firstPageID = str_pad($this->firstPageWritten,9,"0",STR_PAD_LEFT); + $lastPageID = str_pad($this->lastPageWritten,9,"0",STR_PAD_LEFT); + for ( $i = 0; $i < count( $filenameList ); $i++ ) { + $checkpointNameFilledIn = sprintf( $this->checkpointFiles[$i], $firstPageID, $lastPageID ); + $fileinfo = pathinfo($filenameList[$i]); + $newFilenames[] = $fileinfo['dirname'] . '/' . $checkpointNameFilledIn; + } + $this->egress->closeRenameAndReopen( $newFilenames ); + $this->buffer = $this->xmlwriterobj->openStream(); + $this->timeExceeded = false; + $this->timeOfCheckpoint = $this->lastTime; + $this->firstPageWritten = false; + $this->checkpointJustWritten = true; + } + else { + $this->egress->writeClosePage( $this->buffer ); + $this->buffer = ""; + $this->thisPage = ""; + } + + } elseif ( $name == 'mediawiki' ) { + $this->egress->writeCloseStream( $this->buffer ); + $this->buffer = ""; + } + } + + function characterData( $parser, $data ) { + $this->clearOpenElement( null ); + if ( $this->lastName == "id" ) { + if ( $this->state == "revision" ) { + $this->thisRev .= $data; + } elseif ( $this->state == "page" ) { + $this->thisPage .= $data; + } + } + // have to skip the newline left over from closepagetag line of + // end of checkpoint files. nasty hack!! + if ($this->checkpointJustWritten) { + if ($data[0] == "\n") { + $data = substr($data,1); + } + $this->checkpointJustWritten = false; + } + $this->buffer .= htmlspecialchars( $data ); + } + + function clearOpenElement( $style ) { + if ( $this->openElement ) { + $this->buffer .= Xml::element( $this->openElement[0], $this->openElement[1], $style ); + $this->openElement = false; + } + } } $dumper = new TextPassDumper( $argv ); if ( !isset( $options['help'] ) ) { - $dumper->dump( WikiExporter::FULL ); + $dumper->dump( true ); } else { $dumper->progress( <<<ENDS This script postprocesses XML dumps from dumpBackup.php to add @@ -447,17 +667,20 @@ Options: --stub=<type>:<file> To load a compressed stub dump instead of stdin --prefetch=<type>:<file> Use a prior dump file as a text source, to save pressure on the database. + (Requires the XMLReader extension) + --maxtime=<minutes> Write out checkpoint file after this many minutes (writing + out complete page, closing xml file properly, and opening new one + with header). This option requires the checkpointfile option. + --checkpointfile=<filenamepattern> Use this string for checkpoint filenames, + substituting first pageid written for the first %s (required) and the + last pageid written for the second %s if it exists. --quiet Don't dump status reports to stderr. --report=n Report position and speed after every n pages processed. (Default: 100) --server=h Force reading from MySQL server h - --output=<type>:<file> Write to a file instead of stdout - <type>s: file, gzip, bzip2, 7zip --current Base ETA on number of pages in database instead of all revisions --spawn Spawn a subprocess for loading text records --help Display this help message ENDS ); } - - diff --git a/maintenance/dumpUploads.php b/maintenance/dumpUploads.php index 74c0cb0b..919bb4df 100644 --- a/maintenance/dumpUploads.php +++ b/maintenance/dumpUploads.php @@ -104,7 +104,7 @@ By default, outputs relative paths against the parent directory of \$wgUploadDir function outputItem( $name, $shared ) { $file = wfFindFile( $name ); if ( $file && $this->filterItem( $file, $shared ) ) { - $filename = $file->getFullPath(); + $filename = $file->getPath(); $rel = wfRelativePath( $filename, $this->mBasePath ); $this->output( "$rel\n" ); } else { diff --git a/maintenance/edit.php b/maintenance/edit.php index 40623afb..fb462a40 100644 --- a/maintenance/edit.php +++ b/maintenance/edit.php @@ -26,23 +26,23 @@ class EditCLI extends Maintenance { public function __construct() { parent::__construct(); $this->mDescription = "Edit an article from the command line, text is from stdin"; - $this->addOption( 'u', 'Username', false, true ); - $this->addOption( 's', 'Edit summary', false, true ); - $this->addOption( 'm', 'Minor edit' ); - $this->addOption( 'b', 'Bot edit' ); - $this->addOption( 'a', 'Enable autosummary' ); - $this->addOption( 'no-rc', 'Do not show the change in recent changes' ); + $this->addOption( 'user', 'Username', false, true, 'u' ); + $this->addOption( 'summary', 'Edit summary', false, true, 's' ); + $this->addOption( 'minor', 'Minor edit', false, false, 'm' ); + $this->addOption( 'bot', 'Bot edit', false, false, 'b' ); + $this->addOption( 'autosummary', 'Enable autosummary', false, false, 'a' ); + $this->addOption( 'no-rc', 'Do not show the change in recent changes', false, false, 'r' ); $this->addArg( 'title', 'Title of article to edit' ); } public function execute() { global $wgUser, $wgTitle; - $userName = $this->getOption( 'u', 'Maintenance script' ); - $summary = $this->getOption( 's', '' ); - $minor = $this->hasOption( 'm' ); - $bot = $this->hasOption( 'b' ); - $autoSummary = $this->hasOption( 'a' ); + $userName = $this->getOption( 'user', 'Maintenance script' ); + $summary = $this->getOption( 'summary', '' ); + $minor = $this->hasOption( 'minor' ); + $bot = $this->hasOption( 'bot' ); + $autoSummary = $this->hasOption( 'autosummary' ); $noRC = $this->hasOption( 'no-rc' ); $wgUser = User::newFromName( $userName ); diff --git a/maintenance/eval.php b/maintenance/eval.php index 3cc1d16a..33c9a5d7 100644 --- a/maintenance/eval.php +++ b/maintenance/eval.php @@ -12,6 +12,21 @@ * To get decent line editing behavior, you should compile PHP with support * for GNU readline (pass --with-readline to configure). * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance */ @@ -30,8 +45,11 @@ if ( isset( $options['d'] ) ) { } if ( $d > 1 ) { $lb = wfGetLB(); - foreach ( $lb->mServers as $i => $server ) { - $lb->mServers[$i]['flags'] |= DBO_DEBUG; + $serverCount = $lb->getServerCount(); + for ( $i = 0; $i < $serverCount; $i++ ) { + $server = $lb->getServerInfo( $i ); + $server['flags'] |= DBO_DEBUG; + $lb->setServerInfo( $i, $server ); } } if ( $d > 2 ) { @@ -59,7 +77,7 @@ while ( ( $line = Maintenance::readconsole() ) !== false ) { readline_write_history( $historyFile ); } $val = eval( $line . ";" ); - if ( is_null( $val ) ) { + if ( wfIsHipHop() || is_null( $val ) ) { echo "\n"; } elseif ( is_string( $val ) || is_numeric( $val ) ) { echo "$val\n"; diff --git a/maintenance/fetchText.php b/maintenance/fetchText.php index ea56535d..067ffe45 100644 --- a/maintenance/fetchText.php +++ b/maintenance/fetchText.php @@ -1,6 +1,7 @@ <?php /** * Communications protocol... + * This is used by dumpTextPass.php when the --spawn option is present. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -62,7 +63,7 @@ class FetchText extends Maintenance { /** * May throw a database error if, say, the server dies during query. - * @param $db Database object + * @param $db DatabaseBase object * @param $id int The old_id * @return String */ diff --git a/maintenance/findhooks.php b/maintenance/findHooks.php index 04a5faef..5996fd3a 100644 --- a/maintenance/findhooks.php +++ b/maintenance/findHooks.php @@ -39,8 +39,8 @@ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); class FindHooks extends Maintenance { public function __construct() { parent::__construct(); - $this->mDescription = "Find hooks that are undocumented, missing, or just plain wrong"; - $this->addOption( 'online', 'Check against mediawiki.org hook documentation' ); + $this->mDescription = 'Find hooks that are undocumented, missing, or just plain wrong'; + $this->addOption( 'online', 'Check against MediaWiki.org hook documentation' ); } public function getDbType() { @@ -56,11 +56,15 @@ class FindHooks extends Maintenance { $pathinc = array( $IP . '/', $IP . '/includes/', + $IP . '/includes/actions/', $IP . '/includes/api/', + $IP . '/includes/cache/', $IP . '/includes/db/', $IP . '/includes/diff/', $IP . '/includes/filerepo/', $IP . '/includes/installer/', + $IP . '/includes/interwiki/', + $IP . '/includes/media/', $IP . '/includes/parser/', $IP . '/includes/resourceloader/', $IP . '/includes/revisiondelete/', @@ -69,8 +73,9 @@ class FindHooks extends Maintenance { $IP . '/includes/upload/', $IP . '/languages/', $IP . '/maintenance/', - $IP . '/maintenance/tests/', - $IP . '/maintenance/tests/parser/', + $IP . '/tests/', + $IP . '/tests/parser/', + $IP . '/tests/phpunit/suites/', $IP . '/skins/', ); @@ -90,11 +95,13 @@ class FindHooks extends Maintenance { $this->printArray( 'Unclear hook calls', $bad ); if ( count( $todo ) == 0 && count( $deprecated ) == 0 && count( $bad ) == 0 ) + { $this->output( "Looks good!\n" ); + } } /** - * Get the hook documentation, either locally or from mediawiki.org + * Get the hook documentation, either locally or from MediaWiki.org * @return array of documented hooks */ private function getHooksFromDoc( $doc ) { @@ -138,7 +145,7 @@ class FindHooks extends Maintenance { private function getHooksFromFile( $file ) { $content = file_get_contents( $file ); $m = array(); - preg_match_all( '/wfRunHooks\(\s*([\'"])(.*?)\1/', $content, $m ); + preg_match_all( '/(?:wfRunHooks|Hooks\:\:run)\(\s*([\'"])(.*?)\1/', $content, $m ); return $m[2]; } @@ -200,15 +207,19 @@ class FindHooks extends Maintenance { /** * Nicely output the array - * @param $msg A message to show before the value - * @param $arr An array - * @param $sort Boolean : wheter to sort the array (Default: true) + * @param $msg String: a message to show before the value + * @param $arr Array: an array + * @param $sort Boolean: whether to sort the array (Default: true) */ private function printArray( $msg, $arr, $sort = true ) { - if ( $sort ) asort( $arr ); - foreach ( $arr as $v ) $this->output( "$msg: $v\n" ); + if ( $sort ) { + asort( $arr ); + } + foreach ( $arr as $v ) { + $this->output( "$msg: $v\n" ); + } } } -$maintClass = "FindHooks"; +$maintClass = 'FindHooks'; require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/fixDoubleRedirects.php b/maintenance/fixDoubleRedirects.php new file mode 100644 index 00000000..c1d14dd8 --- /dev/null +++ b/maintenance/fixDoubleRedirects.php @@ -0,0 +1,120 @@ +<?php +/** + * Script to fix double redirects. + * + * Copyright (C) 2011 Ilmari Karonen <nospam@vyznev.net> + * http://www.mediawiki.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author Ilmari Karonen <nospam@vyznev.net> + * @ingroup Maintenance + */ + +require_once( dirname( __FILE__ ) . '/Maintenance.php' ); + +class FixDoubleRedirects extends Maintenance { + public function __construct() { + parent::__construct(); + $this->mDescription = "Script to fix double redirects"; + $this->addOption( 'async', 'Don\'t fix anything directly, just queue the jobs' ); + $this->addOption( 'title', 'Fix only redirects pointing to this page', false, true ); + $this->addOption( 'dry-run', 'Perform a dry run, fix nothing' ); + } + + public function execute() { + $async = $this->getOption( 'async', false ); + $dryrun = $this->getOption( 'dry-run', false ); + $title = $this->getOption( 'title' ); + + if ( isset( $title ) ) { + $title = Title::newFromText( $title ); + if ( !$title || !$title->isRedirect() ) { + $this->error( $title->getPrefixedText() . " is not a redirect!\n", true ); + } + } + + $dbr = wfGetDB( DB_SLAVE ); + + $tables = array( 'redirect', 'pa' => 'page', 'pb' => 'page' ); + $fields = array( + 'pa.page_namespace AS pa_namespace', + 'pa.page_title AS pa_title', + 'pb.page_namespace AS pb_namespace', + 'pb.page_title AS pb_title', + ); + $conds = array( + 'rd_from = pa.page_id', + 'rd_namespace = pb.page_namespace', + 'rd_title = pb.page_title', + 'pb.page_is_redirect' => 1, + ); + + if ( isset( $title ) ) { + $conds['pb.page_namespace'] = $title->getNamespace(); + $conds['pb.page_title'] = $title->getDBkey(); + } + // TODO: support batch querying + + $res = $dbr->select( $tables, $fields, $conds, __METHOD__ ); + + if ( !$res->numRows() ) { + $this->output( "No double redirects found.\n" ); + return; + } + + $jobs = array(); + $n = 0; + foreach ( $res as $row ) { + $titleA = Title::makeTitle( $row->pa_namespace, $row->pa_title ); + $titleB = Title::makeTitle( $row->pb_namespace, $row->pb_title ); + + $job = new DoubleRedirectJob( $titleA, array( 'reason' => 'maintenance', 'redirTitle' => $titleB->getPrefixedDBkey() ) ); + + if ( !$async ) { + $success = ( $dryrun ? true : $job->run() ); + if ( !$success ) { + $this->error( "Error fixing " . $titleA->getPrefixedText() . ": " . $job->getLastError() . "\n" ); + } + } else { + $jobs[] = $job; + // @todo FIXME: Hardcoded constant 10000 copied from DoubleRedirectJob class + if ( count( $jobs ) > 10000 ) { + $this->queueJobs( $jobs, $dryrun ); + $jobs = array(); + } + } + + if ( ++$n % 100 == 0 ) { + $this->output( "$n...\n" ); + } + } + + if ( count( $jobs ) ) { + $this->queueJobs( $jobs, $dryrun ); + } + $this->output( "$n double redirects processed.\n" ); + } + + protected function queueJobs( $jobs, $dryrun = false ) { + $this->output( "Queuing batch of " . count( $jobs ) . " double redirects.\n" ); + Job::batchInsert( $dryrun ? array() : $jobs ); + } +} + +$maintClass = "FixDoubleRedirects"; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/fixExtLinksProtocolRelative.php b/maintenance/fixExtLinksProtocolRelative.php new file mode 100644 index 00000000..1a7025ad --- /dev/null +++ b/maintenance/fixExtLinksProtocolRelative.php @@ -0,0 +1,81 @@ +<?php +/** + * Fixes any entries for protocol-relative URLs in the externallinks table, + * replacing each protocol-relative entry with two entries, one for http + * and one for https. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @ingroup Maintenance + */ + +require_once( dirname( __FILE__ ) . '/Maintenance.php' ); + +class FixExtLinksProtocolRelative extends LoggedUpdateMaintenance { + public function __construct() { + parent::__construct(); + $this->mDescription = "Fixes any entries in the externallinks table containing protocol-relative URLs"; + } + + protected function getUpdateKey() { + return 'fix protocol-relative URLs in externallinks'; + } + + protected function updateSkippedMessage() { + return 'protocol-relative URLs in externallinks table already fixed.'; + } + + protected function doDBUpdates() { + $db = wfGetDB( DB_MASTER ); + if ( !$db->tableExists( 'externallinks' ) ) { + $this->error( "externallinks table does not exist" ); + return false; + } + $this->output( "Fixing protocol-relative entries in the externallinks table...\n" ); + $res = $db->select( 'externallinks', array( 'el_from', 'el_to', 'el_index' ), + array( 'el_index' . $db->buildLike( '//', $db->anyString() ) ), + __METHOD__ + ); + $count = 0; + foreach ( $res as $row ) { + $count++; + if ( $count % 100 == 0 ) { + $this->output( $count ); + wfWaitForSlaves(); + } + $db->insert( 'externallinks', + array( + array( + 'el_from' => $row->el_from, + 'el_to' => $row->el_to, + 'el_index' => "http:{$row->el_index}", + ), + array( + 'el_from' => $row->el_from, + 'el_to' => $row->el_to, + 'el_index' => "https:{$row->el_index}", + ) + ), __METHOD__, array( 'IGNORE' ) + ); + $db->delete( 'externallinks', array( 'el_index' => $row->el_index, 'el_from' => $row->el_from, 'el_to' => $row->el_to ), __METHOD__ ); + } + $this->output( "Done, $count rows updated.\n" ); + return true; + } +} + +$maintClass = "FixExtLinksProtocolRelative"; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/fixSlaveDesync.php b/maintenance/fixSlaveDesync.php index fe892944..3c6888ae 100644 --- a/maintenance/fixSlaveDesync.php +++ b/maintenance/fixSlaveDesync.php @@ -124,7 +124,7 @@ class FixSlaveDesync extends Maintenance { $masterIDs[] = $row->rev_id; } - $res = $db->select( 'revision', array( 'rev_id' ), array( 'rev_page' => $pageID ), __METHOD__ ); + $res = $dbw->select( 'revision', array( 'rev_id' ), array( 'rev_page' => $pageID ), __METHOD__ ); $slaveIDs = array(); foreach ( $res as $row ) { $slaveIDs[] = $row->rev_id; @@ -133,7 +133,7 @@ class FixSlaveDesync extends Maintenance { $missingIDs = array_diff( $slaveIDs, $masterIDs ); if ( count( $missingIDs ) ) { $this->output( "Found " . count( $missingIDs ) . " lost in master, copying from slave... " ); - $dbFrom = $db; + $dbFrom = $dbw; $found = true; $toMaster = true; } else { diff --git a/maintenance/formatInstallDoc.php b/maintenance/formatInstallDoc.php new file mode 100644 index 00000000..9acc16a7 --- /dev/null +++ b/maintenance/formatInstallDoc.php @@ -0,0 +1,54 @@ +<?php + +require_once( dirname( __FILE__ ) .'/Maintenance.php' ); + +class MaintenanceFormatInstallDoc extends Maintenance { + function __construct() { + parent::__construct(); + $this->addArg( 'path', 'The file name to format', false ); + $this->addOption( 'outfile', 'The output file name', false, true ); + $this->addOption( 'html', 'Use HTML output format. By default, wikitext is used.' ); + } + + function execute() { + if ( $this->hasArg( 0 ) ) { + $fileName = $this->getArg( 0 ); + $inFile = fopen( $fileName, 'r' ); + if ( !$inFile ) { + $this->error( "Unable to open input file \"$fileName\"" ); + exit( 1 ); + } + } else { + $inFile = STDIN; + } + + if ( $this->hasOption( 'outfile' ) ) { + $fileName = $this->getOption( 'outfile' ); + $outFile = fopen( $fileName, 'w' ); + if ( !$outFile ) { + $this->error( "Unable to open output file \"$fileName\"" ); + exit( 1 ); + } + } else { + $outFile = STDOUT; + } + + $inText = stream_get_contents( $inFile ); + $outText = InstallDocFormatter::format( $inText ); + + if ( $this->hasOption( 'html' ) ) { + global $wgParser; + $opt = new ParserOptions; + $title = Title::newFromText( 'Text file' ); + $out = $wgParser->parse( $outText, $title, $opt ); + $outText = "<html><body>\n" . $out->getText() . "\n</body></html>\n"; + } + + fwrite( $outFile, $outText ); + } +} + +$maintClass = 'MaintenanceFormatInstallDoc'; +require_once( RUN_MAINTENANCE_IF_MAIN ); + + diff --git a/maintenance/fuzz-tester.php b/maintenance/fuzz-tester.php index a78522cd..18af4de4 100644 --- a/maintenance/fuzz-tester.php +++ b/maintenance/fuzz-tester.php @@ -1,10 +1,28 @@ <?php /** -* @file -* @ingroup Maintenance -* @author Nick Jenkins ( http://nickj.org/ ). -* @copyright 2006 Nick Jenkins -* @licence GNU General Public Licence 2.0 + * Performs fuzz-style testing of MediaWiki's parser and forms. + * + * Copyright © 2006 Nick Jenkins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance + * @author Nick Jenkins ( http://nickj.org/ ). + Started: 18 May 2006. @@ -798,6 +816,13 @@ class wikiFuzz { } } + /** + * Returns the matched character slash-escaped as in a C string + * Helper for makeTitleSafe callback + */ + static private function stringEscape( $matches ) { + return sprintf( "\\x%02x", ord( $matches[1] ) ); + } /** ** Strips out the stuff that Mediawiki balks at in a page's title. @@ -806,13 +831,7 @@ class wikiFuzz { static public function makeTitleSafe( $str ) { $legalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF"; return preg_replace_callback( - "/([^$legalTitleChars])/", - create_function( - // single quotes are essential here, - // or alternative escape all $ as \$ - '$matches', - 'return sprintf( "\\x%02x", ord( $matches[1] ) );' - ), + "/([^$legalTitleChars])/", 'wikiFuzz::stringEscape', $str ); } @@ -1027,18 +1046,18 @@ class prefixindexTest extends pageTest { $this->params = array ( "title" => "Special:Prefixindex", - "namespace" => wikiFuzz::randnum( -10, 101 ), + "namespace" => wikiFuzz::randnum( 101, -10 ), "Go" => wikiFuzz::makeFuzz( 2 ) ); // sometimes we want 'prefix', sometimes we want 'from', and sometimes we want nothing. if ( wikiFuzz::randnum( 3 ) == 0 ) { $this->params["prefix"] = wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+++--+1", - wikiFuzz::randnum( -10, 8134 ), wikiFuzz::makeFuzz( 2 ) ) ); + wikiFuzz::randnum( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) ); } if ( wikiFuzz::randnum( 3 ) == 0 ) { $this->params["from"] = wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+++--+1", - wikiFuzz::randnum( -10, 8134 ), wikiFuzz::makeFuzz( 2 ) ) ); + wikiFuzz::randnum( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) ); } } } @@ -1350,7 +1369,7 @@ class viewPageTest extends pageTest { // Tidy does not know how to valid atom or rss, so exclude from testing for the time being. if ( $this->params["feed"] == "atom" ) { unset( $this->params["feed"] ); } - else if ( $this->params["feed"] == "rss" ) { unset( $this->params["feed"] ); } + elseif ( $this->params["feed"] == "rss" ) { unset( $this->params["feed"] ); } // Raw pages cannot really be validated if ( $this->params["action"] == "raw" ) unset( $this->params["action"] ); @@ -1384,7 +1403,7 @@ class specialAllmessagesTest extends pageTest { /** ** a page test for "Special:Newpages". */ -class specialNewpages extends pageTest { +class specialNewpagesPageTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Newpages"; @@ -1397,7 +1416,7 @@ class specialNewpages extends pageTest { // Tidy does not know how to valid atom or rss, so exclude from testing for the time being. if ( $this->params["feed"] == "atom" ) { unset( $this->params["feed"] ); } - else if ( $this->params["feed"] == "rss" ) { unset( $this->params["feed"] ); } + elseif ( $this->params["feed"] == "rss" ) { unset( $this->params["feed"] ); } } } @@ -1470,7 +1489,7 @@ class specialBlockmeTest extends pageTest { // sometimes we specify "ip", and sometimes we don't. if ( wikiFuzz::randnum( 1 ) == 0 ) { - $this->params["ip"] = wikiFuzz::chooseInput( array( "10.12.41.213", wikiFuzz::randnum( -10, 8134 ), wikiFuzz::makeFuzz( 2 ) ) ); + $this->params["ip"] = wikiFuzz::chooseInput( array( "10.12.41.213", wikiFuzz::randnum( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) ); } } } @@ -1512,7 +1531,7 @@ class specialMovePage extends pageTest { /** ** a page test for "Special:Undelete" */ -class specialUndelete extends pageTest { +class specialUndeletePageTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Undelete"; @@ -1539,7 +1558,7 @@ class specialUndelete extends pageTest { /** ** a page test for "Special:Unlockdb" */ -class specialUnlockdb extends pageTest { +class specialUnlockdbPageTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Unlockdb"; @@ -1560,7 +1579,7 @@ class specialUnlockdb extends pageTest { /** ** a page test for "Special:Lockdb" */ -class specialLockdb extends pageTest { +class specialLockdbPageTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Lockdb"; @@ -1713,7 +1732,7 @@ class pageDeletion extends pageTest { /** ** a test for Revision Deletion. */ -class specialRevisionDelete extends pageTest { +class specialRevisionDeletePageTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Revisiondelete"; @@ -1744,7 +1763,7 @@ class specialRevisionDelete extends pageTest { /** ** a test for Special:Import. */ -class specialImport extends pageTest { +class specialImportPageTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Import"; @@ -1842,7 +1861,7 @@ class profileInfo extends pageTest { /** ** a test for Special:Cite (extension Special page). */ -class specialCite extends pageTest { +class specialCitePageTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Cite"; @@ -1861,7 +1880,7 @@ class specialCite extends pageTest { /** ** a test for Special:Filepath (extension Special page). */ -class specialFilepath extends pageTest { +class specialFilepathPageTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Filepath"; @@ -1921,7 +1940,7 @@ class specialMakesysop extends pageTest { /** ** a test for Special:Renameuser (extension Special page). */ -class specialRenameuser extends pageTest { +class specialRenameuserPageTest extends pageTest { function __construct() { $this->pagePath = "index.php?title=Special:Renameuser"; @@ -2044,18 +2063,18 @@ class api extends pageTest { // API watchlist feed mode. private static function feedwatchlistMode() { - // FIXME: add "wikiFuzz::makeFuzz(2)" as possible value below? + // @todo FIXME: Add "wikiFuzz::makeFuzz(2)" as possible value below? return array ( "feedformat" => wikiFuzz::chooseInput( array( "rss", "atom" ) ) ); } // API query mode. private static function queryMode() { - // FIXME: add "wikiFuzz::makeFuzz(2)" as possible params for the elements below? + // @todo FIXME: Add "wikiFuzz::makeFuzz(2)" as possible params for the elements below? // Suspect this will stuff up the tests more, but need to check. $params = array ( - // FIXME: More titles. + // @todo FIXME: More titles. "titles" => wikiFuzz::chooseInput( array( "Main Page" ) ), - // FIXME: More pageids. + // @todo FIXME: More pageids. "pageids" => 1, "prop" => wikiFuzz::chooseInput( array( "info", "revisions", "watchlist" ) ), "list" => wikiFuzz::chooseInput( array( "allpages", "logevents", "watchlist", "usercontribs", "recentchanges", "backlinks", "embeddedin", "imagelinks" ) ), @@ -2149,7 +2168,7 @@ class api extends pageTest { $this->params["action"] = $action; // Set the cookie: - // FIXME: need to get this cookie dynamically set, rather than hard-coded. + // @todo FIXME: Need to get this cookie dynamically set, rather than hard-coded. $this->cookie = "wikidbUserID=10001; wikidbUserName=Test; wikidb_session=178df0fe68c75834643af65dec9ec98a; wikidbToken=1adc6753d62c44aec950c024d7ae0540"; // Output format @@ -2173,7 +2192,7 @@ class GeSHi_Test extends pageTest { return "<source lang=\"" . $this->getLang() . "\" " . ( wikiFuzz::randnum( 2 ) == 0 ? "line " : "" ) . ( wikiFuzz::randnum( 2 ) == 0 ? "strict " : "" ) - . "start=" . wikiFuzz::chooseInput( array( wikiFuzz::randnum( -6000, 6000 ), wikiFuzz::makeFuzz( 2 ) ) ) + . "start=" . wikiFuzz::chooseInput( array( wikiFuzz::randnum( 6000, -6000 ), wikiFuzz::makeFuzz( 2 ) ) ) . ">" . wikiFuzz::makeFuzz( 2 ) . "</source>"; @@ -2238,31 +2257,31 @@ function selectPageTest( $count ) { case 15: return new contributionsTest(); case 16: return new viewPageTest(); case 17: return new specialAllmessagesTest(); - case 18: return new specialNewpages(); + case 18: return new specialNewpagesPageTest(); case 19: return new searchTest(); case 20: return new redirectTest(); case 21: return new confirmEmail(); case 22: return new watchlistTest(); case 23: return new specialBlockmeTest(); - case 24: return new specialUndelete(); + case 24: return new specialUndeletePageTest(); case 25: return new specialMovePage(); - case 26: return new specialUnlockdb(); - case 27: return new specialLockdb(); + case 26: return new specialUnlockdbPageTest(); + case 27: return new specialLockdbPageTest(); case 28: return new specialUserrights(); case 29: return new pageProtectionForm(); case 30: return new specialBlockip(); case 31: return new imagepageTest(); case 32: return new pageDeletion(); - case 33: return new specialRevisionDelete(); - case 34: return new specialImport(); + case 33: return new specialRevisionDeletePageTest(); + case 34: return new specialImportPageTest(); case 35: return new thumbTest(); case 36: return new trackbackTest(); case 37: return new profileInfo(); - case 38: return new specialCite(); - case 39: return new specialFilepath(); + case 38: return new specialCitePageTest(); + case 39: return new specialFilepathPageTest(); case 40: return new specialMakebot(); case 41: return new specialMakesysop(); - case 42: return new specialRenameuser(); + case 42: return new specialRenameuserPageTest(); case 43: return new specialLinksearch(); case 44: return new specialCategoryTree(); case 45: return new api(); @@ -2333,7 +2352,7 @@ function saveTestAsPHP( pageTest $test, $filename ) { . "\$result=curl_exec(\$ch);\n" . "curl_close (\$ch);\n" . "print \$result;\n" - . "?>\n"; + . "\n"; saveFile( $str, $filename ); } @@ -2453,7 +2472,7 @@ function validateHTML( $text ) { curl_close ( $ch ); - $valid = ( strpos( $result, "Failed validation" ) === false ? true : false ); + $valid = ( strpos( $result, "Failed validation" ) === false ); return array( $valid, $result ); } @@ -2490,8 +2509,8 @@ function dbErrorLogged() { // first time running this function if ( !isset( $filesize ) ) { // create log if it does not exist - if ( !file_exists( DB_ERROR_LOG_FILE ) ) { - saveFile( "", DB_ERROR_LOG_FILE ); + if ( DB_ERROR_LOG_FILE && !file_exists( DB_ERROR_LOG_FILE ) ) { + saveFile( '', DB_ERROR_LOG_FILE ); } $filesize = filesize( DB_ERROR_LOG_FILE ); return false; @@ -2683,7 +2702,7 @@ if ( !is_dir( DIRECTORY ) ) { mkdir ( DIRECTORY, 0700 ); } // otherwise, we first retest the things that we have found in previous runs -else if ( RERUN_OLD_TESTS ) { +elseif ( RERUN_OLD_TESTS ) { rerunPreviousTests(); } @@ -2731,7 +2750,7 @@ for ( $count = 0; true; $count++ ) { } saveTest( $test, $testname ); $num_errors += 1; - } else if ( KEEP_PASSED_TESTS ) { + } elseif ( KEEP_PASSED_TESTS ) { // print current time, with microseconds (matches "strace" format), and the test name. print " " . date( "H:i:s." ) . substr( current( explode( " ", microtime() ) ), 2 ) . " " . $testname; saveTest( $test, $testname ); diff --git a/maintenance/gearman/gearmanWorker.php b/maintenance/gearman/gearmanWorker.php index aea126a7..3ea10081 100644 --- a/maintenance/gearman/gearmanWorker.php +++ b/maintenance/gearman/gearmanWorker.php @@ -36,7 +36,7 @@ function wfGearmanMonitor( $idle, $lastJob ) { $interval = 5; $now = time(); if ( $now - $lastSleep >= $interval ) { - wfWaitForSlaves( $interval ); + wfWaitForSlaves(); $lastSleep = $now; } return false; diff --git a/maintenance/generateSitemap.php b/maintenance/generateSitemap.php index e483f7c9..403e5a24 100644 --- a/maintenance/generateSitemap.php +++ b/maintenance/generateSitemap.php @@ -1,6 +1,4 @@ <?php -define( 'GS_MAIN', -2 ); -define( 'GS_TALK', -1 ); /** * Creates a sitemap for the site * @@ -31,6 +29,9 @@ define( 'GS_TALK', -1 ); require_once( dirname( __FILE__ ) . '/Maintenance.php' ); class GenerateSitemap extends Maintenance { + const GS_MAIN = -2; + const GS_TALK = -1; + /** * The maximum amount of urls in a sitemap file * @@ -121,6 +122,13 @@ class GenerateSitemap extends Maintenance { var $file; /** + * Identifier to use in filenames, default $wgDBname + * + * @var string + */ + private $identifier; + + /** * Constructor */ public function __construct() { @@ -129,6 +137,7 @@ class GenerateSitemap extends Maintenance { $this->addOption( 'fspath', 'The file system path to save to, e.g. /tmp/sitemap; defaults to current directory', false, true ); $this->addOption( 'urlpath', 'The URL path corresponding to --fspath, prepended to filenames in the index; defaults to an empty string', false, true ); $this->addOption( 'compress', 'Compress the sitemap files, can take value yes|no, default yes', false, true ); + $this->addOption( 'identifier', 'What site identifier to use for the wiki, defaults to $wgDBname', false, true ); } /** @@ -143,19 +152,20 @@ class GenerateSitemap extends Maintenance { if ( $this->urlpath !== "" && substr( $this->urlpath, -1 ) !== '/' ) { $this->urlpath .= '/'; } + $this->identifier = $this->getOption( 'identifier', wfWikiID() ); $this->compress = $this->getOption( 'compress', 'yes' ) !== 'no'; $this->dbr = wfGetDB( DB_SLAVE ); $this->generateNamespaces(); $this->timestamp = wfTimestamp( TS_ISO_8601, wfTimestampNow() ); - $this->findex = fopen( "{$this->fspath}sitemap-index-" . wfWikiID() . ".xml", 'wb' ); + $this->findex = fopen( "{$this->fspath}sitemap-index-{$this->identifier}.xml", 'wb' ); $this->main(); } private function setNamespacePriorities() { // Custom main namespaces - $this->priorities[GS_MAIN] = '0.5'; + $this->priorities[self::GS_MAIN] = '0.5'; // Custom talk namesspaces - $this->priorities[GS_TALK] = '0.1'; + $this->priorities[self::GS_TALK] = '0.1'; // MediaWiki standard namespaces $this->priorities[NS_MAIN] = '1.0'; $this->priorities[NS_TALK] = '0.1'; @@ -234,7 +244,7 @@ class GenerateSitemap extends Maintenance { * @return String */ function guessPriority( $namespace ) { - return MWNamespace::isMain( $namespace ) ? $this->priorities[GS_MAIN] : $this->priorities[GS_TALK]; + return MWNamespace::isMain( $namespace ) ? $this->priorities[self::GS_MAIN] : $this->priorities[self::GS_TALK]; } /** @@ -271,7 +281,7 @@ class GenerateSitemap extends Maintenance { $i = $smcount = 0; $fns = $wgContLang->getFormattedNsText( $namespace ); - $this->output( "$namespace ($fns)" ); + $this->output( "$namespace ($fns)\n" ); foreach ( $res as $row ) { if ( $i++ === 0 || $i === $this->url_limit + 1 || $length + $this->limit[1] + $this->limit[2] > $this->size_limit ) { if ( $this->file !== false ) { @@ -288,7 +298,7 @@ class GenerateSitemap extends Maintenance { } $title = Title::makeTitle( $row->page_namespace, $row->page_title ); $date = wfTimestamp( TS_ISO_8601, $row->page_touched ); - $entry = $this->fileEntry( $title->getFullURL(), $date, $this->priority( $namespace ) ); + $entry = $this->fileEntry( $title->getCanonicalURL(), $date, $this->priority( $namespace ) ); $length += strlen( $entry ); $this->write( $this->file, $entry ); // generate pages for language variants @@ -296,7 +306,7 @@ class GenerateSitemap extends Maintenance { $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 ) ); + $entry = $this->fileEntry( $title->getCanonicalURL( '', $vCode ), $date, $this->priority( $namespace ) ); $length += strlen( $entry ); $this->write( $this->file, $entry ); } @@ -349,7 +359,7 @@ class GenerateSitemap extends Maintenance { */ function sitemapFilename( $namespace, $count ) { $ext = $this->compress ? '.gz' : ''; - return "sitemap-" . wfWikiID() . "-NS_$namespace-$count.xml$ext"; + return "sitemap-{$this->identifier}-NS_$namespace-$count.xml$ext"; } /** @@ -441,11 +451,12 @@ class GenerateSitemap extends Maintenance { * Populate $this->limit */ function generateLimit( $namespace ) { + // bug 17961: make a title with the longest possible URL in this namespace $title = Title::makeTitle( $namespace, str_repeat( "\xf0\xa8\xae\x81", 63 ) . "\xe5\x96\x83" ); $this->limit = array( strlen( $this->openFile() ), - strlen( $this->fileEntry( $title->getFullUrl(), wfTimestamp( TS_ISO_8601, wfTimestamp() ), $this->priority( $namespace ) ) ), + strlen( $this->fileEntry( $title->getCanonicalURL(), wfTimestamp( TS_ISO_8601, wfTimestamp() ), $this->priority( $namespace ) ) ), strlen( $this->closeFile() ) ); } diff --git a/maintenance/hiphop/compiler.conf b/maintenance/hiphop/compiler.conf new file mode 100644 index 00000000..3e01640d --- /dev/null +++ b/maintenance/hiphop/compiler.conf @@ -0,0 +1,5 @@ +GenerateSourceInfo = true +EnableEval = 2 +AllDynamic = true +EnableHipHopSyntax = true +EnableHipHopExperimentalSyntax = true diff --git a/maintenance/hiphop/extra-files b/maintenance/hiphop/extra-files new file mode 100644 index 00000000..15f48577 --- /dev/null +++ b/maintenance/hiphop/extra-files @@ -0,0 +1,35 @@ +img_auth.php +includes/AjaxFunctions.php +includes/AutoLoader.php +includes/DefaultSettings.php +includes/Defines.php +includes/GlobalFunctions.php +includes/ImageFunctions.php +includes/OutputHandler.php +includes/ProxyTools.php +includes/SeleniumWebSettings.php +includes/Setup.php +includes/StreamFile.php +includes/WebStart.php +includes/filerepo/NullRepo.php +includes/normal/UtfNormalDefines.php +includes/normal/UtfNormalUtil.php +index.php +languages/Names.php +load.php +maintenance/Maintenance.php +maintenance/commandLine.inc +maintenance/doMaintenance.php +maintenance/eval.php +opensearch_desc.php +profileinfo.php +redirect.php +resources/Resources.php +serialized/serialize.php +skins/MonoBook.deps.php +skins/MonoBook.php +skins/Vector.deps.php +skins/Vector.php +thumb.php +trackback.php + diff --git a/maintenance/hiphop/make b/maintenance/hiphop/make new file mode 100644 index 00000000..e792e08b --- /dev/null +++ b/maintenance/hiphop/make @@ -0,0 +1,308 @@ +#!/usr/bin/hphpi -f +<?php + +require( dirname( __FILE__ ) . '/../Maintenance.php' ); + +class MakeHipHop extends Maintenance { + function execute() { + global $wgHipHopBuildDirectory; + + $startTime = time(); + + $thisDir = realpath( dirname( __FILE__ ) ); + $IP = realpath( "$thisDir/../.." ); + if ( strval( $wgHipHopBuildDirectory ) !== '' ) { + $buildDir = $wgHipHopBuildDirectory; + } else { + $buildDir = "$thisDir/build"; + } + $extensionsDir = realpath( MWInit::getExtensionsDirectory() ); + $outDir = "$buildDir/hiphop-output"; + $persistentDir = "$buildDir/persistent"; + + if ( !is_dir( $buildDir ) ) { + mkdir( $buildDir, 0777, true ); + } + if ( !is_dir( $persistentDir ) ) { + mkdir( $persistentDir, 0777, true ); + } + + if ( realpath( "$IP/../phase3" ) !== $IP + || realpath( "$IP/../extensions" ) !== $extensionsDir ) + { + # Set up a fake source directory with the correct layout + $sourceBase = "$buildDir/source"; + $this->setupFakeSourceBase( $IP, $extensionsDir, $sourceBase ); + } else { + $sourceBase = realpath( "$IP/.." ); + unlink( "$buildDir/source" ); + } + + # With the CentOS RPMs, you just get g++44, no g++, so we have to + # use the environment + if ( isset( $_ENV['CXX'] ) ) { + $cxx = $_ENV['CXX']; + } else { + $cxx = 'g++'; + } + + # Create a function that provides the HipHop compiler version, and + # doesn't exist when MediaWiki is invoked in interpreter mode. + $version = str_replace( PHP_EOL, ' ', trim( `hphp --version` ) ); + file_put_contents( + "$buildDir/HipHopCompilerVersion.php", + "<" . "?php\n" . + "function wfHipHopCompilerVersion() {\n" . + "return " . var_export( $version, true ) . ";\n" . + "}\n" + ); + + # Generate the file list + $files = $this->getFileList(); + file_put_contents( + "$buildDir/file-list", + implode( "\n", $files ) . "\n" ); + + # Generate the C++ + passthru( + 'hphp' . + ' --target=cpp' . + ' --format=file' . + ' --input-dir=' . wfEscapeShellArg( $sourceBase ) . + ' --input-list=' . wfEscapeShellArg( "$buildDir/file-list" ) . + ' --inputs=' . wfEscapeShellArg( "$buildDir/HipHopCompilerVersion.php" ) . + ' -c ' . wfEscapeShellArg( "$thisDir/compiler.conf" ) . + ' --parse-on-demand=false' . + ' --program=mediawiki-hphp' . + ' --output-dir=' . wfEscapeShellArg( $outDir ) . + ' --log=3', $ret ); + + if ( $ret ) { + $this->error( "hphp hit an error. Stopping build.\n" ); + exit( 1 ); + } + + # Sanity check, quickly make sure we've got an output directory + if( !is_dir( $outDir ) ) { + $this->error( "No output directory", true ); + } + + # Warn about volatile classes + $this->checkVolatileClasses( $outDir ); + + # Copy the generated C++ files into the source directory for cmake + $iter = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator( $outDir ), + RecursiveIteratorIterator::SELF_FIRST ); + $sourceFiles = array(); + $regenerateMakefile = false; + $numFiles = 0; + $numFilesChanged = 0; + foreach ( $iter as $sourcePath => $file ) { + $name = substr( $sourcePath, strlen( $outDir ) + 1 ); + $sourceFiles[$name] = true; + $destPath = "$persistentDir/$name"; + if ( $file->isDir() ) { + if ( !is_dir( $destPath ) ) { + mkdir( $destPath ); + } + continue; + } + + $numFiles++; + # Remove any files that weren't touched, these may have been removed + # from file-list, we should not compile them + if ( $file->getMTime() < $startTime ) { + if ( file_exists( $destPath ) ) { + unlink( $destPath ); + # Files removed, regenerate the makefile + $regenerateMakefile = true; + } + unlink( $sourcePath ); + $numFilesChanged++; + continue; + } + + if ( file_exists( $destPath ) ) { + $sourceHash = md5( file_get_contents( $sourcePath ) ); + $destHash = md5( file_get_contents( $destPath ) ); + if ( $sourceHash == $destHash ) { + continue; + } + } else { + # New files added, regenerate the makefile + $regenerateMakefile = true; + } + $numFilesChanged++; + copy( $sourcePath, $destPath ); + } + + echo "MediaWiki: $numFilesChanged files changed out of $numFiles\n"; + + if ( !file_exists( "$persistentDir/CMakeLists.txt" ) ) { + # Run cmake for the first time + $regenerateMakefile = true; + } + + # Do our own version of $HPHP_HOME/bin/run.sh, which isn't so broken. + # HipHop's RELEASE mode seems to be stuck always on, so symbols get + # stripped. Also we will try keeping the generated .o files instead of + # throwing away hours of CPU time every time you make a typo. + + chdir( $persistentDir ); + + if ( $regenerateMakefile ) { + copy( $_ENV['HPHP_HOME'] . '/bin/CMakeLists.base.txt', + "$persistentDir/CMakeLists.txt" ); + + if ( file_exists( "$persistentDir/CMakeCache.txt" ) ) { + unlink( "$persistentDir/CMakeCache.txt" ); + } + + $cmd = 'cmake' . + " -D CMAKE_BUILD_TYPE:string=" . wfEscapeShellArg( $GLOBALS['wgHipHopBuildType'] ) . + ' -D PROGRAM_NAME:string=mediawiki-hphp'; + + if ( file_exists( '/usr/bin/ccache' ) ) { + $cmd .= ' -D CMAKE_CXX_COMPILER:string=ccache' . + ' -D CMAKE_CXX_COMPILER_ARG1:string=' . wfEscapeShellArg( $cxx ); + } + + $cmd .= ' .'; + echo "$cmd\n"; + passthru( $cmd ); + } + + # Determine appropriate make concurrency + # Compilation can take a lot of memory, let's assume that that is limiting. + $procs = $this->getNumProcs(); + + # Run make. This is the slow step. + passthru( 'make -j' . wfEscapeShellArg( $procs ) ); + + $elapsed = time() - $startTime; + + echo "Completed in "; + if ( $elapsed >= 3600 ) { + $hours = floor( $elapsed / 3600 ); + echo $hours . 'h '; + $elapsed -= $hours * 3600; + } + if ( $elapsed >= 60 ) { + $minutes = floor( $elapsed / 60 ); + echo $minutes . 'm '; + $elapsed -= $minutes * 60; + } + echo $elapsed . "s\n"; + echo "The MediaWiki executable is at $buildDir/persistent/mediawiki-hphp\n"; + } + + function checkVolatileClasses( $dir ) { + $lines = file( "$dir/sys/dynamic_table_class.cpp" ); + $classes = array(); + foreach ( $lines as $line ) { + if ( preg_match( '/^\s+\(const char \*\)"([^"]*)", \(const char \*\)-1/', $line, $m ) ) { + $classes[] = $m[1]; + } + } + if ( !count( $classes ) ) { + print "No volatile classes found\n"; + return; + } + sort( $classes ); + $classes = array_unique( $classes ); + print "WARNING: The following classes are volatile: " . implode( ', ', $classes ) . "\n"; + } + + function getNumProcs() { + global $wgHipHopCompilerProcs; + if ( $wgHipHopCompilerProcs !== 'detect' ) { + return intval( $wgHipHopCompilerProcs ); + } + + if ( !file_exists( '/proc/meminfo' ) ) { + return 1; + } + $mem = false; + foreach ( file( '/proc/meminfo' ) as $line ) { + if ( preg_match( '/^MemTotal:\s+(\d+)\s+kB/', $line, $m ) ) { + $mem = intval( $m[1] ); + break; + } + } + if ( $mem ) { + // At least one process + return max( 1, floor( $mem / 1000000 ) ); + } else { + return 1; + } + } + + function setupFakeSourceBase( $phase3, $extensions, $dest ) { + if ( !file_exists( $dest ) ) { + mkdir( $dest, 0777, true ); + } + + $this->forceCreateLink( "$dest/phase3", $phase3 ); + $this->forceCreateLink( "$dest/extensions", $extensions ); + } + + function forceCreateLink( $target, $link ) { + if ( file_exists( $target ) ) { + if ( readlink( $target ) === $link ) { + return; + } + unlink( $target ); + } + symlink( $target, $link ); + } + + function getFileList() { + global $wgAutoloadClasses, $wgAutoloadLocalClasses, $wgCompiledFiles; + $inputFiles = array_merge( + array_values( $wgAutoloadClasses ), + array_values( $wgAutoloadLocalClasses ), + $wgCompiledFiles + ); + $processedFiles = array(); + foreach ( $inputFiles as $file ) { + if ( substr( $file, 0, 1 ) === '/' ) { + $processedFiles[] = $this->absoluteToRelative( $file ); + } elseif ( preg_match( '/^extensions/', $file ) ) { + $processedFiles[] = $file; + } else { + $processedFiles[] = "phase3/$file"; + } + } + + $extraCoreFiles = array_map( 'trim', file( dirname( __FILE__ ) . '/extra-files' ) ); + foreach ( $extraCoreFiles as $file ) { + if ( $file === '' ) { + continue; + } + $processedFiles[] = "phase3/$file"; + } + return array_unique( $processedFiles ); + } + + function absoluteToRelative( $file ) { + global $IP; + + $coreBase = realpath( $IP ) . '/'; + $extBase = realpath( MWInit::getExtensionsDirectory() ) . '/'; + $file = realpath( $file ); + + if ( substr( $file, 0, strlen( $extBase ) ) === $extBase ) { + return 'extensions/' . substr( $file, strlen( $extBase ) ); + } elseif ( substr( $file, 0, strlen( $coreBase ) ) === $coreBase ) { + return 'phase3/' . substr( $file, strlen( $coreBase ) ); + } else { + $this->error( "The following file is registered for compilation but is not in \$IP or " . + "\$wgExtensionsDirectory: $file \n" ); + exit( 1 ); + } + } +} + +$maintClass = 'MakeHipHop'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/hiphop/run-server b/maintenance/hiphop/run-server new file mode 100644 index 00000000..0ad43134 --- /dev/null +++ b/maintenance/hiphop/run-server @@ -0,0 +1,75 @@ +#!/usr/bin/hphpi -f +<?php + +require( dirname( __FILE__ ) . '/../Maintenance.php' ); + +class RunHipHopServer extends Maintenance { + function __construct() { + parent::__construct(); + $this->addOption( 'interpret', 'Run in interpreted mode' ); + } + + function execute() { + if ( $this->hasOption( 'interpret' ) ) { + $this->runInterpreted(); + } else { + $this->runCompiled(); + } + } + + function runCompiled() { + global $wgHipHopBuildDirectory; + $thisDir = realpath( dirname( __FILE__ ) ); + $IP = realpath( "$thisDir/../.." ); + if ( strval( $wgHipHopBuildDirectory ) !== '' ) { + $buildDir = $wgHipHopBuildDirectory; + } else { + $buildDir = "$thisDir/build"; + } + + if ( file_exists( "$buildDir/source" ) ) { + $sourceBase = "$buildDir/source"; + } else { + $sourceBase = realpath( "$IP/.." ); + } + + passthru( + 'cd ' . wfEscapeShellArg( $sourceBase ) . " && " . + 'MW_INSTALL_PATH=' . wfEscapeShellArg( $IP ) . ' ' . + wfEscapeShellArg( + "$buildDir/persistent/mediawiki-hphp", + '-c', "$thisDir/server.conf", + '-v', "Server.SourceRoot=$sourceBase", + '-v', "Server.IncludeSearchPaths.0=$sourceBase", + '-v', 'ServerVariables.MW_COMPILED=1', + '--mode=server', + '--port=8080' + ), + $ret + ); + exit( $ret ); + } + + function runInterpreted() { + $thisDir = realpath( dirname( __FILE__ ) ); + $IP = realpath( "$thisDir/../.." ); + $sourceBase = realpath( "$IP/.." ); + + passthru( + 'cd ' . wfEscapeShellArg( $sourceBase ) . " && " . + 'MW_INSTALL_PATH=' . wfEscapeShellArg( $IP ) . ' ' . + wfEscapeShellArg( + 'hphpi', + '-c', "$thisDir/server.conf", + '-v', "Server.SourceRoot=$sourceBase", + '-v', "Server.IncludeSearchPaths.0=$sourceBase", + '--mode=server', + '--port=8080' + ), + $ret + ); + exit( $ret ); + } +} +$maintClass = 'RunHipHopServer'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/hiphop/server.conf b/maintenance/hiphop/server.conf new file mode 100644 index 00000000..16af0f2f --- /dev/null +++ b/maintenance/hiphop/server.conf @@ -0,0 +1,30 @@ +Log { + Level = Warning + UseLogFile = true + NativeStackTrace = true + InjectedStackTrace = true +} +Debug { + FullBacktrace = true + ServerStackTrace = true + ServerErrorMessage = true + TranslateSource = true +} +Server { + EnableStaticContentCache = false + EnableStaticContentFromDisk = false + AlwaysUseRelativePath = true +} +VirtualHost { + * { + ServerName = localhost + Pattern = . + RewriteRules { + * { + pattern = ^/wiki/(.*)$ + to = /phase3/index.php?title=$1 + qsa = true + } + } + } +} diff --git a/maintenance/httpSessionDownload.php b/maintenance/httpSessionDownload.php deleted file mode 100644 index 3c4f16a0..00000000 --- a/maintenance/httpSessionDownload.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php -/* - * Simple entry point to initiate a background download - * - * arguments: - * --sid {$session_id} --usk {$upload_session_key} --wiki {wfWikiId()} - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @ingroup Maintenance - */ - -require_once( dirname( __FILE__ ) . '/Maintenance.php' ); - -class HttpSessionDownload extends Maintenance { - public function __construct() { - parent::__construct(); - $this->mDescription = "Simple entry point to initiate a background download"; - $this->addOption( 'sid', 'Session ID', true, true ); - $this->addOption( 'usk', 'Upload session key', true, true ); - } - - public function execute() { - wfProfileIn( __METHOD__ ); - - // run the download: - Http::doSessionIdDownload( $this->getOption( 'sid' ), $this->getOption( 'usk' ) ); - - // close up shop: - // Execute any deferred updates - wfDoUpdates(); - - // Log what the user did, for book-keeping purposes. - wfLogProfilingData(); - - // Shut down the database before exit - wfGetLBFactory()->shutdown(); - - wfProfileOut( __METHOD__ ); - } -} - -$maintClass = "HttpSessionDownload"; -require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/ibm_db2/patch-categorylinks-better-collation.sql b/maintenance/ibm_db2/patch-categorylinks-better-collation.sql new file mode 100644 index 00000000..312583ac --- /dev/null +++ b/maintenance/ibm_db2/patch-categorylinks-better-collation.sql @@ -0,0 +1,21 @@ +-- +-- patch-categorylinks-better-collation.sql +-- +-- +-- Track category inclusions *used inline* +-- This tracks a single level of category membership +-- (folksonomic tagging, really). +-- +CREATE TABLE categorylinks ( + cl_from BIGINT NOT NULL DEFAULT 0, + -- REFERENCES page(page_id) ON DELETE CASCADE, + cl_to VARCHAR(255) NOT NULL, + -- cl_sortkey has to be at least 86 wide + -- in order to be compatible with the old MySQL schema from MW 1.10 + --cl_sortkey VARCHAR(86), + cl_sortkey VARCHAR(230) FOR BIT DATA NOT NULL , + cl_sortkey_prefix VARCHAR(255) FOR BIT DATA NOT NULL , + cl_timestamp TIMESTAMP(3) NOT NULL, + cl_collation VARCHAR(32) FOR BIT DATA NOT NULL , + cl_type VARCHAR(6) FOR BIT DATA NOT NULL +); diff --git a/maintenance/ibm_db2/patch-change_tag-indexes.sql b/maintenance/ibm_db2/patch-change_tag-indexes.sql new file mode 100644 index 00000000..1621a038 --- /dev/null +++ b/maintenance/ibm_db2/patch-change_tag-indexes.sql @@ -0,0 +1,5 @@ +CREATE UNIQUE INDEX change_tag_rc_tag ON change_tag (ct_rc_id,ct_tag); +CREATE UNIQUE INDEX change_tag_log_tag ON change_tag (ct_log_id,ct_tag); +CREATE UNIQUE INDEX change_tag_rev_tag ON change_tag (ct_rev_id,ct_tag); +-- Covering index, so we can pull all the info only out of the index. +CREATE INDEX change_tag_tag_id ON change_tag (ct_tag,ct_rc_id,ct_rev_id,ct_log_id); diff --git a/maintenance/ibm_db2/patch-change_tag.sql b/maintenance/ibm_db2/patch-change_tag.sql new file mode 100644 index 00000000..3b6f9d54 --- /dev/null +++ b/maintenance/ibm_db2/patch-change_tag.sql @@ -0,0 +1,8 @@ +-- A table to track tags for revisions, logs and recent changes. +CREATE TABLE change_tag ( + ct_rc_id INTEGER, + ct_log_id INTEGER, + ct_rev_id INTEGER, + ct_tag varchar(255) NOT NULL, + ct_params CLOB(64K) INLINE LENGTH 4096 +); diff --git a/maintenance/ibm_db2/patch-change_tag_summary.sql b/maintenance/ibm_db2/patch-change_tag_summary.sql new file mode 100644 index 00000000..768cbfaa --- /dev/null +++ b/maintenance/ibm_db2/patch-change_tag_summary.sql @@ -0,0 +1,7 @@ +-- Rollup table to pull a LIST of tags simply +CREATE TABLE tag_summary ( + ts_rc_id INTEGER, + ts_log_id INTEGER, + ts_rev_id INTEGER, + ts_tags CLOB(64K) INLINE LENGTH 4096 NOT NULL +); diff --git a/maintenance/ibm_db2/patch-change_valid_tag.sql b/maintenance/ibm_db2/patch-change_valid_tag.sql new file mode 100644 index 00000000..9bdcbc92 --- /dev/null +++ b/maintenance/ibm_db2/patch-change_valid_tag.sql @@ -0,0 +1,3 @@ +CREATE TABLE valid_tag ( + vt_tag varchar(255) NOT NULL PRIMARY KEY +); diff --git a/maintenance/ibm_db2/patch-cl_collation-field.sql b/maintenance/ibm_db2/patch-cl_collation-field.sql new file mode 100644 index 00000000..6999dace --- /dev/null +++ b/maintenance/ibm_db2/patch-cl_collation-field.sql @@ -0,0 +1 @@ +ALTER TABLE categorylinks ADD cl_collation VARCHAR(32) FOR BIT DATA NOT NULL diff --git a/maintenance/ibm_db2/patch-cl_sortkey_prefix-field.sql b/maintenance/ibm_db2/patch-cl_sortkey_prefix-field.sql new file mode 100644 index 00000000..58b78147 --- /dev/null +++ b/maintenance/ibm_db2/patch-cl_sortkey_prefix-field.sql @@ -0,0 +1 @@ +ALTER TABLE categorylinks ADD cl_sortkey_prefix VARCHAR(255) FOR BIT DATA NOT NULL diff --git a/maintenance/ibm_db2/patch-cl_type-field.sql b/maintenance/ibm_db2/patch-cl_type-field.sql new file mode 100644 index 00000000..5952c989 --- /dev/null +++ b/maintenance/ibm_db2/patch-cl_type-field.sql @@ -0,0 +1 @@ +ALTER TABLE categorylinks ADD cl_type VARCHAR(6) FOR BIT DATA NOT NULL diff --git a/maintenance/ibm_db2/patch-external_user.sql b/maintenance/ibm_db2/patch-external_user.sql new file mode 100644 index 00000000..96cb8237 --- /dev/null +++ b/maintenance/ibm_db2/patch-external_user.sql @@ -0,0 +1,7 @@ +CREATE TABLE external_user ( + -- Foreign key to user_id + eu_local_id BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + + -- Some opaque identifier provided by the external database + eu_external_id VARCHAR(255) NOT NULL +); diff --git a/maintenance/ibm_db2/patch-ipb_allow_usertalk.sql b/maintenance/ibm_db2/patch-ipb_allow_usertalk.sql new file mode 100644 index 00000000..6274bb22 --- /dev/null +++ b/maintenance/ibm_db2/patch-ipb_allow_usertalk.sql @@ -0,0 +1,23 @@ +CREATE TABLE ipblocks ( + ipb_id INTEGER NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + --DEFAULT nextval('ipblocks_ipb_id_val'), + ipb_address VARCHAR(1024), + ipb_user BIGINT NOT NULL DEFAULT 0, + -- REFERENCES user(user_id) ON DELETE SET NULL, + ipb_by BIGINT NOT NULL DEFAULT 0, + -- REFERENCES user(user_id) ON DELETE CASCADE, + ipb_by_text VARCHAR(255) NOT NULL DEFAULT '', + ipb_reason VARCHAR(1024) NOT NULL, + ipb_timestamp TIMESTAMP(3) NOT NULL, + ipb_auto SMALLINT NOT NULL DEFAULT 0, + ipb_anon_only SMALLINT NOT NULL DEFAULT 0, + ipb_create_account SMALLINT NOT NULL DEFAULT 1, + ipb_enable_autoblock SMALLINT NOT NULL DEFAULT 1, + ipb_expiry TIMESTAMP(3) NOT NULL, + ipb_range_start VARCHAR(1024), + ipb_range_end VARCHAR(1024), + ipb_deleted SMALLINT NOT NULL DEFAULT 0, + ipb_block_email SMALLINT NOT NULL DEFAULT 0, + ipb_allow_usertalk SMALLINT NOT NULL DEFAULT 0 + +); diff --git a/maintenance/ibm_db2/patch-iw_api-field.sql b/maintenance/ibm_db2/patch-iw_api-field.sql new file mode 100644 index 00000000..dd732a58 --- /dev/null +++ b/maintenance/ibm_db2/patch-iw_api-field.sql @@ -0,0 +1 @@ +ALTER TABLE interwiki ADD iw_api CLOB(64K) INLINE LENGTH 4096 NOT NULL diff --git a/maintenance/ibm_db2/patch-iw_api_and_wikiid.sql b/maintenance/ibm_db2/patch-iw_api_and_wikiid.sql new file mode 100644 index 00000000..1b1e3592 --- /dev/null +++ b/maintenance/ibm_db2/patch-iw_api_and_wikiid.sql @@ -0,0 +1,8 @@ +CREATE TABLE interwiki ( + iw_prefix VARCHAR(32) NOT NULL UNIQUE, + iw_url CLOB(64K) INLINE LENGTH 4096 NOT NULL, + iw_api CLOB(64K) INLINE LENGTH 4096 NOT NULL, + iw_wikiid varchar(64) NOT NULL, + iw_local SMALLINT NOT NULL, + iw_trans SMALLINT NOT NULL DEFAULT 0 +); diff --git a/maintenance/ibm_db2/patch-iw_wikiid-field.sql b/maintenance/ibm_db2/patch-iw_wikiid-field.sql new file mode 100644 index 00000000..fe49e3c0 --- /dev/null +++ b/maintenance/ibm_db2/patch-iw_wikiid-field.sql @@ -0,0 +1 @@ +ALTER TABLE interwiki ADD iw_wikiid varchar(64) NOT NULL diff --git a/maintenance/ibm_db2/patch-iwlinks.sql b/maintenance/ibm_db2/patch-iwlinks.sql new file mode 100644 index 00000000..2902512f --- /dev/null +++ b/maintenance/ibm_db2/patch-iwlinks.sql @@ -0,0 +1,7 @@ +CREATE TABLE "IWLINKS" +( +"IWL_FROM" INT NOT NULL , +"IWL_PREFIX" VARCHAR(20) FOR BIT DATA NOT NULL , +"IWL_TITLE" VARCHAR(255) FOR BIT DATA NOT NULL +) +; diff --git a/maintenance/ibm_db2/patch-l10n_cache.sql b/maintenance/ibm_db2/patch-l10n_cache.sql new file mode 100644 index 00000000..49ebed2b --- /dev/null +++ b/maintenance/ibm_db2/patch-l10n_cache.sql @@ -0,0 +1,8 @@ +CREATE TABLE l10n_cache ( + -- Language code + lc_lang VARCHAR(32) NOT NULL, + -- Cache key + lc_key VARCHAR(255) NOT NULL, + -- Value + lc_value CLOB(16M) INLINE LENGTH 4096 NOT NULL +); diff --git a/maintenance/ibm_db2/patch-log_search-rename-index.sql b/maintenance/ibm_db2/patch-log_search-rename-index.sql new file mode 100644 index 00000000..a6a696e1 --- /dev/null +++ b/maintenance/ibm_db2/patch-log_search-rename-index.sql @@ -0,0 +1,8 @@ +CREATE TABLE log_search ( + -- The type of ID (rev ID, log ID, rev TIMESTAMP(3), username) + ls_field VARCHAR(32) FOR BIT DATA NOT NULL, + -- The value of the ID + ls_value varchar(255) NOT NULL, + -- Key to log_id + ls_log_id BIGINT NOT NULL default 0 +); diff --git a/maintenance/ibm_db2/patch-log_search.sql b/maintenance/ibm_db2/patch-log_search.sql new file mode 100644 index 00000000..a6a696e1 --- /dev/null +++ b/maintenance/ibm_db2/patch-log_search.sql @@ -0,0 +1,8 @@ +CREATE TABLE log_search ( + -- The type of ID (rev ID, log ID, rev TIMESTAMP(3), username) + ls_field VARCHAR(32) FOR BIT DATA NOT NULL, + -- The value of the ID + ls_value varchar(255) NOT NULL, + -- Key to log_id + ls_log_id BIGINT NOT NULL default 0 +); diff --git a/maintenance/ibm_db2/patch-log_user_text.sql b/maintenance/ibm_db2/patch-log_user_text.sql new file mode 100644 index 00000000..3534057a --- /dev/null +++ b/maintenance/ibm_db2/patch-log_user_text.sql @@ -0,0 +1,17 @@ +CREATE TABLE logging ( + log_id BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + --PRIMARY KEY DEFAULT nextval('log_log_id_seq'), + log_type VARCHAR(32) NOT NULL, + log_action VARCHAR(32) NOT NULL, + log_timestamp TIMESTAMP(3) NOT NULL, + log_user BIGINT NOT NULL DEFAULT 0, + -- REFERENCES user(user_id) ON DELETE SET NULL, + -- Name of the user who performed this action + log_user_text VARCHAR(255) NOT NULL default '', + log_namespace SMALLINT NOT NULL, + log_title VARCHAR(255) NOT NULL, + log_page BIGINT, + log_comment VARCHAR(255), + log_params CLOB(64K) INLINE LENGTH 4096, + log_deleted SMALLINT NOT NULL DEFAULT 0 +); diff --git a/maintenance/ibm_db2/patch-module_deps.sql b/maintenance/ibm_db2/patch-module_deps.sql new file mode 100644 index 00000000..5058d1f5 --- /dev/null +++ b/maintenance/ibm_db2/patch-module_deps.sql @@ -0,0 +1,6 @@ +CREATE TABLE "MODULE_DEPS" ( +"MD_MODULE" VARCHAR(255) FOR BIT DATA NOT NULL , +"MD_SKIN" VARCHAR(32) FOR BIT DATA NOT NULL , +"MD_DEPS" CLOB(16M) INLINE LENGTH 4096 NOT NULL +) +; diff --git a/maintenance/ibm_db2/patch-msg_resource.sql b/maintenance/ibm_db2/patch-msg_resource.sql new file mode 100644 index 00000000..58b3dd6c --- /dev/null +++ b/maintenance/ibm_db2/patch-msg_resource.sql @@ -0,0 +1,8 @@ +CREATE TABLE "MSG_RESOURCE" +( +"MR_RESOURCE" VARCHAR(255) FOR BIT DATA NOT NULL , +"MR_LANG" VARCHAR(32) FOR BIT DATA NOT NULL , +"MR_BLOB" BLOB NOT NULL , +"MR_TIMESTAMP" TIMESTAMP(3) NOT NULL +) +; diff --git a/maintenance/ibm_db2/patch-msg_resource_links.sql b/maintenance/ibm_db2/patch-msg_resource_links.sql new file mode 100644 index 00000000..4c0ff918 --- /dev/null +++ b/maintenance/ibm_db2/patch-msg_resource_links.sql @@ -0,0 +1,6 @@ +CREATE TABLE "MSG_RESOURCE_LINKS" +( +"MRL_RESOURCE" VARCHAR(255) FOR BIT DATA NOT NULL , +"MRL_MESSAGE" VARCHAR(255) FOR BIT DATA NOT NULL +) +; diff --git a/maintenance/ibm_db2/patch-rd_interwiki.sql b/maintenance/ibm_db2/patch-rd_interwiki.sql new file mode 100644 index 00000000..c162548c --- /dev/null +++ b/maintenance/ibm_db2/patch-rd_interwiki.sql @@ -0,0 +1,8 @@ +CREATE TABLE redirect ( + rd_from BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + --REFERENCES page(page_id) ON DELETE CASCADE, + rd_namespace SMALLINT NOT NULL DEFAULT 0, + rd_title VARCHAR(255) NOT NULL DEFAULT '', + rd_interwiki varchar(32), + rd_fragment VARCHAR(255) +); diff --git a/maintenance/ibm_db2/patch-ss_active_users.sql b/maintenance/ibm_db2/patch-ss_active_users.sql new file mode 100644 index 00000000..f0e6d145 --- /dev/null +++ b/maintenance/ibm_db2/patch-ss_active_users.sql @@ -0,0 +1,11 @@ +CREATE TABLE site_stats ( + ss_row_id BIGINT NOT NULL UNIQUE, + ss_total_views BIGINT DEFAULT 0, + ss_total_edits BIGINT DEFAULT 0, + ss_good_articles BIGINT DEFAULT 0, + ss_total_pages INTEGER DEFAULT -1, + ss_users INTEGER DEFAULT -1, + ss_active_users INTEGER DEFAULT -1, + ss_admins INTEGER DEFAULT -1, + ss_images INTEGER DEFAULT 0 +); diff --git a/maintenance/ibm_db2/patch-ul_value.sql b/maintenance/ibm_db2/patch-ul_value.sql new file mode 100644 index 00000000..cd00f8e0 --- /dev/null +++ b/maintenance/ibm_db2/patch-ul_value.sql @@ -0,0 +1,3 @@ +CREATE TABLE updatelog ( + ul_key VARCHAR(255) NOT NULL PRIMARY KEY +); diff --git a/maintenance/ibm_db2/patch-uq61_msg_resource_links.sql b/maintenance/ibm_db2/patch-uq61_msg_resource_links.sql new file mode 100644 index 00000000..d9185c0a --- /dev/null +++ b/maintenance/ibm_db2/patch-uq61_msg_resource_links.sql @@ -0,0 +1,7 @@ +CREATE UNIQUE INDEX "UQ61_MSG_RESOURCE_LINKS" ON "MSG_RESOURCE_LINKS" +( +"MRL_MESSAGE", +"MRL_RESOURCE" +) +ALLOW REVERSE SCANS +; diff --git a/maintenance/ibm_db2/patch-uq81_msg_resource.sql b/maintenance/ibm_db2/patch-uq81_msg_resource.sql new file mode 100644 index 00000000..8ed85379 --- /dev/null +++ b/maintenance/ibm_db2/patch-uq81_msg_resource.sql @@ -0,0 +1,7 @@ +CREATE UNIQUE INDEX "UQ81_MSG_RESOURCE" ON "MSG_RESOURCE" +( +"MR_RESOURCE" +,"MR_LANG" +) +ALLOW REVERSE SCANS +; diff --git a/maintenance/ibm_db2/patch-uq96_module_deps.sql b/maintenance/ibm_db2/patch-uq96_module_deps.sql new file mode 100644 index 00000000..e0cc879a --- /dev/null +++ b/maintenance/ibm_db2/patch-uq96_module_deps.sql @@ -0,0 +1,7 @@ +CREATE UNIQUE INDEX "UQ96_MODULE_DEPS" ON "MODULE_DEPS" +( +"MD_MODULE" +,"MD_SKIN" +) +ALLOW REVERSE SCANS +; diff --git a/maintenance/ibm_db2/patch-user_properties.sql b/maintenance/ibm_db2/patch-user_properties.sql new file mode 100644 index 00000000..72dcd792 --- /dev/null +++ b/maintenance/ibm_db2/patch-user_properties.sql @@ -0,0 +1,10 @@ +CREATE TABLE user_properties ( + -- Foreign key to user.user_id + up_user BIGINT NOT NULL, + + -- Name of the option being saved. This is indexed for bulk lookup. + up_property VARCHAR(32) FOR BIT DATA NOT NULL, + + -- Property value as a string. + up_value CLOB(64K) INLINE LENGTH 4096 +); diff --git a/maintenance/ibm_db2/tables.sql b/maintenance/ibm_db2/tables.sql index 546c871d..261a3a2b 100644 --- a/maintenance/ibm_db2/tables.sql +++ b/maintenance/ibm_db2/tables.sql @@ -9,7 +9,7 @@ CREATE TABLE user ( -- Needs to start with 0 - user_id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0), + user_id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0), user_name VARCHAR(255) NOT NULL UNIQUE, user_real_name VARCHAR(255), user_password VARCHAR(1024), @@ -220,8 +220,12 @@ CREATE TABLE categorylinks ( cl_to VARCHAR(255) NOT NULL, -- cl_sortkey has to be at least 86 wide -- in order to be compatible with the old MySQL schema from MW 1.10 - cl_sortkey VARCHAR(86), - cl_timestamp TIMESTAMP(3) NOT NULL + --cl_sortkey VARCHAR(86), + cl_sortkey VARCHAR(230) FOR BIT DATA NOT NULL , + cl_sortkey_prefix VARCHAR(255) FOR BIT DATA NOT NULL , + cl_timestamp TIMESTAMP(3) NOT NULL, + cl_collation VARCHAR(32) FOR BIT DATA NOT NULL , + cl_type VARCHAR(6) FOR BIT DATA NOT NULL ); CREATE UNIQUE INDEX cl_from ON categorylinks (cl_from, cl_to); CREATE INDEX cl_sortkey ON categorylinks (cl_to, cl_sortkey, cl_from); @@ -441,18 +445,11 @@ CREATE TABLE watchlist ( CREATE UNIQUE INDEX wl_user_namespace_title ON watchlist (wl_namespace, wl_title, wl_user); -CREATE TABLE math ( - math_inputhash VARCHAR(16) FOR BIT DATA NOT NULL UNIQUE, - math_outputhash VARCHAR(16) FOR BIT DATA NOT NULL, - math_html_conservativeness SMALLINT NOT NULL, - math_html CLOB(64K) INLINE LENGTH 4096, - math_mathml CLOB(64K) INLINE LENGTH 4096 -); - - CREATE TABLE interwiki ( iw_prefix VARCHAR(32) NOT NULL UNIQUE, iw_url CLOB(64K) INLINE LENGTH 4096 NOT NULL, + iw_api CLOB(64K) INLINE LENGTH 4096 NOT NULL, + iw_wikiid varchar(64) NOT NULL, iw_local SMALLINT NOT NULL, iw_trans SMALLINT NOT NULL DEFAULT 0 ); @@ -489,7 +486,7 @@ CREATE INDEX querycachetwo_titletwo ON querycachetwo (qcc_type,qcc_namespacetw CREATE TABLE objectcache ( keyname VARCHAR(255) NOT NULL UNIQUE, -- was nullable value CLOB(16M) INLINE LENGTH 4096 NOT NULL DEFAULT '', - exptime TIMESTAMP(3) NOT NULL + exptime TIMESTAMP(3) NOT NULL ); CREATE INDEX objectcacache_exptime ON objectcache (exptime); @@ -686,3 +683,58 @@ CREATE TABLE l10n_cache ( ); CREATE INDEX lc_lang_key ON l10n_cache (lc_lang, lc_key); + +CREATE TABLE "MSG_RESOURCE_LINKS" +( +"MRL_RESOURCE" VARCHAR(255) FOR BIT DATA NOT NULL , +"MRL_MESSAGE" VARCHAR(255) FOR BIT DATA NOT NULL +) +; + +CREATE UNIQUE INDEX "UQ61_MSG_RESOURCE_LINKS" ON "MSG_RESOURCE_LINKS" +( +"MRL_MESSAGE", +"MRL_RESOURCE" +) +ALLOW REVERSE SCANS +; + +CREATE TABLE "MSG_RESOURCE" +( +"MR_RESOURCE" VARCHAR(255) FOR BIT DATA NOT NULL , +"MR_LANG" VARCHAR(32) FOR BIT DATA NOT NULL , +"MR_BLOB" BLOB NOT NULL , +"MR_TIMESTAMP" TIMESTAMP(3) NOT NULL +) +; + +CREATE UNIQUE INDEX "UQ81_MSG_RESOURCE" ON "MSG_RESOURCE" +( +"MR_RESOURCE" +,"MR_LANG" +) +ALLOW REVERSE SCANS +; + +CREATE TABLE "MODULE_DEPS" ( +"MD_MODULE" VARCHAR(255) FOR BIT DATA NOT NULL , +"MD_SKIN" VARCHAR(32) FOR BIT DATA NOT NULL , +"MD_DEPS" CLOB(16M) INLINE LENGTH 4096 NOT NULL +) +; + +CREATE UNIQUE INDEX "UQ96_MODULE_DEPS" ON "MODULE_DEPS" +( +"MD_MODULE" +,"MD_SKIN" +) +ALLOW REVERSE SCANS +; + +CREATE TABLE "IWLINKS" +( +"IWL_FROM" INT NOT NULL , +"IWL_PREFIX" VARCHAR(20) FOR BIT DATA NOT NULL , +"IWL_TITLE" VARCHAR(255) FOR BIT DATA NOT NULL +) +; diff --git a/maintenance/importDump.php b/maintenance/importDump.php index 5f47635e..c160b036 100644 --- a/maintenance/importDump.php +++ b/maintenance/importDump.php @@ -22,24 +22,108 @@ * @ingroup Maintenance */ -$optionsWithArgs = array( 'report' ); - -require_once( dirname( __FILE__ ) . '/commandLine.inc' ); +require_once( dirname( __FILE__ ) . '/Maintenance.php' ); /** * @ingroup Maintenance */ -class BackupReader { +class BackupReader extends Maintenance { var $reportingInterval = 100; - var $reporting = true; var $pageCount = 0; var $revCount = 0; var $dryRun = false; - var $debug = false; var $uploads = false; + var $imageBasePath = false; + var $nsFilter = false; function __construct() { + parent::__construct(); + $gz = in_array('compress.zlib', stream_get_wrappers()) ? 'ok' : '(disabled; requires PHP zlib module)'; + $bz2 = in_array('compress.bzip2', stream_get_wrappers()) ? 'ok' : '(disabled; requires PHP bzip2 module)'; + + $this->mDescription = <<<TEXT +This script reads pages from an XML file as produced from Special:Export or +dumpBackup.php, and saves them into the current wiki. + +Compressed XML files may be read directly: + .gz $gz + .bz2 $bz2 + .7z (if 7za executable is in PATH) + +Note that for very large data sets, importDump.php may be slow; there are +alternate methods which can be much faster for full site restoration: +<http://www.mediawiki.org/wiki/Manual:Importing_XML_dumps> +TEXT; $this->stderr = fopen( "php://stderr", "wt" ); + $this->addOption( 'report', + 'Report position and speed after every n pages processed', false, true ); + $this->addOption( 'namespaces', + 'Import only the pages from namespaces belonging to the list of ' . + 'pipe-separated namespace names or namespace indexes', false, true ); + $this->addOption( 'dry-run', 'Parse dump without actually importing pages' ); + $this->addOption( 'debug', 'Output extra verbose debug information' ); + $this->addOption( 'uploads', 'Process file upload data if included (experimental)' ); + $this->addOption( 'image-base-path', 'Import files from a specified path', false, true ); + $this->addArg( 'file', 'Dump file to import [else use stdin]', false ); + } + + public function execute() { + if( wfReadOnly() ) { + $this->error( "Wiki is in read-only mode; you'll need to disable it for import to work.", true ); + } + + $this->reportingInterval = intval( $this->getOption( 'report', 100 ) ); + $this->dryRun = $this->hasOption( 'dry-run' ); + $this->uploads = $this->hasOption( 'uploads' ); // experimental! + if ( $this->hasOption( 'image-base-path' ) ) { + $this->imageBasePath = $this->getOption( 'image-base-path' ); + } + if ( $this->hasOption( 'namespaces' ) ) { + $this->setNsfilter( explode( '|', $this->getOption( 'namespaces' ) ) ); + } + + if( $this->hasArg() ) { + $this->importFromFile( $this->getArg() ); + } else { + $this->importFromStdin(); + } + + $this->output( "Done!\n" ); + $this->output( "You might want to run rebuildrecentchanges.php to regenerate RecentChanges\n" ); + } + + function setNsfilter( array $namespaces ) { + if ( count( $namespaces ) == 0 ) { + $this->nsFilter = false; + return; + } + $this->nsFilter = array_unique( array_map( array( $this, 'getNsIndex' ), $namespaces ) ); + } + + private function getNsIndex( $namespace ) { + global $wgContLang; + if ( ( $result = $wgContLang->getNsIndex( $namespace ) ) !== false ) { + return $result; + } + $ns = intval( $namespace ); + if ( strval( $ns ) === $namespace && $wgContLang->getNsText( $ns ) !== false ) { + return $ns; + } + $this->error( "Unknown namespace text / index specified: $namespace", true ); + } + + private function skippedNamespace( $obj ) { + if ( $obj instanceof Title ) { + $ns = $obj->getNamespace(); + } elseif ( $obj instanceof Revision ) { + $ns = $obj->getTitle()->getNamespace(); + } elseif ( $obj instanceof WikiRevision ) { + $ns = $obj->title->getNamespace(); + } else { + echo wfBacktrace(); + $this->error( "Cannot get namespace of object in " . __METHOD__, true ); + } + return is_array( $this->nsFilter ) && !in_array( $ns, $this->nsFilter ); } function reportPage( $page ) { @@ -53,6 +137,10 @@ class BackupReader { return; } + if ( $this->skippedNamespace( $title ) ) { + return; + } + $this->revCount++; $this->report(); @@ -63,6 +151,9 @@ class BackupReader { function handleUpload( $revision ) { if ( $this->uploads ) { + if ( $this->skippedNamespace( $revision ) ) { + return; + } $this->uploadCount++; // $this->report(); $this->progress( "upload: " . $revision->getFilename() ); @@ -77,6 +168,9 @@ class BackupReader { } function handleLogItem( $rev ) { + if ( $this->skippedNamespace( $rev ) ) { + return; + } $this->revCount++; $this->report(); @@ -92,7 +186,7 @@ class BackupReader { } function showReport() { - if ( $this->reporting ) { + if ( $this->mQuiet ) { $delta = wfTime() - $this->startTime; if ( $delta ) { $rate = sprintf( "%.2f", $this->pageCount / $delta ); @@ -102,12 +196,15 @@ class BackupReader { $revrate = '-'; } # Logs dumps don't have page tallies - if ( $this->pageCount ) + if ( $this->pageCount ) { $this->progress( "$this->pageCount ($rate pages/sec $revrate revs/sec)" ); - else + } else { $this->progress( "$this->revCount ($revrate revs/sec)" ); + } } - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); + // XXX: Don't let deferred jobs array get absurdly large (bug 24375) + wfDoUpdates( 'commit' ); } function progress( $string ) { @@ -131,6 +228,9 @@ class BackupReader { function importFromStdin() { $file = fopen( 'php://stdin', 'rt' ); + if( posix_isatty( $file ) ) { + $this->maybeHelp( true ); + } return $this->importFromHandle( $file ); } @@ -140,7 +240,9 @@ class BackupReader { $source = new ImportStreamSource( $handle ); $importer = new WikiImporter( $source ); - $importer->setDebug( $this->debug ); + if( $this->hasOption( 'debug' ) ) { + $importer->setDebug( true ); + } $importer->setPageCallback( array( &$this, 'reportPage' ) ); $this->importCallback = $importer->setRevisionCallback( array( &$this, 'handleRevision' ) ); @@ -148,6 +250,12 @@ class BackupReader { array( &$this, 'handleUpload' ) ); $this->logItemCallback = $importer->setLogItemCallback( array( &$this, 'handleLogItem' ) ); + if ( $this->uploads ) { + $importer->setImportUploads( true ); + } + if ( $this->imageBasePath ) { + $importer->setImageBasePath( $this->imageBasePath ); + } if ( $this->dryRun ) { $importer->setPageOutCallback( null ); @@ -157,33 +265,5 @@ class BackupReader { } } -if ( wfReadOnly() ) { - wfDie( "Wiki is in read-only mode; you'll need to disable it for import to work.\n" ); -} - -$reader = new BackupReader(); -if ( isset( $options['quiet'] ) ) { - $reader->reporting = false; -} -if ( isset( $options['report'] ) ) { - $reader->reportingInterval = intval( $options['report'] ); -} -if ( isset( $options['dry-run'] ) ) { - $reader->dryRun = true; -} -if ( isset( $options['debug'] ) ) { - $reader->debug = true; -} -if ( isset( $options['uploads'] ) ) { - $reader->uploads = true; // experimental! -} - -if ( isset( $args[0] ) ) { - $result = $reader->importFromFile( $args[0] ); -} else { - $result = $reader->importFromStdin(); -} - -echo "Done!\n"; -echo "You might want to run rebuildrecentchanges.php to regenerate\n"; -echo "the recentchanges page.\n"; +$maintClass = 'BackupReader'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/importImages.inc b/maintenance/importImages.inc index ad88b07c..5d35e2c0 100644 --- a/maintenance/importImages.inc +++ b/maintenance/importImages.inc @@ -1,8 +1,22 @@ <?php - /** * Support functions for the importImages script * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance * @author Rob Church <robchur@gmail.com> @@ -40,7 +54,7 @@ function findFiles( $dir, $exts ) { /** * Split a filename into filename and extension * - * @param $filename Filename + * @param $filename string Filename * @return array */ function splitFilename( $filename ) { diff --git a/maintenance/importImages.php b/maintenance/importImages.php index befbe64d..f3b10ea9 100644 --- a/maintenance/importImages.php +++ b/maintenance/importImages.php @@ -11,6 +11,21 @@ * - fetch metadata from source wiki for each file to import. * - commit the fetched metadata to the destination wiki while submitting. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance * @author Rob Church <robchur@gmail.com> @@ -81,7 +96,7 @@ if ( count( $args ) > 0 ) { die( "failed to read comment file: {$options['comment-file']}\n" ); } } - else if ( isset( $options['comment'] ) ) { + elseif ( isset( $options['comment'] ) ) { $comment = $options['comment']; } @@ -197,7 +212,9 @@ if ( count( $args ) > 0 ) { } else { $archive = $image->publish( $file ); if ( !$archive->isGood() ) { - echo( "failed.\n" ); + echo( "failed. (" . + $archive->getWikiText() . + ")\n" ); $failed++; continue; } @@ -224,7 +241,7 @@ if ( count( $args ) > 0 ) { if ( isset( $options['dry'] ) ) { echo( "done.\n" ); - } else if ( $image->recordUpload( $archive->value, $commentText, $license ) ) { + } elseif ( $image->recordUpload( $archive->value, $commentText, $license ) ) { # We're done! echo( "done.\n" ); if ( $doProtect ) { @@ -232,8 +249,8 @@ if ( count( $args ) > 0 ) { $article = new Article( $title ); echo "\nWaiting for slaves...\n"; // Wait for slaves. - sleep( 2.0 ); - wfWaitForSlaves( 1.0 ); + sleep( 2.0 ); # Why this sleep? + wfWaitForSlaves(); echo( "\nSetting image restrictions ... " ); if ( $article->updateRestrictions( $restrictions ) ) @@ -243,7 +260,7 @@ if ( count( $args ) > 0 ) { } } else { - echo( "failed.\n" ); + echo( "failed. (at recordUpload stage)\n" ); $svar = 'failed'; } diff --git a/maintenance/importSiteScripts.php b/maintenance/importSiteScripts.php new file mode 100644 index 00000000..849c7b1b --- /dev/null +++ b/maintenance/importSiteScripts.php @@ -0,0 +1,76 @@ +<?php +/** + * Maintenance script to import all scripts in the MediaWiki namespace from a + * local site. + */ +require_once( dirname( __FILE__ ) . '/Maintenance.php' ); + +class ImportSiteScripts extends Maintenance { + public function __construct() { + parent::__construct(); + $this->mDescription = 'Import site scripts from a site'; + $this->addArg( 'api', 'API base url' ); + $this->addArg( 'index', 'index.php base url' ); + $this->addOption( 'username', 'User name of the script importer' ); + } + + public function execute() { + global $wgUser; + $wgUser = User::newFromName( $this->getOption( 'username', 'ScriptImporter' ) ); + + $baseUrl = $this->getArg( 1 ); + $pageList = $this->fetchScriptList(); + $this->output( 'Importing ' . count( $pageList ) . " pages\n" ); + + foreach ( $pageList as $page ) { + $this->output( "Importing $page\n" ); + $url = wfAppendQuery( $baseUrl, array( + 'action' => 'raw', + 'title' => "MediaWiki:{$page}" ) ); + $text = Http::get( $url ); + + $title = Title::makeTitleSafe( NS_MEDIAWIKI, $page ); + $article = new Article( $title ); + $article->doEdit( $text, "Importing from $url", 0 ); + } + + } + + protected function fetchScriptList() { + $data = array( + 'action' => 'query', + 'format' => 'php',//'json', + 'list' => 'allpages', + 'apnamespace' => '8', + 'aplimit' => '500', + ); + $baseUrl = $this->getArg( 0 ); + $pages = array(); + + do { + $url = wfAppendQuery( $baseUrl, $data ); + $strResult = Http::get( $url ); + //$result = FormatJson::decode( $strResult ); // Still broken + $result = unserialize( $strResult ); + + if ( !empty( $result['query']['allpages'] ) ) { + foreach ( $result['query']['allpages'] as $page ) { + if ( substr( $page['title'], -3 ) === '.js' ) { + strtok( $page['title'], ':' ); + $pages[] = strtok( '' ); + } + } + } + if ( !empty( $result['query-continue'] ) ) { + $data['apfrom'] = $result['query-continue']['allpages']['apfrom']; + $this->output( "Fetching new batch from {$data['apfrom']}\n" ); + } + } while ( isset( $result['query-continue'] ) ); + + return $pages; + + } +} + +$maintClass = 'ImportSiteScripts'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/importTextFile.php b/maintenance/importTextFile.php index 3b77eb5f..b78ae039 100644 --- a/maintenance/importTextFile.php +++ b/maintenance/importTextFile.php @@ -4,6 +4,21 @@ * Maintenance script allows creating or editing pages using * the contents of a text file * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance * @author Rob Church <robchur@gmail.com> diff --git a/maintenance/importUseModWiki.php b/maintenance/importUseModWiki.php index bff4cd02..a28d57a5 100644 --- a/maintenance/importUseModWiki.php +++ b/maintenance/importUseModWiki.php @@ -1,5 +1,4 @@ <?php - /** * Import data from a UseModWiki into a MediaWiki wiki * 2003-02-09 Brion VIBBER <brion@pobox.com> @@ -21,45 +20,69 @@ * schema changes. * 2005-03-14 * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @todo document * @file * @ingroup Maintenance */ -if ( php_sapi_name() != 'cli' ) { - echo "Please customize the settings and run me from the command line."; - die( -1 ); -} - -/** Set these correctly! */ -$wgImportEncoding = "CP1252"; /* We convert all to UTF-8 */ -$wgRootDirectory = "/kalman/Projects/wiki2002/wiki/lib-http/db/wiki"; +require_once( "Maintenance.php" ); -/* On a large wiki, you might run out of memory */ -@ini_set( 'memory_limit', '40M' ); +class ImportUseModWiki extends Maintenance { -/* globals */ -$wgFieldSeparator = "\xb3"; # Some wikis may use different char - $FS = $wgFieldSeparator ; - $FS1 = $FS . "1" ; - $FS2 = $FS . "2" ; - $FS3 = $FS . "3" ; + private $encoding, $rootDirectory = ''; -# Unicode sanitization tools -require_once( dirname( dirname( __FILE__ ) ) . '/includes/normal/UtfNormal.php' ); - -$usercache = array(); + /** + * Field separators + * @var String + */ + private $FS1, $FS2, $FS3 = ''; -importPages(); + /** + * @var Array + */ + private $usercache, $nowiki = array(); -# ------------------------------------------------------------------------------ + public function __construct() { + parent::__construct(); + $this->mDescription = "Import pages from UseMod wikis"; + $this->addOption( 'encoding', 'Encoding of the imported text, default CP1252', false, true ); + /** + * If UseModWiki's New File System is used: + * $NewFS = 1; # 1 = new multibyte $FS, 0 = old $FS + * Use "\xb3"; for the Old File System + * Changed with UTF-8 UseModWiki + * http://www.usemod.com/cgi-bin/wiki.pl?SupportForUtf8 + * http://www.usemod.com/cgi-bin/wiki.pl?WikiBugs/NewFieldSeparatorWronglyTreated + * http://www.meatballwiki.org/wiki/WikiEngine#Q_amp_A + */ + $this->addOption( 'separator', 'Field separator to use, default \x1E\xFF\xFE\x1E', false, true ); + $this->addArg( 'path', 'Path to your UseMod wiki' ); + } -function importPages() -{ - global $wgRootDirectory; + public function execute() { + $this->rootDirectory = $this->getArg(); + $this->encoding = $this->getOption( 'encoding', 'CP1252' ); + $sep = $this->getOption( 'separator', "\x1E\xFF\xFE\x1E" ); + $this->FS1 = "{$sep}1"; + $this->FS2 = "{$sep}2"; + $this->FS3 = "{$sep}3"; - $gt = '>'; - echo <<<XML + echo <<<XML <?xml version="1.0" encoding="UTF-8" ?> <mediawiki xmlns="http://www.mediawiki.org/xml/export-0.1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" @@ -75,290 +98,278 @@ XML; 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'other' ); foreach ( $letters as $letter ) { - $dir = "$wgRootDirectory/page/$letter"; + $dir = "{$this->rootDirectory}/page/$letter"; if ( is_dir( $dir ) ) - importPageDirectory( $dir ); + $this->importPageDirectory( $dir ); } echo <<<XML </mediawiki> XML; -} + } -function importPageDirectory( $dir, $prefix = "" ) -{ - echo "\n<!-- Checking page directory " . xmlCommentSafe( $dir ) . " -->\n"; - $mydir = opendir( $dir ); - while ( $entry = readdir( $mydir ) ) { - $m = array(); - if ( preg_match( '/^(.+)\.db$/', $entry, $m ) ) { - echo importPage( $prefix . $m[1] ); - } else { - if ( is_dir( "$dir/$entry" ) ) { - if ( $entry != '.' && $entry != '..' ) { - importPageDirectory( "$dir/$entry", "$entry/" ); - } + private function importPageDirectory( $dir, $prefix = "" ) { + echo "\n<!-- Checking page directory " . $this->xmlCommentSafe( $dir ) . " -->\n"; + $mydir = opendir( $dir ); + while ( $entry = readdir( $mydir ) ) { + $m = array(); + if ( preg_match( '/^(.+)\.db$/', $entry, $m ) ) { + echo $this->importPage( $prefix . $m[1] ); } else { - echo "<!-- File '" . xmlCommentSafe( $entry ) . "' doesn't seem to contain an article. Skipping. -->\n"; + if ( is_dir( "$dir/$entry" ) ) { + if ( $entry != '.' && $entry != '..' ) { + $this->importPageDirectory( "$dir/$entry", "$entry/" ); + } + } else { + echo "<!-- File '" . $this->xmlCommentSafe( $entry ) . "' doesn't seem to contain an article. Skipping. -->\n"; + } } } } -} - -# ------------------------------------------------------------------------------ - -/* fetch_ functions - Grab a given item from the database - */ - -function useModFilename( $title ) { - $c = substr( $title, 0, 1 ); - if ( preg_match( '/[A-Z]/i', $c ) ) { - return strtoupper( $c ) . "/$title"; - } - return "other/$title"; -} - -function fetchPage( $title ) -{ - global $FS1, $FS2, $FS3, $wgRootDirectory; - - $fname = $wgRootDirectory . "/page/" . useModFilename( $title ) . ".db"; - if ( !file_exists( $fname ) ) { - echo "Couldn't open file '$fname' for page '$title'.\n"; - die( -1 ); + private function useModFilename( $title ) { + $c = substr( $title, 0, 1 ); + if ( preg_match( '/[A-Z]/i', $c ) ) { + return strtoupper( $c ) . "/$title"; + } + return "other/$title"; } - $page = splitHash( $FS1, file_get_contents( $fname ) ); - $section = splitHash( $FS2, $page["text_default"] ); - $text = splitHash( $FS3, $section["data"] ); - - return array2object( array( "text" => $text["text"] , "summary" => $text["summary"] , - "minor" => $text["minor"] , "ts" => $section["ts"] , - "username" => $section["username"] , "host" => $section["host"] ) ); -} - -function fetchKeptPages( $title ) -{ - global $FS1, $FS2, $FS3, $wgRootDirectory; - - $fname = $wgRootDirectory . "/keep/" . useModFilename( $title ) . ".kp"; - if ( !file_exists( $fname ) ) return array(); + private function fetchPage( $title ) { + $fname = $this->rootDirectory . "/page/" . $this->useModFilename( $title ) . ".db"; + if ( !file_exists( $fname ) ) { + echo "Couldn't open file '$fname' for page '$title'.\n"; + die( -1 ); + } - $keptlist = explode( $FS1, file_get_contents( $fname ) ); - array_shift( $keptlist ); # Drop the junk at beginning of file + $page = $this->splitHash( $this->FS1, file_get_contents( $fname ) ); + $section = $this->splitHash( $this->FS2, $page["text_default"] ); + $text = $this->splitHash( $this->FS3, $section["data"] ); - $revisions = array(); - foreach ( $keptlist as $rev ) { - $section = splitHash( $FS2, $rev ); - $text = splitHash( $FS3, $section["data"] ); - if ( $text["text"] && $text["minor"] != "" && ( $section["ts"] * 1 > 0 ) ) { - array_push( $revisions, array2object( array ( "text" => $text["text"] , "summary" => $text["summary"] , - "minor" => $text["minor"] , "ts" => $section["ts"] , - "username" => $section["username"] , "host" => $section["host"] ) ) ); - } else { - echo "<!-- skipped a bad old revision -->\n"; - } + return $this->array2object( array( "text" => $text["text"] , "summary" => $text["summary"] , + "minor" => $text["minor"] , "ts" => $section["ts"] , + "username" => $section["username"] , "host" => $section["host"] ) ); } - return $revisions; -} -function splitHash ( $sep , $str ) { - $temp = explode ( $sep , $str ) ; - $ret = array () ; - for ( $i = 0; $i + 1 < count ( $temp ) ; $i++ ) { - $ret[$temp[$i]] = $temp[++$i] ; + private function fetchKeptPages( $title ) { + $fname = $this->rootDirectory . "/keep/" . $this->useModFilename( $title ) . ".kp"; + if ( !file_exists( $fname ) ) return array(); + + $keptlist = explode( $this->FS1, file_get_contents( $fname ) ); + array_shift( $keptlist ); # Drop the junk at beginning of file + + $revisions = array(); + foreach ( $keptlist as $rev ) { + $section = $this->splitHash( $this->FS2, $rev ); + $text = $this->splitHash( $this->FS3, $section["data"] ); + if ( $text["text"] && $text["minor"] != "" && ( $section["ts"] * 1 > 0 ) ) { + array_push( $revisions, $this->array2object( array ( "text" => $text["text"] , "summary" => $text["summary"] , + "minor" => $text["minor"] , "ts" => $section["ts"] , + "username" => $section["username"] , "host" => $section["host"] ) ) ); + } else { + echo "<!-- skipped a bad old revision -->\n"; + } } - return $ret ; + return $revisions; } + private function splitHash( $sep , $str ) { + $temp = explode ( $sep , $str ) ; + $ret = array () ; + for ( $i = 0; $i + 1 < count ( $temp ) ; $i++ ) { + $ret[$temp[$i]] = $temp[++$i] ; + } + return $ret ; + } -/* import_ functions - Take a fetched item and produce SQL - */ - -function checkUserCache( $name, $host ) -{ - global $usercache; - - if ( $name ) { - if ( in_array( $name, $usercache ) ) { - $userid = $usercache[$name]; + private function checkUserCache( $name, $host ) { + if ( $name ) { + if ( in_array( $name, $this->usercache ) ) { + $userid = $this->usercache[$name]; + } else { + # If we haven't imported user accounts + $userid = 0; + } + $username = str_replace( '_', ' ', $name ); } else { - # If we haven't imported user accounts $userid = 0; + $username = $host; } - $username = str_replace( '_', ' ', $name ); - } else { - $userid = 0; - $username = $host; + return array( $userid, $username ); } - return array( $userid, $username ); -} - -function importPage( $title ) -{ - echo "\n<!-- Importing page " . xmlCommentSafe( $title ) . " -->\n"; - $page = fetchPage( $title ); - - $newtitle = xmlsafe( str_replace( '_', ' ', recodeText( $title ) ) ); - $munged = mungeFormat( $page->text ); - if ( $munged != $page->text ) { - /** - * Save a *new* revision with the conversion, and put the - * previous last version into the history. - */ - $next = array2object( array( - 'text' => $munged, - 'minor' => 1, - 'username' => 'Conversion script', - 'host' => '127.0.0.1', - 'ts' => time(), - 'summary' => 'link fix', - ) ); - $revisions = array( $page, $next ); - } else { - /** - * Current revision: - */ - $revisions = array( $page ); - } - $xml = <<<XML - <page> - <title>$newtitle</title> + private function importPage( $title ) { + echo "\n<!-- Importing page " . $this->xmlCommentSafe( $title ) . " -->\n"; + $page = $this->fetchPage( $title ); + + $newtitle = $this->xmlsafe( str_replace( '_', ' ', $this->recodeText( $title ) ) ); + + $munged = $this->mungeFormat( $page->text ); + if ( $munged != $page->text ) { + /** + * Save a *new* revision with the conversion, and put the + * previous last version into the history. + */ + $next = $this->array2object( array( + 'text' => $munged, + 'minor' => 1, + 'username' => 'Conversion script', + 'host' => '127.0.0.1', + 'ts' => time(), + 'summary' => 'link fix', + ) ); + $revisions = array( $page, $next ); + } else { + /** + * Current revision: + */ + $revisions = array( $page ); + } + $xml = <<<XML + <page> + <title>$newtitle</title> XML; - # History - $revisions = array_merge( $revisions, fetchKeptPages( $title ) ); - if ( count( $revisions ) == 0 ) { - return NULL; // Was "$sql", which does not appear to be defined. - } + # History + $revisions = array_merge( $revisions, $this->fetchKeptPages( $title ) ); + if ( count( $revisions ) == 0 ) { + return NULL; // Was "$sql", which does not appear to be defined. + } - foreach ( $revisions as $rev ) { - $text = xmlsafe( recodeText( $rev->text ) ); - $minor = ( $rev->minor ? '<minor/>' : '' ); - list( /* $userid */ , $username ) = checkUserCache( $rev->username, $rev->host ); - $username = xmlsafe( recodeText( $username ) ); - $timestamp = xmlsafe( timestamp2ISO8601( $rev->ts ) ); - $comment = xmlsafe( recodeText( $rev->summary ) ); - - $xml .= <<<XML - <revision> - <timestamp>$timestamp</timestamp> - <contributor><username>$username</username></contributor> - $minor - <comment>$comment</comment> - <text>$text</text> - </revision> + foreach ( $revisions as $rev ) { + $text = $this->xmlsafe( $this->recodeText( $rev->text ) ); + $minor = ( $rev->minor ? '<minor/>' : '' ); + list( /* $userid */ , $username ) = $this->checkUserCache( $rev->username, $rev->host ); + $username = $this->xmlsafe( $this->recodeText( $username ) ); + $timestamp = $this->xmlsafe( $this->timestamp2ISO8601( $rev->ts ) ); + $comment = $this->xmlsafe( $this->recodeText( $rev->summary ) ); + + $xml .= <<<XML + <revision> + <timestamp>$timestamp</timestamp> + <contributor><username>$username</username></contributor> + $minor + <comment>$comment</comment> + <text>$text</text> + </revision> XML; + } + $xml .= "</page>\n\n"; + return $xml; } - $xml .= "</page>\n\n"; - return $xml; -} -# Whee! -function recodeText( $string ) { - global $wgImportEncoding; - # For currently latin-1 wikis - $string = str_replace( "\r\n", "\n", $string ); - $string = @iconv( $wgImportEncoding, "UTF-8", $string ); - $string = wfMungeToUtf8( $string ); # Any old Ӓ stuff - return $string; -} - -function wfUtf8Sequence( $codepoint ) { - if ( $codepoint < 0x80 ) return chr( $codepoint ); - if ( $codepoint < 0x800 ) return chr( $codepoint >> 6 & 0x3f | 0xc0 ) . - chr( $codepoint & 0x3f | 0x80 ); - if ( $codepoint < 0x10000 ) return chr( $codepoint >> 12 & 0x0f | 0xe0 ) . - chr( $codepoint >> 6 & 0x3f | 0x80 ) . - chr( $codepoint & 0x3f | 0x80 ); - if ( $codepoint < 0x100000 ) return chr( $codepoint >> 18 & 0x07 | 0xf0 ) . # Double-check this - chr( $codepoint >> 12 & 0x3f | 0x80 ) . - chr( $codepoint >> 6 & 0x3f | 0x80 ) . - chr( $codepoint & 0x3f | 0x80 ); - # Doesn't yet handle outside the BMP - return "&#$codepoint;"; -} + private function recodeText( $string ) { + # For currently latin-1 wikis + $string = str_replace( "\r\n", "\n", $string ); + $string = @iconv( $this->encoding, "UTF-8", $string ); + $string = $this->mungeToUtf8( $string ); # Any old Ӓ stuff + return $string; + } -function wfMungeToUtf8( $string ) { - $string = preg_replace ( '/&#([0-9]+);/e', 'wfUtf8Sequence($1)', $string ); - $string = preg_replace ( '/&#x([0-9a-f]+);/ie', 'wfUtf8Sequence(0x$1)', $string ); - # Should also do named entities here - return $string; -} + /** + * @todo FIXME: Don't use /e + */ + private function mungeToUtf8( $string ) { + $string = preg_replace ( '/&#([0-9]+);/e', 'wfUtf8Sequence($1)', $string ); + $string = preg_replace ( '/&#x([0-9a-f]+);/ie', 'wfUtf8Sequence(0x$1)', $string ); + # Should also do named entities here + return $string; + } -function timestamp2ISO8601( $ts ) { - # 2003-08-05T18:30:02Z - return gmdate( 'Y-m-d', $ts ) . 'T' . gmdate( 'H:i:s', $ts ) . 'Z'; -} + private function timestamp2ISO8601( $ts ) { + # 2003-08-05T18:30:02Z + return gmdate( 'Y-m-d', $ts ) . 'T' . gmdate( 'H:i:s', $ts ) . 'Z'; + } -function xmlsafe( $string ) { /** * The page may contain old data which has not been properly normalized. * Invalid UTF-8 sequences or forbidden control characters will make our * XML output invalid, so be sure to strip them out. + * @param String $string Text to clean up + * @return String */ - $string = UtfNormal::cleanUp( $string ); - - $string = htmlspecialchars( $string ); - return $string; -} - -function xmlCommentSafe( $text ) { - return str_replace( '--', '\\-\\-', xmlsafe( recodeText( $text ) ) ); -} + private function xmlsafe( $string ) { + $string = UtfNormal::cleanUp( $string ); + $string = htmlspecialchars( $string ); + return $string; + } + private function xmlCommentSafe( $text ) { + return str_replace( '--', '\\-\\-', $this->xmlsafe( $this->recodeText( $text ) ) ); + } -function array2object( $arr ) { - $o = (object)0; - foreach ( $arr as $x => $y ) { - $o->$x = $y; + private function array2object( $arr ) { + $o = (object)0; + foreach ( $arr as $x => $y ) { + $o->$x = $y; + } + return $o; } - return $o; -} + /** + * Make CamelCase and /Talk links work + */ + private function mungeFormat( $text ) { + $this->nowiki = array(); + $staged = preg_replace_callback( + '/(<nowiki>.*?<\\/nowiki>|(?:http|https|ftp):\\S+|\[\[[^]\\n]+]])/s', + array( $this, 'nowikiPlaceholder' ), $text ); + + # This is probably not 100% correct, I'm just + # glancing at the UseModWiki code. + $upper = "[A-Z]"; + $lower = "[a-z_0-9]"; + $any = "[A-Za-z_0-9]"; + $camel = "(?:$upper+$lower+$upper+$any*)"; + $subpage = "(?:\\/$any+)"; + $substart = "(?:\\/$upper$any*)"; + + $munged = preg_replace( "/(?!\\[\\[)($camel$subpage*|$substart$subpage*)\\b(?!\\]\\]|>)/", + '[[$1]]', $staged ); + + $final = preg_replace( '/' . preg_quote( $this->placeholder() ) . '/s', + array( $this, 'nowikiShift' ), $munged ); + return $final; + } -/** - * Make CamelCase and /Talk links work - */ -function mungeFormat( $text ) { - global $nowiki; - $nowiki = array(); - $staged = preg_replace_callback( - '/(<nowiki>.*?<\\/nowiki>|(?:http|https|ftp):\\S+|\[\[[^]\\n]+]])/s', - 'nowikiPlaceholder', $text ); - - # This is probably not 100% correct, I'm just - # glancing at the UseModWiki code. - $upper = "[A-Z]"; - $lower = "[a-z_0-9]"; - $any = "[A-Za-z_0-9]"; - $camel = "(?:$upper+$lower+$upper+$any*)"; - $subpage = "(?:\\/$any+)"; - $substart = "(?:\\/$upper$any*)"; - - $munged = preg_replace( "/(?!\\[\\[)($camel$subpage*|$substart$subpage*)\\b(?!\\]\\]|>)/", - '[[$1]]', $staged ); - - $final = preg_replace( '/' . preg_quote( placeholder() ) . '/es', - 'array_shift( $nowiki )', $munged ); - return $final; -} + private function placeholder( $x = null ) { + return '\xffplaceholder\xff'; + } + public function nowikiPlaceholder( $matches ) { + $this->nowiki[] = $matches[1]; + return $this->placeholder(); + } -function placeholder( $x = null ) { - return '\xffplaceholder\xff'; + public function nowikiShift() { + return array_shift( $this->nowiki ); + } } -function nowikiPlaceholder( $matches ) { - global $nowiki; - $nowiki[] = $matches[1]; - return placeholder(); +function wfUtf8Sequence( $codepoint ) { + if ( $codepoint < 0x80 ) { + return chr( $codepoint ); + } + if ( $codepoint < 0x800 ) { + return chr( $codepoint >> 6 & 0x3f | 0xc0 ) . + chr( $codepoint & 0x3f | 0x80 ); + } + if ( $codepoint < 0x10000 ) { + return chr( $codepoint >> 12 & 0x0f | 0xe0 ) . + chr( $codepoint >> 6 & 0x3f | 0x80 ) . + chr( $codepoint & 0x3f | 0x80 ); + } + if ( $codepoint < 0x100000 ) { + return chr( $codepoint >> 18 & 0x07 | 0xf0 ) . # Double-check this + chr( $codepoint >> 12 & 0x3f | 0x80 ) . + chr( $codepoint >> 6 & 0x3f | 0x80 ) . + chr( $codepoint & 0x3f | 0x80 ); + } + # Doesn't yet handle outside the BMP + return "&#$codepoint;"; } - +$maintClass = 'ImportUseModWiki'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/importUseModWikipedia.php b/maintenance/importUseModWikipedia.php new file mode 100644 index 00000000..c4b8112f --- /dev/null +++ b/maintenance/importUseModWikipedia.php @@ -0,0 +1,892 @@ +<?php + +/** + * A script to read a dump of the English Wikipedia from the UseModWiki period, and to + * generate an XML dump in MediaWiki format. + * + * Some relevant code was ported from UseModWiki 0.92. + * + */ + +require_once( dirname( __FILE__ ) . '/Maintenance.php' ); +require_once( dirname( __FILE__ ) .'/../includes/normal/UtfNormalUtil.php' ); + + +class ImportUseModWikipedia extends Maintenance { + var $encodeMap, $decodeMap; + + var $deepRenames = array( + 'JimboWales' => 983862286, + 'TexaS' => 983918410, + 'HistoryOfUnitedStatesTalk' => 984795423, + 'MetallicA' => 985128533, + 'PythagoreanTheorem' => 985225545, + 'TheCanonofScripture' => 985368223, + 'TaoTehChing' => 985368222, + //'TheMostRemarkableFormulaInTheWorld' => 985368221, + 'TheRecorder' => 985368220, + 'GladstoneOregon' => 985368219, + 'PacificBeach' => '?', + 'AaRiver' => '?', + ); + + var $replacements = array(); + + var $renameTextLinksOps = array( + 983846265 => array( + 'TestIgnore' => 'IgnoreTest', + ), + 983848080 => array( + 'UnitedLocomotiveWorks' => 'Atlas Shrugged/United Locomotive Works' + ), + 983856376 => array( + 'WikiPedia' => 'Wikipedia', + ), + 983896152 => array( + 'John_F_Kennedy' => 'John_F._Kennedy', + ), + 983905871 => array( + 'LarrySanger' => 'Larry_Sanger' + ), + 984697068 => array( + 'UnitedStates' => 'United States', + ), + 984792748 => array( + 'LibertarianisM' => 'Libertarianism' + ), + 985327832 => array( + 'AnarchisM' => 'Anarchism', + ), + 985290063 => array( + 'HistoryOfUnitedStatesDiscussion' => 'History_Of_United_States_Discussion' + ), + 985290091 => array( + 'BritishEmpire' => 'British Empire' + ), + /* + 985468958 => array( + 'ScienceFiction' => 'Science fiction', + ),*/ + ); + + /** + * Hack for observed substitution issues + */ + var $skipSelfSubstitution = array( + 'Pythagorean_Theorem', + 'The_Most_Remarkable_Formula_In_The_World', + 'Wine', + ); + + var $unixLineEndingsOps = array( + 987743732 => 'Wikipedia_FAQ' + ); + + var $replacementsDone = array(); + + var $moveLog = array(); + var $moveDests = array(); + var $revId; + + var $rc = array(); + var $textCache = array(); + var $blacklist = array(); + + var $FS, $FS1, $FS2, $FS3; + var $FreeLinkPattern, $UrlPattern, $LinkPattern, $InterLinkPattern; + + var $cp1252Table = array( +0x80 => 0x20ac, +0x81 => 0x0081, +0x82 => 0x201a, +0x83 => 0x0192, +0x84 => 0x201e, +0x85 => 0x2026, +0x86 => 0x2020, +0x87 => 0x2021, +0x88 => 0x02c6, +0x89 => 0x2030, +0x8a => 0x0160, +0x8b => 0x2039, +0x8c => 0x0152, +0x8d => 0x008d, +0x8e => 0x017d, +0x8f => 0x008f, +0x90 => 0x0090, +0x91 => 0x2018, +0x92 => 0x2019, +0x93 => 0x201c, +0x94 => 0x201d, +0x95 => 0x2022, +0x96 => 0x2013, +0x97 => 0x2014, +0x98 => 0x02dc, +0x99 => 0x2122, +0x9a => 0x0161, +0x9b => 0x203a, +0x9c => 0x0153, +0x9d => 0x009d, +0x9e => 0x017e, +0x9f => 0x0178); + + public function __construct() { + parent::__construct(); + $this->addOption( 'datadir', 'the value of $DataDir from wiki.cgi', true, true ); + $this->addOption( 'outfile', 'the name of the output XML file', true, true ); + $this->initLinkPatterns(); + + $this->encodeMap = $this->decodeMap = array(); + + for ($source = 0; $source <= 0xff; $source++) { + if ( isset( $this->cp1252Table[$source] ) ) { + $dest = $this->cp1252Table[$source]; + } else { + $dest = $source; + } + $sourceChar = chr( $source ); + $destChar = codepointToUtf8( $dest ); + $this->encodeMap[$sourceChar] = $destChar; + $this->decodeMap[$destChar] = $sourceChar; + } + } + + function initLinkPatterns() { + # Field separators are used in the URL-style patterns below. + $this->FS = "\xb3"; # The FS character is a superscript "3" + $this->FS1 = $this->FS . "1"; # The FS values are used to separate fields + $this->FS2 = $this->FS . "2"; # in stored hashtables and other data structures. + $this->FS3 = $this->FS . "3"; # The FS character is not allowed in user data. + + $UpperLetter = "[A-Z"; + $LowerLetter = "[a-z"; + $AnyLetter = "[A-Za-z"; + $AnyLetter .= "_0-9"; + $UpperLetter .= "]"; $LowerLetter .= "]"; $AnyLetter .= "]"; + + # Main link pattern: lowercase between uppercase, then anything + $LpA = $UpperLetter . "+" . $LowerLetter . "+" . $UpperLetter + . $AnyLetter . "*"; + # Optional subpage link pattern: uppercase, lowercase, then anything + $LpB = $UpperLetter . "+" . $LowerLetter . "+" . $AnyLetter . "*"; + + # Loose pattern: If subpage is used, subpage may be simple name + $this->LinkPattern = "((?:(?:$LpA)?\\/$LpB)|$LpA)"; + $QDelim = '(?:"")?'; # Optional quote delimiter (not in output) + $this->LinkPattern .= $QDelim; + + # Inter-site convention: sites must start with uppercase letter + # (Uppercase letter avoids confusion with URLs) + $InterSitePattern = $UpperLetter . $AnyLetter . "+"; + $this->InterLinkPattern = "((?:$InterSitePattern:[^\\]\\s\"<>{$this->FS}]+)$QDelim)"; + + $AnyLetter = "[-,. _0-9A-Za-z]"; + $this->FreeLinkPattern = "($AnyLetter+)"; + $this->FreeLinkPattern = "((?:(?:$AnyLetter+)?\\/)?$AnyLetter+)"; + $this->FreeLinkPattern .= $QDelim; + + # Url-style links are delimited by one of: + # 1. Whitespace (kept in output) + # 2. Left or right angle-bracket (< or >) (kept in output) + # 3. Right square-bracket (]) (kept in output) + # 4. A single double-quote (") (kept in output) + # 5. A $FS (field separator) character (kept in output) + # 6. A double double-quote ("") (removed from output) + + $UrlProtocols = "http|https|ftp|afs|news|nntp|mid|cid|mailto|wais|" + . "prospero|telnet|gopher"; + $UrlProtocols .= '|file'; + $this->UrlPattern = "((?:(?:$UrlProtocols):[^\\]\\s\"<>{$this->FS}]+)$QDelim)"; + $ImageExtensions = "(gif|jpg|png|bmp|jpeg)"; + $RFCPattern = "RFC\\s?(\\d+)"; + $ISBNPattern = "ISBN:?([0-9- xX]{10,})"; + } + + function execute() { + $this->articleFileName = '/tmp/importUseMod.' . mt_rand( 0, 0x7ffffff ) . '.tmp'; + $this->patchFileName = '/tmp/importUseMod.' . mt_rand( 0, 0x7ffffff ) . '.tmp'; + $this->dataDir = $this->getOption( 'datadir' ); + $this->outFile = fopen( $this->getOption( 'outfile' ), 'w' ); + if ( !$this->outFile ) { + echo "Unable to open output file\n"; + return 1; + } + $this->writeXmlHeader(); + $this->readRclog(); + $this->writeMoveLog(); + $this->writeRevisions(); + $this->reconcileCurrentRevs(); + $this->writeXmlFooter(); + unlink( $this->articleFileName ); + unlink( $this->patchFileName ); + return 0; + } + + function writeXmlHeader() { + fwrite( $this->outFile, <<<EOT +<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.3/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.3/ http://www.mediawiki.org/xml/export-0.3.xsd" version="0.3" xml:lang="en"> + <siteinfo> + <sitename>Wikipedia</sitename> + <base>http://www.wikipedia.com/</base> + <generator>MediaWiki 1.18alpha importUseModWikipedia.php</generator> + <case>case-sensitive</case> + <namespaces> + <namespace key="0" /> + </namespaces> + </siteinfo> + +EOT + ); + } + + function writeXmlFooter() { + fwrite( $this->outFile, "</mediawiki>\n" ); + } + + function readRclog() { + $rcFile = fopen( "{$this->dataDir}/rclog", 'r' ); + while ( $line = fgets( $rcFile ) ) { + $bits = explode( $this->FS3, $line ); + if ( count( $bits ) !== 7 ) { + echo "Error reading rclog\n"; + return; + } + $params = array( + 'timestamp' => $bits[0], + 'rctitle' => $bits[1], + 'summary' => $bits[2], + 'minor' => $bits[3], + 'host' => $bits[4], + 'kind' => $bits[5], + 'extra' => array() + ); + $extraList = explode( $this->FS2, $bits[6] ); + + for ( $i = 0; $i < count( $extraList ); $i += 2 ) { + $params['extra'][$extraList[$i]] = $extraList[$i + 1]; + } + $this->rc[$params['timestamp']][] = $params; + } + } + + function writeMoveLog() { + $this->moveLog = array(); + $deepRenames = $this->deepRenames; + echo "Calculating move log...\n"; + $this->processDiffFile( array( $this, 'moveLogCallback' ) ); + + // We have the timestamp intervals, now make a guess at the actual timestamp + foreach ( $this->moveLog as $newTitle => $params ) { + // Is there a time specified? + $drTime = false; + if ( isset( $deepRenames[$params['old']] ) ) { + $drTime = $deepRenames[$params['old']]; + if ( $drTime !== '?' ) { + if ( ( !isset( $params['endTime'] ) || $drTime < $params['endTime'] ) + && $drTime > $params['startTime'] ) + { + $this->moveLog[$newTitle]['timestamp'] = $drTime; + $this->moveLog[$newTitle]['deep'] = true; + + echo "{$params['old']} -> $newTitle at $drTime\n"; + unset( $deepRenames[$params['old']] ); + continue; + } else { + echo "WARNING: deep rename time invalid: {$params['old']}\n"; + unset( $deepRenames[$params['old']] ); + } + } + } + + // Guess that it is one second after the last edit to the page before it was moved + $this->moveLog[$newTitle]['timestamp'] = $params['startTime'] + 1; + if ( $drTime === '?' ) { + $this->moveLog[$newTitle]['deep'] = true; + unset( $deepRenames[$params['old']] ); + } + if ( isset( $params['endTime'] ) ) { + $this->printLatin1( "{$params['old']} -> $newTitle between " . + "{$params['startTime']} and {$params['endTime']}\n" ); + } else { + $this->printLatin1( "{$params['old']} -> $newTitle after " . + "{$params['startTime']}\n" ); + } + } + + // Write the move log to the XML file + $id = 1; + foreach ( $this->moveLog as $newTitle => $params ) { + $out = "<logitem>\n" . + $this->element( 'id', $id++ ) . + $this->element( 'timestamp', wfTimestamp( TS_ISO_8601, $params['timestamp'] ) ) . + "<contributor>\n" . + $this->element( 'username', 'UseModWiki admin' ) . + "</contributor>" . + $this->element( 'type', 'move' ) . + $this->element( 'action', 'move' ) . + $this->element( 'logtitle', $params['old'] ) . + "<params xml:space=\"preserve\">" . + htmlspecialchars( $this->encode( "{$newTitle}\n1" ) ) . + "</params>\n" . + "</logitem>\n"; + fwrite( $this->outFile, $out ); + } + + // Check for remaining deep rename entries + if ( $deepRenames ) { + echo "WARNING: the following entries in \$this->deepRenames are " . + "invalid, since no such move exists:\n" . + implode( "\n", array_keys( $deepRenames ) ) . + "\n\n"; + } + + } + + function element( $name, $value ) { + return "<$name>" . htmlspecialchars( $this->encode( $value ) ) . "</$name>\n"; + } + + function moveLogCallback( $entry ) { + $rctitle = $entry['rctitle']; + $title = $entry['title']; + $this->moveDests[$rctitle] = $title; + + if ( $rctitle === $title ) { + if ( isset( $this->moveLog[$rctitle] ) + && !isset( $this->moveLog[$rctitle]['endTime'] ) ) + { + // This is the latest time that the page could have been moved + $this->moveLog[$rctitle]['endTime'] = $entry['timestamp']; + } + } else { + if ( !isset( $this->moveLog[$rctitle] ) ) { + // Initialise the move log entry + $this->moveLog[$rctitle] = array( + 'old' => $title + ); + } + // Update the earliest time the page could have been moved + $this->moveLog[$rctitle]['startTime'] = $entry['timestamp']; + } + } + + function writeRevisions() { + $this->numGoodRevs = 0; + $this->revId = 1; + $this->processDiffFile( array( $this, 'revisionCallback' ) ); + echo "\n\nImported {$this->numGoodRevs} out of {$this->numRevs}\n"; + } + + function revisionCallback( $params ) { + $title = $params['rctitle']; + $editTime = $params['timestamp']; + + if ( isset( $this->blacklist[$title] ) ) { + return; + } + $this->doPendingOps( $editTime ); + + $origText = $this->getText( $title ); + $text = $this->patch( $origText, $params['diff'] ); + if ( $text === false ) { + echo "$editTime $title attempting resolution...\n"; + $linkSubstitutes = $this->resolveFailedDiff( $origText, $params['diff'] ); + if ( !$linkSubstitutes ) { + $this->printLatin1( "$editTime $title DIFF FAILED\n" ); + $this->blacklist[$title] = true; + return; + } + $this->printLatin1( "$editTime $title requires substitutions:\n" ); + $time = $editTime - 1; + foreach ( $linkSubstitutes as $old => $new ) { + $this->printLatin1( "SUBSTITUTE $old -> $new\n" ); + $this->renameTextLinks( $old, $new, $time-- ); + } + $origText = $this->getText( $title ); + $text = $this->patch( $origText, $params['diff'] ); + if ( $text === false ) { + $this->printLatin1( "$editTime $title STILL FAILS!\n" ); + $this->blacklist[$title] = true; + return; + } + + echo "\n"; + } + + $params['text'] = $text; + $this->saveRevision( $params ); + $this->numGoodRevs++; + #$this->printLatin1( "$editTime $title\n" ); + } + + function doPendingOps( $editTime ) { + foreach ( $this->moveLog as $newTitle => $entry ) { + if ( $entry['timestamp'] <= $editTime ) { + unset( $this->moveLog[$newTitle] ); + if ( isset( $entry['deep'] ) ) { + $this->renameTextLinks( $entry['old'], $newTitle, $entry['timestamp'] ); + } + } + } + + foreach ( $this->renameTextLinksOps as $renameTime => $replacements ) { + if ( $editTime >= $renameTime ) { + foreach ( $replacements as $old => $new ) { + $this->printLatin1( "SUBSTITUTE $old -> $new\n" ); + $this->renameTextLinks( $old, $new, $renameTime ); + } + unset( $this->renameTextLinksOps[$renameTime] ); + } + } + + foreach ( $this->unixLineEndingsOps as $fixTime => $title ) { + if ( $editTime >= $fixTime ) { + $this->printLatin1( "$fixTime $title FIXING LINE ENDINGS\n" ); + $text = $this->getText( $title ); + $text = str_replace( "\r", '', $text ); + $this->saveRevision( array( + 'rctitle' => $title, + 'timestamp' => $fixTime, + 'extra' => array( 'name' => 'UseModWiki admin' ), + 'text' => $text, + 'summary' => 'Fixing line endings', + ) ); + unset( $this->unixLineEndingsOps[$fixTime] ); + } + } + } + + function patch( $source, $diff ) { + file_put_contents( $this->articleFileName, $source ); + file_put_contents( $this->patchFileName, $diff ); + $error = wfShellExec( + wfEscapeShellArg( + 'patch', + '-n', + '-r', '-', + '--no-backup-if-mismatch', + '--binary', + $this->articleFileName, + $this->patchFileName + ) . ' 2>&1', + $status + ); + $text = file_get_contents( $this->articleFileName ); + if ( $status || $text === false ) { + return false; + } else { + return $text; + } + } + + function resolveFailedDiff( $origText, $diff ) { + $context = array(); + $diffLines = explode( "\n", $diff ); + for ( $i = 0; $i < count( $diffLines ); $i++ ) { + $diffLine = $diffLines[$i]; + if ( !preg_match( '/^(\d+)(?:,\d+)?[acd]\d+(?:,\d+)?$/', $diffLine, $m ) ) { + continue; + } + + $sourceIndex = intval( $m[1] ); + $i++; + while ( $i < count( $diffLines ) && substr( $diffLines[$i], 0, 1 ) === '<' ) { + $context[$sourceIndex - 1] = substr( $diffLines[$i], 2 ); + $sourceIndex++; + $i++; + } + $i--; + } + + $changedLinks = array(); + $origLines = explode( "\n", $origText ); + foreach ( $context as $i => $contextLine ) { + $origLine = isset( $origLines[$i] ) ? $origLines[$i] : ''; + if ( $contextLine === $origLine ) { + continue; + } + $newChanges = $this->resolveTextChange( $origLine, $contextLine ); + if ( is_array( $newChanges ) ) { + $changedLinks += $newChanges; + } else { + echo "Resolution failure on line " . ( $i + 1 ) . "\n"; + $this->printLatin1( $newChanges ); + } + } + + return $changedLinks; + } + + function resolveTextChange( $source, $dest ) { + $changedLinks = array(); + $sourceLinks = $this->getLinkList( $source ); + $destLinks = $this->getLinkList( $dest ); + $newLinks = array_diff( $destLinks, $sourceLinks ); + $removedLinks = array_diff( $sourceLinks, $destLinks ); + + // Match up the removed links with the new links + foreach ( $newLinks as $newLink ) { + $minDistance = 100000000; + $bestRemovedLink = false; + foreach ( $removedLinks as $removedLink ) { + $editDistance = levenshtein( $newLink, $removedLink ); + if ( $editDistance < $minDistance ) { + $minDistance = $editDistance; + $bestRemovedLink = $removedLink; + } + } + if ( $bestRemovedLink !== false ) { + $changedLinks[$bestRemovedLink] = $newLink; + $newLinks = array_diff( $newLinks, array( $newLink ) ); + $removedLinks = array_diff( $removedLinks, array( $bestRemovedLink ) ); + } + } + + $proposal = $source; + foreach ( $changedLinks as $removedLink => $newLink ) { + $proposal = $this->substituteTextLinks( $removedLink, $newLink, $proposal ); + } + if ( $proposal !== $dest ) { + // Resolution failed + $msg = "Source line: $source\n" . + "Source links: " . implode( ', ', $sourceLinks ) . "\n" . + "Context line: $dest\n" . + "Context links: " . implode( ', ', $destLinks ) . "\n" . + "Proposal: $proposal\n"; + return $msg; + } + return $changedLinks; + } + + function processDiffFile( $callback ) { + $diffFile = fopen( "{$this->dataDir}/diff_log", 'r' ); + + $delimiter = "------\n"; + file_put_contents( $this->articleFileName, "Describe the new page here.\n" ); + + $line = fgets( $diffFile ); + $lineNum = 1; + if ( $line !== $delimiter ) { + echo "Invalid diff file\n"; + return false; + } + $lastReportLine = 0; + $this->numRevs = 0; + + while ( true ) { + $line = fgets( $diffFile ); + $lineNum++; + if ( $line === false ) { + break; + } + if ( $lineNum > $lastReportLine + 1000 ) { + $lastReportLine = $lineNum; + fwrite( STDERR, "$lineNum \r" ); + fflush( STDERR ); + } + $line = trim( $line ); + if ( !preg_match( '/^([^|]+)\|(\d+)$/', $line, $matches ) ) { + echo "Invalid header on line $lineNum\n"; + return true; + } + list( , $title, $editTime ) = $matches; + + $diff = ''; + $diffStartLine = $lineNum; + while ( true ) { + $line = fgets( $diffFile ); + $lineNum++; + if ( $line === $delimiter ) { + break; + } + if ( $line === false ) { + break 2; + } + $diff .= $line; + } + + $this->numRevs++; + + if ( !isset( $this->rc[$editTime] ) ) { + $this->printLatin1( "$editTime $title DELETED, skipping\n" ); + continue; + } + + if ( count( $this->rc[$editTime] ) == 1 ) { + $params = $this->rc[$editTime][0]; + } else { + $params = false; + $candidates = ''; + foreach ( $this->rc[$editTime] as $rc ) { + if ( $rc['rctitle'] === $title ) { + $params = $rc; + break; + } + if ( $candidates === '' ) { + $candidates = $rc['rctitle']; + } else { + $candidates .= ', ' . $rc['rctitle']; + } + } + if ( !$params ) { + $this->printLatin1( "$editTime $title ERROR cannot resolve rclog\n" ); + $this->printLatin1( "$editTime $title CANDIDATES: $candidates\n" ); + continue; + } + } + $params['diff'] = $diff; + $params['title'] = $title; + $params['diffStartLine'] = $diffStartLine; + call_user_func( $callback, $params ); + } + echo "\n"; + + if ( !feof( $diffFile ) ) { + echo "Stopped at line $lineNum\n"; + } + return true; + } + + function reconcileCurrentRevs() { + foreach ( $this->textCache as $title => $text ) { + $fileName = "{$this->dataDir}/page/"; + if ( preg_match( '/^[A-Z]/', $title, $m ) ) { + $fileName .= $m[0]; + } else { + $fileName .= 'other'; + } + $fileName .= "/$title.db"; + + if ( !file_exists( $fileName ) ) { + $this->printLatin1( "ERROR: Cannot find page file for {$title}\n" ); + continue; + } + + $fileContents = file_get_contents( $fileName ); + $page = $this->unserializeUseMod( $fileContents, $this->FS1 ); + $section = $this->unserializeUseMod( $page['text_default'], $this->FS2 ); + $data = $this->unserializeUseMod( $section['data'], $this->FS3 ); + $pageText = $data['text']; + if ( $text !== $pageText ) { + $substs = $this->resolveTextChange( $text, $pageText ); + if ( is_array( $substs ) ) { + foreach ( $substs as $source => $dest ) { + if ( isset( $this->moveLog[$dest] ) ) { + $this->printLatin1( "ERROR: need deep rename: $source\n" ); + } else { + $this->printLatin1( "ERROR: need substitute: $source -> $dest\n" ); + } + } + } else { + $this->printLatin1( "ERROR: unresolved diff in $title:\n" ); + wfSuppressWarnings(); + $diff = xdiff_string_diff( $text, $pageText ) . ''; + wfRestoreWarnings(); + $this->printLatin1( "$diff\n" ); + } + } + } + } + + function makeTitle( $titleText ) { + return Title::newFromText( $this->encode( $titleText ) ); + } + + function getText( $titleText ) { + if ( !isset( $this->textCache[$titleText] ) ) { + return "Describe the new page here.\n"; + } else { + return $this->textCache[$titleText]; + } + } + + function saveRevision( $params ) { + $this->textCache[$params['rctitle']] = $params['text']; + + $out = "<page>\n" . + $this->element( 'title', $params['rctitle'] ) . + "<revision>\n" . + $this->element( 'id', $this->revId ++ ) . + $this->element( 'timestamp', wfTimestamp( TS_ISO_8601, $params['timestamp'] ) ) . + "<contributor>\n"; + if ( isset( $params['extra']['name'] ) ) { + $out .= $this->element( 'username', $params['extra']['name'] ); + } + if ( isset( $params['extra']['id'] ) ) { + $out .= $this->element( 'id', $params['extra']['id'] ); + } + if ( isset( $params['host'] ) ) { + $out .= $this->element( 'ip', $params['host'] ); + } + $out .= + "</contributor>\n" . + $this->element( 'comment', $params['summary'] ) . + "<text xml:space=\"preserve\">" . + htmlspecialchars( $this->encode( $params['text'] ) ) . + "</text>\n" . + "</revision>\n" . + "</page>\n"; + fwrite( $this->outFile, $out ); + } + + function renameTextLinks( $old, $new, $timestamp ) { + $newWithUnderscores = $new; + $old = str_replace( '_', ' ', $old ); + $new = str_replace( '_', ' ', $new ); + + foreach ( $this->textCache as $title => $oldText ) { + if ( $newWithUnderscores === $title + && in_array( $title, $this->skipSelfSubstitution ) ) + { + // Hack to make Pythagorean_Theorem etc. work + continue; + } + + $newText = $this->substituteTextLinks( $old, $new, $oldText ); + if ( $oldText !== $newText ) { + $this->saveRevision( array( + 'rctitle' => $title, + 'timestamp' => $timestamp, + 'text' => $newText, + 'extra' => array( 'name' => 'Page move link fixup script' ), + 'summary' => '', + 'minor' => true + ) ); + } + } + } + + function substituteTextLinks( $old, $new, $text ) { + $this->saveUrl = array(); + $this->old = $old; + $this->new = $new; + + $text = str_replace( $this->FS, '', $text ); # Remove separators (paranoia) + $text = preg_replace_callback( '/(<pre>(.*?)<\/pre>)/is', + array( $this, 'storeRaw' ), $text ); + $text = preg_replace_callback( '/(<code>(.*?)<\/code>)/is', + array( $this, 'storeRaw' ), $text ); + $text = preg_replace_callback( '/(<nowiki>(.*?)<\/nowiki>)/s', + array( $this, 'storeRaw' ), $text ); + + $text = preg_replace_callback( "/\[\[{$this->FreeLinkPattern}\|([^\]]+)\]\]/", + array( $this, 'subFreeLink' ), $text ); + $text = preg_replace_callback( "/\[\[{$this->FreeLinkPattern}\]\]/", + array( $this, 'subFreeLink' ), $text ); + $text = preg_replace_callback( "/(\[{$this->UrlPattern}\s+([^\]]+?)\])/", + array( $this, 'storeRaw' ), $text ); + $text = preg_replace_callback( "/(\[{$this->InterLinkPattern}\s+([^\]]+?)\])/", + array( $this, 'storeRaw' ), $text ); + $text = preg_replace_callback( "/(\[?{$this->UrlPattern}\]?)/", + array( $this, 'storeRaw' ), $text ); + $text = preg_replace_callback( "/(\[?{$this->InterLinkPattern}\]?)/", + array( $this, 'storeRaw' ), $text ); + $text = preg_replace_callback( "/{$this->LinkPattern}/", + array( $this, 'subWikiLink' ), $text ); + + $text = preg_replace_callback( "/{$this->FS}(\d+){$this->FS}/", + array( $this, 'restoreRaw' ), $text ); # Restore saved text + return $text; + } + + function getLinkList( $text ) { + $this->saveUrl = array(); + $this->linkList = array(); + + $text = str_replace( $this->FS, '', $text ); # Remove separators (paranoia) + $text = preg_replace_callback( '/(<pre>(.*?)<\/pre>)/is', + array( $this, 'storeRaw' ), $text ); + $text = preg_replace_callback( '/(<code>(.*?)<\/code>)/is', + array( $this, 'storeRaw' ), $text ); + $text = preg_replace_callback( '/(<nowiki>(.*?)<\/nowiki>)/s', + array( $this, 'storeRaw' ), $text ); + + $text = preg_replace_callback( "/\[\[{$this->FreeLinkPattern}\|([^\]]+)\]\]/", + array( $this, 'storeLink' ), $text ); + $text = preg_replace_callback( "/\[\[{$this->FreeLinkPattern}\]\]/", + array( $this, 'storeLink' ), $text ); + $text = preg_replace_callback( "/(\[{$this->UrlPattern}\s+([^\]]+?)\])/", + array( $this, 'storeRaw' ), $text ); + $text = preg_replace_callback( "/(\[{$this->InterLinkPattern}\s+([^\]]+?)\])/", + array( $this, 'storeRaw' ), $text ); + $text = preg_replace_callback( "/(\[?{$this->UrlPattern}\]?)/", + array( $this, 'storeRaw' ), $text ); + $text = preg_replace_callback( "/(\[?{$this->InterLinkPattern}\]?)/", + array( $this, 'storeRaw' ), $text ); + $text = preg_replace_callback( "/{$this->LinkPattern}/", + array( $this, 'storeLink' ), $text ); + + return $this->linkList; + } + + function storeRaw( $m ) { + $this->saveUrl[] = $m[1]; + return $this->FS . (count( $this->saveUrl ) - 1) . $this->FS; + } + + function subFreeLink( $m ) { + $link = $m[1]; + if ( isset( $m[2] ) ) { + $name = $m[2]; + } else { + $name = ''; + } + $oldlink = $link; + $link = preg_replace( '/^\s+/', '', $link ); + $link = preg_replace( '/\s+$/', '', $link ); + if ( $link == $this->old ) { + $link = $this->new; + } else { + $link = $oldlink; # Preserve spaces if no match + } + $link = "[[$link"; + if ( $name !== "" ) { + $link .= "|$name"; + } + $link .= "]]"; + return $this->storeRaw( array( 1 => $link ) ); + } + + function subWikiLink( $m ) { + $link = $m[1]; + if ( $link == $this->old ) { + $link = $this->new; + if ( !preg_match( "/^{$this->LinkPattern}$/", $this->new ) ) { + $link = "[[$link]]"; + } + } + return $this->storeRaw( array( 1 => $link ) ); + } + + function restoreRaw( $m ) { + return $this->saveUrl[$m[1]]; + } + + function storeLink( $m ) { + $this->linkList[] = $m[1]; + return $this->storeRaw( $m ); + } + + function encode( $s ) { + return strtr( $s, $this->encodeMap ); + } + + function decode( $s ) { + return strtr( $s, $this->decodeMap ); + } + + function printLatin1( $s ) { + echo $this->encode( $s ); + } + + function unserializeUseMod( $s, $sep ) { + $parts = explode( $sep, $s ); + $result = array(); + for ( $i = 0; $i < count( $parts ); $i += 2 ) { + $result[$parts[$i]] = $parts[$i+1]; + } + return $result; + } +} + +$maintClass = 'ImportUseModWikipedia'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/initEditCount.php b/maintenance/initEditCount.php index e421e29b..0f136450 100644 --- a/maintenance/initEditCount.php +++ b/maintenance/initEditCount.php @@ -93,7 +93,7 @@ in the load balancer, usually indicating a replication environment.' ); $delta, $rate ) ); - wfWaitForSlaves( 10 ); + wfWaitForSlaves(); } } else { // Subselect should work on modern MySQLs etc diff --git a/maintenance/initStats.php b/maintenance/initStats.php index 1ec5c925..eab9c8df 100644 --- a/maintenance/initStats.php +++ b/maintenance/initStats.php @@ -63,7 +63,7 @@ class InitStats extends Maintenance { if ( $this->hasOption( 'active' ) ) { $this->output( "Counting active users..." ); - $active = SiteStatsUpdate::cacheUpdate(); + $active = SiteStatsUpdate::cacheUpdate( wfGetDB( DB_MASTER ) ); $this->output( "{$active}\n" ); } diff --git a/maintenance/install-utils.inc b/maintenance/install-utils.inc deleted file mode 100644 index 93ca0b58..00000000 --- a/maintenance/install-utils.inc +++ /dev/null @@ -1,31 +0,0 @@ -<?php -/** - * This file contains ancient db-related functions that have been deprecated. Do - * not use them. Please find the appropriate replacements. - * - * @file - */ - -/** - * @deprecated Use DatabaseBase::sourceFile(). Will probably be removed in 1.19 - */ -function dbsource( $fname, $db = false ) { - wfDeprecated( __METHOD__ ); - if ( !$db ) { - $db = wfGetDB( DB_MASTER ); - } - $error = $db->sourceFile( $fname ); - if ( $error !== true ) { - print $error; - exit( 1 ); - } -} - -/** - * @deprecated Use DatabaseBase::patchPath(). Will probably be removed in 1.18 - */ -function archive( $name ) { - wfDeprecated( __METHOD__ ); - $dbr = wfGetDB( DB_SLAVE ); - return $dbr->patchPath( $name ); -} diff --git a/maintenance/install.php b/maintenance/install.php index 34e3ea85..dba43400 100644 --- a/maintenance/install.php +++ b/maintenance/install.php @@ -40,7 +40,7 @@ class CommandLineInstaller extends Maintenance { $this->addArg( 'admin', 'The username of the wiki administrator (WikiSysop)', true ); $this->addOption( 'pass', 'The password for the wiki administrator. You will be prompted for this if it isn\'t provided', false, true ); - $this->addOption( 'email', 'The email for the wiki administrator', false, true ); + /* $this->addOption( 'email', 'The email for the wiki administrator', false, true ); */ $this->addOption( 'scriptpath', 'The relative path of the wiki in the web server (/wiki)', false, true ); $this->addOption( 'lang', 'The language to use (en)', false, true ); @@ -55,6 +55,7 @@ class CommandLineInstaller extends Maintenance { $this->addOption( 'installdbpass', 'The pasword for the DB user to install as.', false, true ); $this->addOption( 'dbuser', 'The user to use for normal operations (wikiuser)', false, true ); $this->addOption( 'dbpass', 'The pasword for the DB user for normal operations', false, true ); + $this->addOption( 'dbpassfile', 'An alternative way to provide dbpass option, as the contents of this file', false, true ); $this->addOption( 'confpath', "Path to write LocalSettings.php to, default $IP", false, true ); /* $this->addOption( 'dbschema', 'The schema for the MediaWiki DB in pg (mediawiki)', false, true ); */ /* $this->addOption( 'namespace', 'The project namespace (same as the name)', false, true ); */ @@ -67,6 +68,17 @@ class CommandLineInstaller extends Maintenance { $adminName = isset( $this->mArgs[1] ) ? $this->mArgs[1] : null; $wgTitle = Title::newFromText( 'Installer script' ); + $dbpassfile = $this->getOption( 'dbpassfile', false ); + if ( $dbpassfile !== false ) { + wfSuppressWarnings(); + $dbpass = file_get_contents( $dbpassfile ); + wfRestoreWarnings(); + if ( $dbpass === false ) { + $this->error( "Couldn't open $dbpassfile", true ); + } + $this->mOptions['dbpass'] = trim( $dbpass, "\r\n" ); + } + $installer = new CliInstaller( $siteName, $adminName, $this->mOptions ); diff --git a/maintenance/jsparse.php b/maintenance/jsparse.php new file mode 100644 index 00000000..ae6f1f1d --- /dev/null +++ b/maintenance/jsparse.php @@ -0,0 +1,72 @@ +<?php +/** + * Maintenance script to do test JavaScript validity parses using jsmin+'s parser + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @ingroup Maintenance + */ + +require_once( dirname( __FILE__ ) . '/Maintenance.php' ); + +class JSParseHelper extends Maintenance { + var $errs = 0; + + public function __construct() { + parent::__construct(); + $this->mDescription = "Runs parsing/syntax checks on JavaScript files"; + $this->addArg( 'file(s)', 'JavaScript file to test', false ); + } + + public function execute() { + $iterations = $this->getOption( 'i', 100 ); + if ( $this->hasArg() ) { + $files = $this->mArgs; + } else { + $this->maybeHelp( true ); // @fixme this is a lame API :) + exit( 1 ); // it should exit from the above first... + } + + $parser = new JSParser(); + foreach ( $files as $filename ) { + wfSuppressWarnings(); + $js = file_get_contents( $filename ); + wfRestoreWarnings(); + if ($js === false) { + $this->output( "$filename ERROR: could not read file\n" ); + $this->errs++; + continue; + } + + try { + $parser->parse( $js, $filename, 1 ); + } catch (Exception $e) { + $this->errs++; + $this->output( "$filename ERROR: " . $e->getMessage() . "\n" ); + continue; + } + + $this->output( "$filename OK\n" ); + } + + if ($this->errs > 0) { + exit(1); + } + } +} + +$maintClass = "JSParseHelper"; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/lag.php b/maintenance/lag.php index fdc74293..dc8bff5f 100644 --- a/maintenance/lag.php +++ b/maintenance/lag.php @@ -1,8 +1,23 @@ <?php - /** * Shows database lag * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file * @ingroup Maintenance */ diff --git a/maintenance/language/StatOutputs.php b/maintenance/language/StatOutputs.php index b8e28302..c6d03641 100644 --- a/maintenance/language/StatOutputs.php +++ b/maintenance/language/StatOutputs.php @@ -3,6 +3,21 @@ if ( !defined( 'MEDIAWIKI' ) ) die(); /** * Statistic output classes. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup MaintenanceLanguage * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com> @@ -31,10 +46,20 @@ class statsOutput { /** Outputs WikiText */ class wikiStatsOutput extends statsOutput { function heading() { + global $wgDummyLanguageCodes, $wgContLang; $version = SpecialVersion::getVersion( 'nodb' ); echo "'''Statistics are based on:''' <code>" . $version . "</code>\n\n"; echo "'''Note:''' These statistics can be generated by running <code>php maintenance/language/transstat.php</code>.\n\n"; echo "For additional information on specific languages (the message names, the actual problems, etc.), run <code>php maintenance/language/checkLanguage.php --lang=foo</code>.\n\n"; + echo 'English (en) is excluded because it is the default localization'; + if( is_array( $wgDummyLanguageCodes ) ) { + $dummyCodes = array(); + foreach( $wgDummyLanguageCodes as $dummyCode ) { + $dummyCodes[] = $wgContLang->getLanguageName( $dummyCode ) . ' (' . $dummyCode . ')'; + } + echo ', as well as the following languages that are not intended for system message translations, usually because they redirect to other language codes: ' . implode( ', ', $dummyCodes ); + } + echo ".\n\n"; # dot to end sentence echo '{| class="sortable wikitable" border="2" cellpadding="4" cellspacing="0" style="background-color: #F9F9F9; border: 1px #AAAAAA solid; border-collapse: collapse; clear:both;" width="100%"' . "\n"; } function footer() { diff --git a/maintenance/language/checkDupeMessages.php b/maintenance/language/checkDupeMessages.php index ea9d5fb7..ea5b1870 100644 --- a/maintenance/language/checkDupeMessages.php +++ b/maintenance/language/checkDupeMessages.php @@ -1,6 +1,22 @@ <?php /** - * @todo document + * Script to print out duplicates in message array + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup MaintenanceLanguage */ @@ -18,9 +34,9 @@ if ( isset( $options['lang'] ) && isset( $options['clang'] ) ) { } else { if ( !strcmp( $options['mode'], 'wiki' ) ) { $runMode = 'wiki'; - } else if ( !strcmp( $options['mode'], 'php' ) ) { + } elseif ( !strcmp( $options['mode'], 'php' ) ) { $runMode = 'php'; - } else if ( !strcmp( $options['mode'], 'raw' ) ) { + } elseif ( !strcmp( $options['mode'], 'raw' ) ) { $runMode = 'raw'; } else { } @@ -61,7 +77,7 @@ if ( $runTest ) { if ( $run ) { if ( !strcmp( $runMode, 'wiki' ) ) { $runMode = 'wiki'; - } else if ( !strcmp( $runMode, 'raw' ) ) { + } elseif ( !strcmp( $runMode, 'raw' ) ) { $runMode = 'raw'; } include( $messagesFile ); @@ -86,9 +102,9 @@ if ( $run ) { if ( ( !strcmp( $key, $ckey ) ) && ( !strcmp( $value, $cvalue ) ) ) { if ( !strcmp( $runMode, 'raw' ) ) { print( "$key\n" ); - } else if ( !strcmp( $runMode, 'php' ) ) { + } elseif ( !strcmp( $runMode, 'php' ) ) { print( "'$key' => '',\n" ); - } else if ( !strcmp( $runMode, 'wiki' ) ) { + } elseif ( !strcmp( $runMode, 'wiki' ) ) { $uKey = ucfirst( $key ); print( "* MediaWiki:$uKey/$langCode\n" ); } else { diff --git a/maintenance/language/checkExtensions.php b/maintenance/language/checkExtensions.php index c05cf193..a58a8f5c 100644 --- a/maintenance/language/checkExtensions.php +++ b/maintenance/language/checkExtensions.php @@ -2,6 +2,21 @@ /** * Check the extensions language files. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup MaintenanceLanguage */ diff --git a/maintenance/language/checkLanguage.inc b/maintenance/language/checkLanguage.inc index d8480c0f..1e4b94a2 100644 --- a/maintenance/language/checkLanguage.inc +++ b/maintenance/language/checkLanguage.inc @@ -1,4 +1,25 @@ <?php +/** + * Helper class for checkLanguage.php script. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup MaintenanceLanguage + */ /** * @ingroup MaintenanceLanguage @@ -97,7 +118,7 @@ class CheckLanguageCLI { /** * Get the checks that can easily be treated by non-speakers of the language. - * @return A list of the easy checks. + * @return Array A list of the easy checks. */ protected function easyChecks() { return array( diff --git a/maintenance/language/checkLanguage.php b/maintenance/language/checkLanguage.php index 9396e8c1..69f61084 100644 --- a/maintenance/language/checkLanguage.php +++ b/maintenance/language/checkLanguage.php @@ -2,6 +2,21 @@ /** * Check a language file. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup MaintenanceLanguage */ diff --git a/maintenance/language/function-list.php b/maintenance/language/function-list.php index 7985f37d..7b0e57c2 100644 --- a/maintenance/language/function-list.php +++ b/maintenance/language/function-list.php @@ -1,5 +1,20 @@ <?php /** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup MaintenanceLanguage */ diff --git a/maintenance/language/generateCollationData.php b/maintenance/language/generateCollationData.php index 68ad2ddf..2c3ffedc 100644 --- a/maintenance/language/generateCollationData.php +++ b/maintenance/language/generateCollationData.php @@ -68,9 +68,12 @@ class GenerateCollationData extends Maintenance { } function charCallback( $data ) { - // Skip non-printable characters + // Skip non-printable characters, + // but do not skip a normal space (U+0020) since + // people like to use that as a fake no header symbol. $category = substr( $data['gc'], 0, 1 ); - if ( strpos( 'LNPS', $category ) === false ) { + if ( strpos( 'LNPS', $category ) === false + && $data['cp'] !== '0020' ) { return; } $cp = hexdec( $data['cp'] ); @@ -193,7 +196,7 @@ class GenerateCollationData extends Maintenance { // portion equal to the first character, then remove the second // character. This avoids having characters like U+A732 (double A) // polluting the basic latin sort area. - $prevWeights = array(); + foreach ( $this->groups as $weight => $group ) { if ( preg_match( '/(\.[0-9A-F]*)\./', $weight, $m ) ) { if ( isset( $this->groups[$m[1]] ) ) { @@ -377,5 +380,5 @@ class UcdXmlReader { } $maintClass = 'GenerateCollationData'; -require_once( DO_MAINTENANCE ); +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/language/generateNormalizerData.php b/maintenance/language/generateNormalizerData.php index cb9910f3..a958abf1 100644 --- a/maintenance/language/generateNormalizerData.php +++ b/maintenance/language/generateNormalizerData.php @@ -1,4 +1,25 @@ <?php +/** + * Generates normalizer data files for Arabic and Malayalam. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup MaintenanceLanguage + */ require_once( dirname( __FILE__ ) . '/../Maintenance.php' ); diff --git a/maintenance/language/languages.inc b/maintenance/language/languages.inc index c5c9f650..60b1112f 100644 --- a/maintenance/language/languages.inc +++ b/maintenance/language/languages.inc @@ -2,6 +2,21 @@ /** * Handle messages in the language files. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup MaintenanceLanguage */ @@ -73,7 +88,7 @@ class languages { /** * Load the language file. * - * @param $code The language code. + * @param $code string The language code. */ protected function loadFile( $code ) { if ( isset( $this->mRawMessages[$code] ) && @@ -190,7 +205,7 @@ class languages { * * @param $code The language code. * - * @return The messages in this language. + * @return string The messages in this language. */ public function getMessages( $code ) { $this->loadMessages( $code ); diff --git a/maintenance/language/messageTypes.inc b/maintenance/language/messageTypes.inc index 9baf7e76..62374500 100644 --- a/maintenance/language/messageTypes.inc +++ b/maintenance/language/messageTypes.inc @@ -2,6 +2,21 @@ /** * Several types of messages. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup MaintenanceLanguage */ @@ -86,9 +101,11 @@ $wgIgnoredMessages = array( 'exif-make-value', 'exif-model-value', 'exif-software-value', + 'exif-software-version-value', 'history_copyright', 'licenses', 'loginstart', + 'loginend-https', 'loginend', 'loginlanguagelinks', 'pear-mail-error', @@ -96,6 +113,7 @@ $wgIgnoredMessages = array( 'markaspatrolledlink', 'newarticletextanon', 'newsectionheaderdefaultlevel', + 'mainpage-nstab', 'newtalkseparator', 'noarticletextanon', 'number_of_watching_users_RCview', @@ -111,6 +129,7 @@ $wgIgnoredMessages = array( 'signature-anon', 'signupstart', 'signupend', + 'signupend-https', 'sitenotice', 'sitesubtitle', 'sitetitle', @@ -125,7 +144,7 @@ $wgIgnoredMessages = array( 'allpages-summary', 'booksources-summary', 'categories-summary', - 'ipblocklist-summary', + 'blocklist-summary', 'protectedtitles-summary', 'listusers-summary', 'longpages-summary', @@ -183,7 +202,6 @@ $wgOptionalMessages = array( 'userrights-irreversible-marker', 'tog-nolangconversion', 'tog-noconvertlink', - 'yourvariant', 'variantname-zh-hans', 'variantname-zh-hant', 'variantname-zh-cn', @@ -212,6 +230,9 @@ $wgOptionalMessages = array( 'variantname-tg-cyrl', 'variantname-tg-latn', 'variantname-tg', + 'variantname-ike-cans', + 'variantname-ike-latn', + 'variantname-iu', 'rc-change-size', 'resetpass_text', 'image_sample', @@ -237,6 +258,11 @@ $wgOptionalMessages = array( 'vector.css', 'print.css', 'handheld.css', + 'noscript.css', + 'group-autoconfirmed.css', + 'group-bot.css', + 'group-sysop.css', + 'group-bureaucrat.css', 'common.js', 'standard.js', 'nostalgia.js', @@ -247,26 +273,39 @@ $wgOptionalMessages = array( 'simple.js', 'modern.js', 'vector.js', + 'group-autoconfirmed.js', + 'group-bot.js', + 'group-sysop.js', + 'group-bureaucrat.js', 'widthheight', 'exif-fnumber-format', 'exif-focallength-format', + 'exif-compression-5', 'exif-compression-6', + 'exif-compression-7', + 'exif-compression-8', + 'exif-compression-32773', + 'exif-compression-32946', + 'exif-compression-34712', 'exif-photometricinterpretation-2', 'exif-photometricinterpretation-6', 'exif-xyresolution-i', 'exif-xyresolution-c', 'exif-colorspace-1', - 'exif-colorspace-ffff.h', 'exif-componentsconfiguration-1', 'exif-componentsconfiguration-2', 'exif-componentsconfiguration-3', 'exif-componentsconfiguration-4', 'exif-componentsconfiguration-5', 'exif-componentsconfiguration-6', + 'exif-contact-value', + 'exif-coordinate-format', 'exif-lightsource-20', 'exif-lightsource-21', 'exif-lightsource-22', 'exif-lightsource-23', + 'exif-maxaperturevalue-value', + 'exif-subjectnewscode-value', 'booksources-isbn', 'sp-contributions-explain', 'sorbs', @@ -274,14 +313,15 @@ $wgOptionalMessages = array( 'seconds-abbrev', 'minutes-abbrev', 'hours-abbrev', + 'days-abbrev', 'filerevert-backlink', 'filedelete-backlink', 'delete-backlink', - 'move-page-backlink', 'protect-backlink', 'pagetitle', 'filename-prefix-blacklist', 'edittools', + 'edittools-upload', 'size-bytes', 'size-kilobytes', 'size-megabytes', @@ -338,8 +378,8 @@ $wgOptionalMessages = array( 'hebrew-calendar-m10-gen', 'hebrew-calendar-m11-gen', 'hebrew-calendar-m12-gen', + 'version-api', 'version-svn-revision', - 'catseparator', 'semicolon-separator', 'comma-separator', 'colon-separator', @@ -364,6 +404,9 @@ $wgOptionalMessages = array( 'shared-repo-name-wikimediacommons', 'usermessage-template', 'filepage.css', + 'metadata-langitem', + 'metadata-langitem-default', + 'nocookiesforlogin', ); /** EXIF messages, which may be set as optional in several checks, but are generally mandatory */ @@ -380,13 +423,11 @@ $wgEXIFMessages = array( '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', @@ -405,7 +446,6 @@ $wgEXIFMessages = array( 'exif-compressedbitsperpixel', 'exif-pixelydimension', 'exif-pixelxdimension', - 'exif-makernote', 'exif-usercomment', 'exif-relatedsoundfile', 'exif-datetimeoriginal', @@ -416,10 +456,10 @@ $wgEXIFMessages = array( 'exif-exposuretime', 'exif-exposuretime-format', 'exif-fnumber', + 'exif-fnumber-format', 'exif-exposureprogram', 'exif-spectralsensitivity', 'exif-isospeedratings', - 'exif-oecf', 'exif-shutterspeedvalue', 'exif-aperturevalue', 'exif-brightnessvalue', @@ -430,9 +470,9 @@ $wgEXIFMessages = array( 'exif-lightsource', 'exif-flash', 'exif-focallength', + 'exif-focallength-format', 'exif-subjectarea', 'exif-flashenergy', - 'exif-spatialfrequencyresponse', 'exif-focalplanexresolution', 'exif-focalplaneyresolution', 'exif-focalplaneresolutionunit', @@ -441,7 +481,6 @@ $wgEXIFMessages = array( 'exif-sensingmethod', 'exif-filesource', 'exif-scenetype', - 'exif-cfapattern', 'exif-customrendered', 'exif-exposuremode', 'exif-whitebalance', @@ -486,7 +525,92 @@ $wgEXIFMessages = array( 'exif-gpsareainformation', 'exif-gpsdatestamp', 'exif-gpsdifferential', + 'exif-coordinate-format', + 'exif-jpegfilecomment', + 'exif-keywords', + 'exif-worldregioncreated', + 'exif-countrycreated', + 'exif-countrycodecreated', + 'exif-provinceorstatecreated', + 'exif-citycreated', + 'exif-sublocationcreated', + 'exif-worldregiondest', + 'exif-countrydest', + 'exif-countrycodedest', + 'exif-provinceorstatedest', + 'exif-citydest', + 'exif-sublocationdest', + 'exif-objectname', + 'exif-specialinstructions', + 'exif-headline', + 'exif-credit', + 'exif-source', + 'exif-editstatus', + 'exif-urgency', + 'exif-fixtureidentifier', + 'exif-locationdest', + 'exif-locationdestcode', + 'exif-objectcycle', + 'exif-contact', + 'exif-writer', + 'exif-languagecode', + 'exif-iimversion', + 'exif-iimcategory', + 'exif-iimsupplementalcategory', + 'exif-datetimeexpires', + 'exif-datetimereleased', + 'exif-originaltransmissionref', + 'exif-identifier', + 'exif-lens', + 'exif-serialnumber', + 'exif-cameraownername', + 'exif-label', + 'exif-datetimemetadata', + 'exif-nickname', + 'exif-rating', + 'exif-rightscertificate', + 'exif-copyrighted', + 'exif-copyrightowner', + 'exif-usageterms', + 'exif-webstatement', + 'exif-originaldocumentid', + 'exif-licenseurl', + 'exif-morepermissionsurl', + 'exif-attributionurl', + 'exif-preferredattributionname', + 'exif-pngfilecomment', + 'exif-disclaimer', + 'exif-contentwarning', + 'exif-giffilecomment', + 'exif-intellectualgenre', + 'exif-subjectnewscode', + 'exif-scenecode', + 'exif-event', + 'exif-organisationinimage', + 'exif-personinimage', + 'exif-originalimageheight', + 'exif-originalimagewidth', + 'exif-make-value', + 'exif-model-value', + 'exif-software-value', + 'exif-software-version-value', + 'exif-contact-value', + 'exif-subjectnewscode-value', 'exif-compression-1', + 'exif-compression-2', + 'exif-compression-3', + 'exif-compression-4', + 'exif-compression-5', + 'exif-compression-6', + 'exif-compression-7', + 'exif-compression-8', + 'exif-compression-32773', + 'exif-compression-32946', + 'exif-compression-34712', + 'exif-copyrighted-true', + 'exif-copyrighted-false', + 'exif-photometricinterpretation-2', + 'exif-photometricinterpretation-6', 'exif-unknowndate', 'exif-orientation-1', 'exif-orientation-2', @@ -498,7 +622,17 @@ $wgEXIFMessages = array( 'exif-orientation-8', 'exif-planarconfiguration-1', 'exif-planarconfiguration-2', + 'exif-xyresolution-i', + 'exif-xyresolution-c', + 'exif-colorspace-1', + 'exif-colorspace-65535', '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', @@ -532,18 +666,22 @@ $wgEXIFMessages = array( '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-flash-fired-0' , - 'exif-flash-fired-1' , - 'exif-flash-return-0' , - 'exif-flash-return-2' , - 'exif-flash-return-3' , - 'exif-flash-mode-1' , - 'exif-flash-mode-2' , - 'exif-flash-mode-3' , - 'exif-flash-function-1' , - 'exif-flash-redeye-1' , + 'exif-flash-fired-0', + 'exif-flash-fired-1', + 'exif-flash-return-0', + 'exif-flash-return-2', + 'exif-flash-return-3', + 'exif-flash-mode-1', + 'exif-flash-mode-2', + 'exif-flash-mode-3', + 'exif-flash-function-1', + 'exif-flash-redeye-1', 'exif-focalplaneresolutionunit-2', 'exif-sensingmethod-1', 'exif-sensingmethod-2', @@ -552,6 +690,7 @@ $wgEXIFMessages = array( 'exif-sensingmethod-5', 'exif-sensingmethod-7', 'exif-sensingmethod-8', + 'exif-filesource-3', 'exif-scenetype-1', 'exif-customrendered-0', 'exif-customrendered-1', @@ -586,8 +725,8 @@ $wgEXIFMessages = array( 'exif-gpslatitude-s', 'exif-gpslongitude-e', 'exif-gpslongitude-w', - 'exif-gpsaltitude-0', - 'exif-gpsaltitude-1', + 'exif-gpsaltitude-above-sealevel', + 'exif-gpsaltitude-below-sealevel', 'exif-gpsstatus-a', 'exif-gpsstatus-v', 'exif-gpsmeasuremode-2', @@ -598,7 +737,48 @@ $wgEXIFMessages = array( 'exif-gpsdestdistance-k', 'exif-gpsdestdistance-m', 'exif-gpsdestdistance-n', + 'exif-gpsdop-excellent', + 'exif-gpsdop-good', + 'exif-gpsdop-moderate', + 'exif-gpsdop-fair', + 'exif-gpsdop-poor', + 'exif-objectcycle-a', + 'exif-objectcycle-p', + 'exif-objectcycle-b', 'exif-gpsdirection-t', 'exif-gpsdirection-m', - 'exif-objectname', + 'exif-ycbcrpositioning-1', + 'exif-ycbcrpositioning-2', + 'exif-dc-contributor', + 'exif-dc-coverage', + 'exif-dc-date', + 'exif-dc-publisher', + 'exif-dc-relation', + 'exif-dc-rights', + 'exif-dc-source', + 'exif-dc-type', + 'exif-rating-rejected', + 'exif-isospeedratings-overflow', + 'exif-maxaperturevalue-value', + 'exif-iimcategory-ace', + 'exif-iimcategory-clj', + 'exif-iimcategory-dis', + 'exif-iimcategory-fin', + 'exif-iimcategory-edu', + 'exif-iimcategory-evn', + 'exif-iimcategory-hth', + 'exif-iimcategory-hum', + 'exif-iimcategory-lab', + 'exif-iimcategory-lif', + 'exif-iimcategory-pol', + 'exif-iimcategory-rel', + 'exif-iimcategory-sci', + 'exif-iimcategory-soi', + 'exif-iimcategory-spo', + 'exif-iimcategory-war', + 'exif-iimcategory-wea', + 'exif-urgency-normal', + 'exif-urgency-low', + 'exif-urgency-high', + 'exif-urgency-other', ); diff --git a/maintenance/language/messages.inc b/maintenance/language/messages.inc index a0b19ac6..d707cf5d 100644 --- a/maintenance/language/messages.inc +++ b/maintenance/language/messages.inc @@ -2,6 +2,21 @@ /** * Define the messages structure in the messages file, for an automated rewriting. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup MaintenanceLanguage */ @@ -142,11 +157,10 @@ $wgMessageStructure = array( 'listingcontinuesabbrev', 'index-category', 'noindex-category', + 'broken-file-category', ), 'mainpage' => array( 'linkprefix', - 'mainpagetext', - 'mainpagedocfooter', ), 'miscellaneous1' => array( 'about', @@ -202,10 +216,10 @@ $wgMessageStructure = array( 'history', 'history_short', 'updatedmarker', - 'info_short', 'printableversion', 'permalink', 'print', + 'view', 'edit', 'create', 'editthispage', @@ -213,6 +227,7 @@ $wgMessageStructure = array( 'delete', 'deletethispage', 'undelete_short', + 'viewdeleted_short', 'protect', 'protect_change', 'protectthispage', @@ -302,6 +317,8 @@ $wgMessageStructure = array( 'toc', 'showtoc', 'hidetoc', + 'collapsible-collapse', + 'collapsible-expand', 'thisisdeleted', 'viewdeleted', 'restorelink', @@ -318,6 +335,9 @@ $wgMessageStructure = array( 'anonnotice', 'newsectionheaderdefaultlevel', 'red-link-title', + 'sort-descending', + 'sort-ascending', + ), 'nstab' => array( 'nstab-main', @@ -330,6 +350,7 @@ $wgMessageStructure = array( 'nstab-template', 'nstab-help', 'nstab-category', + 'mainpage-nstab', ), 'main' => array( 'nosuchaction', @@ -381,7 +402,8 @@ $wgMessageStructure = array( 'sqlhidden', 'cascadeprotected', 'namespaceprotected', - 'customcssjsprotected', + 'customcssprotected', + 'customjsprotected', 'ns-specialprotected', 'titleprotected', ), @@ -413,6 +435,7 @@ $wgMessageStructure = array( 'createaccount', 'gotaccount', 'gotaccountlink', + 'userlogin-resetlink', 'createaccountmail', 'createaccountreason', 'badretype', @@ -421,6 +444,8 @@ $wgMessageStructure = array( 'createaccounterror', 'nocookiesnew', 'nocookieslogin', + 'nocookiesfornew', + 'nocookiesforlogin', 'noname', 'loginsuccesstitle', 'loginsuccess', @@ -444,8 +469,10 @@ $wgMessageStructure = array( 'throttled-mailpassword', 'loginstart', 'loginend', + 'loginend-https', 'signupstart', 'signupend', + 'signupend-https', 'mailerror', 'acct_creation_throttle_hit', 'emailauthenticated', @@ -459,6 +486,7 @@ $wgMessageStructure = array( 'createaccount-text', 'usernamehasherror', 'login-throttled', + 'login-abort-generic', 'loginlanguagelabel', 'loginlanguagelinks', 'suspicious-userlogout', @@ -485,6 +513,21 @@ $wgMessageStructure = array( 'resetpass-wrong-oldpass', 'resetpass-temp-password', ), + 'passwordreset' => array( + 'passwordreset', + 'passwordreset-text', + 'passwordreset-legend', + 'passwordreset-disabled', + 'passwordreset-pretext', + 'passwordreset-username', + 'passwordreset-domain', + 'passwordreset-email', + 'passwordreset-emailtitle', + 'passwordreset-emailtext-ip', + 'passwordreset-emailtext-user', + 'passwordreset-emailelement', + 'passwordreset-emailsent', + ), 'toolbar' => array( 'bold_sample', 'bold_tip', @@ -496,8 +539,6 @@ $wgMessageStructure = array( 'extlink_tip', 'headline_sample', 'headline_tip', - 'math_sample', - 'math_tip', 'nowiki_sample', 'nowiki_tip', 'image_sample', @@ -566,6 +607,7 @@ $wgMessageStructure = array( 'session_fail_preview', 'session_fail_preview_html', 'token_suffix_mismatch', + 'edit_form_incomplete', 'editing', 'editingsection', 'editingcomment', @@ -593,6 +635,7 @@ $wgMessageStructure = array( 'template-semiprotected', 'hiddencategories', 'edittools', + 'edittools-upload', 'nocreatetitle', 'nocreatetext', 'nocreate-loggedin', @@ -869,6 +912,7 @@ $wgMessageStructure = array( 'qbsettings-fixedright', 'qbsettings-floatingleft', 'qbsettings-floatingright', + 'qbsettings-directionality', ), 'preferences' => array( 'preferences', @@ -880,9 +924,10 @@ $wgMessageStructure = array( 'changepassword', 'prefs-skin', 'skin-preview', - 'prefs-math', 'datedefault', + 'prefs-beta', 'prefs-datetime', + 'prefs-labs', 'prefs-personal', 'prefs-rc', 'prefs-watchlist', @@ -904,8 +949,6 @@ $wgMessageStructure = array( 'columns', 'searchresultshead', 'resultsperpage', - 'contextlines', - 'contextchars', 'stub-threshold', 'stub-threshold-disabled', 'recentchangesdays', @@ -965,8 +1008,12 @@ $wgMessageStructure = array( 'prefs-help-gender', 'email', 'prefs-help-realname', + + # 3 messages depending upon wgEmailConfirmToEdit and $wgEnableUserEmail 'prefs-help-email', + 'prefs-help-email-others', 'prefs-help-email-required', + 'prefs-info', 'prefs-i18n', 'prefs-signature', @@ -1091,7 +1138,6 @@ $wgMessageStructure = array( 'right-userrights', 'right-userrights-interwiki', 'right-siteadmin', - 'right-reset-passwords', 'right-override-export-depth', 'right-sendemail', ), @@ -1099,6 +1145,7 @@ $wgMessageStructure = array( 'rightslog', 'rightslogtext', 'rightslogentry', + 'rightslogentry-autopromote', 'rightsnone', ), 'action' => array( @@ -1239,6 +1286,7 @@ $wgMessageStructure = array( 'large-file', 'largefileserver', 'emptyfile', + 'windows-nonascii-filename', 'fileexists', 'filepageexists', 'fileexists-extension', @@ -1260,6 +1308,7 @@ $wgMessageStructure = array( 'php-uploaddisabledtext', 'uploadscripted', 'uploadvirus', + 'uploadjava', 'upload-source', 'sourcefilename', 'sourceurl', @@ -1269,7 +1318,6 @@ $wgMessageStructure = array( 'upload-options', 'watchthisupload', 'filewasdeleted', - 'upload-wasdeleted', 'filename-bad-prefix', 'filename-prefix-blacklist', 'upload-success-subj', @@ -1291,6 +1339,23 @@ $wgMessageStructure = array( 'upload-http-error', ), + 'zip' => array( + 'zip-file-open-error', + 'zip-wrong-format', + 'zip-bad', + 'zip-unsupported' + ), + + 'uploadstash' => array( + 'uploadstash', + 'uploadstash-summary', + 'uploadstash-clear', + 'uploadstash-nofiles', + 'uploadstash-badtoken', + 'uploadstash-errclear', + 'uploadstash-refresh', + ), + 'img-auth' => array( 'img-auth-accessdenied', 'img-auth-desc', @@ -1367,7 +1432,7 @@ $wgMessageStructure = array( 'linkstoimage-more', 'nolinkstoimage', 'morelinkstoimage', - 'redirectstofile', + 'linkstoimage-redirect', 'duplicatesoffile', 'sharedupload', 'sharedupload-desc-there', @@ -1473,6 +1538,7 @@ $wgMessageStructure = array( 'doubleredirects-summary', 'doubleredirectstext', 'double-redirect-fixed-move', + 'double-redirect-fixed-maintenance', 'double-redirect-fixer', ), 'brokenredirects' => array( @@ -1580,6 +1646,7 @@ $wgMessageStructure = array( 'pager-newer-n', 'pager-older-n', 'suppress', + 'querypage-disabled', ), 'booksources' => array( 'booksources', @@ -1703,6 +1770,10 @@ $wgMessageStructure = array( 'noemailtext', 'nowikiemailtitle', 'nowikiemailtext', + 'emailnotarget', + 'emailtarget', + 'emailusername', + 'emailusernamesubmit', 'email-legend', 'emailfrom', 'emailto', @@ -1728,9 +1799,9 @@ $wgMessageStructure = array( 'watchlistanontext', 'watchnologin', 'watchnologintext', - 'addedwatch', + 'addwatch', 'addedwatchtext', - 'removedwatch', + 'removewatch', 'removedwatchtext', 'watch', 'watchthispage', @@ -1753,6 +1824,7 @@ $wgMessageStructure = array( 'watching' => array( 'watching', 'unwatching', + 'watcherrortext', ), 'enotif' => array( 'enotif_mailer', @@ -1911,6 +1983,9 @@ $wgMessageStructure = array( 'nsform' => array( 'namespace', 'invert', + 'tooltip-invert', + 'namespace_association', + 'tooltip-namespace_association', 'blanknamespace', ), 'contributions' => array( @@ -1965,17 +2040,19 @@ $wgMessageStructure = array( 'whatlinkshere-filters', ), 'block' => array( + 'autoblockid', + 'block', + 'unblock', 'blockip', 'blockip-title', 'blockip-legend', 'blockiptext', - 'ipaddress', 'ipadressorusername', 'ipbexpiry', 'ipbreason', 'ipbreasonotherlist', 'ipbreason-dropdown', - 'ipbanononly', + 'ipb-hardblock', 'ipbcreateaccount', 'ipbemailban', 'ipbenableautoblock', @@ -1986,11 +2063,14 @@ $wgMessageStructure = array( 'ipbotherreason', 'ipbhidename', 'ipbwatchuser', - 'ipballowusertalk', + 'ipb-disableusertalk', 'ipb-change-block', + 'ipb-confirm', 'badipaddress', 'blockipsuccesssub', 'blockipsuccesstext', + 'ipb-blockingself', + 'ipb-confirmhideuser', 'ipb-edit-dropdown', 'ipb-unblock-addr', 'ipb-unblock', @@ -2000,18 +2080,25 @@ $wgMessageStructure = array( 'unblockiptext', 'ipusubmit', 'unblocked', + 'unblocked-range', 'unblocked-id', + 'blocklist', 'ipblocklist', 'ipblocklist-legend', - 'ipblocklist-username', - 'ipblocklist-sh-userblocks', - 'ipblocklist-sh-tempblocks', - 'ipblocklist-sh-addressblocks', - 'ipblocklist-summary', + 'blocklist-userblocks', + 'blocklist-tempblocks', + 'blocklist-addressblocks', + 'blocklist-timestamp', + 'blocklist-target', + 'blocklist-expiry', + 'blocklist-by', + 'blocklist-params', + 'blocklist-reason', + 'blocklist-summary', 'ipblocklist-submit', 'ipblocklist-localblock', 'ipblocklist-otherblocks', - 'blocklistline', + 'infiniteblock', 'expiringblock', 'anononlyblock', @@ -2047,6 +2134,7 @@ $wgMessageStructure = array( 'ipb_already_blocked', 'ipb-needreblock', 'ipb-otherblocks-header', + 'unblock-hideuser', 'ipb_cant_unblock', 'ipb_blocked_as_range', 'ip_range_invalid', @@ -2080,10 +2168,10 @@ $wgMessageStructure = array( 'unlockdbsuccesstext', 'lockfilenotwritable', 'databasenotlocked', + 'lockedbyandtime', ), 'movepage' => array( 'move-page', - 'move-page-backlink', 'move-page-legend', 'movepagetext', 'movepagetext-noredirectfixer', @@ -2382,6 +2470,11 @@ $wgMessageStructure = array( 'vector.css', 'print.css', 'handheld.css', + 'noscript.css', + 'group-autoconfirmed.css', + 'group-bot.css', + 'group-sysop.css', + 'group-bureaucrat.css', ), 'scripts' => array( 'common.js', @@ -2394,10 +2487,12 @@ $wgMessageStructure = array( 'simple.js', 'modern.js', 'vector.js', + 'group-autoconfirmed.js', + 'group-bot.js', + 'group-sysop.js', + 'group-bureaucrat.js', ), 'metadata_cc' => array( - 'nodublincore', - 'nocreativecommons', 'notacceptable', ), 'attribution' => array( @@ -2421,12 +2516,17 @@ $wgMessageStructure = array( 'spam_blanking', ), 'info' => array( - 'infosubtitle', - 'numedits', - 'numtalkedits', - 'numwatchers', - 'numauthors', - 'numtalkauthors', + 'pageinfo-title', + 'pageinfo-header-edits', + 'pageinfo-header-watchlist', + 'pageinfo-header-views', + 'pageinfo-subjectpage', + 'pageinfo-talkpage', + 'pageinfo-watchers', + 'pageinfo-edits', + 'pageinfo-authors', + 'pageinfo-views', + 'pageinfo-viewsperedit', ), 'skin' => array( 'skinname-standard', @@ -2439,25 +2539,6 @@ $wgMessageStructure = array( 'skinname-modern', 'skinname-vector', ), - 'math' => array( - 'mw_math_png', - 'mw_math_simple', - 'mw_math_html', - 'mw_math_source', - 'mw_math_modern', - 'mw_math_mathml', - ), - 'matherrors' => array( - 'math_failure', - 'math_unknown_error', - 'math_unknown_function', - 'math_lexing_error', - 'math_syntax_error', - 'math_image_error', - 'math_bad_tmpdir', - 'math_bad_output', - 'math_notexvc', - ), 'patrolling' => array( 'markaspatrolleddiff', 'markaspatrolledlink', @@ -2499,10 +2580,13 @@ $wgMessageStructure = array( 'widthheightpage', 'file-info', 'file-info-size', + 'file-info-size-pages', 'file-nohires', 'svg-long-desc', 'show-big-image', - 'show-big-image-thumb', + 'show-big-image-preview', + 'show-big-image-other', + 'show-big-image-size', 'file-info-gif-looped', 'file-info-gif-frames', 'file-info-png-looped', @@ -2526,6 +2610,7 @@ $wgMessageStructure = array( 'seconds-abbrev', 'minutes-abbrev', 'hours-abbrev', + 'days-abbrev', ), 'badimagelist' => array( 'bad_image_list', @@ -2570,12 +2655,19 @@ $wgMessageStructure = array( 'variantname-tg-latn', 'variantname-tg', ), + 'variantname-iu' => array( + 'variantname-ike-cans', + 'variantname-ike-latn', + 'variantname-iu', + ), 'metadata' => array( 'metadata', 'metadata-help', 'metadata-expand', 'metadata-collapse', 'metadata-fields', + 'metadata-langitem', + 'metadata-langitem-default', ), 'exif' => array( 'exif-imagewidth', @@ -2596,7 +2688,6 @@ $wgMessageStructure = array( 'exif-stripbytecounts', 'exif-jpeginterchangeformat', 'exif-jpeginterchangeformatlength', - 'exif-transferfunction', 'exif-whitepoint', 'exif-primarychromaticities', 'exif-ycbcrcoefficients', @@ -2615,7 +2706,6 @@ $wgMessageStructure = array( 'exif-compressedbitsperpixel', 'exif-pixelydimension', 'exif-pixelxdimension', - 'exif-makernote', 'exif-usercomment', 'exif-relatedsoundfile', 'exif-datetimeoriginal', @@ -2630,7 +2720,6 @@ $wgMessageStructure = array( 'exif-exposureprogram', 'exif-spectralsensitivity', 'exif-isospeedratings', - 'exif-oecf', 'exif-shutterspeedvalue', 'exif-aperturevalue', 'exif-brightnessvalue', @@ -2644,7 +2733,6 @@ $wgMessageStructure = array( 'exif-focallength-format', 'exif-subjectarea', 'exif-flashenergy', - 'exif-spatialfrequencyresponse', 'exif-focalplanexresolution', 'exif-focalplaneyresolution', 'exif-focalplaneresolutionunit', @@ -2653,7 +2741,6 @@ $wgMessageStructure = array( 'exif-sensingmethod', 'exif-filesource', 'exif-scenetype', - 'exif-cfapattern', 'exif-customrendered', 'exif-exposuremode', 'exif-whitebalance', @@ -2698,16 +2785,96 @@ $wgMessageStructure = array( 'exif-gpsareainformation', 'exif-gpsdatestamp', 'exif-gpsdifferential', + 'exif-coordinate-format', + 'exif-jpegfilecomment', + 'exif-keywords', + 'exif-worldregioncreated', + 'exif-countrycreated', + 'exif-countrycodecreated', + 'exif-provinceorstatecreated', + 'exif-citycreated', + 'exif-sublocationcreated', + 'exif-worldregiondest', + 'exif-countrydest', + 'exif-countrycodedest', + 'exif-provinceorstatedest', + 'exif-citydest', + 'exif-sublocationdest', 'exif-objectname', + 'exif-specialinstructions', + 'exif-headline', + 'exif-credit', + 'exif-source', + 'exif-editstatus', + 'exif-urgency', + 'exif-fixtureidentifier', + 'exif-locationdest', + 'exif-locationdestcode', + 'exif-objectcycle', + 'exif-contact', + 'exif-writer', + 'exif-languagecode', + 'exif-iimversion', + 'exif-iimcategory', + 'exif-iimsupplementalcategory', + 'exif-datetimeexpires', + 'exif-datetimereleased', + 'exif-originaltransmissionref', + 'exif-identifier', + 'exif-lens', + 'exif-serialnumber', + 'exif-cameraownername', + 'exif-label', + 'exif-datetimemetadata', + 'exif-nickname', + 'exif-rating', + 'exif-rightscertificate', + 'exif-copyrighted', + 'exif-copyrightowner', + 'exif-usageterms', + 'exif-webstatement', + 'exif-originaldocumentid', + 'exif-licenseurl', + 'exif-morepermissionsurl', + 'exif-attributionurl', + 'exif-preferredattributionname', + 'exif-pngfilecomment', + 'exif-disclaimer', + 'exif-contentwarning', + 'exif-giffilecomment', + 'exif-intellectualgenre', + 'exif-subjectnewscode', + 'exif-scenecode', + 'exif-event', + 'exif-organisationinimage', + 'exif-personinimage', + 'exif-originalimageheight', + 'exif-originalimagewidth', ), 'exif-values' => array( 'exif-make-value', 'exif-model-value', 'exif-software-value', + 'exif-software-version-value', + 'exif-contact-value', + 'exif-subjectnewscode-value', ), 'exif-compression' => array( 'exif-compression-1', + 'exif-compression-2', + 'exif-compression-3', + 'exif-compression-4', + 'exif-compression-5', 'exif-compression-6', + 'exif-compression-7', + 'exif-compression-8', + 'exif-compression-32773', + 'exif-compression-32946', + 'exif-compression-34712', + ), + 'exif-copyrighted' => array( + 'exif-copyrighted-true', + 'exif-copyrighted-false', ), 'exif-photometricinterpretation' => array( 'exif-photometricinterpretation-2', @@ -2736,7 +2903,7 @@ $wgMessageStructure = array( ), 'exif-colorspace' => array( 'exif-colorspace-1', - 'exif-colorspace-ffff.h', + 'exif-colorspace-65535', ), 'exif-componentsconfiguration' => array( 'exif-componentsconfiguration-0', @@ -2879,6 +3046,10 @@ $wgMessageStructure = array( 'exif-gpslongitude-e', 'exif-gpslongitude-w', ), + 'exif-altituderef' => array( + 'exif-gpsaltitude-above-sealevel', + 'exif-gpsaltitude-below-sealevel', + ), 'exif-gpsstatus' => array( 'exif-gpsstatus-a', 'exif-gpsstatus-v', @@ -2892,17 +3063,80 @@ $wgMessageStructure = array( 'exif-gpsspeed-m', 'exif-gpsspeed-n', ), + 'exif-gpsdestdistanceref' => array( + 'exif-gpsdestdistance-k', + 'exif-gpsdestdistance-m', + 'exif-gpsdestdistance-n', + ), + 'exif-gdop' => array( + 'exif-gpsdop-excellent', + 'exif-gpsdop-good', + 'exif-gpsdop-moderate', + 'exif-gpsdop-fair', + 'exif-gpsdop-poor', + ), + 'exif-objectcycle' => array( + 'exif-objectcycle-a', + 'exif-objectcycle-p', + 'exif-objectcycle-b', + ), 'exif-gpsdirection' => array( 'exif-gpsdirection-t', 'exif-gpsdirection-m', ), + 'exif-ycbcrpositioning' => array( + 'exif-ycbcrpositioning-1', + 'exif-ycbcrpositioning-2', + ), + 'exif-dc' => array( + 'exif-dc-contributor', + 'exif-dc-coverage', + 'exif-dc-date', + 'exif-dc-publisher', + 'exif-dc-relation', + 'exif-dc-rights', + 'exif-dc-source', + 'exif-dc-type', + ), + 'exif-rating' => array( + 'exif-rating-rejected', + ), + 'exif-isospeedratings' => array( + 'exif-isospeedratings-overflow', + ), + 'exif-maxaperturevalue' => array( + 'exif-maxaperturevalue-value', + ), + 'exif-iimcategory' => array( + 'exif-iimcategory-ace', + 'exif-iimcategory-clj', + 'exif-iimcategory-dis', + 'exif-iimcategory-fin', + 'exif-iimcategory-edu', + 'exif-iimcategory-evn', + 'exif-iimcategory-hth', + 'exif-iimcategory-hum', + 'exif-iimcategory-lab', + 'exif-iimcategory-lif', + 'exif-iimcategory-pol', + 'exif-iimcategory-rel', + 'exif-iimcategory-sci', + 'exif-iimcategory-soi', + 'exif-iimcategory-spo', + 'exif-iimcategory-war', + 'exif-iimcategory-wea', + ), + 'exif-urgency' => array( + 'exif-urgency-normal', + 'exif-urgency-low', + 'exif-urgency-high', + 'exif-urgency-other', + ), 'edit-externally' => array( 'edit-externally', 'edit-externally-help', ), 'all' => array( - 'recentchangesall', - 'imagelistall', 'watchlistall2', 'namespacesall', 'monthsall', @@ -2945,6 +3179,7 @@ $wgMessageStructure = array( 'deleteconflict' => array( 'deletedwhileediting', 'confirmrecreate', + 'confirmrecreate-noreason', 'recreate', ), 'unit-pixel' => array( @@ -2955,8 +3190,13 @@ $wgMessageStructure = array( 'confirm-purge-top', 'confirm-purge-bottom', ), + 'watch-unwatch' => array( + 'confirm-watch-button', + 'confirm-watch-top', + 'confirm-unwatch-button', + 'confirm-unwatch-top', + ), 'separators' => array( - 'catseparator', 'semicolon-separator', 'comma-separator', 'colon-separator', @@ -3010,6 +3250,9 @@ $wgMessageStructure = array( 'lag-warn-normal', 'lag-warn-high', ), + 'watch' => array( + 'confirm-watch-button', + ), 'watchlisteditor' => array( 'watchlistedit-numitems', 'watchlistedit-noitems', @@ -3105,14 +3348,15 @@ $wgMessageStructure = array( 'version-specialpages', 'version-parserhooks', 'version-variables', + 'version-antispam', 'version-skins', + 'version-api', 'version-other', 'version-mediahandlers', 'version-hooks', 'version-extension-functions', 'version-parser-extensiontags', 'version-parser-function-hooks', - 'version-skin-extension-functions', 'version-hook-name', 'version-hook-subscribedby', 'version-version', @@ -3140,6 +3384,7 @@ $wgMessageStructure = array( 'fileduplicatesearch-info', 'fileduplicatesearch-result-1', 'fileduplicatesearch-result-n', + 'fileduplicatesearch-noresults', ), 'special-specialpages' => array( 'specialpages', @@ -3212,6 +3457,9 @@ $wgMessageStructure = array( 'sqlite-has-fts', 'sqlite-no-fts', ), + 'unwatch' => array( + 'confirm-unwatch-button', + ), ); /** Comments for each block */ @@ -3243,7 +3491,8 @@ XHTML id names.", 'login' => 'Login and logout pages', 'mail' => 'E-mail sending', 'passwordstrength' => 'JavaScript password checks', - 'resetpass' => 'Password reset dialog', + 'resetpass' => 'Change password dialog', + 'passwordreset' => 'Special:PasswordReset', 'toolbar' => 'Edit page toolbar', 'edit' => 'Edit pages', 'parserwarnings' => 'Parser/template warnings', @@ -3271,7 +3520,9 @@ XHTML id names.", 'recentchanges' => 'Recent changes', 'recentchangeslinked' => 'Recent changes linked', 'upload' => 'Upload', + 'zip' => 'ZipDirectoryReader', 'upload-errors' => '', + 'uploadstash' => 'Special:UploadStash', 'img-auth' => 'img_auth script messages', 'http-errors' => 'HTTP errors', 'upload-curl-errors' => 'Some likely curl errors. More could be added from <http://curl.haxx.se/libcurl/c/libcurl-errors.html>', @@ -3337,8 +3588,6 @@ XHTML id names.", 'spamprotection' => 'Spam protection', 'info' => 'Info page', 'skin' => 'Skin names', - 'math' => 'Math options', - 'matherrors' => 'Math errors', 'patrolling' => 'Patrolling', 'patrol-log' => 'Patrol log', 'imagedeletion' => 'Image deletion', @@ -3355,11 +3604,13 @@ Variants for Chinese language", 'variantname-kk' => 'Variants for Kazakh language', 'variantname-ku' => 'Variants for Kurdish language', 'variantname-tg' => 'Variants for Tajiki language', + 'variantname-iu' => 'Variants for Inuktitut language', 'media-info' => 'Media information', '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-copyrighted' => '', 'exif-unknowndate' => '', 'exif-photometricinterpretation' => '', 'exif-orientation' => '', @@ -3387,10 +3638,21 @@ Variants for Chinese language", 'exif-subjectdistancerange' => '', 'exif-gpslatitude' => 'Pseudotags used for GPSLatitudeRef and GPSDestLatitudeRef', 'exif-gpslongitude' => 'Pseudotags used for GPSLongitudeRef and GPSDestLongitudeRef', + 'exif-altituderef' => 'Pseudotags used for GPSAltitudeRef', 'exif-gpsstatus' => '', 'exif-gpsmeasuremode' => '', 'exif-gpsspeed' => 'Pseudotags used for GPSSpeedRef', + 'exif-gpsdestdistanceref' => 'Pseudotags used for GPSDestDistanceRef', + 'exif-gdop' => '', + 'exif-objectcycle' => '', 'exif-gpsdirection' => 'Pseudotags used for GPSTrackRef, GPSImgDirectionRef and GPSDestBearingRef', + 'exif-ycbcrpositioning' => '', + 'exif-dc' => '', + 'exif-rating' => '', + 'exif-isospeedratings' => '', + 'exif-maxaperturevalue' => '', + 'exif-iimcategory' => '', + 'exif-urgency' => '', 'edit-externally' => 'External editor support', 'all' => "'all' in various places, this might be different for inflected languages", 'confirmemail' => 'E-mail address confirmation', @@ -3399,6 +3661,7 @@ Variants for Chinese language", 'deleteconflict' => 'Delete conflict', 'unit-pixel' => '', 'purge' => 'action=purge', + 'watch-unwatch' => 'action=watch/unwatch', 'separators' => 'Separators for various lists, etc.', 'imgmulti' => 'Multipage image navigation', 'tablepager' => 'Table pager', @@ -3425,4 +3688,5 @@ Variants for Chinese language", 'db-error-messages' => 'Database error messages', 'html-forms' => 'HTML forms', 'sqlite' => 'SQLite database support', + 'ajax-category' => 'Add categories per AJAX', ); diff --git a/maintenance/language/rebuildLanguage.php b/maintenance/language/rebuildLanguage.php index fd8d62ee..9b3a4b9d 100644 --- a/maintenance/language/rebuildLanguage.php +++ b/maintenance/language/rebuildLanguage.php @@ -2,6 +2,21 @@ /** * Rewrite the messages array in the files languages/messages/MessagesXx.php. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup MaintenanceLanguage * @defgroup MaintenanceLanguage MaintenanceLanguage @@ -14,11 +29,12 @@ require_once( 'writeMessagesArray.inc' ); /** * Rewrite a messages array. * + * @param $languages * @param $code The language code. - * @param $write Write to the messages file? - * @param $listUnknown List the unknown messages? - * @param $removeUnknown Remove the unknown messages? - * @param $removeDupes Remove the duplicated messages? + * @param bool $write Write to the messages file? + * @param bool $listUnknown List the unknown messages? + * @param bool $removeUnknown Remove the unknown messages? + * @param bool $removeDupes Remove the duplicated messages? * @param $dupeMsgSource The source file intended to remove from the array. */ function rebuildLanguage( $languages, $code, $write, $listUnknown, $removeUnknown, $removeDupes, $dupeMsgSource ) { @@ -35,7 +51,7 @@ function rebuildLanguage( $languages, $code, $write, $listUnknown, $removeUnknow * * @param $oldMsgArray The input message array. * @param $dupeMsgSource The source file path for duplicates. - * @return $newMsgArray The output message array, with duplicates removed. + * @return Array $newMsgArray The output message array, with duplicates removed. */ function removeDupes( $oldMsgArray, $dupeMsgSource ) { if ( file_exists( $dupeMsgSource ) ) { diff --git a/maintenance/language/transstat.php b/maintenance/language/transstat.php index c2144eb6..a3213266 100644 --- a/maintenance/language/transstat.php +++ b/maintenance/language/transstat.php @@ -2,6 +2,21 @@ /** * Statistics about the localisation. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup MaintenanceLanguage * @@ -80,8 +95,9 @@ $wgGeneralMessages = $wgLanguages->getGeneralMessages(); $wgRequiredMessagesNumber = count( $wgGeneralMessages['required'] ); foreach ( $wgLanguages->getLanguages() as $code ) { - # Don't check English or RTL English - if ( $code == 'en' || $code == 'enRTL' ) { + # Don't check English, RTL English or dummy language codes + if ( $code == 'en' || $code == 'enRTL' || (is_array( $wgDummyLanguageCodes ) && + in_array( $code, $wgDummyLanguageCodes ) ) ) { continue; } diff --git a/maintenance/language/validate.php b/maintenance/language/validate.php index d897e467..57517644 100644 --- a/maintenance/language/validate.php +++ b/maintenance/language/validate.php @@ -1,5 +1,22 @@ <?php /** + * Check language files for unrecognised variables. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup MaintenanceLanguage */ diff --git a/maintenance/language/writeMessagesArray.inc b/maintenance/language/writeMessagesArray.inc index e3a48abd..093359ca 100644 --- a/maintenance/language/writeMessagesArray.inc +++ b/maintenance/language/writeMessagesArray.inc @@ -2,6 +2,21 @@ /** * Write a messages array as a PHP text. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup MaintenanceLanguage */ diff --git a/maintenance/mcc.php b/maintenance/mcc.php index a8c79a72..02445278 100644 --- a/maintenance/mcc.php +++ b/maintenance/mcc.php @@ -2,6 +2,21 @@ /** * memcached diagnostic tool * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @todo document * @ingroup Maintenance @@ -10,7 +25,7 @@ /** */ require_once( dirname( __FILE__ ) . '/commandLine.inc' ); -$mcc = new MWMemcached( array( 'persistant' => true/*, 'debug' => true*/ ) ); +$mcc = new MWMemcached( array( 'persistent' => true/*, 'debug' => true*/ ) ); $mcc->set_servers( $wgMemCachedServers ); # $mcc->set_debug( true ); diff --git a/maintenance/mergeMessageFileList.php b/maintenance/mergeMessageFileList.php index 00c8cae5..8cfefcbd 100644 --- a/maintenance/mergeMessageFileList.php +++ b/maintenance/mergeMessageFileList.php @@ -1,4 +1,26 @@ <?php +/** + * Merge $wgExtensionMessagesFiles from various extensions to produce a + * single array containing all message files. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance + */ # Start from scratch define( 'MW_NO_EXTENSION_MESSAGES', 1 ); @@ -9,6 +31,7 @@ $mmfl = false; class MergeMessageFileList extends Maintenance { function __construct() { + parent::__construct(); $this->addOption( 'list-file', 'A file containing a list of extension setup files, one per line.', false, true ); $this->addOption( 'output', 'Send output to this file (omit for stdout)', false, true ); $this->mDescription = 'Merge $wgExtensionMessagesFiles from various extensions to produce a ' . diff --git a/maintenance/migrateUserGroup.php b/maintenance/migrateUserGroup.php index c4163208..771ed947 100644 --- a/maintenance/migrateUserGroup.php +++ b/maintenance/migrateUserGroup.php @@ -60,7 +60,7 @@ class MigrateUserGroup extends Maintenance { $dbw->commit(); $blockStart += $this->mBatchSize; $blockEnd += $this->mBatchSize; - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } $this->output( "Done! $count user(s) in group '$oldGroup' are now in '$newGroup' instead.\n" ); } diff --git a/maintenance/minify.php b/maintenance/minify.php index 2884fd7b..e1fd862d 100644 --- a/maintenance/minify.php +++ b/maintenance/minify.php @@ -1,6 +1,24 @@ <?php /** * Minify a file or set of files + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance */ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); diff --git a/maintenance/moveBatch.php b/maintenance/moveBatch.php index c7495338..9b9f9105 100644 --- a/maintenance/moveBatch.php +++ b/maintenance/moveBatch.php @@ -101,7 +101,7 @@ class MoveBatch extends Maintenance { if ( $interval ) { sleep( $interval ); } - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } } } diff --git a/maintenance/mssql/tables.sql b/maintenance/mssql/tables.sql index 3d3d3592..dca64f17 100644 --- a/maintenance/mssql/tables.sql +++ b/maintenance/mssql/tables.sql @@ -545,18 +545,6 @@ CREATE TABLE /*$wgDBprefix*/watchlist ( ); CREATE UNIQUE INDEX /*$wgDBprefix*/namespace_title ON /*$wgDBprefix*/watchlist(wl_namespace,wl_title); --- --- Used by the math module to keep track --- of previously-rendered items. --- -CREATE TABLE /*$wgDBprefix*/math ( - math_inputhash varbinary(16) NOT NULL PRIMARY KEY, - math_outputhash varbinary(16) NOT NULL, - math_html_conservativeness tinyint NOT NULL, - math_html NVARCHAR(MAX), - math_mathml NVARCHAR(MAX), -); - -- Needs fulltext index. CREATE TABLE /*$wgDBprefix*/searchindex ( si_page INT NOT NULL unique REFERENCES /*$wgDBprefix*/page(page_id) ON DELETE CASCADE, diff --git a/maintenance/mwdocgen.php b/maintenance/mwdocgen.php index 92311521..ed511f74 100644 --- a/maintenance/mwdocgen.php +++ b/maintenance/mwdocgen.php @@ -1,21 +1,33 @@ <?php /** - * Script to easily generate the mediawiki documentation using doxygen. + * Generate class and file reference documentation for MediaWiki using doxygen. * - * By default it will generate the whole documentation but you will be able to - * generate just some parts. + * If the dot DOT language processor is available, attempt call graph + * generation. * * Usage: * php mwdocgen.php * - * Then make a selection from the menu - * * KNOWN BUGS: * * - pass_thru seems to always use buffering (even with ob_implicit_flush()), * that make output slow when doxygen parses language files. * - the menu doesnt work, got disabled at revision 13740. Need to code it. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html * * @file * @todo document @@ -32,7 +44,7 @@ # if ( php_sapi_name() != 'cli' ) { - echo 'Run me from the command line.'; + echo 'Run "' . __FILE__ . '" from the command line.'; die( -1 ); } @@ -49,7 +61,6 @@ $doxygenTemplate = $mwPath . 'maintenance/Doxyfile'; $svnstat = $mwPath . 'bin/svnstat'; /** where Phpdoc should output documentation */ -# $doxyOutput = '/var/www/mwdoc/'; $doxyOutput = $mwPath . 'docs' . DIRECTORY_SEPARATOR ; /** MediaWiki subpaths */ @@ -58,9 +69,15 @@ $mwPathL = $mwPath . 'languages/'; $mwPathM = $mwPath . 'maintenance/'; $mwPathS = $mwPath . 'skins/'; +/** Ignored paths relative to $mwPath */ +$mwExcludePaths = array( + 'images', + 'static', +); + /** Variable to get user input */ $input = ''; -$exclude = ''; +$exclude_patterns = ''; # # Functions @@ -131,10 +148,11 @@ function getSvnRevision( $dir ) { * @param $currentVersion String: Version number of the software * @param $svnstat String: path to the svnstat file * @param $input String: Path to analyze. - * @param $exclude String: Additionals path regex to exlcude + * @param $exclude String: Additionals path regex to exclude + * @param $exclude_patterns String: Additionals path regex to exclude * (LocalSettings.php, AdminSettings.php, .svn and .git directories are always excluded) */ -function generateConfigFile( $doxygenTemplate, $outputDirectory, $stripFromPath, $currentVersion, $svnstat, $input, $exclude ) { +function generateConfigFile( $doxygenTemplate, $outputDirectory, $stripFromPath, $currentVersion, $svnstat, $input, $exclude, $exclude_patterns ) { $template = file_get_contents( $doxygenTemplate ); @@ -146,6 +164,8 @@ function generateConfigFile( $doxygenTemplate, $outputDirectory, $stripFromPath, '{{SVNSTAT}}' => $svnstat, '{{INPUT}}' => $input, '{{EXCLUDE}}' => $exclude, + '{{EXCLUDE_PATTERNS}}' => $exclude_patterns, + '{{HAVE_DOT}}' => `which dot` ? 'YES' : 'NO', ); $tmpCfg = str_replace( array_keys( $replacements ), array_values( $replacements ), $template ); $tmpFileName = tempnam( wfTempDir(), 'mwdocgen-' ); @@ -212,18 +232,23 @@ case 5: $input = $mwPath . $file; case 6: $input = $mwPath; - $exclude = 'extensions'; + $exclude_patterns = 'extensions'; } $versionNumber = getSvnRevision( $input ); if ( $versionNumber === false ) { # Not using subversion ? $svnstat = ''; # Not really useful if subversion not available - $version = 'trunk'; # FIXME + # @todo FIXME + $version = 'trunk'; } else { $version = "trunk (r$versionNumber)"; } -$generatedConf = generateConfigFile( $doxygenTemplate, $doxyOutput, $mwPath, $version, $svnstat, $input, $exclude ); +// Generate path exclusions +$excludedPaths = $mwPath . join( " $mwPath", $mwExcludePaths ); +print "EXCLUDE: $excludedPaths\n\n"; + +$generatedConf = generateConfigFile( $doxygenTemplate, $doxyOutput, $mwPath, $version, $svnstat, $input, $excludedPaths, $exclude_patterns ); $command = $doxygenBin . ' ' . $generatedConf; echo <<<TEXT diff --git a/maintenance/namespaceDupes.php b/maintenance/namespaceDupes.php index dbb16bcd..b883ea68 100644 --- a/maintenance/namespaceDupes.php +++ b/maintenance/namespaceDupes.php @@ -30,10 +30,10 @@ class NamespaceConflictChecker extends Maintenance { parent::__construct(); $this->mDescription = ""; $this->addOption( 'fix', 'Attempt to automatically fix errors' ); - $this->addOption( 'suffix', "Dupes will be renamed with correct namespace with\n" . - "\t\t<text> Appended after the article name", false, true ); - $this->addOption( 'prefix', "Do an explicit check for the given title prefix\n" . - "\t\tappended after the article name", false, true ); + $this->addOption( 'suffix', "Dupes will be renamed with correct namespace with " . + "<text> appended after the article name", false, true ); + $this->addOption( 'prefix', "Do an explicit check for the given title prefix " . + "appended after the article name", false, true ); } public function execute() { @@ -66,8 +66,7 @@ class NamespaceConflictChecker extends Maintenance { * @param $suffix String: suffix to append to renamed articles */ private function checkAll( $fix, $suffix = '' ) { - global $wgContLang, $wgNamespaceAliases, $wgCanonicalNamespaceNames; - global $wgCapitalLinks; + global $wgContLang, $wgNamespaceAliases, $wgCapitalLinks; $spaces = array(); @@ -79,7 +78,7 @@ class NamespaceConflictChecker extends Maintenance { } // Now pull in all canonical and alias namespaces... - foreach ( $wgCanonicalNamespaceNames as $ns => $name ) { + foreach ( MWNamespace::getCanonicalNamespaces() as $ns => $name ) { // This includes $wgExtraNamespaces if ( $name !== '' ) { $spaces[$name] = $ns; diff --git a/maintenance/nextJobDB.php b/maintenance/nextJobDB.php index 8d8229e2..2d3608d5 100644 --- a/maintenance/nextJobDB.php +++ b/maintenance/nextJobDB.php @@ -33,20 +33,72 @@ class nextJobDB extends Maintenance { public function execute() { global $wgMemc; $type = $this->getOption( 'type', false ); - $mckey = $type === false - ? "jobqueue:dbs" - : "jobqueue:dbs:$type"; - $pendingDBs = $wgMemc->get( $mckey ); - # If we didn't get it from the cache + $memcKey = 'jobqueue:dbs:v2'; + $pendingDBs = $wgMemc->get( $memcKey ); + + // If the cache entry wasn't present, or in 1% of cases otherwise, + // regenerate the cache. + if ( !$pendingDBs || mt_rand( 0, 100 ) == 0 ) { + $pendingDBs = $this->getPendingDbs(); + $wgMemc->set( $memcKey, $pendingDBs, 300 ); + } + if ( !$pendingDBs ) { - $pendingDBs = $this->getPendingDbs( $type ); - $wgMemc->set( $mckey, $pendingDBs, 300 ); + return; } - # If we've got a pending job in a db, display it. - if ( $pendingDBs ) { - $this->output( $pendingDBs[mt_rand( 0, count( $pendingDBs ) - 1 )] ); + + do { + $again = false; + + if ( $type === false ) { + $candidates = call_user_func_array( 'array_merge', $pendingDBs ); + } elseif ( isset( $pendingDBs[$type] ) ) { + $candidates = $pendingDBs[$type]; + } else { + $candidates = array(); + } + if ( !$candidates ) { + return; + } + + $candidates = array_values( $candidates ); + $db = $candidates[ mt_rand( 0, count( $candidates ) - 1 ) ]; + if ( !$this->checkJob( $type, $db ) ) { + // This job is not available in the current database. Remove it from + // the cache. + if ( $type === false ) { + foreach ( $pendingDBs as $type2 => $dbs ) { + $pendingDBs[$type2] = array_diff( $pendingDBs[$type2], array( $db ) ); + } + } else { + $pendingDBs[$type] = array_diff( $pendingDBs[$type], array( $db ) ); + } + + $wgMemc->set( $memcKey, $pendingDBs, 300 ); + $again = true; + } + } while ( $again ); + + $this->output( $db . "\n" ); + } + + /** + * Check if the specified database has a job of the specified type in it. + * The type may be false to indicate "all". + */ + function checkJob( $type, $dbName ) { + $lb = wfGetLB( $dbName ); + $db = $lb->getConnection( DB_MASTER, array(), $dbName ); + if ( $type === false ) { + $conds = array(); + } else { + $conds = array( 'job_cmd' => $type ); } + + $exists = (bool) $db->selectField( 'job', '1', $conds, __METHOD__ ); + $lb->reuseConnection( $db ); + return $exists; } /** @@ -54,7 +106,7 @@ class nextJobDB extends Maintenance { * @param $type String Job type * @return array */ - private function getPendingDbs( $type ) { + private function getPendingDbs() { global $wgLocalDatabases; $pendingDBs = array(); # Cross-reference DBs by master DB server @@ -66,10 +118,10 @@ class nextJobDB extends Maintenance { foreach ( $dbsByMaster as $dbs ) { $dbConn = wfGetDB( DB_MASTER, array(), $dbs[0] ); - $stype = $dbConn->addQuotes( $type ); # Padding row for MySQL bug - $sql = "(SELECT '-------------------------------------------' as db)"; + $pad = str_repeat( '-', 40 ); + $sql = "(SELECT '$pad' as db, '$pad' as job_cmd)"; foreach ( $dbs as $wikiId ) { if ( $sql != '' ) { $sql .= ' UNION '; @@ -79,10 +131,7 @@ class nextJobDB extends Maintenance { $dbConn->tablePrefix( $tablePrefix ); $jobTable = $dbConn->tableName( 'job' ); - if ( $type === false ) - $sql .= "(SELECT '$wikiId' as db FROM $dbName.$jobTable LIMIT 1)"; - else - $sql .= "(SELECT '$wikiId' as db FROM $dbName.$jobTable WHERE job_cmd=$stype LIMIT 1)"; + $sql .= "(SELECT DISTINCT '$wikiId' as db, job_cmd FROM $dbName.$jobTable GROUP BY job_cmd)"; } $res = $dbConn->query( $sql, __METHOD__ ); $first = true; @@ -92,7 +141,7 @@ class nextJobDB extends Maintenance { $first = false; continue; } - $pendingDBs[] = $row->db; + $pendingDBs[$row->job_cmd][] = $row->db; } } return $pendingDBs; diff --git a/maintenance/nukePage.php b/maintenance/nukePage.php index 4a073a5e..c818e73a 100644 --- a/maintenance/nukePage.php +++ b/maintenance/nukePage.php @@ -56,6 +56,7 @@ class NukePage extends Maintenance { # Get corresponding revisions $this->output( "Searching for revisions..." ); $res = $dbw->query( "SELECT rev_id FROM $tbl_rev WHERE rev_page = $id" ); + $revs = array(); foreach ( $res as $row ) { $revs[] = $row->rev_id; } diff --git a/maintenance/oracle/alterSharedConstraints.php b/maintenance/oracle/alterSharedConstraints.php new file mode 100644 index 00000000..aa207821 --- /dev/null +++ b/maintenance/oracle/alterSharedConstraints.php @@ -0,0 +1,90 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @ingroup Maintenance + */ + +/** + * When using shared tables that are referenced by foreign keys on local + * tables you have to change the constraints on local tables. + * + * The shared tables have to have GRANT REFERENCE on shared tables to local schema + * i.e.: GRANT REFERENCES (user_id) ON mwuser TO hubclient; + */ + +require_once( dirname( __FILE__ ) . '/../Maintenance.php' ); + +class AlterSharedConstraints extends Maintenance { + public function __construct() { + parent::__construct(); + $this->mDescription = "Alter foreign key to reference master tables in shared database setup."; + } + + public function getDbType() { + return Maintenance::DB_ADMIN; + } + + public function execute() { + global $wgSharedDB, $wgSharedTables, $wgSharedPrefix, $wgDBprefix; + + if ( $wgSharedDB == null ) { + $this->output( "Database sharing is not enabled\n" ); + return; + } + + $dbw = wfGetDB( DB_MASTER ); + foreach ( $wgSharedTables as $table ) { + $stable = $dbw->tableNameInternal($table); + if ( $wgSharedPrefix != null ) { + $ltable = preg_replace( "/^$wgSharedPrefix(.*)/i", "$wgDBprefix\\1", $stable ); + } else { + $ltable = "{$wgDBprefix}{$stable}" ; + } + + $result = $dbw->query( "SELECT uc.constraint_name, uc.table_name, ucc.column_name, uccpk.table_name pk_table_name, uccpk.column_name pk_column_name, uc.delete_rule, uc.deferrable, uc.deferred + FROM user_constraints uc, user_cons_columns ucc, user_cons_columns uccpk + WHERE uc.constraint_type = 'R' + AND ucc.constraint_name = uc.constraint_name + AND uccpk.constraint_name = uc.r_constraint_name + AND uccpk.table_name = '$ltable'" ); + while (($row = $result->fetchRow()) !== false) { + + $this->output( "Altering {$row['constraint_name']} ..."); + + try { + $dbw->query( "ALTER TABLE {$row['table_name']} DROP CONSTRAINT {$wgDBprefix}{$row['constraint_name']}" ); + } catch (DBQueryError $exdb) { + if ($exdb->errno != 2443) { + throw $exdb; + } + } + + $deleteRule = $row['delete_rule'] == 'NO ACTION' ? '' : "ON DELETE {$row['delete_rule']}"; + $dbw->query( "ALTER TABLE {$row['table_name']} ADD CONSTRAINT {$wgDBprefix}{$row['constraint_name']} + FOREIGN KEY ({$row['column_name']}) + REFERENCES {$wgSharedDB}.$stable({$row['pk_column_name']}) + {$deleteRule} {$row['deferrable']} INITIALLY {$row['deferred']}" ); + + $this->output( "DONE\n" ); + } + } + } + +} + +$maintClass = "AlterSharedConstraints"; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/oracle/archives/patch-config.sql b/maintenance/oracle/archives/patch-config.sql new file mode 100644 index 00000000..66714a73 --- /dev/null +++ b/maintenance/oracle/archives/patch-config.sql @@ -0,0 +1,8 @@ +define mw_prefix='{$wgDBprefix}'; + +CREATE TABLE &mw_prefix.config ( + cf_name VARCHAR2(255) NOT NULL, + cf_value blob NOT NULL +); +ALTER TABLE &mw_prefix.config ADD CONSTRAINT &mw_prefix.config_pk PRIMARY KEY (cf_name); + diff --git a/maintenance/oracle/archives/patch-up_property.sql b/maintenance/oracle/archives/patch-up_property.sql new file mode 100644 index 00000000..c8e2dd95 --- /dev/null +++ b/maintenance/oracle/archives/patch-up_property.sql @@ -0,0 +1,3 @@ +define mw_prefix='{$wgDBprefix}'; + +ALTER TABLE &mw_prefix.user_properties MODIFY up_property varchar2(255); diff --git a/maintenance/oracle/archives/patch-uploadstash.sql b/maintenance/oracle/archives/patch-uploadstash.sql new file mode 100644 index 00000000..3e37ceff --- /dev/null +++ b/maintenance/oracle/archives/patch-uploadstash.sql @@ -0,0 +1,25 @@ +define mw_prefix='{$wgDBprefix}'; + +CREATE SEQUENCE uploadstash_us_id_seq; +CREATE TABLE &mw_prefix.uploadstash ( + us_id NUMBER NOT NULL, + us_user NUMBER DEFAULT 0 NOT NULL, + us_key VARCHAR2(255) NOT NULL, + us_orig_path VARCHAR2(255) NOT NULL, + us_path VARCHAR2(255) NOT NULL, + us_source_type VARCHAR2(50), + us_timestamp TIMESTAMP(6) WITH TIME ZONE, + us_status VARCHAR2(50) NOT NULL, + us_size NUMBER NOT NULL, + us_sha1 VARCHAR2(32) NOT NULL, + us_mime VARCHAR2(255), + us_media_type VARCHAR2(32) DEFAULT NULL, + us_image_width NUMBER, + us_image_height NUMBER, + us_image_bits NUMBER +); +ALTER TABLE &mw_prefix.uploadstash ADD CONSTRAINT &mw_prefix.uploadstash_pk PRIMARY KEY (us_id); +ALTER TABLE &mw_prefix.uploadstash ADD CONSTRAINT &mw_prefix.uploadstash_fk1 FOREIGN KEY (us_user) REFERENCES &mw_prefix.mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED; +CREATE INDEX &mw_prefix.uploadstash_i01 ON &mw_prefix.uploadstash (us_user); +CREATE INDEX &mw_prefix.uploadstash_i02 ON &mw_prefix.uploadstash (us_timestamp); +CREATE UNIQUE INDEX &mw_prefix.uploadstash_u01 ON &mw_prefix.uploadstash (us_key); diff --git a/maintenance/oracle/archives/patch-user_email_index.sql b/maintenance/oracle/archives/patch-user_email_index.sql new file mode 100644 index 00000000..e34d8656 --- /dev/null +++ b/maintenance/oracle/archives/patch-user_email_index.sql @@ -0,0 +1,4 @@ +define mw_prefix='{$wgDBprefix}'; + +CREATE INDEX &mw_prefix.mwuser_i02 ON &mw_prefix.mwuser (user_email); + diff --git a/maintenance/oracle/archives/patch-user_former_groups.sql b/maintenance/oracle/archives/patch-user_former_groups.sql new file mode 100644 index 00000000..59147eb2 --- /dev/null +++ b/maintenance/oracle/archives/patch-user_former_groups.sql @@ -0,0 +1,9 @@ +define mw_prefix='{$wgDBprefix}'; + +CREATE TABLE &mw_prefix.user_former_groups ( + ufg_user NUMBER DEFAULT 0 NOT NULL, + ufg_group VARCHAR2(16) NOT NULL +); +ALTER TABLE &mw_prefix.user_former_groups ADD CONSTRAINT &mw_prefix.user_former_groups_fk1 FOREIGN KEY (ufg_user) REFERENCES &mw_prefix.mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED; +CREATE UNIQUE INDEX &mw_prefix.user_former_groups_u01 ON &mw_prefix.user_former_groups (ufg_user,ufg_group); + diff --git a/maintenance/oracle/archives/patch_rebuild_dupfunc.sql b/maintenance/oracle/archives/patch_rebuild_dupfunc.sql new file mode 100644 index 00000000..0a232dbc --- /dev/null +++ b/maintenance/oracle/archives/patch_rebuild_dupfunc.sql @@ -0,0 +1,146 @@ +/*$mw$*/ +CREATE OR REPLACE PROCEDURE duplicate_table(p_tabname IN VARCHAR2, + p_oldprefix IN VARCHAR2, + p_newprefix IN VARCHAR2, + p_temporary IN BOOLEAN) IS + e_table_not_exist EXCEPTION; + PRAGMA EXCEPTION_INIT(e_table_not_exist, -00942); + l_temp_ei_sql VARCHAR2(2000); + l_temporary BOOLEAN := p_temporary; +BEGIN + BEGIN + EXECUTE IMMEDIATE 'DROP TABLE ' || p_newprefix || p_tabname || + ' CASCADE CONSTRAINTS'; + EXCEPTION + WHEN e_table_not_exist THEN + NULL; + END; + IF (p_tabname = 'SEARCHINDEX') THEN + l_temporary := FALSE; + END IF; + IF (l_temporary) THEN + EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE ' || p_newprefix || + p_tabname || ' AS SELECT * FROM ' || p_oldprefix || + p_tabname || ' WHERE ROWNUM = 0'; + ELSE + EXECUTE IMMEDIATE 'CREATE TABLE ' || p_newprefix || p_tabname || + ' AS SELECT * FROM ' || p_oldprefix || p_tabname || + ' WHERE ROWNUM = 0'; + END IF; + FOR rc IN (SELECT column_name, data_default + FROM user_tab_columns + WHERE table_name = p_oldprefix || p_tabname + AND data_default IS NOT NULL) LOOP + EXECUTE IMMEDIATE 'ALTER TABLE ' || p_newprefix || p_tabname || + ' MODIFY ' || rc.column_name || ' DEFAULT ' || + SUBSTR(rc.data_default, 1, 2000); + END LOOP; + FOR rc IN (SELECT REPLACE(REPLACE(DBMS_LOB.SUBSTR(DBMS_METADATA.get_ddl('CONSTRAINT', + constraint_name), + 32767, + 1), + USER || '"."' || p_oldprefix, + USER || '"."' || p_newprefix), + '"' || constraint_name || '"', + '"' || p_newprefix || constraint_name || '"') DDLVC2, + constraint_name + FROM user_constraints uc + WHERE table_name = p_oldprefix || p_tabname + AND constraint_type = 'P') LOOP + l_temp_ei_sql := SUBSTR(rc.ddlvc2, 1, INSTR(rc.ddlvc2, 'PCTFREE') - 1); + l_temp_ei_sql := SUBSTR(l_temp_ei_sql, + 1, + INSTR(l_temp_ei_sql, + ')', + INSTR(l_temp_ei_sql, 'PRIMARY KEY') + 1) + 1); + IF nvl(length(l_temp_ei_sql), 0) > 0 THEN + EXECUTE IMMEDIATE l_temp_ei_sql; + END IF; + END LOOP; + IF (NOT l_temporary) THEN + FOR rc IN (SELECT REPLACE(DBMS_LOB.SUBSTR(DBMS_METADATA.get_ddl('REF_CONSTRAINT', + constraint_name), + 32767, + 1), + USER || '"."' || p_oldprefix, + USER || '"."' || p_newprefix) DDLVC2, + constraint_name + FROM user_constraints uc + WHERE table_name = p_oldprefix || p_tabname + AND constraint_type = 'R') LOOP + IF nvl(length(l_temp_ei_sql), 0) > 0 THEN + EXECUTE IMMEDIATE l_temp_ei_sql; + END IF; + END LOOP; + END IF; + FOR rc IN (SELECT REPLACE(REPLACE(DBMS_LOB.SUBSTR(DBMS_METADATA.get_ddl('INDEX', + index_name), + 32767, + 1), + USER || '"."' || p_oldprefix, + USER || '"."' || p_newprefix), + '"' || index_name || '"', + '"' || p_newprefix || index_name || '"') DDLVC2, + index_name, + index_type + FROM user_indexes ui + WHERE table_name = p_oldprefix || p_tabname + AND index_type NOT IN ('LOB', 'DOMAIN') + AND NOT EXISTS + (SELECT NULL + FROM user_constraints + WHERE table_name = ui.table_name + AND constraint_name = ui.index_name)) LOOP + l_temp_ei_sql := SUBSTR(rc.ddlvc2, 1, INSTR(rc.ddlvc2, 'PCTFREE') - 1); + l_temp_ei_sql := SUBSTR(l_temp_ei_sql, + 1, + INSTR(l_temp_ei_sql, + ')', + INSTR(l_temp_ei_sql, + '"' || USER || '"."' || p_newprefix || '"') + 1) + 1); + IF nvl(length(l_temp_ei_sql), 0) > 0 THEN + EXECUTE IMMEDIATE l_temp_ei_sql; + END IF; + END LOOP; + FOR rc IN (SELECT REPLACE(REPLACE(DBMS_LOB.SUBSTR(DBMS_METADATA.get_ddl('INDEX', + index_name), + 32767, + 1), + USER || '"."' || p_oldprefix, + USER || '"."' || p_newprefix), + '"' || index_name || '"', + '"' || p_newprefix || index_name || '"') DDLVC2, + index_name, + index_type + FROM user_indexes ui + WHERE table_name = p_oldprefix || p_tabname + AND index_type = 'DOMAIN' + AND NOT EXISTS + (SELECT NULL + FROM user_constraints + WHERE table_name = ui.table_name + AND constraint_name = ui.index_name)) LOOP + l_temp_ei_sql := rc.ddlvc2; + IF nvl(length(l_temp_ei_sql), 0) > 0 THEN + EXECUTE IMMEDIATE l_temp_ei_sql; + END IF; + END LOOP; + FOR rc IN (SELECT REPLACE(REPLACE(UPPER(DBMS_LOB.SUBSTR(DBMS_METADATA.get_ddl('TRIGGER', + trigger_name), + 32767, + 1)), + USER || '"."' || p_oldprefix, + USER || '"."' || p_newprefix), + ' ON ' || p_oldprefix || p_tabname, + ' ON ' || p_newprefix || p_tabname) DDLVC2, + trigger_name + FROM user_triggers + WHERE table_name = p_oldprefix || p_tabname) LOOP + l_temp_ei_sql := SUBSTR(rc.ddlvc2, 1, INSTR(rc.ddlvc2, 'ALTER ') - 1); + IF nvl(length(l_temp_ei_sql), 0) > 0 THEN + EXECUTE IMMEDIATE l_temp_ei_sql; + END IF; + END LOOP; +END; +/*$mw$*/ + diff --git a/maintenance/oracle/tables.sql b/maintenance/oracle/tables.sql index f6d29f54..2fd62ef7 100644 --- a/maintenance/oracle/tables.sql +++ b/maintenance/oracle/tables.sql @@ -23,6 +23,7 @@ CREATE TABLE &mw_prefix.mwuser ( -- replace reserved word 'user' ALTER TABLE &mw_prefix.mwuser ADD CONSTRAINT &mw_prefix.mwuser_pk PRIMARY KEY (user_id); CREATE UNIQUE INDEX &mw_prefix.mwuser_u01 ON &mw_prefix.mwuser (user_name); CREATE INDEX &mw_prefix.mwuser_i01 ON &mw_prefix.mwuser (user_email_token); +CREATE INDEX &mw_prefix.mwuser_i02 ON &mw_prefix.mwuser (user_email, user_name); -- Create a dummy user to satisfy fk contraints especially with revisions INSERT INTO &mw_prefix.mwuser @@ -47,7 +48,7 @@ CREATE INDEX &mw_prefix.user_newtalk_i02 ON &mw_prefix.user_newtalk (user_ip); CREATE TABLE &mw_prefix.user_properties ( up_user NUMBER NOT NULL, - up_property VARCHAR2(32) NOT NULL, + up_property VARCHAR2(255) NOT NULL, up_value CLOB ); CREATE UNIQUE INDEX &mw_prefix.user_properties_u01 on &mw_prefix.user_properties (up_user,up_property); @@ -405,15 +406,6 @@ CREATE UNIQUE INDEX &mw_prefix.watchlist_u01 ON &mw_prefix.watchlist (wl_user, w CREATE INDEX &mw_prefix.watchlist_i01 ON &mw_prefix.watchlist (wl_namespace, wl_title); -CREATE TABLE &mw_prefix.math ( - math_inputhash VARCHAR2(32) NOT NULL, - math_outputhash VARCHAR2(32) NOT NULL, - math_html_conservativeness NUMBER NOT NULL, - math_html CLOB, - math_mathml CLOB -); -CREATE UNIQUE INDEX &mw_prefix.math_u01 ON &mw_prefix.math (math_inputhash); - CREATE TABLE &mw_prefix.searchindex ( si_page NUMBER NOT NULL, si_title VARCHAR2(255), @@ -645,6 +637,14 @@ CREATE TABLE &mw_prefix.module_deps ( ); CREATE UNIQUE INDEX &mw_prefix.module_deps_u01 ON &mw_prefix.module_deps (md_module, md_skin); +CREATE TABLE &mw_prefix.config ( + cf_name VARCHAR2(255) NOT NULL, + cf_value blob NOT NULL +); +ALTER TABLE &mw_prefix.config ADD CONSTRAINT &mw_prefix.config_pk PRIMARY KEY (cf_name); +-- leaving index out for now ... + + -- do not prefix this table as it breaks parserTests CREATE TABLE wiki_field_info_full ( table_name VARCHAR2(35) NOT NULL, @@ -718,6 +718,7 @@ CREATE OR REPLACE PROCEDURE duplicate_table(p_tabname IN VARCHAR2, e_table_not_exist EXCEPTION; PRAGMA EXCEPTION_INIT(e_table_not_exist, -00942); l_temp_ei_sql VARCHAR2(2000); + l_temporary BOOLEAN := p_temporary; BEGIN BEGIN EXECUTE IMMEDIATE 'DROP TABLE ' || p_newprefix || p_tabname || @@ -726,7 +727,10 @@ BEGIN WHEN e_table_not_exist THEN NULL; END; - IF (p_temporary) THEN + IF (p_tabname = 'SEARCHINDEX') THEN + l_temporary := FALSE; + END IF; + IF (l_temporary) THEN EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE ' || p_newprefix || p_tabname || ' AS SELECT * FROM ' || p_oldprefix || p_tabname || ' WHERE ROWNUM = 0'; @@ -756,22 +760,30 @@ BEGIN WHERE table_name = p_oldprefix || p_tabname AND constraint_type = 'P') LOOP l_temp_ei_sql := SUBSTR(rc.ddlvc2, 1, INSTR(rc.ddlvc2, 'PCTFREE') - 1); - l_temp_ei_sql := SUBSTR(l_temp_ei_sql, 1, INSTR(l_temp_ei_sql, ')', INSTR(l_temp_ei_sql, 'PRIMARY KEY')+1)+1); - EXECUTE IMMEDIATE l_temp_ei_sql; - END LOOP; - IF (NOT p_temporary) THEN - FOR rc IN (SELECT REPLACE(DBMS_LOB.SUBSTR(DBMS_METADATA.get_ddl('REF_CONSTRAINT', - constraint_name), - 32767, - 1), - USER || '"."' || p_oldprefix, - USER || '"."' || p_newprefix) DDLVC2, - constraint_name - FROM user_constraints uc - WHERE table_name = p_oldprefix || p_tabname - AND constraint_type = 'R') LOOP - EXECUTE IMMEDIATE rc.ddlvc2; + l_temp_ei_sql := SUBSTR(l_temp_ei_sql, + 1, + INSTR(l_temp_ei_sql, + ')', + INSTR(l_temp_ei_sql, 'PRIMARY KEY') + 1) + 1); + IF nvl(length(l_temp_ei_sql), 0) > 0 THEN + EXECUTE IMMEDIATE l_temp_ei_sql; + END IF; END LOOP; + IF (NOT l_temporary) THEN + FOR rc IN (SELECT REPLACE(DBMS_LOB.SUBSTR(DBMS_METADATA.get_ddl('REF_CONSTRAINT', + constraint_name), + 32767, + 1), + USER || '"."' || p_oldprefix, + USER || '"."' || p_newprefix) DDLVC2, + constraint_name + FROM user_constraints uc + WHERE table_name = p_oldprefix || p_tabname + AND constraint_type = 'R') LOOP + IF nvl(length(l_temp_ei_sql), 0) > 0 THEN + EXECUTE IMMEDIATE l_temp_ei_sql; + END IF; + END LOOP; END IF; FOR rc IN (SELECT REPLACE(REPLACE(DBMS_LOB.SUBSTR(DBMS_METADATA.get_ddl('INDEX', index_name), @@ -792,8 +804,38 @@ BEGIN WHERE table_name = ui.table_name AND constraint_name = ui.index_name)) LOOP l_temp_ei_sql := SUBSTR(rc.ddlvc2, 1, INSTR(rc.ddlvc2, 'PCTFREE') - 1); - l_temp_ei_sql := SUBSTR(l_temp_ei_sql, 1, INSTR(l_temp_ei_sql, ')', INSTR(l_temp_ei_sql, '"' || USER || '"."' || p_newprefix || '"')+1)+1); - EXECUTE IMMEDIATE l_temp_ei_sql; + l_temp_ei_sql := SUBSTR(l_temp_ei_sql, + 1, + INSTR(l_temp_ei_sql, + ')', + INSTR(l_temp_ei_sql, + '"' || USER || '"."' || p_newprefix || '"') + 1) + 1); + IF nvl(length(l_temp_ei_sql), 0) > 0 THEN + EXECUTE IMMEDIATE l_temp_ei_sql; + END IF; + END LOOP; + FOR rc IN (SELECT REPLACE(REPLACE(DBMS_LOB.SUBSTR(DBMS_METADATA.get_ddl('INDEX', + index_name), + 32767, + 1), + USER || '"."' || p_oldprefix, + USER || '"."' || p_newprefix), + '"' || index_name || '"', + '"' || p_newprefix || index_name || '"') DDLVC2, + index_name, + index_type + FROM user_indexes ui + WHERE table_name = p_oldprefix || p_tabname + AND index_type = 'DOMAIN' + AND NOT EXISTS + (SELECT NULL + FROM user_constraints + WHERE table_name = ui.table_name + AND constraint_name = ui.index_name)) LOOP + l_temp_ei_sql := rc.ddlvc2; + IF nvl(length(l_temp_ei_sql), 0) > 0 THEN + EXECUTE IMMEDIATE l_temp_ei_sql; + END IF; END LOOP; FOR rc IN (SELECT REPLACE(REPLACE(UPPER(DBMS_LOB.SUBSTR(DBMS_METADATA.get_ddl('TRIGGER', trigger_name), @@ -807,9 +849,12 @@ BEGIN FROM user_triggers WHERE table_name = p_oldprefix || p_tabname) LOOP l_temp_ei_sql := SUBSTR(rc.ddlvc2, 1, INSTR(rc.ddlvc2, 'ALTER ') - 1); - EXECUTE IMMEDIATE l_temp_ei_sql; + IF nvl(length(l_temp_ei_sql), 0) > 0 THEN + EXECUTE IMMEDIATE l_temp_ei_sql; + END IF; END LOOP; END; + /*$mw$*/ /*$mw$*/ diff --git a/maintenance/orphans.php b/maintenance/orphans.php index dbbddb9c..1986ff35 100644 --- a/maintenance/orphans.php +++ b/maintenance/orphans.php @@ -50,7 +50,7 @@ class Orphans extends Maintenance { /** * Lock the appropriate tables for the script - * @param $db Database object + * @param $db DatabaseBase object * @param $extraTable String The name of any extra tables to lock (eg: text) */ private function lockTables( &$db, $extraTable = null ) { diff --git a/maintenance/ourusers.php b/maintenance/ourusers.php index 499da5cf..c8578e24 100644 --- a/maintenance/ourusers.php +++ b/maintenance/ourusers.php @@ -6,6 +6,21 @@ * list of hosts. It takes care of setting the wikiuser for every * database as well as setting up wikiadmin. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @todo document * @file * @ingroup Maintenance diff --git a/maintenance/populateCategory.php b/maintenance/populateCategory.php index 4f494e15..4e9c44c4 100644 --- a/maintenance/populateCategory.php +++ b/maintenance/populateCategory.php @@ -1,5 +1,22 @@ <?php /** + * Script to populate category table. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance * @author Simetrical @@ -64,7 +81,6 @@ TEXT; } } - $maxlag = intval( $maxlag ); $throttle = intval( $throttle ); if ( $begin !== '' ) { $where = 'cl_to > ' . $dbw->addQuotes( $begin ); @@ -102,7 +118,7 @@ TEXT; ++$i; if ( !( $i % self::REPORTING_INTERVAL ) ) { $this->output( "$name\n" ); - wfWaitForSlaves( $maxlag ); + wfWaitForSlaves(); } usleep( $throttle * 1000 ); } diff --git a/maintenance/populateLogSearch.php b/maintenance/populateLogSearch.php index ce2d95cc..f13873cb 100644 --- a/maintenance/populateLogSearch.php +++ b/maintenance/populateLogSearch.php @@ -35,7 +35,7 @@ class PopulateLogSearch extends Maintenance { } public function execute() { - $db = wfGetDB( DB_MASTER ); + $db = $this->getDB( DB_MASTER ); if ( !$db->tableExists( 'log_search' ) ) { $this->error( "log_search does not exist", true ); } @@ -97,14 +97,14 @@ class PopulateLogSearch extends Maintenance { foreach ( $sres as $srow ) { if ( $srow->$userField > 0 ) $userIds[] = intval( $srow->$userField ); - else if ( $srow->$userTextField != '' ) + elseif ( $srow->$userTextField != '' ) $userIPs[] = $srow->$userTextField; } // Add item author relations... $log->addRelations( 'target_author_id', $userIds, $row->log_id ); $log->addRelations( 'target_author_ip', $userIPs, $row->log_id ); // RevisionDelete logs - log events - } else if ( LogEventsList::typeAction( $row, $delTypes, 'event' ) ) { + } elseif ( LogEventsList::typeAction( $row, $delTypes, 'event' ) ) { $params = LogPage::extractParams( $row->log_params ); // Param format: <item CSV> [<ofield> <nfield>] if ( count( $params ) < 1 ) continue; // bad row @@ -121,7 +121,7 @@ class PopulateLogSearch extends Maintenance { foreach ( $sres as $srow ) { if ( $srow->log_user > 0 ) $userIds[] = intval( $srow->log_user ); - else if ( IP::isIPAddress( $srow->log_user_text ) ) + elseif ( IP::isIPAddress( $srow->log_user_text ) ) $userIPs[] = $srow->log_user_text; } $log->addRelations( 'target_author_id', $userIds, $row->log_id ); @@ -130,7 +130,7 @@ class PopulateLogSearch extends Maintenance { } $blockStart += self::LOG_SEARCH_BATCH_SIZE; $blockEnd += self::LOG_SEARCH_BATCH_SIZE; - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } if ( $db->insert( 'updatelog', diff --git a/maintenance/populateLogUsertext.php b/maintenance/populateLogUsertext.php index bb3927ce..e9e6926e 100644 --- a/maintenance/populateLogUsertext.php +++ b/maintenance/populateLogUsertext.php @@ -33,7 +33,7 @@ class PopulateLogUsertext extends Maintenance { } public function execute() { - $db = wfGetDB( DB_MASTER ); + $db = $this->getDB( DB_MASTER ); $start = $db->selectField( 'logging', 'MIN(log_id)', false, __METHOD__ ); if ( !$start ) { $this->output( "Nothing to do.\n" ); @@ -59,7 +59,7 @@ class PopulateLogUsertext extends Maintenance { $db->commit(); $blockStart += $this->mBatchSize; $blockEnd += $this->mBatchSize; - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } if ( $db->insert( 'updatelog', diff --git a/maintenance/populateParentId.php b/maintenance/populateParentId.php index 387f5a56..7cbc267b 100644 --- a/maintenance/populateParentId.php +++ b/maintenance/populateParentId.php @@ -98,7 +98,7 @@ class PopulateParentId extends Maintenance { } $blockStart += $this->mBatchSize; $blockEnd += $this->mBatchSize; - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } $logged = $db->insert( 'updatelog', array( 'ul_key' => 'populate rev_parent_id' ), diff --git a/maintenance/populateRevisionLength.php b/maintenance/populateRevisionLength.php index 0af51dc1..d020b4cb 100644 --- a/maintenance/populateRevisionLength.php +++ b/maintenance/populateRevisionLength.php @@ -30,7 +30,7 @@ class PopulateRevisionLength extends Maintenance { } public function execute() { - $db = wfGetDB( DB_MASTER ); + $db = $this->getDB( DB_MASTER ); if ( !$db->tableExists( 'revision' ) ) { $this->error( "revision table does not exist", true ); } @@ -78,7 +78,7 @@ class PopulateRevisionLength extends Maintenance { } $blockStart += $this->mBatchSize; $blockEnd += $this->mBatchSize; - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } $logged = $db->insert( 'updatelog', array( 'ul_key' => 'populate rev_len' ), diff --git a/maintenance/populateSha1.php b/maintenance/populateSha1.php index 1714c0d6..1ab9109d 100644 --- a/maintenance/populateSha1.php +++ b/maintenance/populateSha1.php @@ -54,7 +54,7 @@ class PopulateSha1 extends Maintenance { $imageTable = $dbw->tableName( 'image' ); if ( $method == 'pipe' ) { - // @fixme kill this and replace with a second unbuffered DB connection. + // @todo FIXME: Kill this and replace with a second unbuffered DB connection. global $wgDBuser, $wgDBserver, $wgDBpassword, $wgDBname; $cmd = 'mysql -u' . wfEscapeShellArg( $wgDBuser ) . ' -h' . wfEscapeShellArg( $wgDBserver ) . @@ -68,7 +68,7 @@ class PopulateSha1 extends Maintenance { foreach ( $res as $row ) { if ( $i % 100 == 0 ) { $this->output( sprintf( "Done %d of %d, %5.3f%% \r", $i, $numRows, $i / $numRows * 100 ) ); - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } $file = wfLocalFile( $row->img_name ); if ( !$file ) { diff --git a/maintenance/postgres/archives/patch-user_former_groups.sql b/maintenance/postgres/archives/patch-user_former_groups.sql new file mode 100644 index 00000000..1ba011e3 --- /dev/null +++ b/maintenance/postgres/archives/patch-user_former_groups.sql @@ -0,0 +1,5 @@ +CREATE TABLE user_former_groups ( + ufg_user INTEGER NULL REFERENCES mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, + ufg_group TEXT NOT NULL +); +CREATE UNIQUE INDEX ufg_user_group ON user_former_groups (ufg_user, ufg_group); diff --git a/maintenance/postgres/compare_schemas.pl b/maintenance/postgres/compare_schemas.pl index 7e3cdf71..18210fcf 100644 --- a/maintenance/postgres/compare_schemas.pl +++ b/maintenance/postgres/compare_schemas.pl @@ -94,7 +94,7 @@ sub parse_sql { next if /^\s*\-\-/ or /^\s+$/; s/\s*\-\- [\w ]+$//; chomp; - + if (/CREATE\s*TABLE/i) { if (m{^CREATE TABLE /\*_\*/(\w+) \($}) { $table = $1; @@ -179,6 +179,10 @@ while (<$newfh>) { next if /^CREATE TRIGGER/ or /^ FOR EACH ROW/; next if /^INSERT INTO/ or /^ VALUES \(/; next if /^ALTER TABLE/; + next if /^DROP SEQUENCE/; + next if /^DROP FUNCTION/; + + chomp; if (/^\$mw\$;?$/) { diff --git a/maintenance/postgres/mediawiki_mysql2postgres.pl b/maintenance/postgres/mediawiki_mysql2postgres.pl index 2b2bf50e..16012762 100644 --- a/maintenance/postgres/mediawiki_mysql2postgres.pl +++ b/maintenance/postgres/mediawiki_mysql2postgres.pl @@ -1,7 +1,7 @@ #!/usr/bin/perl ## Convert data from a MySQL mediawiki database into a Postgres mediawiki database -## svn: $Id: mediawiki_mysql2postgres.pl 65542 2010-04-26 13:46:04Z demon $ +## svn: $Id: mediawiki_mysql2postgres.pl 86721 2011-04-22 18:47:17Z mah $ ## NOTE: It is probably easier to dump your wiki using maintenance/dumpBackup.php ## and then import it with maintenance/importDump.php @@ -181,7 +181,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: 65542 $}.qq{) +-- Version: $VERSION (subversion }.q{$LastChangedRevision: 86721 $}.qq{) -- Author: Greg Sabino Mullane <greg\@turnstep.com> Comments welcome -- -- This file was created: $now @@ -279,8 +279,8 @@ for my $t (@torder, 'objectcache', 'querycache') { } print "\n\n"; -print qq{-- Temporarily rename pagecontent to "text"\n}; -print qq{ALTER TABLE pagecontent RENAME TO "text";\n\n}; +print qq{-- Temporarily rename pagecontent to "${table_prefix}text"\n}; +print qq{ALTER TABLE pagecontent RENAME TO "${table_prefix}text";\n\n}; print qq{-- Allow rc_ip to contain empty string, will convert at end\n}; print qq{ALTER TABLE recentchanges ALTER rc_ip TYPE text USING host(rc_ip);\n\n}; @@ -304,9 +304,9 @@ INSERT INTO page VALUES (0,-1,'Dummy Page','',0,0,0,default,now(),0,10); if (length $table_prefix) { print qq{\n\n-- Temporarily renaming tables to accomodate the table_prefix "$table_prefix"\n\n}; for my $t (@torder) { - next if $t eq '---'; + next if $t eq '---' or $t eq 'text' or $t eq 'user'; my $tname = $special{$t}||$t; - printf qq{ALTER TABLE %-18s RENAME TO "${table_prefix}$tname"\n}, qq{"$tname"}; + printf qq{ALTER TABLE %-18s RENAME TO "${table_prefix}$tname";\n}, qq{"$tname"}; } } @@ -391,9 +391,9 @@ if (length $table_prefix) { $maxsize = length "$_$table_prefix" if length "$_$table_prefix" > $maxsize; } for my $t (@torder) { - next if $t eq '---' or $t eq 'text'; + next if $t eq '---' or $t eq 'text' or $t eq 'user'; my $tname = $special{$t}||$t; - printf qq{ALTER TABLE %*s RENAME TO "$tname"\n}, $maxsize+1, qq{"${table_prefix}$tname"}; + printf qq{ALTER TABLE %*s RENAME TO "$tname";\n}, $maxsize+1, qq{"${table_prefix}$tname"}; } } @@ -409,13 +409,13 @@ for my $t (sort keys %tz) { ## Reset sequences print q{ SELECT setval('filearchive_fa_id_seq', 1+coalesce(max(fa_id) ,0),false) FROM filearchive; -SELECT setval('ipblocks_ipb_id_val', 1+coalesce(max(ipb_id) ,0),false) FROM ipblocks; +SELECT setval('ipblocks_ipb_id_seq', 1+coalesce(max(ipb_id) ,0),false) FROM ipblocks; SELECT setval('job_job_id_seq', 1+coalesce(max(job_id) ,0),false) FROM job; -SELECT setval('log_log_id_seq', 1+coalesce(max(log_id) ,0),false) FROM logging; +SELECT setval('logging_log_id_seq', 1+coalesce(max(log_id) ,0),false) FROM logging; SELECT setval('page_page_id_seq', 1+coalesce(max(page_id),0),false) FROM page; -SELECT setval('pr_id_val', 1+coalesce(max(pr_id) ,0),false) FROM page_restrictions; -SELECT setval('rc_rc_id_seq', 1+coalesce(max(rc_id) ,0),false) FROM recentchanges; -SELECT setval('rev_rev_id_val', 1+coalesce(max(rev_id) ,0),false) FROM revision; +SELECT setval('page_restrictions_pr_id_seq', 1+coalesce(max(pr_id) ,0),false) FROM page_restrictions; +SELECT setval('recentchanges_rc_id_seq', 1+coalesce(max(rc_id) ,0),false) FROM recentchanges; +SELECT setval('revision_rev_id_seq', 1+coalesce(max(rev_id) ,0),false) FROM revision; SELECT setval('text_old_id_seq', 1+coalesce(max(old_id) ,0),false) FROM pagecontent; SELECT setval('trackbacks_tb_id_seq', 1+coalesce(max(tb_id) ,0),false) FROM trackbacks; SELECT setval('user_user_id_seq', 1+coalesce(max(user_id),0),false) FROM mwuser; diff --git a/maintenance/postgres/tables.sql b/maintenance/postgres/tables.sql index 8e869da7..ac0258ff 100644 --- a/maintenance/postgres/tables.sql +++ b/maintenance/postgres/tables.sql @@ -9,6 +9,21 @@ BEGIN; SET client_min_messages = 'ERROR'; +DROP SEQUENCE IF EXISTS user_user_id_seq CASCADE; +DROP SEQUENCE IF EXISTS page_page_id_seq CASCADE; +DROP SEQUENCE IF EXISTS revision_rev_id_seq CASCADE; +DROP SEQUENCE IF EXISTS page_restrictions_id_seq CASCADE; +DROP SEQUENCE IF EXISTS ipblocks_ipb_id_seq CASCADE; +DROP SEQUENCE IF EXISTS recentchanges_rc_id_seq CASCADE; +DROP SEQUENCE IF EXISTS logging_log_id_seq CASCADE; +DROP SEQUENCE IF EXISTS trackbacks_tb_id_seq CASCADE; +DROP SEQUENCE IF EXISTS job_job_id_seq CASCADE; +DROP SEQUENCE IF EXISTS category_cat_id_seq CASCADE; +DROP FUNCTION IF EXISTS page_deleted() CASCADE; +DROP FUNCTION IF EXISTS ts2_page_title() CASCADE; +DROP FUNCTION IF EXISTS ts2_page_text() CASCADE; +DROP FUNCTION IF EXISTS add_interwiki(TEXT,INT,SMALLINT) CASCADE; + CREATE SEQUENCE user_user_id_seq MINVALUE 0 START WITH 0; CREATE TABLE mwuser ( -- replace reserved word 'user' user_id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('user_user_id_seq'), @@ -39,6 +54,12 @@ CREATE TABLE user_groups ( ); CREATE UNIQUE INDEX user_groups_unique ON user_groups (ug_user, ug_group); +CREATE TABLE user_former_groups ( + ufg_user INTEGER NULL REFERENCES mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, + ufg_group TEXT NOT NULL +); +CREATE UNIQUE INDEX ufg_user_group ON user_former_groups (ufg_user, ufg_group); + CREATE TABLE user_newtalk ( user_id INTEGER NOT NULL REFERENCES mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, user_ip TEXT NULL, @@ -63,12 +84,12 @@ CREATE TABLE page ( page_len INTEGER NOT NULL ); CREATE UNIQUE INDEX page_unique_name ON page (page_namespace, page_title); -CREATE INDEX page_main_title ON page (page_title) WHERE page_namespace = 0; -CREATE INDEX page_talk_title ON page (page_title) WHERE page_namespace = 1; -CREATE INDEX page_user_title ON page (page_title) WHERE page_namespace = 2; -CREATE INDEX page_utalk_title ON page (page_title) WHERE page_namespace = 3; -CREATE INDEX page_project_title ON page (page_title) WHERE page_namespace = 4; -CREATE INDEX page_mediawiki_title ON page (page_title) WHERE page_namespace = 8; +CREATE INDEX page_main_title ON page (page_title text_pattern_ops) WHERE page_namespace = 0; +CREATE INDEX page_talk_title ON page (page_title text_pattern_ops) WHERE page_namespace = 1; +CREATE INDEX page_user_title ON page (page_title text_pattern_ops) WHERE page_namespace = 2; +CREATE INDEX page_utalk_title ON page (page_title text_pattern_ops) WHERE page_namespace = 3; +CREATE INDEX page_project_title ON page (page_title text_pattern_ops) WHERE page_namespace = 4; +CREATE INDEX page_mediawiki_title ON page (page_title text_pattern_ops) WHERE page_namespace = 8; CREATE INDEX page_random_idx ON page (page_random); CREATE INDEX page_len_idx ON page (page_len); @@ -384,14 +405,6 @@ CREATE TABLE watchlist ( CREATE UNIQUE INDEX wl_user_namespace_title ON watchlist (wl_namespace, wl_title, wl_user); CREATE INDEX wl_user ON watchlist (wl_user); -CREATE TABLE math ( - math_inputhash BYTEA NOT NULL UNIQUE, - math_outputhash BYTEA NOT NULL, - math_html_conservativeness SMALLINT NOT NULL, - math_html TEXT, - math_mathml TEXT -); - CREATE TABLE interwiki ( iw_prefix TEXT NOT NULL UNIQUE, diff --git a/maintenance/preprocessDump.php b/maintenance/preprocessDump.php new file mode 100644 index 00000000..ad9b4f14 --- /dev/null +++ b/maintenance/preprocessDump.php @@ -0,0 +1,86 @@ +<?php +/** + * Take page text out of an XML dump file and preprocess it to obj. + * It may be useful for getting preprocessor statistics or filling the + * preprocessor cache. + * + * Copyright (C) 2011 Platonides - http://www.mediawiki.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance + */ + +require_once( dirname( __FILE__ ) . '/dumpIterator.php' ); + +class PreprocessDump extends DumpIterator { + + /* Variables for dressing up as a parser */ + public $mTitle = 'PreprocessDump'; + public $mPPNodeCount = 0; + + public function getStripList() { + global $wgParser; + return $wgParser->getStripList(); + } + + public function __construct() { + parent::__construct(); + $this->addOption( 'cache', 'Use and populate the preprocessor cache.', false, false ); + $this->addOption( 'preprocessor', 'Preprocessor to use.', false, false ); + } + + public function getDbType() { + return Maintenance::DB_NONE; + } + + public function checkOptions() { + global $wgParser, $wgParserConf, $wgPreprocessorCacheThreshold; + + if ( !$this->hasOption( 'cache' ) ) { + $wgPreprocessorCacheThreshold = false; + } + + if ( $this->hasOption( 'preprocessor' ) ) { + $name = $this->getOption( 'preprocessor' ); + } elseif ( isset( $wgParserConf['preprocessorClass'] ) ) { + $name = $wgParserConf['preprocessorClass']; + } else { + $name = 'Preprocessor_DOM'; + } + + $wgParser->firstCallInit(); + $this->mPreprocessor = new $name( $this ); + } + + /** + * Callback function for each revision, preprocessToObj() + * @param $rev Revision + */ + public function processRevision( $rev ) { + try { + $this->mPreprocessor->preprocessToObj( $rev->getText(), 0 ); + } + catch(Exception $e) { + $this->error("Caught exception " . $e->getMessage() . " in " . $rev->getTitle()->getPrefixedText() ); + } + } +} + +$maintClass = "PreprocessDump"; +require_once( RUN_MAINTENANCE_IF_MAIN ); + diff --git a/maintenance/preprocessorFuzzTest.php b/maintenance/preprocessorFuzzTest.php index 31b372c2..9dee67e2 100644 --- a/maintenance/preprocessorFuzzTest.php +++ b/maintenance/preprocessorFuzzTest.php @@ -1,5 +1,22 @@ <?php /** + * Performs fuzz-style testing of MediaWiki's preprocessor. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance */ @@ -85,7 +102,6 @@ class PPFuzzTester { file_put_contents( $filename, "Input:\n$testReport\n" );*/ } } - wfLogProfilingData(); } function makeInputText( $max = false ) { @@ -138,6 +154,9 @@ class PPFuzzTest { $this->templates = array(); } + /** + * @param $title Title + */ function templateHook( $title ) { $titleText = $title->getPrefixedDBkey(); @@ -173,10 +192,10 @@ class PPFuzzTest { $wgUser->mFrom = 'name'; $wgUser->ppfz_test = $this; - $options = new ParserOptions; + $options = ParserOptions::newFromUser( $wgUser ); $options->setTemplateCallback( array( $this, 'templateHook' ) ); $options->setTimestamp( wfTimestampNow() ); - $this->output = call_user_func( array( $wgParser, $this->entryPoint ), $this->mainText, $this->title->getPrefixedText(), $options ); + $this->output = call_user_func( array( $wgParser, $this->entryPoint ), $this->mainText, $this->title, $options ); return $this->output; } @@ -200,7 +219,7 @@ class PPFuzzTest { } class PPFuzzUser extends User { - var $ppfz_test; + var $ppfz_test, $mDataLoaded; function load() { if ( $this->mDataLoaded ) { @@ -210,13 +229,13 @@ class PPFuzzUser extends User { $this->loadDefaults( $this->mName ); } - function getOption( $option, $defaultOverride = '' ) { - if ( $option === 'fancysig' ) { + function getOption( $oname, $defaultOverride = null, $ignoreHidden = false ) { + if ( $oname === 'fancysig' ) { return $this->ppfz_test->fancySig; - } elseif ( $option === 'nickname' ) { + } elseif ( $oname === 'nickname' ) { return $this->ppfz_test->nickname; } else { - return parent::getOption( $option, $defaultOverride ); + return parent::getOption( $oname, $defaultOverride, $ignoreHidden ); } } } diff --git a/maintenance/protect.php b/maintenance/protect.php index baef45fb..aab84d68 100644 --- a/maintenance/protect.php +++ b/maintenance/protect.php @@ -1,5 +1,7 @@ <?php /** + * Protect or unprotect an article. + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/maintenance/proxy_check.php b/maintenance/proxy_check.php new file mode 100644 index 00000000..2bc46c0d --- /dev/null +++ b/maintenance/proxy_check.php @@ -0,0 +1,54 @@ +<?php +/** + * Command line script to check for an open proxy at a specified location + * + * @file + */ + +if( php_sapi_name() != 'cli' ) { + die( 1 ); +} + +/** + * + */ +$output = ''; + +/** + * Exit if there are not enough parameters, or if it's not command line mode + */ +if ( ( isset( $_REQUEST ) && array_key_exists( 'argv', $_REQUEST ) ) || count( $argv ) < 4 ) { + $output .= "Incorrect parameters\n"; +} else { + /** + * Get parameters + */ + $ip = $argv[1]; + $port = $argv[2]; + $url = $argv[3]; + $host = trim(`hostname`); + $output = "Connecting to $ip:$port, target $url, this hostname $host\n"; + + # Open socket + $sock = @fsockopen($ip, $port, $errno, $errstr, 5); + if ($errno == 0 ) { + $output .= "Connected\n"; + # Send payload + $request = "GET $url HTTP/1.0\r\n"; +# $request .= "Proxy-Connection: Keep-Alive\r\n"; +# $request .= "Pragma: no-cache\r\n"; +# $request .= "Host: ".$url."\r\n"; +# $request .= "User-Agent: MediaWiki open proxy check\r\n"; + $request .= "\r\n"; + @fputs($sock, $request); + $response = fgets($sock, 65536); + $output .= $response; + @fclose($sock); + } else { + $output .= "No connection\n"; + } +} + +$output = escapeshellarg( $output ); + +#`echo $output >> /home/tstarling/open/proxy.log`; diff --git a/maintenance/purgeList.php b/maintenance/purgeList.php index 2c0308c0..85a7aae2 100644 --- a/maintenance/purgeList.php +++ b/maintenance/purgeList.php @@ -26,10 +26,22 @@ class PurgeList extends Maintenance { public function __construct() { parent::__construct(); $this->mDescription = "Send purge requests for listed pages to squid"; - $this->addOption( 'purge', 'Whether to update page touched.' , false, false ); + $this->addOption( 'purge', 'Whether to update page_touched.' , false, false ); + $this->addOption( 'namespace', 'Namespace number', false, true ); + $this->setBatchSize( 100 ); } public function execute() { + if( $this->hasOption( 'namespace' ) ) { + $this->purgeNamespace(); + } else { + $this->purgeList(); + } + $this->output( "Done!\n" ); + } + + /** Purge URL coming from stdin */ + private function purgeList() { $stdin = $this->getStdin(); $urls = array(); @@ -40,7 +52,7 @@ class PurgeList extends Maintenance { } elseif ( $page !== '' ) { $title = Title::newFromText( $page ); if ( $title ) { - $url = $title->getFullUrl(); + $url = $title->getInternalUrl(); $this->output( "$url\n" ); $urls[] = $url; if ( $this->getOption( 'purge' ) ) { @@ -51,13 +63,69 @@ class PurgeList extends Maintenance { } } } + $this->sendPurgeRequest( $urls ); + } + + /** Purge a namespace given by --namespace */ + private function purgeNamespace() { + $dbr = wfGetDB( DB_SLAVE ); + $ns = $dbr->addQuotes( $this->getOption( 'namespace') ); + + $result = $dbr->select( + array( 'page' ), + array( 'page_namespace', 'page_title' ), + array( "page_namespace = $ns" ), + __METHOD__, + array( 'ORDER BY' => 'page_id' ) + ); + + $start = 0; + $end = $dbr->numRows( $result ); + $this->output( "Will purge $end pages from namespace $ns\n" ); + + # Do remaining chunk + $end += $this->mBatchSize - 1; + $blockStart = $start; + $blockEnd = $start + $this->mBatchSize - 1; + + while( $blockEnd <= $end ) { + # Select pages we will purge: + $result = $dbr->select( + array( 'page' ), + array( 'page_namespace', 'page_title' ), + array( "page_namespace = $ns" ), + __METHOD__, + array( # conditions + 'ORDER BY' => 'page_id', + 'LIMIT' => $this->mBatchSize, + 'OFFSET' => $blockStart, + ) + ); + # Initialize/reset URLs to be purged + $urls = array(); + foreach( $result as $row ) { + $title = Title::makeTitle( $row->page_namespace, $row->page_title ); + $url = $title->getInternalUrl(); + $urls[] = $url; + } + + $this->sendPurgeRequest( $urls ); + + $blockStart += $this->mBatchSize; + $blockEnd += $this->mBatchSize; + } + } - $this->output( "Purging " . count( $urls ) . " urls...\n" ); + /** + * Helper to purge an array of $urls + * @param $urls array List of URLS to purge from squids + */ + private function sendPurgeRequest( $urls ) { + $this->output( "Purging " . count( $urls ). " urls\n" ); $u = new SquidUpdate( $urls ); $u->doUpdate(); - - $this->output( "Done!\n" ); } + } $maintClass = "PurgeList"; diff --git a/maintenance/purgeOldText.inc b/maintenance/purgeOldText.inc index 381d62a7..45a7ae28 100644 --- a/maintenance/purgeOldText.inc +++ b/maintenance/purgeOldText.inc @@ -3,6 +3,21 @@ /** * Support functions for cleaning up redundant text records * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance * @author Rob Church <robchur@gmail.com> @@ -29,6 +44,7 @@ function PurgeRedundantText( $delete = false ) { # Get "active" text records from the archive table echo( "Searching for active text records in archive table..." ); $res = $dbw->query( "SELECT DISTINCT ar_text_id FROM $tbl_arc" ); + $cur = array(); foreach ( $res as $row ) { $cur[] = $row->ar_text_id; } diff --git a/maintenance/purgeParserCache.php b/maintenance/purgeParserCache.php new file mode 100644 index 00000000..4b550b6e --- /dev/null +++ b/maintenance/purgeParserCache.php @@ -0,0 +1,43 @@ +<?php + +require( dirname( __FILE__ ) . '/Maintenance.php' ); + +class PurgeParserCache extends Maintenance { + function __construct() { + parent::__construct(); + $this->addDescription( "Remove old objects from the parser cache. " . + "This only works when the parser cache is in an SQL database." ); + $this->addOption( 'expiredate', 'Delete objects expiring before this date.', false, true ); + $this->addOption( 'age', + 'Delete objects created more than this many seconds ago, assuming $wgParserCacheExpireTime '. + 'has been consistent.', + false, true ); + } + + function execute() { + $inputDate = $this->getOption( 'expiredate' ); + $inputAge = $this->getOption( 'age' ); + if ( $inputDate !== null ) { + $date = wfTimestamp( TS_MW, strtotime( $inputDate ) ); + } elseif ( $inputAge !== null ) { + global $wgParserCacheExpireTime; + $date = wfTimestamp( TS_MW, time() + $wgParserCacheExpireTime - intval( $inputAge ) ); + } else { + echo "Must specify either --expiredate or --age\n"; + exit( 1 ); + } + + $english = Language::factory( 'en' ); + echo "Deleting objects expiring before " . $english->timeanddate( $date ) . "\n"; + + $pc = wfGetParserCacheStorage(); + $success = $pc->deleteObjectsExpiringBefore( $date ); + if ( !$success ) { + echo "Cannot purge this kind of parser cache.\n"; + exit( 1 ); + } + echo "Done\n"; + } +} +$maintClass = 'PurgeParserCache'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/purgeStaleMemcachedText.php b/maintenance/purgeStaleMemcachedText.php new file mode 100644 index 00000000..c4f5006f --- /dev/null +++ b/maintenance/purgeStaleMemcachedText.php @@ -0,0 +1,34 @@ +<?php + +require_once( dirname( __FILE__ ) . '/commandLine.inc' ); + +function purgeStaleMemcachedText() { + global $wgMemc, $wgDBname; + $db = wfGetDB( DB_MASTER ); + $maxTextId = $db->selectField( 'text', 'max(old_id)' ); + $latestReplicatedTextId = $db->selectField( array( 'recentchanges', 'revision' ), 'rev_text_id', + array( 'rev_id = rc_this_oldid', "rc_timestamp < '20101225183000'"), 'purgeStaleMemcachedText', + array( 'ORDER BY' => 'rc_timestamp DESC' ) ); + $latestReplicatedTextId -= 100; # A bit of paranoia + + echo "Going to purge text entries from $latestReplicatedTextId to $maxTextId in $wgDBname\n"; + + for ( $i = $latestReplicatedTextId; $i < $maxTextId; $i++ ) { + $key = wfMemcKey( 'revisiontext', 'textid', $i ); + + while (1) { + if (! $wgMemc->delete( $key ) ) { + echo "Memcache delete for $key returned false\n"; + } + if ( $wgMemc->get( $key ) ) { + echo "There's still content in $key!\n"; + } else { + break; + } + } + + } +} + +purgeStaleMemcachedText(); + diff --git a/maintenance/reassignEdits.php b/maintenance/reassignEdits.php index 039422b3..bb34e51c 100644 --- a/maintenance/reassignEdits.php +++ b/maintenance/reassignEdits.php @@ -126,8 +126,8 @@ class ReassignEdits extends Maintenance { * i.e. a user => id mapping, or a user_text => text mapping * * @param $user User for the condition - * @param $idfield Field name containing the identifier - * @param $utfield Field name containing the user text + * @param $idfield string Field name containing the identifier + * @param $utfield string Field name containing the user text * @return array */ private function userConditions( &$user, $idfield, $utfield ) { diff --git a/maintenance/rebuildFileCache.php b/maintenance/rebuildFileCache.php index a1aecd80..84ada11c 100644 --- a/maintenance/rebuildFileCache.php +++ b/maintenance/rebuildFileCache.php @@ -33,7 +33,7 @@ class RebuildFileCache extends Maintenance { public function execute() { global $wgUseFileCache, $wgDisableCounters, $wgContentNamespaces, $wgRequestTime; - global $wgTitle, $wgArticle, $wgOut, $wgUser; + global $wgTitle, $wgOut; if ( !$wgUseFileCache ) { $this->error( "Nothing to do -- \$wgUseFileCache is disabled.", true ); } @@ -54,7 +54,6 @@ class RebuildFileCache extends Maintenance { } $_SERVER['HTTP_ACCEPT_ENCODING'] = 'bgzip'; // hack, no real client - OutputPage::setEncodings(); # Not really used yet # Do remaining chunk $end += $this->mBatchSize - 1; @@ -73,16 +72,17 @@ class RebuildFileCache extends Maintenance { foreach ( $res as $row ) { $rebuilt = false; $wgRequestTime = wfTime(); # bug 22852 + $context = new RequestContext; $wgTitle = Title::makeTitleSafe( $row->page_namespace, $row->page_title ); + $context->setTitle( $wgTitle ); if ( null == $wgTitle ) { $this->output( "Page {$row->page_id} has bad title\n" ); continue; // broken title? } - $wgOut->setTitle( $wgTitle ); // set display title - $wgUser->getSkin( $wgTitle ); // set skin title - $wgArticle = new Article( $wgTitle ); + $wgOut = $context->getOutput(); // set display title + $article = new Article( $wgTitle ); // If the article is cacheable, then load it - if ( $wgArticle->isFileCacheable() ) { + if ( $article->isFileCacheable() ) { $cache = new HTMLFileCache( $wgTitle ); if ( $cache->isFileCacheGood() ) { if ( $overwrite ) { @@ -93,12 +93,13 @@ class RebuildFileCache extends Maintenance { } } ob_start( array( &$cache, 'saveToFileCache' ) ); // save on ob_end_clean() - $wgUseFileCache = false; // hack, we don't want $wgArticle fiddling with filecache - $wgArticle->view(); - @$wgOut->output(); // header notices + $wgUseFileCache = false; // hack, we don't want $article fiddling with filecache + $article->view(); + wfSuppressWarnings(); // header notices + $wgOut->output(); + wfRestoreWarnings(); $wgUseFileCache = true; ob_end_clean(); // clear buffer - $wgOut = new OutputPage(); // empty out any output page garbage if ( $rebuilt ) $this->output( "Re-cached page {$row->page_id}\n" ); else @@ -110,15 +111,13 @@ class RebuildFileCache extends Maintenance { } $blockStart += $this->mBatchSize; $blockEnd += $this->mBatchSize; - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } $this->output( "Done!\n" ); // Remove these to be safe if ( isset( $wgTitle ) ) unset( $wgTitle ); - if ( isset( $wgArticle ) ) - unset( $wgArticle ); } } diff --git a/maintenance/rebuildImages.php b/maintenance/rebuildImages.php index c61d9480..17111831 100644 --- a/maintenance/rebuildImages.php +++ b/maintenance/rebuildImages.php @@ -33,9 +33,19 @@ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); class ImageBuilder extends Maintenance { + + /** + * @var DatabaseBase + */ + protected $dbw; + function __construct() { parent::__construct(); + global $wgUpdateCompatibleMetadata; + //make sure to update old, but compatible img_metadata fields. + $wgUpdateCompatibleMetadata = true; + $this->mDescription = 'Script to update image metadata records'; $this->addOption( 'missing', 'Check for files without associated database record' ); @@ -57,6 +67,9 @@ class ImageBuilder extends Maintenance { } } + /** + * @return FileRepo + */ function getRepo() { if ( !isset( $this->repo ) ) { $this->repo = RepoGroup::singleton()->getLocalRepo(); diff --git a/maintenance/rebuildInterwiki.php b/maintenance/rebuildInterwiki.php index 3da920f8..25aea2de 100644 --- a/maintenance/rebuildInterwiki.php +++ b/maintenance/rebuildInterwiki.php @@ -3,30 +3,28 @@ * Rebuild interwiki table using the file on meta and the language list * Wikimedia specific! * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @todo document * @ingroup Maintenance * @ingroup Wikimedia */ -/** - * @todo document - * @ingroup Maintenance - */ -class Site { - var $suffix, $lateral, $url; - - function __construct( $s, $l, $u ) { - $this->suffix = $s; - $this->lateral = $l; - $this->url = $u; - } - - function getURL( $lang ) { - $xlang = str_replace( '_', '-', $lang ); - return "http://$xlang.{$this->url}/wiki/\$1"; - } -} +require_once( dirname( __FILE__ ) . '/Site.php' ); require_once( dirname( __FILE__ ) . '/Maintenance.php' ); diff --git a/maintenance/rebuildLocalisationCache.php b/maintenance/rebuildLocalisationCache.php index 0ca99610..831d808a 100644 --- a/maintenance/rebuildLocalisationCache.php +++ b/maintenance/rebuildLocalisationCache.php @@ -112,9 +112,9 @@ class RebuildLocalisationCache extends Maintenance { * Helper function to rebuild list of languages codes. Prints the code * for each language which is rebuilt. * @param $codes list List of language codes to rebuild. - * @param $lc object Instance of LocalisationCache_BulkLoad (?) - * @param $force bool Rebuild up-to-date languages - * @return int Number of rebuilt languages + * @param $lc LocalisationCache Instance of LocalisationCache_BulkLoad (?) + * @param $force bool Rebuild up-to-date languages + * @return int Number of rebuilt languages */ private function doRebuild( $codes, $lc, $force ) { $numRebuilt = 0; @@ -127,6 +127,15 @@ class RebuildLocalisationCache extends Maintenance { } return $numRebuilt; } + + /** + * Sets whether a run of this maintenance script has the force parameter set + * + * @param bool $forced + */ + public function setForce( $forced = true ) { + $this->mOptions['force'] = $forced; + } } $maintClass = "RebuildLocalisationCache"; diff --git a/maintenance/rebuildall.php b/maintenance/rebuildall.php index 82619048..dbbed86d 100644 --- a/maintenance/rebuildall.php +++ b/maintenance/rebuildall.php @@ -30,9 +30,8 @@ class RebuildAll extends Maintenance { } public function execute() { - global $wgDBtype; // Rebuild the text index - if ( $wgDBtype != 'postgres' ) { + if ( wfGetDB( DB_SLAVE )->getType() != 'postgres' ) { $this->output( "** Rebuilding fulltext search index (if you abort this will break searching; run this script again to fix):\n" ); $rebuildText = $this->runChild( 'RebuildTextIndex', 'rebuildtextindex.php' ); $rebuildText->execute(); diff --git a/maintenance/rebuildtextindex.php b/maintenance/rebuildtextindex.php index 46282c18..04606c75 100644 --- a/maintenance/rebuildtextindex.php +++ b/maintenance/rebuildtextindex.php @@ -28,6 +28,10 @@ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); class RebuildTextIndex extends Maintenance { const RTI_CHUNK_SIZE = 500; + + /** + * @var DatabaseBase + */ private $db; public function __construct() { @@ -40,10 +44,11 @@ class RebuildTextIndex extends Maintenance { } public function execute() { - global $wgTitle, $wgDBtype; + global $wgTitle; // Shouldn't be needed for Postgres - if ( $wgDBtype == 'postgres' ) { + $this->db = wfGetDB( DB_MASTER ); + if ( $this->db->getType() == 'postgres' ) { $this->error( "This script is not needed when using Postgres.\n", true ); } diff --git a/maintenance/refreshImageMetadata.php b/maintenance/refreshImageMetadata.php new file mode 100644 index 00000000..ec612183 --- /dev/null +++ b/maintenance/refreshImageMetadata.php @@ -0,0 +1,199 @@ +<?php +/** + * Script to refresh image metadata fields. See also rebuildImages.php + * + * Usage: php refreshImageMetadata.php + * + * Copyright © 2011 Brian Wolff + * http://www.mediawiki.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @author Brian Wolff + * @ingroup maintenance + */ + +require_once( dirname( __FILE__ ) . '/Maintenance.php' ); + +class RefreshImageMetadata extends Maintenance { + + /** + * @var DatabaseBase + */ + protected $dbw; + + function __construct() { + parent::__construct(); + + $this->mDescription = 'Script to update image metadata records'; + $this->setBatchSize( 200 ); + + $this->addOption( 'force', 'Reload metadata from file even if the metadata looks ok', false, false, 'f' ); + $this->addOption( 'broken-only', 'Only fix really broken records, leave old but still compatible records alone.' ); + $this->addOption( 'verbose', 'Output extra information about each upgraded/non-upgraded file.', false, false, 'v' ); + $this->addOption( 'start', 'Name of file to start with', false, true ); + $this->addOption( 'end', 'Name of file to end with', false, true ); + + $this->addOption( 'mime', '(Inefficient!) Only refresh files with this mime type. Can accept wild-card image/*' , false, true ); + $this->addOption( 'metadata-contains', '(Inefficient!) Only refresh files where the img_metadata field contains this string. Can be used if its known a specific property was being extracted incorrectly.', false, true ); + + } + + public function execute() { + $force = $this->hasOption( 'force' ); + $brokenOnly = $this->hasOption( 'broken-only' ); + $verbose = $this->hasOption( 'verbose' ); + $start = $this->getOption( 'start', false ); + $this->setupParameters( $force, $brokenOnly ); + + $upgraded = 0; + $leftAlone = 0; + $error = 0; + + $dbw = wfGetDB( DB_MASTER ); + if ( $this->mBatchSize <= 0 ) { + $this->error( "Batch size is too low...", 12 ); + } + + $repo = RepoGroup::singleton()->getLocalRepo(); + $conds = $this->getConditions( $dbw ); + + // For the WHERE img_name > 'foo' condition that comes after doing a batch + $conds2 = array(); + if ( $start !== false ) { + $conds2[] = 'img_name >= ' . $dbw->addQuotes( $start ); + } + + $options = array( + 'LIMIT' => $this->mBatchSize, + 'ORDER BY' => 'img_name ASC', + ); + + do { + $res = $dbw->select( + 'image', + '*', + array_merge( $conds, $conds2 ), + __METHOD__, + $options + ); + + if ( $res->numRows() > 0 ) { + $row1 = $res->current(); + $this->output( "Processing next {$this->mBatchSize} rows starting with {$row1->img_name}.\n"); + $res->rewind(); + } else { + $this->error( "No images to process.", 4 ); + } + + foreach ( $res as $row ) { + $file = $repo->newFileFromRow( $row ); + if ( $file->getUpgraded() ) { + // File was upgraded. + $upgraded++; + $newLength = strlen( $file->getMetadata() ); + $oldLength = strlen( $row->img_metadata ); + if ( $newLength < $oldLength - 5 ) { + // If after updating, the metadata is smaller then + // what it was before, that's probably not a good thing + // because we extract more data with time, not less. + // Thus this probably indicates an error of some sort, + // or at the very least is suspicious. Have the - 5 just + // to weed out any inconsequential changes. + $error++; + $this->output( "Warning: File:{$row->img_name} used to have " . + "$oldLength bytes of metadata but now has $newLength bytes.\n" ); + } elseif ( $verbose ) { + $this->output("Refreshed File:{$row->img_name}.\n" ); + } + } else { + $leftAlone++; + if ( $force ) { + $file->upgradeRow(); + $newLength = strlen( $file->getMetadata() ); + $oldLength = strlen( $row->img_metadata ); + if ( $newLength < $oldLength - 5 ) { + $error++; + $this->output( "Warning: File:{$row->img_name} used to have " . + "$oldLength bytes of metadata but now has $newLength bytes. (forced)\n" ); + + } + if ( $verbose ) { + $this->output("Forcibly refreshed File:{$row->img_name}.\n" ); + } + } + else { + if ( $verbose ) { + $this->output( "Skipping File:{$row->img_name}.\n" ); + } + } + } + + } + $conds2 = array( 'img_name > ' . $dbw->addQuotes( $row->img_name ) ); + wfWaitForSlaves(); + } while( $res->numRows() === $this->mBatchSize ); + + $total = $upgraded + $leftAlone; + if ( $force ) { + $this->output( "\nFinished refreshing file metadata for $total files. $upgraded needed to be refreshed, $leftAlone did not need to be but were refreshed anyways, and $error refreshes were suspicious.\n" ); + } else { + $this->output( "\nFinished refreshing file metadata for $total files. $upgraded were refreshed, $leftAlone were already up to date, and $error refreshes were suspicious.\n" ); + } + } + + function getConditions( $dbw ) { + $conds = array(); + + $end = $this->getOption( 'end', false ); + $mime = $this->getOption( 'mime', false ); + $like = $this->getOption( 'metadata-contains', false ); + + if ( $end !== false ) { + $conds[] = 'img_name <= ' . $dbw->addQuotes( $end ) ; + } + if ( $mime !== false ) { + list( $major, $minor ) = File::splitMime( $mime ); + $conds['img_major_mime'] = $major; + if ( $minor !== '*' ) { + $conds['img_minor_mime'] = $minor; + } + } + if ( $like ) { + $conds[] = 'img_metadata ' . $dbw->buildLike( $dbw->anyString(), $like, $dbw->anyString() ); + } + return $conds; + } + + function setupParameters( $force, $brokenOnly ) { + global $wgUpdateCompatibleMetadata, $wgReadOnly; + + if ( $brokenOnly ) { + $wgUpdateCompatibleMetadata = false; + } else { + $wgUpdateCompatibleMetadata = true; + } + + if ( $brokenOnly && $force ) { + $this->error( 'Cannot use --broken-only and --force together. ', 2 ); + } + } +} + + +$maintClass = 'RefreshImageMetadata'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/refreshLinks.php b/maintenance/refreshLinks.php index 144e96c5..0515ef1b 100644 --- a/maintenance/refreshLinks.php +++ b/maintenance/refreshLinks.php @@ -1,5 +1,7 @@ <?php /** + * Refresh link tables. + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -58,19 +60,17 @@ class RefreshLinks extends Maintenance { */ private function doRefreshLinks( $start, $newOnly = false, $maxLag = false, $end = 0, $redirectsOnly = false, $oldRedirectsOnly = false ) { - global $wgUser, $wgParser, $wgUseTidy; + global $wgParser, $wgUseTidy; $reportingInterval = 100; $dbr = wfGetDB( DB_SLAVE ); $start = intval( $start ); - # Don't generate TeX PNGs (lack of a sensible current directory causes errors anyway) - $wgUser->setOption( 'math', MW_MATH_SOURCE ); + // Give extensions a chance to optimize settings + wfRunHooks( 'MaintenanceRefreshLinksInit', array( $this ) ); # Don't generate extension images (e.g. Timeline) - if ( method_exists( $wgParser, "clearTagHooks" ) ) { - $wgParser->clearTagHooks(); - } + $wgParser->clearTagHooks(); # Don't use HTML tidy $wgUseTidy = false; @@ -79,23 +79,35 @@ class RefreshLinks extends Maintenance { if ( $oldRedirectsOnly ) { # This entire code path is cut-and-pasted from below. Hurrah. - $res = $dbr->query( - "SELECT page_id " . - "FROM page " . - "LEFT JOIN redirect ON page_id=rd_from " . - "WHERE page_is_redirect=1 AND rd_from IS NULL AND " . - ( $end == 0 ? "page_id >= $start" - : "page_id BETWEEN $start AND $end" ), - __METHOD__ + + $conds = array( + "page_is_redirect=1", + "rd_from IS NULL" + ); + + if ( $end == 0 ) { + $conds[] = "page_id >= $start"; + } else { + $conds[] = "page_id BETWEEN $start AND $end"; + } + + $res = $dbr->select( + array( 'page', 'redirect' ), + 'page_id', + $conds, + __METHOD__, + array(), + array( 'redirect' => array( "LEFT JOIN", "page_id=rd_from" ) ) ); $num = $dbr->numRows( $res ); $this->output( "Refreshing $num old redirects from $start...\n" ); $i = 0; + foreach ( $res as $row ) { if ( !( ++$i % $reportingInterval ) ) { $this->output( "$i\n" ); - wfWaitForSlaves( $maxLag ); + wfWaitForSlaves(); } $this->fixRedirect( $row->page_id ); } @@ -115,12 +127,13 @@ class RefreshLinks extends Maintenance { foreach ( $res as $row ) { if ( !( ++$i % $reportingInterval ) ) { $this->output( "$i\n" ); - wfWaitForSlaves( $maxLag ); + wfWaitForSlaves(); } - if ( $redirectsOnly ) + if ( $redirectsOnly ) { $this->fixRedirect( $row->page_id ); - else + } else { self::fixLinksFromArticle( $row->page_id ); + } } } else { if ( !$end ) { @@ -135,7 +148,7 @@ class RefreshLinks extends Maintenance { if ( !( $id % $reportingInterval ) ) { $this->output( "$id\n" ); - wfWaitForSlaves( $maxLag ); + wfWaitForSlaves(); } $this->fixRedirect( $id ); } @@ -148,7 +161,7 @@ class RefreshLinks extends Maintenance { if ( !( $id % $reportingInterval ) ) { $this->output( "$id\n" ); - wfWaitForSlaves( $maxLag ); + wfWaitForSlaves(); } self::fixLinksFromArticle( $id ); } @@ -161,29 +174,27 @@ class RefreshLinks extends Maintenance { * @param $id int The page_id of the redirect */ private function fixRedirect( $id ) { - global $wgTitle, $wgArticle; - - $wgTitle = Title::newFromID( $id ); + $title = Title::newFromID( $id ); $dbw = wfGetDB( DB_MASTER ); - if ( is_null( $wgTitle ) ) { + if ( is_null( $title ) ) { // This page doesn't exist (any more) // Delete any redirect table entry for it $dbw->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__ ); return; } - $wgArticle = new Article( $wgTitle ); + $article = new Article( $title ); - $rt = $wgArticle->followRedirect(); + $rt = $article->followRedirect(); if ( !$rt || !is_object( $rt ) ) { - // $wgTitle is not a redirect + // $title is not a redirect // Delete any redirect table entry for it $dbw->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__ ); } else { - $wgArticle->updateRedirectOn( $dbw, $rt ); + $article->updateRedirectOn( $dbw, $rt ); } } @@ -192,31 +203,31 @@ class RefreshLinks extends Maintenance { * @param $id int The page_id */ public static function fixLinksFromArticle( $id ) { - global $wgTitle, $wgParser; + global $wgParser; - $wgTitle = Title::newFromID( $id ); + $title = Title::newFromID( $id ); $dbw = wfGetDB( DB_MASTER ); LinkCache::singleton()->clear(); - if ( is_null( $wgTitle ) ) { + if ( is_null( $title ) ) { return; } $dbw->begin(); - $revision = Revision::newFromTitle( $wgTitle ); + $revision = Revision::newFromTitle( $title ); if ( !$revision ) { return; } $options = new ParserOptions; - $parserOutput = $wgParser->parse( $revision->getText(), $wgTitle, $options, true, true, $revision->getId() ); - $update = new LinksUpdate( $wgTitle, $parserOutput, false ); + $parserOutput = $wgParser->parse( $revision->getText(), $title, $options, true, true, $revision->getId() ); + $update = new LinksUpdate( $title, $parserOutput, false ); $update->doUpdate(); $dbw->commit(); } - /* + /** * Removes non-existing links from pages from pagelinks, imagelinks, * categorylinks, templatelinks and externallinks tables. * @@ -226,7 +237,7 @@ class RefreshLinks extends Maintenance { * @author Merlijn van Deen <valhallasw@arctus.nl> */ private function deleteLinksFromNonexistent( $maxLag = 0, $batchSize = 100 ) { - wfWaitForSlaves( $maxLag ); + wfWaitForSlaves(); $dbw = wfGetDB( DB_MASTER ); @@ -240,6 +251,9 @@ class RefreshLinks extends Maintenance { 'categorylinks' => 'cl_from', 'templatelinks' => 'tl_from', 'externallinks' => 'el_from', + 'iwlinks' => 'iwl_from', + 'langlinks' => 'll_from', + 'redirect' => 'rd_from', ); foreach ( $linksTables as $table => $field ) { @@ -262,7 +276,7 @@ class RefreshLinks extends Maintenance { $counter++; $list[] = $row->$field; if ( ( $counter % $batchSize ) == 0 ) { - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); $dbw->delete( $table, array( $field => $list ), __METHOD__ ); $this->output( $counter . ".." ); diff --git a/maintenance/resetUserTokens.php b/maintenance/resetUserTokens.php new file mode 100644 index 00000000..a1c4eaeb --- /dev/null +++ b/maintenance/resetUserTokens.php @@ -0,0 +1,73 @@ +<?php +/** + * Script to reset the user_token for all users on the wiki. Useful if you + * believe that your user table was acidentally leaked to an external source. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance + * @author Daniel Friesen <mediawiki@danielfriesen.name> + */ + +require_once( dirname( __FILE__ ) . '/Maintenance.php' ); + +class ResetUserTokens extends Maintenance { + public function __construct() { + parent::__construct(); + $this->mDescription = "Reset the user_token of all users on the wiki. Note that this may log some of them out."; + $this->addOption( 'nowarn', "Hides the 5 seconds warning", false, false ); + } + + public function execute() { + + if ( !$this->getOption( 'nowarn' ) ) { + $this->output( "The script is about to reset the user_token for ALL USERS in the database.\n" ); + $this->output( "This may log some of them out and is not necessary unless you believe your\n" ); + $this->output( "user table has been compromised.\n" ); + $this->output( "\n" ); + $this->output( "Abort with control-c in the next five seconds (skip this countdown with --nowarn) ... " ); + wfCountDown( 5 ); + } + + // We list user by user_id from one of the slave database + $dbr = wfGetDB( DB_SLAVE ); + $result = $dbr->select( 'user', + array( 'user_id' ), + array(), + __METHOD__ + ); + + foreach ( $result as $id ) { + $user = User::newFromId( $id->user_id ); + + $username = $user->getName(); + + $this->output( "Resetting user_token for $username: " ); + + // Change value + $user->setToken(); + $user->saveSettings(); + + $this->output( " OK\n" ); + + } + + } +} + +$maintClass = "ResetUserTokens"; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/runBatchedQuery.php b/maintenance/runBatchedQuery.php index dd3680c9..9a6b6383 100644 --- a/maintenance/runBatchedQuery.php +++ b/maintenance/runBatchedQuery.php @@ -29,7 +29,6 @@ class BatchedQueryRunner extends Maintenance { parent::__construct(); $this->mDescription = "Run a query repeatedly until it affects 0 rows, and wait for slaves in between.\n" . "NOTE: You need to set a LIMIT clause yourself."; - $this->addOption( 'wait', "Wait for replication lag to go down to this value. Default: 5", false, true ); } public function execute() { @@ -39,14 +38,14 @@ class BatchedQueryRunner extends Maintenance { $query = $this->getArg(); $wait = $this->getOption( 'wait', 5 ); $n = 1; - $dbw = wfGetDb( DB_MASTER ); + $dbw = wfGetDB( DB_MASTER ); do { $this->output( "Batch $n: " ); $n++; $dbw->query( $query, __METHOD__ ); $affected = $dbw->affectedRows(); $this->output( "$affected rows\n" ); - wfWaitForSlaves( $wait ); + wfWaitForSlaves(); } while ( $affected > 0 ); } diff --git a/maintenance/runJobs.php b/maintenance/runJobs.php index 79ea7bfe..0edf7ac9 100644 --- a/maintenance/runJobs.php +++ b/maintenance/runJobs.php @@ -31,6 +31,7 @@ class RunJobs extends Maintenance { parent::__construct(); $this->mDescription = "Run pending jobs"; $this->addOption( 'maxjobs', 'Maximum number of jobs to run', false, true ); + $this->addOption( 'maxtime', 'Maximum amount of wall-clock time', false, true ); $this->addOption( 'type', 'Type of job to run', false, true ); $this->addOption( 'procs', 'Number of processes to use', false, true ); } @@ -48,11 +49,13 @@ class RunJobs extends Maintenance { $this->error( "Invalid argument to --procs", true ); } $fc = new ForkController( $procs ); - if ( $fc->start( $procs ) != 'child' ) { + if ( $fc->start() != 'child' ) { exit( 0 ); } } - $maxJobs = $this->getOption( 'maxjobs', 10000 ); + $maxJobs = $this->getOption( 'maxjobs', false ); + $maxTime = $this->getOption( 'maxtime', false ); + $startTime = time(); $type = $this->getOption( 'type', false ); $wgTitle = Title::newFromText( 'RunJobs.php' ); $dbw = wfGetDB( DB_MASTER ); @@ -69,7 +72,7 @@ class RunJobs extends Maintenance { if ( !$job ) break; - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); $t = microtime( true ); $offset = $job->id; $status = $job->run(); @@ -80,9 +83,13 @@ class RunJobs extends Maintenance { } else { $this->runJobsLog( $job->toString() . " t=$timeMs good" ); } + if ( $maxJobs && ++$n > $maxJobs ) { break 2; } + if ( $maxTime && time() - $startTime > $maxTime ) { + break 2; + } } } } diff --git a/maintenance/sqlite.inc b/maintenance/sqlite.inc index 238fe82b..1f821917 100644 --- a/maintenance/sqlite.inc +++ b/maintenance/sqlite.inc @@ -1,4 +1,25 @@ <?php +/** + * Helper class for sqlite-specific scripts + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance + */ /** * This class contains code common to different SQLite-related maintenance scripts diff --git a/maintenance/sqlite.php b/maintenance/sqlite.php index 13d136d8..dc8a430e 100644 --- a/maintenance/sqlite.php +++ b/maintenance/sqlite.php @@ -41,20 +41,19 @@ class SqliteMaintenance extends Maintenance { } public function execute() { - global $wgDBtype; - // Should work even if we use a non-SQLite database if ( $this->hasOption( 'check-syntax' ) ) { $this->checkSyntax(); + return; } - if ( $wgDBtype != 'sqlite' ) { + $this->db = wfGetDB( DB_MASTER ); + + if ( $this->db->getType() != 'sqlite' ) { $this->error( "This maintenance script requires a SQLite database.\n" ); return; } - $this->db = wfGetDB( DB_MASTER ); - if ( $this->hasOption( 'vacuum' ) ) { $this->vacuum(); } diff --git a/maintenance/sqlite/archives/patch-archive_kill_ar_page_revid.sql b/maintenance/sqlite/archives/patch-archive_kill_ar_page_revid.sql new file mode 100644 index 00000000..0f3e9c7f --- /dev/null +++ b/maintenance/sqlite/archives/patch-archive_kill_ar_page_revid.sql @@ -0,0 +1,3 @@ +-- Used for killing the wrong index added during SVN for 1.17 +-- Won't affect most people, but it doesn't need to exist +DROP INDEX IF EXISTS ar_page_revid;
\ No newline at end of file diff --git a/maintenance/sqlite/archives/patch-rename-iwl_prefix.sql b/maintenance/sqlite/archives/patch-rename-iwl_prefix.sql index 08c3ae5f..851a6b37 100644 --- a/maintenance/sqlite/archives/patch-rename-iwl_prefix.sql +++ b/maintenance/sqlite/archives/patch-rename-iwl_prefix.sql @@ -2,4 +2,4 @@ -- Recreates the iwl_prefix for the iwlinks table -- DROP INDEX IF EXISTS /*i*/iwl_prefix; -CREATE INDEX /*i*/iwl_prefix_from_title ON /*_*/iwlinks (iwl_prefix, iwl_from, iwl_title);
\ No newline at end of file +CREATE INDEX IF NOT EXISTS /*i*/iwl_prefix_from_title ON /*_*/iwlinks (iwl_prefix, iwl_from, iwl_title); diff --git a/maintenance/stats.php b/maintenance/stats.php index 2cbbcf91..1fb46fa9 100644 --- a/maintenance/stats.php +++ b/maintenance/stats.php @@ -37,8 +37,8 @@ class CacheStats extends Maintenance { global $wgMemc; // Can't do stats if - if ( get_class( $wgMemc ) == 'FakeMemCachedClient' ) { - $this->error( "You are running FakeMemCachedClient, I can not provide any statistics.", true ); + if ( get_class( $wgMemc ) == 'EmptyBagOStuff' ) { + $this->error( "You are running EmptyBagOStuff, I can not provide any statistics.", true ); } $session = intval( $wgMemc->get( wfMemcKey( 'stats', 'request_with_session' ) ) ); $noSession = intval( $wgMemc->get( wfMemcKey( 'stats', 'request_without_session' ) ) ); @@ -59,30 +59,42 @@ class CacheStats extends Maintenance { $absent = intval( $wgMemc->get( wfMemcKey( 'stats', 'pcache_miss_absent' ) ) ); $stub = intval( $wgMemc->get( wfMemcKey( 'stats', 'pcache_miss_stub' ) ) ); $total = $hits + $invalid + $expired + $absent + $stub; - $this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits / $total * 100 ) ); - $this->output( sprintf( "invalid: %-10d %6.2f%%\n", $invalid, $invalid / $total * 100 ) ); - $this->output( sprintf( "expired: %-10d %6.2f%%\n", $expired, $expired / $total * 100 ) ); - $this->output( sprintf( "absent: %-10d %6.2f%%\n", $absent, $absent / $total * 100 ) ); - $this->output( sprintf( "stub threshold: %-10d %6.2f%%\n", $stub, $stub / $total * 100 ) ); - $this->output( sprintf( "total: %-10d %6.2f%%\n", $total, 100 ) ); + if ( $total ) { + $this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits / $total * 100 ) ); + $this->output( sprintf( "invalid: %-10d %6.2f%%\n", $invalid, $invalid / $total * 100 ) ); + $this->output( sprintf( "expired: %-10d %6.2f%%\n", $expired, $expired / $total * 100 ) ); + $this->output( sprintf( "absent: %-10d %6.2f%%\n", $absent, $absent / $total * 100 ) ); + $this->output( sprintf( "stub threshold: %-10d %6.2f%%\n", $stub, $stub / $total * 100 ) ); + $this->output( sprintf( "total: %-10d %6.2f%%\n", $total, 100 ) ); + } else { + $this->output( "no statistics available\n" ); + } + $this->output( "\nImage cache\n" ); $hits = intval( $wgMemc->get( wfMemcKey( 'stats', 'image_cache_hit' ) ) ); $misses = intval( $wgMemc->get( wfMemcKey( 'stats', 'image_cache_miss' ) ) ); $updates = intval( $wgMemc->get( wfMemcKey( 'stats', 'image_cache_update' ) ) ); $total = $hits + $misses; - $this->output( "\nImage cache\n" ); - $this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits / $total * 100 ) ); - $this->output( sprintf( "misses: %-10d %6.2f%%\n", $misses, $misses / $total * 100 ) ); - $this->output( sprintf( "updates: %-10d\n", $updates ) ); + if ( $total ) { + $this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits / $total * 100 ) ); + $this->output( sprintf( "misses: %-10d %6.2f%%\n", $misses, $misses / $total * 100 ) ); + $this->output( sprintf( "updates: %-10d\n", $updates ) ); + } else { + $this->output( "no statistics available\n" ); + } + $this->output( "\nDiff cache\n" ); $hits = intval( $wgMemc->get( wfMemcKey( 'stats', 'diff_cache_hit' ) ) ); $misses = intval( $wgMemc->get( wfMemcKey( 'stats', 'diff_cache_miss' ) ) ); $uncacheable = intval( $wgMemc->get( wfMemcKey( 'stats', 'diff_uncacheable' ) ) ); $total = $hits + $misses + $uncacheable; - $this->output( "\nDiff cache\n" ); - $this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits / $total * 100 ) ); - $this->output( sprintf( "misses: %-10d %6.2f%%\n", $misses, $misses / $total * 100 ) ); - $this->output( sprintf( "uncacheable: %-10d %6.2f%%\n", $uncacheable, $uncacheable / $total * 100 ) ); + if ( $total ) { + $this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits / $total * 100 ) ); + $this->output( sprintf( "misses: %-10d %6.2f%%\n", $misses, $misses / $total * 100 ) ); + $this->output( sprintf( "uncacheable: %-10d %6.2f%%\n", $uncacheable, $uncacheable / $total * 100 ) ); + } else { + $this->output( "no statistics available\n" ); + } } } diff --git a/maintenance/storage/checkStorage.php b/maintenance/storage/checkStorage.php index c288d682..c372b9c4 100644 --- a/maintenance/storage/checkStorage.php +++ b/maintenance/storage/checkStorage.php @@ -2,6 +2,21 @@ /** * Fsck for MediaWiki * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance ExternalStorage */ @@ -115,6 +130,7 @@ class CheckStorage { // It's safe to just erase the old_flags field if ( $fix ) { $this->error( 'fixed', "Warning: old_flags set to 0", $id ); + $dbw = wfGetDB( DB_MASTER ); $dbw->ping(); $dbw->update( 'text', array( 'old_flags' => '' ), array( 'old_id' => $id ), $fname ); diff --git a/maintenance/storage/compressOld.inc b/maintenance/storage/compressOld.inc deleted file mode 100644 index 93be5f75..00000000 --- a/maintenance/storage/compressOld.inc +++ /dev/null @@ -1,300 +0,0 @@ -<?php -/** - * @file - * @ingroup Maintenance ExternalStorage - */ - -/** @todo document */ -function compressOldPages( $start = 0, $extdb = '' ) { - $fname = 'compressOldPages'; - - $chunksize = 50; - print "Starting from old_id $start...\n"; - $dbw = wfGetDB( DB_MASTER ); - do { - $res = $dbw->select( 'text', array( 'old_id','old_flags','old_text' ), - "old_id>=$start", $fname, array( 'ORDER BY' => 'old_id', 'LIMIT' => $chunksize, 'FOR UPDATE' ) ); - if( $dbw->numRows( $res ) == 0 ) { - break; - } - $last = $start; - foreach ( $res as $row ) { - # print " {$row->old_id} - {$row->old_namespace}:{$row->old_title}\n"; - compressPage( $row, $extdb ); - $last = $row->old_id; - } - $start = $last + 1; # Deletion may leave long empty stretches - print "$start...\n"; - } while( true ); -} - -/** @todo document */ -function compressPage( $row, $extdb ) { - $fname = 'compressPage'; - if ( false !== strpos( $row->old_flags, 'gzip' ) || false !== strpos( $row->old_flags, 'object' ) ) { - #print "Already compressed row {$row->old_id}\n"; - return false; - } - $dbw = wfGetDB( DB_MASTER ); - $flags = $row->old_flags ? "{$row->old_flags},gzip" : "gzip"; - $compress = gzdeflate( $row->old_text ); - - # Store in external storage if required - if ( $extdb !== '' ) { - $storeObj = new ExternalStoreDB; - $compress = $storeObj->store( $extdb, $compress ); - if ( $compress === false ) { - print "Unable to store object\n"; - return false; - } - } - - # Update text row - $dbw->update( 'text', - array( /* SET */ - 'old_flags' => $flags, - 'old_text' => $compress - ), array( /* WHERE */ - 'old_id' => $row->old_id - ), $fname, - array( 'LIMIT' => 1 ) - ); - return true; -} - -define( 'LS_INDIVIDUAL', 0 ); -define( 'LS_CHUNKED', 1 ); - -/** @todo document */ -function compressWithConcat( $startId, $maxChunkSize, $beginDate, - $endDate, $extdb="", $maxPageId = false ) -{ - $fname = 'compressWithConcat'; - $loadStyle = LS_CHUNKED; - - $dbr = wfGetDB( DB_SLAVE ); - $dbw = wfGetDB( DB_MASTER ); - - # Set up external storage - if ( $extdb != '' ) { - $storeObj = new ExternalStoreDB; - } - - # Get all articles by page_id - if ( !$maxPageId ) { - $maxPageId = $dbr->selectField( 'page', 'max(page_id)', '', $fname ); - } - print "Starting from $startId of $maxPageId\n"; - $pageConds = array(); - - /* - if ( $exclude_ns0 ) { - print "Excluding main namespace\n"; - $pageConds[] = 'page_namespace<>0'; - } - if ( $queryExtra ) { - $pageConds[] = $queryExtra; - } - */ - - # For each article, get a list of revisions which fit the criteria - - # No recompression, use a condition on old_flags - # Don't compress object type entities, because that might produce data loss when - # overwriting bulk storage concat rows. Don't compress external references, because - # the script doesn't yet delete rows from external storage. - $conds = array( - 'old_flags NOT ' . $dbr->buildLike( $dbr->anyString(), 'object', $dbr->anyString() ) . ' AND old_flags NOT ' - . $dbr->buildLike( $dbr->anyString(), 'external', $dbr->anyString() ) ); - - if ( $beginDate ) { - if ( !preg_match( '/^\d{14}$/', $beginDate ) ) { - print "Invalid begin date \"$beginDate\"\n"; - return false; - } - $conds[] = "rev_timestamp>'" . $beginDate . "'"; - } - if ( $endDate ) { - if ( !preg_match( '/^\d{14}$/', $endDate ) ) { - print "Invalid end date \"$endDate\"\n"; - return false; - } - $conds[] = "rev_timestamp<'" . $endDate . "'"; - } - if ( $loadStyle == LS_CHUNKED ) { - $tables = array( 'revision', 'text' ); - $fields = array( 'rev_id', 'rev_text_id', 'old_flags', 'old_text' ); - $conds[] = 'rev_text_id=old_id'; - $revLoadOptions = 'FOR UPDATE'; - } else { - $tables = array( 'revision' ); - $fields = array( 'rev_id', 'rev_text_id' ); - $revLoadOptions = array(); - } - - # Don't work with current revisions - # Don't lock the page table for update either -- TS 2006-04-04 - #$tables[] = 'page'; - #$conds[] = 'page_id=rev_page AND rev_id != page_latest'; - - for ( $pageId = $startId; $pageId <= $maxPageId; $pageId++ ) { - wfWaitForSlaves( 5 ); - - # Wake up - $dbr->ping(); - - # Get the page row - $pageRes = $dbr->select( 'page', - array('page_id', 'page_namespace', 'page_title','page_latest'), - $pageConds + array('page_id' => $pageId), $fname ); - if ( $dbr->numRows( $pageRes ) == 0 ) { - continue; - } - $pageRow = $dbr->fetchObject( $pageRes ); - - # Display progress - $titleObj = Title::makeTitle( $pageRow->page_namespace, $pageRow->page_title ); - print "$pageId\t" . $titleObj->getPrefixedDBkey() . " "; - - # Load revisions - $revRes = $dbw->select( $tables, $fields, - array_merge( array( - 'rev_page' => $pageRow->page_id, - # Don't operate on the current revision - # Use < instead of <> in case the current revision has changed - # since the page select, which wasn't locking - 'rev_id < ' . $pageRow->page_latest - ), $conds ), - $fname, - $revLoadOptions - ); - $revs = array(); - foreach ( $revRes as $revRow ) { - $revs[] = $revRow; - } - - if ( count( $revs ) < 2) { - # No revisions matching, no further processing - print "\n"; - continue; - } - - # For each chunk - $i = 0; - while ( $i < count( $revs ) ) { - if ( $i < count( $revs ) - $maxChunkSize ) { - $thisChunkSize = $maxChunkSize; - } else { - $thisChunkSize = count( $revs ) - $i; - } - - $chunk = new ConcatenatedGzipHistoryBlob(); - $stubs = array(); - $dbw->begin(); - $usedChunk = false; - $primaryOldid = $revs[$i]->rev_text_id; - - # Get the text of each revision and add it to the object - for ( $j = 0; $j < $thisChunkSize && $chunk->isHappy(); $j++ ) { - $oldid = $revs[$i + $j]->rev_text_id; - - # Get text - if ( $loadStyle == LS_INDIVIDUAL ) { - $textRow = $dbw->selectRow( 'text', - array( 'old_flags', 'old_text' ), - array( 'old_id' => $oldid ), - $fname, - 'FOR UPDATE' - ); - $text = Revision::getRevisionText( $textRow ); - } else { - $text = Revision::getRevisionText( $revs[$i + $j] ); - } - - if ( $text === false ) { - print "\nError, unable to get text in old_id $oldid\n"; - #$dbw->delete( 'old', array( 'old_id' => $oldid ) ); - } - - if ( $extdb == "" && $j == 0 ) { - $chunk->setText( $text ); - print '.'; - } else { - # Don't make a stub if it's going to be longer than the article - # Stubs are typically about 100 bytes - if ( strlen( $text ) < 120 ) { - $stub = false; - print 'x'; - } else { - $stub = new HistoryBlobStub( $chunk->addItem( $text ) ); - $stub->setLocation( $primaryOldid ); - $stub->setReferrer( $oldid ); - print '.'; - $usedChunk = true; - } - $stubs[$j] = $stub; - } - } - $thisChunkSize = $j; - - # If we couldn't actually use any stubs because the pages were too small, do nothing - if ( $usedChunk ) { - if ( $extdb != "" ) { - # Move blob objects to External Storage - $stored = $storeObj->store( $extdb, serialize( $chunk )); - if ($stored === false) { - print "Unable to store object\n"; - return false; - } - # Store External Storage URLs instead of Stub placeholders - foreach ($stubs as $stub) { - if ($stub===false) - continue; - # $stored should provide base path to a BLOB - $url = $stored."/".$stub->getHash(); - $dbw->update( 'text', - array( /* SET */ - 'old_text' => $url, - 'old_flags' => 'external,utf-8', - ), array ( /* WHERE */ - 'old_id' => $stub->getReferrer(), - ) - ); - } - } else { - # Store the main object locally - $dbw->update( 'text', - array( /* SET */ - 'old_text' => serialize( $chunk ), - 'old_flags' => 'object,utf-8', - ), array( /* WHERE */ - 'old_id' => $primaryOldid - ) - ); - - # Store the stub objects - for ( $j = 1; $j < $thisChunkSize; $j++ ) { - # Skip if not compressing and don't overwrite the first revision - if ( $stubs[$j] !== false && $revs[$i + $j]->rev_text_id != $primaryOldid ) { - $dbw->update( 'text', - array( /* SET */ - 'old_text' => serialize($stubs[$j]), - 'old_flags' => 'object,utf-8', - ), array( /* WHERE */ - 'old_id' => $revs[$i + $j]->rev_text_id - ) - ); - } - } - } - } - # Done, next - print "/"; - $dbw->commit(); - $i += $thisChunkSize; - wfWaitForSlaves( 5 ); - } - print "\n"; - } - return true; -} diff --git a/maintenance/storage/compressOld.php b/maintenance/storage/compressOld.php index bc05b340..da82d93b 100644 --- a/maintenance/storage/compressOld.php +++ b/maintenance/storage/compressOld.php @@ -17,57 +17,379 @@ * -c <chunk-size> maximum number of revisions in a concat chunk * -b <begin-date> earliest date to check for uncompressed revisions * -e <end-date> latest revision date to compress - * -s <start-id> the old_id to start from + * -s <startid> the old_id to start from + * -n <endid> the old_id to stop at * --extdb <cluster> store specified revisions in an external cluster (untested) * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance ExternalStorage */ -$optionsWithArgs = array( 't', 'c', 's', 'f', 'h', 'extdb', 'endid', 'e' ); -require_once( dirname( __FILE__ ) . '/../commandLine.inc' ); -require_once( "compressOld.inc" ); +require_once( dirname( __FILE__ ) . '/../Maintenance.php' ); -if ( !function_exists( "gzdeflate" ) ) { - print "You must enable zlib support in PHP to compress old revisions!\n"; - print "Please see http://www.php.net/manual/en/ref.zlib.php\n\n"; - wfDie(); -} +class CompressOld extends Maintenance { + /** + * @todo document + */ + const LS_INDIVIDUAL = 0; + const LS_CHUNKED = 1; -$defaults = array( - 't' => 'concat', - 'c' => 20, - 's' => 0, - 'b' => '', - 'e' => '', - 'extdb' => '', - 'endid' => false, -); + public function __construct() { + parent::__construct(); + $this->mDescription = 'Compress the text of a wiki'; + $this->addOption( 'type', 'Set compression type to either: gzip|concat', false, true, 't' ); + $this->addOption( 'chunksize', 'Maximum number of revisions in a concat chunk', false, true, 'c' ); + $this->addOption( 'begin-date', 'Earliest date to check for uncompressed revisions', false, true, 'b' ); + $this->addOption( 'end-date', 'Latest revision date to compress', false, true, 'e' ); + $this->addOption( 'startid', 'The old_id to start from', false, true, 's' ); + $this->addOption( 'extdb', 'Store specified revisions in an external cluster (untested)', false, true ); + $this->addOption( 'endid', 'Stop at this old_id', false, true, 'n' ); + } -$options = $options + $defaults; + public function execute() { + global $wgDBname; + if ( !function_exists( "gzdeflate" ) ) { + $this->error( "You must enable zlib support in PHP to compress old revisions!\n" . + "Please see http://www.php.net/manual/en/ref.zlib.php\n", true ); + } -if ( $options['t'] != 'concat' && $options['t'] != 'gzip' ) { - print "Type \"{$options['t']}\" not supported\n"; -} + $type = $this->getOption( 'type', 'concat' ); + $chunkSize = $this->getOption( 'chunksize', 20 ); + $startId = $this->getOption( 'start-id', 0 ); + $beginDate = $this->getOption( 'begin-date', '' ); + $endDate = $this->getOption( 'end-date', '' ); + $extDB = $this->getOption( 'extdb', '' ); + $endId = $this->getOption( 'endid', false ); -if ( $options['extdb'] != '' ) { - print "Compressing database $wgDBname to external cluster {$options['extdb']}\n" . str_repeat( '-', 76 ) . "\n\n"; -} else { - print "Compressing database $wgDBname\n" . str_repeat( '-', 76 ) . "\n\n"; -} + if ( $type != 'concat' && $type != 'gzip' ) { + $this->error( "Type \"{$type}\" not supported" ); + } -$success = true; -if ( $options['t'] == 'concat' ) { - $success = compressWithConcat( $options['s'], $options['c'], $options['b'], - $options['e'], $options['extdb'], $options['endid'] ); -} else { - compressOldPages( $options['s'], $options['extdb'] ); -} + if ( $extDB != '' ) { + $this->output( "Compressing database {$wgDBname} to external cluster {$extDB}\n" + . str_repeat( '-', 76 ) . "\n\n" ); + } else { + $this->output( "Compressing database {$wgDBname}\n" + . str_repeat( '-', 76 ) . "\n\n" ); + } -if ( $success ) { - print "Done.\n"; -} + $success = true; + if ( $type == 'concat' ) { + $success = $this->compressWithConcat( $startId, $chunkSize, $beginDate, + $endDate, $extDB, $endId ); + } else { + $this->compressOldPages( $startId, $extDB ); + } + + if ( $success ) { + $this->output( "Done.\n" ); + } + } + + /** @todo document */ + private function compressOldPages( $start = 0, $extdb = '' ) { + $chunksize = 50; + $this->output( "Starting from old_id $start...\n" ); + $dbw = wfGetDB( DB_MASTER ); + do { + $res = $dbw->select( 'text', array( 'old_id','old_flags','old_text' ), + "old_id>=$start", __METHOD__, array( 'ORDER BY' => 'old_id', 'LIMIT' => $chunksize, 'FOR UPDATE' ) ); + if( $dbw->numRows( $res ) == 0 ) { + break; + } + $last = $start; + foreach ( $res as $row ) { + # print " {$row->old_id} - {$row->old_namespace}:{$row->old_title}\n"; + $this->compressPage( $row, $extdb ); + $last = $row->old_id; + } + $start = $last + 1; # Deletion may leave long empty stretches + $this->output( "$start...\n" ); + } while( true ); + } + + /** @todo document */ + private function compressPage( $row, $extdb ) { + if ( false !== strpos( $row->old_flags, 'gzip' ) || false !== strpos( $row->old_flags, 'object' ) ) { + #print "Already compressed row {$row->old_id}\n"; + return false; + } + $dbw = wfGetDB( DB_MASTER ); + $flags = $row->old_flags ? "{$row->old_flags},gzip" : "gzip"; + $compress = gzdeflate( $row->old_text ); + + # Store in external storage if required + if ( $extdb !== '' ) { + $storeObj = new ExternalStoreDB; + $compress = $storeObj->store( $extdb, $compress ); + if ( $compress === false ) { + $this->error( "Unable to store object" ); + return false; + } + } + + # Update text row + $dbw->update( 'text', + array( /* SET */ + 'old_flags' => $flags, + 'old_text' => $compress + ), array( /* WHERE */ + 'old_id' => $row->old_id + ), __METHOD__, + array( 'LIMIT' => 1 ) + ); + return true; + } + + /** @todo document */ + private function compressWithConcat( $startId, $maxChunkSize, $beginDate, + $endDate, $extdb = "", $maxPageId = false ) + { + $loadStyle = self::LS_CHUNKED; + + $dbr = wfGetDB( DB_SLAVE ); + $dbw = wfGetDB( DB_MASTER ); + + # Set up external storage + if ( $extdb != '' ) { + $storeObj = new ExternalStoreDB; + } + + # Get all articles by page_id + if ( !$maxPageId ) { + $maxPageId = $dbr->selectField( 'page', 'max(page_id)', '', __METHOD__ ); + } + $this->output( "Starting from $startId of $maxPageId\n" ); + $pageConds = array(); + + /* + if ( $exclude_ns0 ) { + print "Excluding main namespace\n"; + $pageConds[] = 'page_namespace<>0'; + } + if ( $queryExtra ) { + $pageConds[] = $queryExtra; + } + */ -exit( 0 ); + # For each article, get a list of revisions which fit the criteria + # No recompression, use a condition on old_flags + # Don't compress object type entities, because that might produce data loss when + # overwriting bulk storage concat rows. Don't compress external references, because + # the script doesn't yet delete rows from external storage. + $conds = array( + 'old_flags NOT ' . $dbr->buildLike( $dbr->anyString(), 'object', $dbr->anyString() ) . ' AND old_flags NOT ' + . $dbr->buildLike( $dbr->anyString(), 'external', $dbr->anyString() ) ); + + if ( $beginDate ) { + if ( !preg_match( '/^\d{14}$/', $beginDate ) ) { + $this->error( "Invalid begin date \"$beginDate\"\n" ); + return false; + } + $conds[] = "rev_timestamp>'" . $beginDate . "'"; + } + if ( $endDate ) { + if ( !preg_match( '/^\d{14}$/', $endDate ) ) { + $this->error( "Invalid end date \"$endDate\"\n" ); + return false; + } + $conds[] = "rev_timestamp<'" . $endDate . "'"; + } + if ( $loadStyle == self::LS_CHUNKED ) { + $tables = array( 'revision', 'text' ); + $fields = array( 'rev_id', 'rev_text_id', 'old_flags', 'old_text' ); + $conds[] = 'rev_text_id=old_id'; + $revLoadOptions = 'FOR UPDATE'; + } else { + $tables = array( 'revision' ); + $fields = array( 'rev_id', 'rev_text_id' ); + $revLoadOptions = array(); + } + + # Don't work with current revisions + # Don't lock the page table for update either -- TS 2006-04-04 + #$tables[] = 'page'; + #$conds[] = 'page_id=rev_page AND rev_id != page_latest'; + + for ( $pageId = $startId; $pageId <= $maxPageId; $pageId++ ) { + wfWaitForSlaves(); + + # Wake up + $dbr->ping(); + + # Get the page row + $pageRes = $dbr->select( 'page', + array('page_id', 'page_namespace', 'page_title','page_latest'), + $pageConds + array('page_id' => $pageId), __METHOD__ ); + if ( $dbr->numRows( $pageRes ) == 0 ) { + continue; + } + $pageRow = $dbr->fetchObject( $pageRes ); + + # Display progress + $titleObj = Title::makeTitle( $pageRow->page_namespace, $pageRow->page_title ); + $this->output( "$pageId\t" . $titleObj->getPrefixedDBkey() . " " ); + + # Load revisions + $revRes = $dbw->select( $tables, $fields, + array_merge( array( + 'rev_page' => $pageRow->page_id, + # Don't operate on the current revision + # Use < instead of <> in case the current revision has changed + # since the page select, which wasn't locking + 'rev_id < ' . $pageRow->page_latest + ), $conds ), + __METHOD__, + $revLoadOptions + ); + $revs = array(); + foreach ( $revRes as $revRow ) { + $revs[] = $revRow; + } + + if ( count( $revs ) < 2) { + # No revisions matching, no further processing + $this->output( "\n" ); + continue; + } + + # For each chunk + $i = 0; + while ( $i < count( $revs ) ) { + if ( $i < count( $revs ) - $maxChunkSize ) { + $thisChunkSize = $maxChunkSize; + } else { + $thisChunkSize = count( $revs ) - $i; + } + + $chunk = new ConcatenatedGzipHistoryBlob(); + $stubs = array(); + $dbw->begin(); + $usedChunk = false; + $primaryOldid = $revs[$i]->rev_text_id; + + # Get the text of each revision and add it to the object + for ( $j = 0; $j < $thisChunkSize && $chunk->isHappy(); $j++ ) { + $oldid = $revs[$i + $j]->rev_text_id; + + # Get text + if ( $loadStyle == self::LS_INDIVIDUAL ) { + $textRow = $dbw->selectRow( 'text', + array( 'old_flags', 'old_text' ), + array( 'old_id' => $oldid ), + __METHOD__, + 'FOR UPDATE' + ); + $text = Revision::getRevisionText( $textRow ); + } else { + $text = Revision::getRevisionText( $revs[$i + $j] ); + } + + if ( $text === false ) { + $this->error( "\nError, unable to get text in old_id $oldid" ); + #$dbw->delete( 'old', array( 'old_id' => $oldid ) ); + } + + if ( $extdb == "" && $j == 0 ) { + $chunk->setText( $text ); + $this->output( '.' ); + } else { + # Don't make a stub if it's going to be longer than the article + # Stubs are typically about 100 bytes + if ( strlen( $text ) < 120 ) { + $stub = false; + $this->output( 'x' ); + } else { + $stub = new HistoryBlobStub( $chunk->addItem( $text ) ); + $stub->setLocation( $primaryOldid ); + $stub->setReferrer( $oldid ); + $this->output( '.' ); + $usedChunk = true; + } + $stubs[$j] = $stub; + } + } + $thisChunkSize = $j; + + # If we couldn't actually use any stubs because the pages were too small, do nothing + if ( $usedChunk ) { + if ( $extdb != "" ) { + # Move blob objects to External Storage + $stored = $storeObj->store( $extdb, serialize( $chunk )); + if ($stored === false) { + $this->error( "Unable to store object" ); + return false; + } + # Store External Storage URLs instead of Stub placeholders + foreach ($stubs as $stub) { + if ($stub===false) + continue; + # $stored should provide base path to a BLOB + $url = $stored."/".$stub->getHash(); + $dbw->update( 'text', + array( /* SET */ + 'old_text' => $url, + 'old_flags' => 'external,utf-8', + ), array ( /* WHERE */ + 'old_id' => $stub->getReferrer(), + ) + ); + } + } else { + # Store the main object locally + $dbw->update( 'text', + array( /* SET */ + 'old_text' => serialize( $chunk ), + 'old_flags' => 'object,utf-8', + ), array( /* WHERE */ + 'old_id' => $primaryOldid + ) + ); + + # Store the stub objects + for ( $j = 1; $j < $thisChunkSize; $j++ ) { + # Skip if not compressing and don't overwrite the first revision + if ( $stubs[$j] !== false && $revs[$i + $j]->rev_text_id != $primaryOldid ) { + $dbw->update( 'text', + array( /* SET */ + 'old_text' => serialize($stubs[$j]), + 'old_flags' => 'object,utf-8', + ), array( /* WHERE */ + 'old_id' => $revs[$i + $j]->rev_text_id + ) + ); + } + } + } + } + # Done, next + $this->output( "/" ); + $dbw->commit(); + $i += $thisChunkSize; + wfWaitForSlaves(); + } + $this->output( "\n" ); + } + return true; + } + +} +$maintClass = 'CompressOld'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/storage/fixBug20757.php b/maintenance/storage/fixBug20757.php index 4aac1202..b6def2da 100644 --- a/maintenance/storage/fixBug20757.php +++ b/maintenance/storage/fixBug20757.php @@ -1,4 +1,25 @@ <?php +/** + * Script to fix bug 20757. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance ExternalStorage + */ require_once( dirname( __FILE__ ) . '/../Maintenance.php' ); @@ -239,7 +260,7 @@ class FixBug20757 extends Maintenance { static $iteration = 0; ++$iteration; if ( ++$iteration > 50 == 0 ) { - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); $iteration = 0; } } diff --git a/maintenance/storage/moveToExternal.php b/maintenance/storage/moveToExternal.php index 928cbf97..64f3adaa 100644 --- a/maintenance/storage/moveToExternal.php +++ b/maintenance/storage/moveToExternal.php @@ -2,6 +2,21 @@ /** * Move revision's text to external storage * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance ExternalStorage */ @@ -51,7 +66,7 @@ function moveToExternal( $cluster, $maxID, $minID = 1 ) { if ( !( $block % REPORTING_INTERVAL ) ) { print "oldid=$blockStart, moved=$numMoved\n"; - wfWaitForSlaves( 2 ); + wfWaitForSlaves(); } $res = $dbr->select( 'text', array( 'old_id', 'old_flags', 'old_text' ), diff --git a/maintenance/storage/recompressTracked.php b/maintenance/storage/recompressTracked.php index 8974a74d..09ab3e57 100644 --- a/maintenance/storage/recompressTracked.php +++ b/maintenance/storage/recompressTracked.php @@ -1,4 +1,26 @@ <?php +/** + * Moves blobs indexed by trackBlobs.php to a specified list of destination + * clusters, and recompresses them in the process. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance ExternalStorage + */ $optionsWithArgs = RecompressTracked::getOptionsWithArgs(); require( dirname( __FILE__ ) . '/../commandLine.inc' ); diff --git a/maintenance/storage/resolveStubs.php b/maintenance/storage/resolveStubs.php index 2269e37f..08d0ee04 100644 --- a/maintenance/storage/resolveStubs.php +++ b/maintenance/storage/resolveStubs.php @@ -1,5 +1,23 @@ <?php /** + * Script to convert history stubs that point to an external row to direct + * external pointers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance ExternalStorage */ @@ -27,7 +45,7 @@ function resolveStubs() { $numBlocks = intval( $maxID / $blockSize ) + 1; for ( $b = 0; $b < $numBlocks; $b++ ) { - wfWaitForSlaves( 2 ); + wfWaitForSlaves(); printf( "%5.2f%%\n", $b / $numBlocks * 100 ); $start = intval( $maxID / $numBlocks ) * $b + 1; diff --git a/maintenance/storage/storageTypeStats.php b/maintenance/storage/storageTypeStats.php index be86c531..817659fc 100644 --- a/maintenance/storage/storageTypeStats.php +++ b/maintenance/storage/storageTypeStats.php @@ -1,4 +1,23 @@ <?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance ExternalStorage + */ require_once( dirname( __FILE__ ) . '/../Maintenance.php' ); diff --git a/maintenance/storage/testCompression.php b/maintenance/storage/testCompression.php index e2718325..9ae26335 100644 --- a/maintenance/storage/testCompression.php +++ b/maintenance/storage/testCompression.php @@ -1,4 +1,24 @@ <?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance + * @see wfWaitForSlaves() + */ $optionsWithArgs = array( 'start', 'limit', 'type' ); require( dirname( __FILE__ ) . '/../commandLine.inc' ); diff --git a/maintenance/storage/trackBlobs.php b/maintenance/storage/trackBlobs.php index 15aeec3b..b5f80047 100644 --- a/maintenance/storage/trackBlobs.php +++ b/maintenance/storage/trackBlobs.php @@ -1,4 +1,26 @@ <?php +/** + * Adds blobs from a given external storage cluster to the blob_tracking table. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Maintenance + * @see wfWaitForSlaves() + */ require( dirname( __FILE__ ) . '/../commandLine.inc' ); @@ -184,7 +206,7 @@ class TrackBlobs { if ( $batchesDone >= $this->reportingInterval ) { $batchesDone = 0; echo "$startId / $endId\n"; - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } } echo "Found $rowsInserted revisions\n"; @@ -268,7 +290,7 @@ class TrackBlobs { if ( $batchesDone >= $this->reportingInterval ) { $batchesDone = 0; echo "$startId / $endId\n"; - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } } echo "Found $rowsInserted orphan text rows\n"; diff --git a/maintenance/tables.sql b/maintenance/tables.sql index 1dcf98b7..f879f02f 100644 --- a/maintenance/tables.sql +++ b/maintenance/tables.sql @@ -135,6 +135,7 @@ CREATE TABLE /*_*/user ( CREATE UNIQUE INDEX /*i*/user_name ON /*_*/user (user_name); CREATE INDEX /*i*/user_email_token ON /*_*/user (user_email_token); +CREATE INDEX /*i*/user_email ON /*_*/user (user_email(50)); -- @@ -163,6 +164,15 @@ CREATE TABLE /*_*/user_groups ( CREATE UNIQUE INDEX /*i*/ug_user_group ON /*_*/user_groups (ug_user,ug_group); CREATE INDEX /*i*/ug_group ON /*_*/user_groups (ug_group); +-- Stores the groups the user has once belonged to. +-- The user may still belong these groups. Check user_groups. +CREATE TABLE /*_*/user_former_groups ( + -- Key to user_id + ufg_user int unsigned NOT NULL default 0, + ufg_group varbinary(16) NOT NULL default '' +) /*$wgDBTableOptions*/; + +CREATE UNIQUE INDEX /*i*/ufg_user_group ON /*_*/user_former_groups (ufg_user,ufg_group); -- -- Stores notifications of user talk page changes, for the display @@ -176,7 +186,7 @@ CREATE TABLE /*_*/user_newtalk ( user_ip varbinary(40) NOT NULL default '', -- The highest timestamp of revisions of the talk page viewed -- by this user - user_last_timestamp binary(14) NOT NULL default '' + user_last_timestamp varbinary(14) NULL default NULL ) /*$wgDBTableOptions*/; -- Indexes renamed for SQLite in 1.14 @@ -198,7 +208,7 @@ CREATE TABLE /*_*/user_properties ( up_user int NOT NULL, -- Name of the option being saved. This is indexed for bulk lookup. - up_property varbinary(32) NOT NULL, + up_property varbinary(255) NOT NULL, -- Property value as a string. up_value blob @@ -502,6 +512,8 @@ CREATE TABLE /*_*/categorylinks ( -- concatenated with a line break followed by the page title before the sortkey -- conversion algorithm is run. We store this so that we can update -- collations without reparsing all pages. + -- Note: If you change the length of this field, you also need to change + -- code in LinksUpdate.php. See bug 25254. cl_sortkey_prefix varchar(255) binary NOT NULL default '', -- This isn't really used at present. Provided for an optional @@ -927,6 +939,57 @@ CREATE INDEX /*i*/fa_user_timestamp ON /*_*/filearchive (fa_user_text,fa_timesta -- +-- Store information about newly uploaded files before they're +-- moved into the actual filestore +-- +CREATE TABLE /*_*/uploadstash ( + us_id int unsigned NOT NULL PRIMARY KEY auto_increment, + + -- the user who uploaded the file. + us_user int unsigned NOT NULL, + + -- file key. this is how applications actually search for the file. + -- this might go away, or become the primary key. + us_key varchar(255) NOT NULL, + + -- the original path + us_orig_path varchar(255) NOT NULL, + + -- the temporary path at which the file is actually stored + us_path varchar(255) NOT NULL, + + -- which type of upload the file came from (sometimes) + us_source_type varchar(50), + + -- the date/time on which the file was added + us_timestamp varbinary(14) not null, + + us_status varchar(50) not null, + + -- file properties from File::getPropsFromPath. these may prove unnecessary. + -- + us_size int unsigned NOT NULL, + -- this hash comes from File::sha1Base36(), and is 31 characters + us_sha1 varchar(31) NOT NULL, + us_mime varchar(255), + -- Media type as defined by the MEDIATYPE_xxx constants, should duplicate definition in the image table + us_media_type ENUM("UNKNOWN", "BITMAP", "DRAWING", "AUDIO", "VIDEO", "MULTIMEDIA", "OFFICE", "TEXT", "EXECUTABLE", "ARCHIVE") default NULL, + -- image-specific properties + us_image_width int unsigned, + us_image_height int unsigned, + us_image_bits smallint unsigned + +) /*$wgDBTableOptions*/; + +-- sometimes there's a delete for all of a user's stuff. +CREATE INDEX /*i*/us_user ON /*_*/uploadstash (us_user); +-- pick out files by key, enforce key uniqueness +CREATE UNIQUE INDEX /*i*/us_key ON /*_*/uploadstash (us_key); +-- the abandoned upload cleanup script needs this +CREATE INDEX /*i*/us_timestamp ON /*_*/uploadstash (us_timestamp); + + +-- -- Primarily a summary table for Special:Recentchanges, -- this table contains some additional info on edits from -- the last few days, see Article::editUpdates() @@ -1029,31 +1092,6 @@ CREATE INDEX /*i*/namespace_title ON /*_*/watchlist (wl_namespace, wl_title); -- --- Used by the math module to keep track --- of previously-rendered items. --- -CREATE TABLE /*_*/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 NOT NULL, - - -- HTML output from texvc, if any - math_html text, - - -- MathML output from texvc, if any - math_mathml text -) /*$wgDBTableOptions*/; - -CREATE UNIQUE INDEX /*i*/math_inputhash ON /*_*/math (math_inputhash); - - --- -- 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. diff --git a/maintenance/tests/.svnignore b/maintenance/tests/.svnignore deleted file mode 100644 index 20cb61e9..00000000 --- a/maintenance/tests/.svnignore +++ /dev/null @@ -1,6 +0,0 @@ -LocalTestSettings.php -*~ -bin -.classpath -.project -project.index diff --git a/maintenance/tests/RunSeleniumTests.php b/maintenance/tests/RunSeleniumTests.php deleted file mode 100644 index 2574f4b2..00000000 --- a/maintenance/tests/RunSeleniumTests.php +++ /dev/null @@ -1,220 +0,0 @@ -#!/usr/bin/php -<?php -/** - * @file - * @ingroup Maintenance - * @copyright Copyright © Wikimedia Deuschland, 2009 - * @author Hallo Welt! Medienwerkstatt GmbH - * @author Markus Glaser, Dan Nessett, Priyanka Dhanda - * initial idea by Daniel Kinzler - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - */ - -define( 'SELENIUMTEST', true ); - -require_once( dirname( dirname( __FILE__ ) )."/Maintenance.php" ); -require_once( 'PHPUnit/Framework.php' ); -require_once( 'PHPUnit/Extensions/SeleniumTestCase.php' ); -include_once( 'PHPUnit/Util/Log/JUnit.php' ); -require_once( dirname( __FILE__ ) . "/selenium/SeleniumServerManager.php" ); - -class SeleniumTester extends Maintenance { - protected $selenium; - protected $serverManager; - protected $seleniumServerExecPath; - - public function __construct() { - parent::__construct(); - $this->mDescription = "Selenium Test Runner. For documentation, visit http://www.mediawiki.org/wiki/SeleniumFramework"; - $this->addOption( 'port', 'Port used by selenium server. Default: 4444', false, true ); - $this->addOption( 'host', 'Host selenium server. Default: $wgServer . $wgScriptPath', false, true ); - $this->addOption( 'testBrowser', 'The browser used during testing. Default: firefox', false, true ); - $this->addOption( 'wikiUrl', 'The Mediawiki installation to point to. Default: http://localhost', false, true ); - $this->addOption( 'username', 'The login username for sunning tests. Default: empty', false, true ); - $this->addOption( 'userPassword', 'The login password for running tests. Default: empty', false, true ); - $this->addOption( 'seleniumConfig', 'Location of the selenium config file. Default: empty', false, true ); - $this->addOption( 'list-browsers', 'List the available browsers.' ); - $this->addOption( 'verbose', 'Be noisier.' ); - $this->addOption( 'startserver', 'Start Selenium Server (on localhost) before the run.' ); - $this->addOption( 'stopserver', 'Stop Selenium Server (on localhost) after the run.' ); - $this->addOption( 'jUnitLogFile', 'Log results in a specified JUnit log file. Default: empty', false, true ); - $this->addOption( 'runAgainstGrid', 'The test will be run against a Selenium Grid. Default: false.', false, true ); - $this->deleteOption( 'dbpass' ); - $this->deleteOption( 'dbuser' ); - $this->deleteOption( 'globals' ); - $this->deleteOption( 'wiki' ); - } - - public function listBrowsers() { - $desc = "Available browsers:\n"; - - foreach ($this->selenium->getAvailableBrowsers() as $k => $v) { - $desc .= " $k => $v\n"; - } - - echo $desc; - } - - protected function startServer() { - if ( $this->seleniumServerExecPath == '' ) { - die ( "The selenium server exec path is not set in " . - "selenium_settings.ini. Cannot start server \n" . - "as requested - terminating RunSeleniumTests\n" ); - } - $this->serverManager = new SeleniumServerManager( 'true', - $this->selenium->getPort(), - $this->seleniumServerExecPath ); - switch ( $this->serverManager->start() ) { - case 'started': - break; - case 'failed': - die ( "Unable to start the Selenium Server - " . - "terminating RunSeleniumTests\n" ); - case 'running': - echo ( "Warning: The Selenium Server is " . - "already running\n" ); - break; - } - - return; - } - - protected function stopServer() { - if ( !isset ( $this->serverManager ) ) { - echo ( "Warning: Request to stop Selenium Server, but it was " . - "not stared by RunSeleniumTests\n" . - "RunSeleniumTests cannot stop a Selenium Server it " . - "did not start\n" ); - } else { - switch ( $this->serverManager->stop() ) { - case 'stopped': - break; - case 'failed': - echo ( "unable to stop the Selenium Server\n" ); - } - } - return; - } - - protected function runTests( $seleniumTestSuites = array() ) { - $result = new PHPUnit_Framework_TestResult; - $result->addListener( new SeleniumTestListener( $this->selenium->getLogger() ) ); - if ( $this->selenium->getJUnitLogFile() ) { - $jUnitListener = new PHPUnit_Util_Log_JUnit( $this->selenium->getJUnitLogFile(), true ); - $result->addListener( $jUnitListener ); - } - - foreach ( $seleniumTestSuites as $testSuiteName => $testSuiteFile ) { - require( $testSuiteFile ); - $suite = new $testSuiteName(); - $suite->setName( $testSuiteName ); - $suite->addTests(); - - try { - $suite->run( $result ); - } catch ( Testing_Selenium_Exception $e ) { - $suite->tearDown(); - throw new MWException( $e->getMessage() ); - } - } - - if ( $this->selenium->getJUnitLogFile() ) { - $jUnitListener->flush(); - } - } - - public function execute() { - global $wgServer, $wgScriptPath, $wgHooks; - - $seleniumSettings = array(); - $seleniumBrowsers = array(); - $seleniumTestSuites = array(); - - $configFile = $this->getOption( 'seleniumConfig', '' ); - if ( strlen( $configFile ) > 0 ) { - $this->output("Using Selenium Configuration file: " . $configFile . "\n"); - SeleniumConfig::getSeleniumSettings( $seleniumSettings, - $seleniumBrowsers, - $seleniumTestSuites, - $configFile ); - } else if ( !isset( $wgHooks['SeleniumSettings'] ) ) { - $this->output("No command line configuration file or configuration hook found.\n"); - SeleniumConfig::getSeleniumSettings( $seleniumSettings, - $seleniumBrowsers, - $seleniumTestSuites - ); - } else { - $this->output("Using 'SeleniumSettings' hook for configuration.\n"); - wfRunHooks('SeleniumSettings', array( $seleniumSettings, - $seleniumBrowsers, - $seleniumTestSuites ) ); - } - - // State for starting/stopping the Selenium server has nothing to do with the Selenium - // class. Keep this state local to SeleniumTester class. Using getOption() is clumsy, but - // the Maintenance class does not have a setOption() - if ( isset( $seleniumSettings['startserver'] ) ) $this->getOption( 'startserver', true ); - if ( isset( $seleniumSettings['stopserver'] ) ) $this->getOption( 'stopserver', true ); - if ( !isset( $seleniumSettings['seleniumserverexecpath'] ) ) $seleniumSettings['seleniumserverexecpath'] = ''; - $this->seleniumServerExecPath = $seleniumSettings['seleniumserverexecpath']; - - //set reasonable defaults if we did not find the settings - if ( !isset( $seleniumBrowsers ) ) $seleniumBrowsers = array ('firefox' => '*firefox'); - if ( !isset( $seleniumSettings['host'] ) ) $seleniumSettings['host'] = $wgServer . $wgScriptPath; - if ( !isset( $seleniumSettings['port'] ) ) $seleniumSettings['port'] = '4444'; - if ( !isset( $seleniumSettings['wikiUrl'] ) ) $seleniumSettings['wikiUrl'] = 'http://localhost'; - if ( !isset( $seleniumSettings['username'] ) ) $seleniumSettings['username'] = ''; - if ( !isset( $seleniumSettings['userPassword'] ) ) $seleniumSettings['userPassword'] = ''; - if ( !isset( $seleniumSettings['testBrowser'] ) ) $seleniumSettings['testBrowser'] = 'firefox'; - if ( !isset( $seleniumSettings['jUnitLogFile'] ) ) $seleniumSettings['jUnitLogFile'] = false; - if ( !isset( $seleniumSettings['runAgainstGrid'] ) ) $seleniumSettings['runAgainstGrid'] = false; - - // Setup Selenium class - $this->selenium = new Selenium( ); - $this->selenium->setAvailableBrowsers( $seleniumBrowsers ); - $this->selenium->setRunAgainstGrid( $this->getOption( 'runAgainstGrid', $seleniumSettings['runAgainstGrid'] ) ); - $this->selenium->setUrl( $this->getOption( 'wikiUrl', $seleniumSettings['wikiUrl'] ) ); - $this->selenium->setBrowser( $this->getOption( 'testBrowser', $seleniumSettings['testBrowser'] ) ); - $this->selenium->setPort( $this->getOption( 'port', $seleniumSettings['port'] ) ); - $this->selenium->setHost( $this->getOption( 'host', $seleniumSettings['host'] ) ); - $this->selenium->setUser( $this->getOption( 'username', $seleniumSettings['username'] ) ); - $this->selenium->setPass( $this->getOption( 'userPassword', $seleniumSettings['userPassword'] ) ); - $this->selenium->setVerbose( $this->hasOption( 'verbose' ) ); - $this->selenium->setJUnitLogFile( $this->getOption( 'jUnitLogFile', $seleniumSettings['jUnitLogFile'] ) ); - - if( $this->hasOption( 'list-browsers' ) ) { - $this->listBrowsers(); - exit(0); - } - if ( $this->hasOption( 'startserver' ) ) { - $this->startServer(); - } - - $logger = new SeleniumTestConsoleLogger; - $this->selenium->setLogger( $logger ); - - $this->runTests( $seleniumTestSuites ); - - if ( $this->hasOption( 'stopserver' ) ) { - $this->stopServer(); - } - } -} - -$maintClass = "SeleniumTester"; - -require_once( DO_MAINTENANCE ); diff --git a/maintenance/tests/parser/ExtraParserTests.txt b/maintenance/tests/parser/ExtraParserTests.txt Binary files differdeleted file mode 100644 index 66b8032d..00000000 --- a/maintenance/tests/parser/ExtraParserTests.txt +++ /dev/null diff --git a/maintenance/tests/parser/parserTest.inc b/maintenance/tests/parser/parserTest.inc deleted file mode 100644 index c553550c..00000000 --- a/maintenance/tests/parser/parserTest.inc +++ /dev/null @@ -1,1305 +0,0 @@ -<?php -# Copyright (C) 2004, 2010 Brion Vibber <brion@pobox.com> -# http://www.mediawiki.org/ -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# http://www.gnu.org/copyleft/gpl.html - -/** - * @todo Make this more independent of the configuration (and if possible the database) - * @todo document - * @file - * @ingroup Maintenance - */ - -/** - * @ingroup Maintenance - */ -class ParserTest { - /** - * boolean $color whereas output should be colorized - */ - private $color; - - /** - * boolean $showOutput Show test output - */ - private $showOutput; - - /** - * boolean $useTemporaryTables Use temporary tables for the temporary database - */ - private $useTemporaryTables = true; - - /** - * boolean $databaseSetupDone True if the database has been set up - */ - private $databaseSetupDone = false; - - /** - * string $oldTablePrefix Original table prefix - */ - private $oldTablePrefix; - - private $maxFuzzTestLength = 300; - private $fuzzSeed = 0; - private $memoryLimit = 50; - private $uploadDir = null; - - public $regex = ""; - private $savedGlobals = array(); - /** - * Sets terminal colorization and diff/quick modes depending on OS and - * command-line options (--color and --quick). - */ - public function ParserTest( $options = array() ) { - # Only colorize output if stdout is a terminal. - $this->color = !wfIsWindows() && posix_isatty( 1 ); - - if ( isset( $options['color'] ) ) { - switch( $options['color'] ) { - case 'no': - $this->color = false; - break; - case 'yes': - default: - $this->color = true; - break; - } - } - - $this->term = $this->color - ? new AnsiTermColorer() - : new DummyTermColorer(); - - $this->showDiffs = !isset( $options['quick'] ); - $this->showProgress = !isset( $options['quiet'] ); - $this->showFailure = !( - isset( $options['quiet'] ) - && ( isset( $options['record'] ) - || isset( $options['compare'] ) ) ); // redundant output - - $this->showOutput = isset( $options['show-output'] ); - - - if ( isset( $options['regex'] ) ) { - if ( isset( $options['record'] ) ) { - echo "Warning: --record cannot be used with --regex, disabling --record\n"; - unset( $options['record'] ); - } - $this->regex = $options['regex']; - } else { - # Matches anything - $this->regex = ''; - } - - $this->setupRecorder( $options ); - $this->keepUploads = isset( $options['keep-uploads'] ); - - if ( isset( $options['seed'] ) ) { - $this->fuzzSeed = intval( $options['seed'] ) - 1; - } - - $this->runDisabled = isset( $options['run-disabled'] ); - - $this->hooks = array(); - $this->functionHooks = array(); - self::setUp(); - } - - static function setUp() { - global $wgParser, $wgParserConf, $IP, $messageMemc, $wgMemc, $wgDeferredUpdateList, - $wgUser, $wgLang, $wgOut, $wgRequest, $wgStyleDirectory, $wgEnableParserCache, - $wgMessageCache, $wgUseDatabaseMessages, $wgMsgCacheExpiry, $parserMemc, - $wgNamespaceAliases, $wgNamespaceProtection, $wgLocalFileRepo, - $wgThumbnailScriptPath, $wgScriptPath, - $wgArticlePath, $wgStyleSheetPath, $wgScript, $wgStylePath; - - $wgScript = '/index.php'; - $wgScriptPath = '/'; - $wgArticlePath = '/wiki/$1'; - $wgStyleSheetPath = '/skins'; - $wgStylePath = '/skins'; - $wgThumbnailScriptPath = false; - $wgLocalFileRepo = array( - 'class' => 'LocalRepo', - 'name' => 'local', - 'directory' => wfTempDir() . '/test-repo', - 'url' => 'http://example.com/images', - 'deletedDir' => wfTempDir() . '/test-repo/delete', - 'hashLevels' => 2, - 'transformVia404' => false, - ); - $wgNamespaceProtection[NS_MEDIAWIKI] = 'editinterface'; - $wgNamespaceAliases['Image'] = NS_FILE; - $wgNamespaceAliases['Image_talk'] = NS_FILE_TALK; - - - $wgEnableParserCache = false; - $wgDeferredUpdateList = array(); - $wgMemc = &wfGetMainCache(); - $messageMemc = &wfGetMessageCacheStorage(); - $parserMemc = &wfGetParserCacheStorage(); - - // $wgContLang = new StubContLang; - $wgUser = new User; - $wgLang = new StubUserLang; - $wgOut = new StubObject( 'wgOut', 'OutputPage' ); - $wgParser = new StubObject( 'wgParser', $wgParserConf['class'], array( $wgParserConf ) ); - $wgRequest = new WebRequest; - - $wgMessageCache = new StubObject( 'wgMessageCache', 'MessageCache', - array( $messageMemc, $wgUseDatabaseMessages, - $wgMsgCacheExpiry ) ); - if ( $wgStyleDirectory === false ) { - $wgStyleDirectory = "$IP/skins"; - } - - } - - public function setupRecorder ( $options ) { - if ( isset( $options['record'] ) ) { - $this->recorder = new DbTestRecorder( $this ); - $this->recorder->version = isset( $options['setversion'] ) ? - $options['setversion'] : SpecialVersion::getVersion(); - } elseif ( isset( $options['compare'] ) ) { - $this->recorder = new DbTestPreviewer( $this ); - } elseif ( isset( $options['upload'] ) ) { - $this->recorder = new RemoteTestRecorder( $this ); - } else { - $this->recorder = new TestRecorder( $this ); - } - } - - /** - * Remove last character if it is a newline - * @group utility - */ - static public function chomp( $s ) { - if ( substr( $s, -1 ) === "\n" ) { - return substr( $s, 0, -1 ); - } - else { - return $s; - } - } - - /** - * Run a fuzz test series - * Draw input from a set of test files - */ - function fuzzTest( $filenames ) { - $GLOBALS['wgContLang'] = Language::factory( 'en' ); - $dict = $this->getFuzzInput( $filenames ); - $dictSize = strlen( $dict ); - $logMaxLength = log( $this->maxFuzzTestLength ); - $this->setupDatabase(); - ini_set( 'memory_limit', $this->memoryLimit * 1048576 ); - - $numTotal = 0; - $numSuccess = 0; - $user = new User; - $opts = ParserOptions::newFromUser( $user ); - $title = Title::makeTitle( NS_MAIN, 'Parser_test' ); - - while ( true ) { - // Generate test input - mt_srand( ++$this->fuzzSeed ); - $totalLength = mt_rand( 1, $this->maxFuzzTestLength ); - $input = ''; - - while ( strlen( $input ) < $totalLength ) { - $logHairLength = mt_rand( 0, 1000000 ) / 1000000 * $logMaxLength; - $hairLength = min( intval( exp( $logHairLength ) ), $dictSize ); - $offset = mt_rand( 0, $dictSize - $hairLength ); - $input .= substr( $dict, $offset, $hairLength ); - } - - $this->setupGlobals(); - $parser = $this->getParser(); - - // Run the test - try { - $parser->parse( $input, $title, $opts ); - $fail = false; - } catch ( Exception $exception ) { - $fail = true; - } - - if ( $fail ) { - echo "Test failed with seed {$this->fuzzSeed}\n"; - echo "Input:\n"; - var_dump( $input ); - echo "\n\n"; - echo "$exception\n"; - } else { - $numSuccess++; - } - - $numTotal++; - $this->teardownGlobals(); - $parser->__destruct(); - - if ( $numTotal % 100 == 0 ) { - $usage = intval( memory_get_usage( true ) / $this->memoryLimit / 1048576 * 100 ); - echo "{$this->fuzzSeed}: $numSuccess/$numTotal (mem: $usage%)\n"; - if ( $usage > 90 ) { - echo "Out of memory:\n"; - $memStats = $this->getMemoryBreakdown(); - - foreach ( $memStats as $name => $usage ) { - echo "$name: $usage\n"; - } - $this->abort(); - } - } - } - } - - /** - * Get an input dictionary from a set of parser test files - */ - function getFuzzInput( $filenames ) { - $dict = ''; - - foreach ( $filenames as $filename ) { - $contents = file_get_contents( $filename ); - preg_match_all( '/!!\s*input\n(.*?)\n!!\s*result/s', $contents, $matches ); - - foreach ( $matches[1] as $match ) { - $dict .= $match . "\n"; - } - } - - return $dict; - } - - /** - * Get a memory usage breakdown - */ - function getMemoryBreakdown() { - $memStats = array(); - - foreach ( $GLOBALS as $name => $value ) { - $memStats['$' . $name] = strlen( serialize( $value ) ); - } - - $classes = get_declared_classes(); - - foreach ( $classes as $class ) { - $rc = new ReflectionClass( $class ); - $props = $rc->getStaticProperties(); - $memStats[$class] = strlen( serialize( $props ) ); - $methods = $rc->getMethods(); - - foreach ( $methods as $method ) { - $memStats[$class] += strlen( serialize( $method->getStaticVariables() ) ); - } - } - - $functions = get_defined_functions(); - - foreach ( $functions['user'] as $function ) { - $rf = new ReflectionFunction( $function ); - $memStats["$function()"] = strlen( serialize( $rf->getStaticVariables() ) ); - } - - asort( $memStats ); - - return $memStats; - } - - function abort() { - $this->abort(); - } - - /** - * 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 $filenames Array of strings - * @return Boolean: true if passed all tests, false if any tests failed. - */ - public function runTestsFromFiles( $filenames ) { - $ok = false; - $GLOBALS['wgContLang'] = Language::factory( 'en' ); - $this->recorder->start(); - try { - $this->setupDatabase(); - $ok = true; - - foreach ( $filenames as $filename ) { - $tests = new TestFileIterator( $filename, $this ); - $ok = $this->runTests( $tests ) && $ok; - } - - $this->teardownDatabase(); - $this->recorder->report(); - } catch (DBError $e) { - echo $e->getMessage(); - } - $this->recorder->end(); - - return $ok; - } - - function runTests( $tests ) { - $ok = true; - - foreach ( $tests as $t ) { - $result = - $this->runTest( $t['test'], $t['input'], $t['result'], $t['options'], $t['config'] ); - $ok = $ok && $result; - $this->recorder->record( $t['test'], $result ); - } - - if ( $this->showProgress ) { - print "\n"; - } - - return $ok; - } - - /** - * Get a Parser object - */ - function getParser( $preprocessor = null ) { - global $wgParserConf; - - $class = $wgParserConf['class']; - $parser = new $class( array( 'preprocessorClass' => $preprocessor ) + $wgParserConf ); - - foreach ( $this->hooks as $tag => $callback ) { - $parser->setHook( $tag, $callback ); - } - - foreach ( $this->functionHooks as $tag => $bits ) { - list( $callback, $flags ) = $bits; - $parser->setFunctionHook( $tag, $callback, $flags ); - } - - wfRunHooks( 'ParserTestParser', array( &$parser ) ); - - return $parser; - } - - /** - * Run a given wikitext input through a freshly-constructed wiki parser, - * and compare the output against the expected results. - * Prints status and explanatory messages to stdout. - * - * @param $desc String: test's description - * @param $input String: wikitext to try rendering - * @param $result String: result to output - * @param $opts Array: test's options - * @param $config String: overrides for global variables, one per line - * @return Boolean - */ - public function runTest( $desc, $input, $result, $opts, $config ) { - if ( $this->showProgress ) { - $this->showTesting( $desc ); - } - - $opts = $this->parseOptions( $opts ); - $this->setupGlobals( $opts, $config ); - - $user = new User(); - $options = ParserOptions::newFromUser( $user ); - - if ( isset( $opts['title'] ) ) { - $titleText = $opts['title']; - } - else { - $titleText = 'Parser test'; - } - - $local = isset( $opts['local'] ); - $preprocessor = isset( $opts['preprocessor'] ) ? $opts['preprocessor'] : null; - $parser = $this->getParser( $preprocessor ); - $title = Title::newFromText( $titleText ); - - if ( isset( $opts['pst'] ) ) { - $out = $parser->preSaveTransform( $input, $title, $user, $options ); - } elseif ( isset( $opts['msg'] ) ) { - $out = $parser->transformMsg( $input, $options ); - } elseif ( isset( $opts['section'] ) ) { - $section = $opts['section']; - $out = $parser->getSection( $input, $section ); - } elseif ( isset( $opts['replace'] ) ) { - $section = $opts['replace'][0]; - $replace = $opts['replace'][1]; - $out = $parser->replaceSection( $input, $section, $replace ); - } elseif ( isset( $opts['comment'] ) ) { - $linker = $user->getSkin(); - $out = $linker->formatComment( $input, $title, $local ); - } elseif ( isset( $opts['preload'] ) ) { - $out = $parser->getpreloadText( $input, $title, $options ); - } else { - $output = $parser->parse( $input, $title, $options, true, true, 1337 ); - $out = $output->getText(); - - if ( isset( $opts['showtitle'] ) ) { - if ( $output->getTitleText() ) { - $title = $output->getTitleText(); - } - - $out = "$title\n$out"; - } - - if ( isset( $opts['ill'] ) ) { - $out = $this->tidy( implode( ' ', $output->getLanguageLinks() ) ); - } elseif ( isset( $opts['cat'] ) ) { - global $wgOut; - - $wgOut->addCategoryLinks( $output->getCategories() ); - $cats = $wgOut->getCategoryLinks(); - - if ( isset( $cats['normal'] ) ) { - $out = $this->tidy( implode( ' ', $cats['normal'] ) ); - } else { - $out = ''; - } - } - - $result = $this->tidy( $result ); - } - - $this->teardownGlobals(); - return $this->showTestResult( $desc, $result, $out ); - } - - /** - * - */ - function showTestResult( $desc, $result, $out ) { - if ( $result === $out ) { - $this->showSuccess( $desc ); - return true; - } else { - $this->showFailure( $desc, $result, $out ); - return false; - } - } - - /** - * Use a regex to find out the value of an option - * @param $key String: name of option val to retrieve - * @param $opts Options array to look in - * @param $default Mixed: default value returned if not found - */ - private static function getOptionValue( $key, $opts, $default ) { - $key = strtolower( $key ); - - if ( isset( $opts[$key] ) ) { - return $opts[$key]; - } else { - return $default; - } - } - - private function parseOptions( $instring ) { - $opts = array(); - // foo - // foo=bar - // foo="bar baz" - // foo=[[bar baz]] - // foo=bar,"baz quux" - $regex = '/\b - ([\w-]+) # Key - \b - (?:\s* - = # First sub-value - \s* - ( - " - [^"]* # Quoted val - " - | - \[\[ - [^]]* # Link target - \]\] - | - [\w-]+ # Plain word - ) - (?:\s* - , # Sub-vals 1..N - \s* - ( - "[^"]*" # Quoted val - | - \[\[[^]]*\]\] # Link target - | - [\w-]+ # Plain word - ) - )* - )? - /x'; - - if ( preg_match_all( $regex, $instring, $matches, PREG_SET_ORDER ) ) { - foreach ( $matches as $bits ) { - array_shift( $bits ); - $key = strtolower( array_shift( $bits ) ); - if ( count( $bits ) == 0 ) { - $opts[$key] = true; - } elseif ( count( $bits ) == 1 ) { - $opts[$key] = $this->cleanupOption( array_shift( $bits ) ); - } else { - // Array! - $opts[$key] = array_map( array( $this, 'cleanupOption' ), $bits ); - } - } - } - return $opts; - } - - private function cleanupOption( $opt ) { - if ( substr( $opt, 0, 1 ) == '"' ) { - return substr( $opt, 1, -1 ); - } - - if ( substr( $opt, 0, 2 ) == '[[' ) { - return substr( $opt, 2, -2 ); - } - return $opt; - } - - /** - * Set up the global variables for a consistent environment for each test. - * Ideally this should replace the global configuration entirely. - */ - private function setupGlobals( $opts = '', $config = '' ) { - global $wgDBtype; - - # Find out values for some special options. - $lang = - self::getOptionValue( 'language', $opts, 'en' ); - $variant = - self::getOptionValue( 'variant', $opts, false ); - $maxtoclevel = - self::getOptionValue( 'wgMaxTocLevel', $opts, 999 ); - $linkHolderBatchSize = - self::getOptionValue( 'wgLinkHolderBatchSize', $opts, 1000 ); - - $settings = array( - 'wgServer' => 'http://Britney-Spears', - 'wgScript' => '/index.php', - 'wgScriptPath' => '/', - 'wgArticlePath' => '/wiki/$1', - 'wgActionPaths' => array(), - 'wgLocalFileRepo' => array( - 'class' => 'LocalRepo', - 'name' => 'local', - 'directory' => $this->uploadDir, - 'url' => 'http://example.com/images', - 'hashLevels' => 2, - 'transformVia404' => false, - ), - 'wgEnableUploads' => self::getOptionValue( 'wgEnableUploads', $opts, true ), - 'wgStylePath' => '/skins', - 'wgStyleSheetPath' => '/skins', - 'wgSitename' => 'MediaWiki', - 'wgLanguageCode' => $lang, - 'wgDBprefix' => $wgDBtype != 'oracle' ? 'parsertest_' : 'pt_', - 'wgRawHtml' => isset( $opts['rawhtml'] ), - 'wgLang' => null, - 'wgContLang' => null, - 'wgNamespacesWithSubpages' => array( 0 => isset( $opts['subpage'] ) ), - 'wgMaxTocLevel' => $maxtoclevel, - 'wgCapitalLinks' => true, - 'wgNoFollowLinks' => true, - 'wgNoFollowDomainExceptions' => array(), - 'wgThumbnailScriptPath' => false, - 'wgUseImageResize' => false, - 'wgUseTeX' => isset( $opts['math'] ), - 'wgMathDirectory' => $this->uploadDir . '/math', - 'wgLocaltimezone' => 'UTC', - 'wgAllowExternalImages' => true, - 'wgUseTidy' => false, - 'wgDefaultLanguageVariant' => $variant, - 'wgVariantArticlePath' => false, - 'wgGroupPermissions' => array( '*' => array( - 'createaccount' => true, - 'read' => true, - 'edit' => true, - 'createpage' => true, - 'createtalk' => true, - ) ), - 'wgNamespaceProtection' => array( NS_MEDIAWIKI => 'editinterface' ), - 'wgDefaultExternalStore' => array(), - 'wgForeignFileRepos' => array(), - 'wgLinkHolderBatchSize' => $linkHolderBatchSize, - 'wgExperimentalHtmlIds' => false, - 'wgExternalLinkTarget' => false, - 'wgAlwaysUseTidy' => false, - 'wgHtml5' => true, - 'wgWellFormedXml' => true, - 'wgAllowMicrodataAttributes' => true, - ); - - if ( $config ) { - $configLines = explode( "\n", $config ); - - foreach ( $configLines as $line ) { - list( $var, $value ) = explode( '=', $line, 2 ); - - $settings[$var] = eval( "return $value;" ); - } - } - - $this->savedGlobals = array(); - - foreach ( $settings as $var => $val ) { - if ( array_key_exists( $var, $GLOBALS ) ) { - $this->savedGlobals[$var] = $GLOBALS[$var]; - } - - $GLOBALS[$var] = $val; - } - - $langObj = Language::factory( $lang ); - $GLOBALS['wgLang'] = $langObj; - $GLOBALS['wgContLang'] = $langObj; - $GLOBALS['wgMemc'] = new FakeMemCachedClient; - $GLOBALS['wgOut'] = new OutputPage; - - global $wgHooks; - - $wgHooks['ParserTestParser'][] = 'ParserTestParserHook::setup'; - $wgHooks['ParserTestParser'][] = 'ParserTestStaticParserHook::setup'; - $wgHooks['ParserGetVariableValueTs'][] = 'ParserTest::getFakeTimestamp'; - - MagicWord::clearCache(); - - global $wgUser; - $wgUser = new User(); - } - - /** - * List of temporary tables to create, without prefix. - * Some of these probably aren't necessary. - */ - private function listTables() { - global $wgDBtype; - - $tables = array( 'user', 'user_properties', 'page', 'page_restrictions', - 'protected_titles', 'revision', 'text', 'pagelinks', 'imagelinks', - 'categorylinks', 'templatelinks', 'externallinks', 'langlinks', 'iwlinks', - 'site_stats', 'hitcounter', 'ipblocks', 'image', 'oldimage', - 'recentchanges', 'watchlist', 'math', 'interwiki', 'logging', - 'querycache', 'objectcache', 'job', 'l10n_cache', 'redirect', 'querycachetwo', - 'archive', 'user_groups', 'page_props', 'category', 'msg_resource', 'msg_resource_links' - ); - - if ( in_array( $wgDBtype, array( 'mysql', 'sqlite', 'oracle' ) ) ) - array_push( $tables, 'searchindex' ); - - // Allow extensions to add to the list of tables to duplicate; - // may be necessary if they hook into page save or other code - // which will require them while running tests. - wfRunHooks( 'ParserTestTables', array( &$tables ) ); - - return $tables; - } - - /** - * Set up a temporary set of wiki tables to work with for the tests. - * Currently this will only be done once per run, and any changes to - * the db will be visible to later tests in the run. - */ - public function setupDatabase() { - global $wgDBprefix, $wgDBtype; - - if ( $this->databaseSetupDone ) { - return; - } - - if ( $wgDBprefix === 'parsertest_' || ( $wgDBtype == 'oracle' && $wgDBprefix === 'pt_' ) ) { - throw new MWException( 'setupDatabase should be called before setupGlobals' ); - } - - $this->databaseSetupDone = true; - $this->oldTablePrefix = $wgDBprefix; - - # SqlBagOStuff broke when using temporary tables on r40209 (bug 15892). - # It seems to have been fixed since (r55079?). - # If it fails, $wgCaches[CACHE_DB] = new HashBagOStuff(); should work around it. - - # CREATE TEMPORARY TABLE breaks if there is more than one server - if ( wfGetLB()->getServerCount() != 1 ) { - $this->useTemporaryTables = false; - } - - $temporary = $this->useTemporaryTables || $wgDBtype == 'postgres'; - - $db = wfGetDB( DB_MASTER ); - $tables = $this->listTables(); - - foreach ( $tables as $tbl ) { - # Clean up from previous aborted run. So that table escaping - # works correctly across DB engines, we need to change the pre- - # fix back and forth so tableName() works right. - $this->changePrefix( $this->oldTablePrefix ); - $oldTableName = $db->tableName( $tbl ); - $this->changePrefix( $wgDBtype != 'oracle' ? 'parsertest_' : 'pt_' ); - $newTableName = $db->tableName( $tbl ); - - if ( $wgDBtype == 'mysql' ) { - $db->query( "DROP TABLE IF EXISTS $newTableName" ); - } elseif ( in_array( $wgDBtype, array( 'postgres', 'oracle' ) ) ) { - /* DROPs wouldn't work due to Foreign Key Constraints (bug 14990, r58669) - * Use "DROP TABLE IF EXISTS $newTableName CASCADE" for postgres? That - * syntax would also work for mysql. - */ - } elseif ( $db->tableExists( $tbl ) ) { - $db->query( "DROP TABLE $newTableName" ); - } - - # Create new table - $db->duplicateTableStructure( $oldTableName, $newTableName, $temporary ); - } - - if ( $wgDBtype == 'oracle' ) - $db->query( 'BEGIN FILL_WIKI_INFO; END;' ); - - $this->changePrefix( $wgDBtype != 'oracle' ? 'parsertest_' : 'pt_' ); - - if ( $wgDBtype == 'oracle' ) { - # Insert 0 user to prevent FK violations - - # Anonymous user - $db->insert( 'user', array( - 'user_id' => 0, - 'user_name' => 'Anonymous' ) ); - } - - # Hack: insert a few Wikipedia in-project interwiki prefixes, - # for testing inter-language links - $db->insert( 'interwiki', array( - array( 'iw_prefix' => 'wikipedia', - 'iw_url' => 'http://en.wikipedia.org/wiki/$1', - 'iw_api' => '', - 'iw_wikiid' => '', - 'iw_local' => 0 ), - array( 'iw_prefix' => 'meatball', - 'iw_url' => 'http://www.usemod.com/cgi-bin/mb.pl?$1', - 'iw_api' => '', - 'iw_wikiid' => '', - 'iw_local' => 0 ), - array( 'iw_prefix' => 'zh', - 'iw_url' => 'http://zh.wikipedia.org/wiki/$1', - 'iw_api' => '', - 'iw_wikiid' => '', - 'iw_local' => 1 ), - array( 'iw_prefix' => 'es', - 'iw_url' => 'http://es.wikipedia.org/wiki/$1', - 'iw_api' => '', - 'iw_wikiid' => '', - 'iw_local' => 1 ), - array( 'iw_prefix' => 'fr', - 'iw_url' => 'http://fr.wikipedia.org/wiki/$1', - 'iw_api' => '', - 'iw_wikiid' => '', - 'iw_local' => 1 ), - array( 'iw_prefix' => 'ru', - 'iw_url' => 'http://ru.wikipedia.org/wiki/$1', - 'iw_api' => '', - 'iw_wikiid' => '', - 'iw_local' => 1 ), - ) ); - - - # Update certain things in site_stats - $db->insert( 'site_stats', array( 'ss_row_id' => 1, 'ss_images' => 2, 'ss_good_articles' => 1 ) ); - - # Reinitialise the LocalisationCache to match the database state - Language::getLocalisationCache()->unloadAll(); - - # Make a new message cache - global $wgMessageCache, $wgMemc; - $wgMessageCache = new MessageCache( $wgMemc, true, 3600 ); - - $this->uploadDir = $this->setupUploadDir(); - $user = User::createNew( 'WikiSysop' ); - $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) ); - $image->recordUpload2( '', 'Upload of some lame file', 'Some lame file', array( - 'size' => 12345, - 'width' => 1941, - 'height' => 220, - 'bits' => 24, - 'media_type' => MEDIATYPE_BITMAP, - 'mime' => 'image/jpeg', - 'metadata' => serialize( array() ), - 'sha1' => wfBaseConvert( '', 16, 36, 31 ), - 'fileExists' => true - ), $db->timestamp( '20010115123500' ), $user ); - - # This image will be blacklisted in [[MediaWiki:Bad image list]] - $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Bad.jpg' ) ); - $image->recordUpload2( '', 'zomgnotcensored', 'Borderline image', array( - 'size' => 12345, - 'width' => 320, - 'height' => 240, - 'bits' => 24, - 'media_type' => MEDIATYPE_BITMAP, - 'mime' => 'image/jpeg', - 'metadata' => serialize( array() ), - 'sha1' => wfBaseConvert( '', 16, 36, 31 ), - 'fileExists' => true - ), $db->timestamp( '20010115123500' ), $user ); - } - - /** - * Change the table prefix on all open DB connections/ - */ - protected function changePrefix( $prefix ) { - global $wgDBprefix; - wfGetLBFactory()->forEachLB( array( $this, 'changeLBPrefix' ), array( $prefix ) ); - $wgDBprefix = $prefix; - } - - public function changeLBPrefix( $lb, $prefix ) { - $lb->forEachOpenConnection( array( $this, 'changeDBPrefix' ), array( $prefix ) ); - } - - public function changeDBPrefix( $db, $prefix ) { - $db->tablePrefix( $prefix ); - } - - public function teardownDatabase() { - global $wgDBtype; - - if ( !$this->databaseSetupDone ) { - $this->teardownGlobals(); - return; - } - $this->teardownUploadDir( $this->uploadDir ); - - $this->changePrefix( $this->oldTablePrefix ); - $this->databaseSetupDone = false; - - if ( $this->useTemporaryTables ) { - # Don't need to do anything - $this->teardownGlobals(); - return; - } - - $tables = $this->listTables(); - $db = wfGetDB( DB_MASTER ); - - foreach ( $tables as $table ) { - $sql = $wgDBtype == 'oracle' ? "DROP TABLE pt_$table DROP CONSTRAINTS" : "DROP TABLE `parsertest_$table`"; - $db->query( $sql ); - } - - if ( $wgDBtype == 'oracle' ) - $db->query( 'BEGIN FILL_WIKI_INFO; END;' ); - - $this->teardownGlobals(); - } - - /** - * Create a dummy uploads directory which will contain a couple - * of files in order to pass existence tests. - * - * @return String: the directory - */ - private function setupUploadDir() { - global $IP; - - if ( $this->keepUploads ) { - $dir = wfTempDir() . '/mwParser-images'; - - if ( is_dir( $dir ) ) { - return $dir; - } - } else { - $dir = wfTempDir() . "/mwParser-" . mt_rand() . "-images"; - } - - // wfDebug( "Creating upload directory $dir\n" ); - if ( file_exists( $dir ) ) { - wfDebug( "Already exists!\n" ); - return $dir; - } - - wfMkdirParents( $dir . '/3/3a' ); - copy( "$IP/skins/monobook/headbg.jpg", "$dir/3/3a/Foobar.jpg" ); - wfMkdirParents( $dir . '/0/09' ); - copy( "$IP/skins/monobook/headbg.jpg", "$dir/0/09/Bad.jpg" ); - - return $dir; - } - - /** - * Restore default values and perform any necessary clean-up - * after each test runs. - */ - private function teardownGlobals() { - RepoGroup::destroySingleton(); - LinkCache::singleton()->clear(); - - foreach ( $this->savedGlobals as $var => $val ) { - $GLOBALS[$var] = $val; - } - } - - /** - * Remove the dummy uploads directory - */ - private function teardownUploadDir( $dir ) { - if ( $this->keepUploads ) { - return; - } - - // 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", - - "$dir/0/09/Bad.jpg", - - "$dir/math/f/a/5/fa50b8b616463173474302ca3e63586b.png", - ) - ); - - 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/0/09/", - "$dir/0/", - "$dir/thumb", - "$dir/math/f/a/5", - "$dir/math/f/a", - "$dir/math/f", - "$dir/math", - "$dir", - ) - ); - } - - /** - * Delete the specified files, if they exist. - * @param $files Array: full paths to files to delete. - */ - private static function deleteFiles( $files ) { - foreach ( $files as $file ) { - if ( file_exists( $file ) ) { - unlink( $file ); - } - } - } - - /** - * Delete the specified directories, if they exist. Must be empty. - * @param $dirs Array: full paths to directories to delete. - */ - private static function deleteDirs( $dirs ) { - foreach ( $dirs as $dir ) { - if ( is_dir( $dir ) ) { - rmdir( $dir ); - } - } - } - - /** - * "Running test $desc..." - */ - protected function showTesting( $desc ) { - print "Running test $desc... "; - } - - /** - * Print a happy success message. - * - * @param $desc String: the test name - * @return Boolean - */ - protected function showSuccess( $desc ) { - if ( $this->showProgress ) { - print $this->term->color( '1;32' ) . 'PASSED' . $this->term->reset() . "\n"; - } - - return true; - } - - /** - * Print a failure message and provide some explanatory output - * about what went wrong if so configured. - * - * @param $desc String: the test name - * @param $result String: expected HTML output - * @param $html String: actual HTML output - * @return Boolean - */ - protected function showFailure( $desc, $result, $html ) { - if ( $this->showFailure ) { - if ( !$this->showProgress ) { - # In quiet mode we didn't show the 'Testing' message before the - # test, in case it succeeded. Show it now: - $this->showTesting( $desc ); - } - - print $this->term->color( '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 ) ) { - print "XML error: $this->mXmlError\n"; - } - } - } - - return false; - } - - /** - * Run given strings through a diff and return the (colorized) output. - * Requires writable /tmp directory and a 'diff' command in the PATH. - * - * @param $input String - * @param $output String - * @param $inFileTail String: tailing for the input file name - * @param $outFileTail String: tailing for the output file name - * @return String - */ - protected function quickDiff( $input, $output, $inFileTail = 'expected', $outFileTail = 'actual' ) { - $prefix = wfTempDir() . "/mwParser-" . mt_rand(); - - $infile = "$prefix-$inFileTail"; - $this->dumpToFile( $input, $infile ); - - $outfile = "$prefix-$outFileTail"; - $this->dumpToFile( $output, $outfile ); - - $diff = `diff -au $infile $outfile`; - unlink( $infile ); - unlink( $outfile ); - - return $this->colorDiff( $diff ); - } - - /** - * Write the given string to a file, adding a final newline. - * - * @param $data String - * @param $filename String - */ - private function dumpToFile( $data, $filename ) { - $file = fopen( $filename, "wt" ); - fwrite( $file, $data . "\n" ); - fclose( $file ); - } - - /** - * Colorize unified diff output if set for ANSI color output. - * Subtractions are colored blue, additions red. - * - * @param $text String - * @return String - */ - protected function colorDiff( $text ) { - return preg_replace( - array( '/^(-.*)$/m', '/^(\+.*)$/m' ), - array( $this->term->color( 34 ) . '$1' . $this->term->reset(), - $this->term->color( 31 ) . '$1' . $this->term->reset() ), - $text ); - } - - /** - * Show "Reading tests from ..." - * - * @param $path String - */ - public function showRunFile( $path ) { - print $this->term->color( 1 ) . - "Reading tests from \"$path\"..." . - $this->term->reset() . - "\n"; - } - - /** - * Insert a temporary test article - * @param $name String: the title, including any prefix - * @param $text String: the article text - * @param $line Integer: the input line number, for reporting errors - */ - static public function addArticle( $name, $text, $line = 'unknown' ) { - global $wgCapitalLinks; - - $text = self::chomp($text); - - $oldCapitalLinks = $wgCapitalLinks; - $wgCapitalLinks = true; // We only need this from SetupGlobals() See r70917#c8637 - - $name = self::chomp( $name ); - $title = Title::newFromText( $name ); - - if ( is_null( $title ) ) { - wfDie( "invalid title ('$name' => '$title') at line $line\n" ); - } - - $aid = $title->getArticleID( Title::GAID_FOR_UPDATE ); - - if ( $aid != 0 ) { - debug_print_backtrace(); - wfDie( "duplicate article '$name' at line $line\n" ); - } - - $art = new Article( $title ); - $art->insertNewArticle( $text, '', false, false ); - - $wgCapitalLinks = $oldCapitalLinks; - } - - /** - * Steal a callback function from the primary parser, save it for - * application to our scary parser. If the hook is not installed, - * abort processing of this file. - * - * @param $name String - * @return Bool true if tag hook is present - */ - public function requireHook( $name ) { - global $wgParser; - - $wgParser->firstCallInit( ); // make sure hooks are loaded. - - if ( isset( $wgParser->mTagHooks[$name] ) ) { - $this->hooks[$name] = $wgParser->mTagHooks[$name]; - } else { - echo " This test suite requires the '$name' hook extension, skipping.\n"; - return false; - } - - return true; - } - - /** - * Steal a callback function from the primary parser, save it for - * application to our scary parser. If the hook is not installed, - * abort processing of this file. - * - * @param $name String - * @return Bool true if function hook is present - */ - public function requireFunctionHook( $name ) { - global $wgParser; - - $wgParser->firstCallInit( ); // make sure hooks are loaded. - - if ( isset( $wgParser->mFunctionHooks[$name] ) ) { - $this->functionHooks[$name] = $wgParser->mFunctionHooks[$name]; - } else { - echo " This test suite requires the '$name' function hook extension, skipping.\n"; - return false; - } - - return true; - } - - /* - * Run the "tidy" command on text if the $wgUseTidy - * global is true - * - * @param $text String: the text to tidy - * @return String - * @static - */ - private function tidy( $text ) { - global $wgUseTidy; - - if ( $wgUseTidy ) { - $text = MWTidy::tidy( $text ); - } - - return $text; - } - - private function wellFormed( $text ) { - $html = - Sanitizer::hackDocType() . - '<html>' . - $text . - '</html>'; - - $parser = xml_parser_create( "UTF-8" ); - - # case folding violates XML standard, turn it off - xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false ); - - if ( !xml_parse( $parser, $html, true ) ) { - $err = xml_error_string( xml_get_error_code( $parser ) ); - $position = xml_get_current_byte_index( $parser ); - $fragment = $this->extractFragment( $html, $position ); - $this->mXmlError = "$err at byte $position:\n$fragment"; - xml_parser_free( $parser ); - - return false; - } - - xml_parser_free( $parser ); - - return true; - } - - private function extractFragment( $text, $position ) { - $start = max( 0, $position - 10 ); - $before = $position - $start; - $fragment = '...' . - $this->term->color( 34 ) . - substr( $text, $start, $before ) . - $this->term->color( 0 ) . - $this->term->color( 31 ) . - $this->term->color( 1 ) . - substr( $text, $position, 1 ) . - $this->term->color( 0 ) . - $this->term->color( 34 ) . - substr( $text, $position + 1, 9 ) . - $this->term->color( 0 ) . - '...'; - $display = str_replace( "\n", ' ', $fragment ); - $caret = ' ' . - str_repeat( ' ', $before ) . - $this->term->color( 31 ) . - '^' . - $this->term->color( 0 ); - - return "$display\n$caret"; - } - - static function getFakeTimestamp( &$parser, &$ts ) { - $ts = 123; - return true; - } -} diff --git a/maintenance/tests/parser/parserTests.txt b/maintenance/tests/parser/parserTests.txt deleted file mode 100644 index b3fa560c..00000000 --- a/maintenance/tests/parser/parserTests.txt +++ /dev/null @@ -1,8315 +0,0 @@ -# MediaWiki Parser test cases -# Some taken from http://meta.wikimedia.org/wiki/Parser_testing -# All (C) their respective authors and released under the GPL -# -# The syntax should be fairly self-explanatory. -# -# Currently supported test options: -# One of the following three: -# -# (default) generate HTML output -# pst apply pre-save transform -# msg apply message transform -# -# Plus any combination of these: -# -# cat add category links -# ill add inter-language links -# 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 -# variant=XXX set the variant of language for this test (eg zh-tw) -# disabled do not run test -# showtitle make the first line the title -# comment run through Linker::formatComment() instead of main parser -# local format section links in edit comment text as local links -# -# For testing purposes, temporary articles can created: -# !!article / NAMESPACE:TITLE / !!text / ARTICLE TEXT / !!endarticle -# where '/' denotes a newline. - -# This is the standard article assumed to exist. -!! article -Main Page -!! text -blah blah -!! endarticle - -!!article -Template:Foo -!!text -FOO -!!endarticle - -!! article -Template:Blank -!! text -!! endarticle - -!! article -Template:! -!! text -| -!! endarticle - -!!article -MediaWiki:bad image list -!!text -* [[File:Bad.jpg]] except [[Nasty page]] -!!endarticle - -### -### Basic tests -### -!! test -Blank input -!! input -!! result -!! end - - -!! test -Simple paragraph -!! input -This is a simple paragraph. -!! result -<p>This is a simple paragraph. -</p> -!! end - -!! test -Simple list -!! input -* Item 1 -* Item 2 -!! result -<ul><li> Item 1 -</li><li> Item 2 -</li></ul> - -!! end - -!! test -Italics and bold -!! input -* plain -* plain''italic''plain -* plain''italic''plain''italic''plain -* plain'''bold'''plain -* plain'''bold'''plain'''bold'''plain -* plain''italic''plain'''bold'''plain -* plain'''bold'''plain''italic''plain -* plain''italic'''bold-italic'''italic''plain -* plain'''bold''bold-italic''bold'''plain -* plain'''''bold-italic'''italic''plain -* plain'''''bold-italic''bold'''plain -* plain''italic'''bold-italic'''''plain -* plain'''bold''bold-italic'''''plain -* plain l'''italic''plain -* plain l''''bold''' plain -!! result -<ul><li> plain -</li><li> plain<i>italic</i>plain -</li><li> plain<i>italic</i>plain<i>italic</i>plain -</li><li> plain<b>bold</b>plain -</li><li> plain<b>bold</b>plain<b>bold</b>plain -</li><li> plain<i>italic</i>plain<b>bold</b>plain -</li><li> plain<b>bold</b>plain<i>italic</i>plain -</li><li> plain<i>italic<b>bold-italic</b>italic</i>plain -</li><li> plain<b>bold<i>bold-italic</i>bold</b>plain -</li><li> plain<i><b>bold-italic</b>italic</i>plain -</li><li> plain<b><i>bold-italic</i>bold</b>plain -</li><li> plain<i>italic<b>bold-italic</b></i>plain -</li><li> plain<b>bold<i>bold-italic</i></b>plain -</li><li> plain l'<i>italic</i>plain -</li><li> plain l'<b>bold</b> plain -</li></ul> - -!! end - -### -### <nowiki> test cases -### - -!! test -<nowiki> unordered list -!! input -<nowiki>* This is not an unordered list item.</nowiki> -!! result -<p>* This is not an unordered list item. -</p> -!! end - -!! test -<nowiki> spacing -!! input -<nowiki>Lorem ipsum dolor - -sed abit. - sed nullum. - -:and a colon -</nowiki> -!! result -<p>Lorem ipsum dolor - -sed abit. - sed nullum. - -:and a colon - -</p> -!! end - -!! test -nowiki 3 -!! input -:There is not nowiki. -:There is <nowiki>nowiki</nowiki>. - -#There is not nowiki. -#There is <nowiki>nowiki</nowiki>. - -*There is not nowiki. -*There is <nowiki>nowiki</nowiki>. -!! result -<dl><dd>There is not nowiki. -</dd><dd>There is nowiki. -</dd></dl> -<ol><li>There is not nowiki. -</li><li>There is nowiki. -</li></ol> -<ul><li>There is not nowiki. -</li><li>There is nowiki. -</li></ul> - -!! end - - -### -### Comments -### -!! test -Comment test 1 -!! input -<!-- comment 1 --> asdf -<!-- comment 2 --> -!! result -<pre>asdf -</pre> - -!! end - -!! test -Comment test 2 -!! input -asdf -<!-- comment 1 --> -jkl -!! result -<p>asdf -jkl -</p> -!! end - -!! test -Comment test 3 -!! input -asdf -<!-- comment 1 --> -<!-- comment 2 --> -jkl -!! result -<p>asdf -jkl -</p> -!! end - -!! test -Comment test 4 -!! input -asdf<!-- comment 1 -->jkl -!! result -<p>asdfjkl -</p> -!! end - -!! test -Comment spacing -!! input -a - <!-- foo --> b <!-- bar --> -c -!! result -<p>a -</p> -<pre> b -</pre> -<p>c -</p> -!! end - -!! test -Comment whitespace -!! input -<!-- returns a single newline, not nothing, since the newline after > is not stripped --> -!! result - -!! end - -!! test -Comment semantics and delimiters -!! input -<!-- --><!----><!-----><!------> -!! result - -!! end - -!! test -Comment semantics and delimiters, redux -!! input -<!-- In SGML every "foo" here would actually show up in the text -- foo -- bar --- foo -- funky huh? ... --> -!! result - -!! end - -!! test -Comment semantics and delimiters: directors cut -!! input -<!-- ... However we like to keep things simple and somewhat XML-ish so we eat -everything starting with < followed by !-- until the first -- and > we see, -that wouldn't be valid XML however, since in XML -- has to terminate a comment --->--> -!! result -<p>--> -</p> -!! end - -!! test -Comment semantics: nesting -!! input -<!--<!-- no, we're not going to do anything fancy here -->--> -!! result -<p>--> -</p> -!! end - -!! test -Comment semantics: unclosed comment at end -!! input -<!--This comment will run out to the end of the document -!! result - -!! end - -!! test -Comment in template title -!! input -{{f<!---->oo}} -!! result -<p>FOO -</p> -!! end - -!! test -Comment on its own line post-expand -!! input -a -{{blank}}<!----> -b -!! result -<p>a -</p><p>b -</p> -!! end - -### -### Preformatted text -### -!! test -Preformatted text -!! input - This is some - Preformatted text - With ''italic'' - And '''bold''' - And a [[Main Page|link]] -!! result -<pre>This is some -Preformatted text -With <i>italic</i> -And <b>bold</b> -And a <a href="/wiki/Main_Page" title="Main Page">link</a> -</pre> -!! end - -!! test -<pre> with <nowiki> inside (compatibility with 1.6 and earlier) -!! input -<pre><nowiki> -<b> -<cite> -<em> -</nowiki></pre> -!! result -<pre> -<b> -<cite> -<em> -</pre> - -!! end - -!! test -Regression with preformatted in <center> -!! input -<center> - Blah -</center> -!! result -<center> -<pre>Blah -</pre> -</center> - -!! end - -# Expected output in the following test is not really expected (there should be -# <pre> in the output) -- it's only testing for well-formedness. -!! test -Bug 6200: Preformatted in <blockquote> -!! input -<blockquote> - Blah -</blockquote> -!! result -<blockquote> - Blah -</blockquote> - -!! end - -!! test -<pre> with attributes (bug 3202) -!! input -<pre style="background: blue; color:white">Bluescreen of WikiDeath</pre> -!! result -<pre style="background: blue; color:white">Bluescreen of WikiDeath</pre> - -!! end - -!! test -<pre> with width attribute (bug 3202) -!! input -<pre width="8">Narrow screen goodies</pre> -!! result -<pre width="8">Narrow screen goodies</pre> - -!! end - -!! test -<pre> with forbidden attribute (bug 3202) -!! input -<pre width="8" onmouseover="alert(document.cookie)">Narrow screen goodies</pre> -!! result -<pre width="8">Narrow screen goodies</pre> - -!! end - -!! test -<pre> with forbidden attribute values (bug 3202) -!! input -<pre width="8" style="border-width: expression(alert(document.cookie))">Narrow screen goodies</pre> -!! result -<pre width="8" style="/* insecure input */">Narrow screen goodies</pre> - -!! end - -!! test -<nowiki> inside <pre> (bug 13238) -!! input -<pre> -<nowiki> -</pre> -<pre> -<nowiki></nowiki> -</pre> -<pre><nowiki><nowiki></nowiki>Foo<nowiki></nowiki></nowiki></pre> -!! result -<pre> -<nowiki> -</pre> -<pre> - -</pre> -<pre><nowiki>Foo</nowiki></pre> - -!! end - -!! test -<nowiki> and <pre> preference (first one wins) -!! input -<pre> -<nowiki> -</pre> -</nowiki> -</pre> - -<nowiki> -<pre> -<nowiki> -</pre> -</nowiki> -</pre> - -!! result -<pre> -<nowiki> -</pre> -<p></nowiki> -</pre> -</p><p> -<pre> -<nowiki> -</pre> - -</pre> -</p> -!! end - - -### -### Definition lists -### -!! test -Simple definition -!! input -; name : Definition -!! result -<dl><dt> name </dt><dd> Definition -</dd></dl> - -!! end - -!! test -Definition list for indentation only -!! input -: Indented text -!! result -<dl><dd> Indented text -</dd></dl> - -!! end - -!! test -Definition list with no space -!! input -;name:Definition -!! result -<dl><dt>name</dt><dd>Definition -</dd></dl> - -!!end - -!! test -Definition list with URL link -!! input -; http://example.com/ : definition -!! result -<dl><dt> <a href="http://example.com/" class="external free" rel="nofollow">http://example.com/</a> </dt><dd> definition -</dd></dl> - -!! end - -!! test -Definition list with bracketed URL link -!! input -;[http://www.example.com/ Example]:Something about it -!! result -<dl><dt><a href="http://www.example.com/" class="external text" rel="nofollow">Example</a></dt><dd>Something about it -</dd></dl> - -!! end - -!! test -Definition list with wikilink containing colon -!! input -; [[Help:FAQ]]: The least-read page on Wikipedia -!! result -<dl><dt> <a href="/index.php?title=Help:FAQ&action=edit&redlink=1" class="new" title="Help:FAQ (page does not exist)">Help:FAQ</a></dt><dd> The least-read page on Wikipedia -</dd></dl> - -!! end - -# At Brion's and JeLuF's insistence... :) -!! test -Definition list with news link containing colon -!! input -; news:alt.wikipedia.rox: This isn't even a real newsgroup! -!! result -<dl><dt> <a href="news:alt.wikipedia.rox" class="external free" rel="nofollow">news:alt.wikipedia.rox</a></dt><dd> This isn't even a real newsgroup! -</dd></dl> - -!! end - -!! test -Malformed definition list with colon -!! input -; news:alt.wikipedia.rox -- don't crash or enter an infinite loop -!! result -<dl><dt> <a href="news:alt.wikipedia.rox" class="external free" rel="nofollow">news:alt.wikipedia.rox</a> -- don't crash or enter an infinite loop -</dt></dl> - -!! end - -!! test -Definition lists: colon in external link text -!! input -; [http://www.wikipedia2.org/ Wikipedia : The Next Generation]: OK, I made that up -!! result -<dl><dt> <a href="http://www.wikipedia2.org/" class="external text" rel="nofollow">Wikipedia : The Next Generation</a></dt><dd> OK, I made that up -</dd></dl> - -!! end - -!! test -Definition lists: colon in HTML attribute -!! input -;<b style="display: inline">bold</b> -!! result -<dl><dt><b style="display: inline">bold</b> -</dt></dl> - -!! end - - -!! test -Definition lists: self-closed tag -!! input -;one<br/>two : two-line fun -!! result -<dl><dt>one<br />two </dt><dd> two-line fun -</dd></dl> - -!! end - - -### -### External links -### -!! test -External links: non-bracketed -!! input -Non-bracketed: http://example.com -!! result -<p>Non-bracketed: <a href="http://example.com" class="external free" rel="nofollow">http://example.com</a> -</p> -!! end - -!! test -External links: numbered -!! input -Numbered: [http://example.com] -Numbered: [http://example.net] -Numbered: [http://example.com] -!! result -<p>Numbered: <a href="http://example.com" class="external autonumber" rel="nofollow">[1]</a> -Numbered: <a href="http://example.net" class="external autonumber" rel="nofollow">[2]</a> -Numbered: <a href="http://example.com" class="external autonumber" rel="nofollow">[3]</a> -</p> -!!end - -!! test -External links: specified text -!! input -Specified text: [http://example.com link] -!! result -<p>Specified text: <a href="http://example.com" class="external text" rel="nofollow">link</a> -</p> -!!end - -!! test -External links: trail -!! input -Linktrails should not work for external links: [http://example.com link]s -!! result -<p>Linktrails should not work for external links: <a href="http://example.com" class="external text" rel="nofollow">link</a>s -</p> -!! end - -!! test -External links: dollar sign in URL -!! input -http://example.com/1$2345 -!! result -<p><a href="http://example.com/1$2345" class="external free" rel="nofollow">http://example.com/1$2345</a> -</p> -!! end - -!! test -External links: dollar sign in URL (named) -!! input -[http://example.com/1$2345] -!! result -<p><a href="http://example.com/1$2345" class="external autonumber" rel="nofollow">[1]</a> -</p> -!!end - -!! test -External links: open square bracket forbidden in URL (bug 4377) -!! input -http://example.com/1[2345 -!! result -<p><a href="http://example.com/1" class="external free" rel="nofollow">http://example.com/1</a>[2345 -</p> -!! end - -!! test -External links: open square bracket forbidden in URL (named) (bug 4377) -!! input -[http://example.com/1[2345] -!! result -<p><a href="http://example.com/1" class="external text" rel="nofollow">[2345</a> -</p> -!!end - -!! test -External links: nowiki in URL link text (bug 6230) -!!input -[http://example.com/ <nowiki>''example site''</nowiki>] -!! result -<p><a href="http://example.com/" class="external text" rel="nofollow">''example site''</a> -</p> -!! end - -!! test -External links: newline forbidden in text (bug 6230 regression check) -!! input -[http://example.com/ first -second] -!! result -<p>[<a href="http://example.com/" class="external free" rel="nofollow">http://example.com/</a> first -second] -</p> -!!end - -!! test -External image -!! input -External image: http://meta.wikimedia.org/upload/f/f1/Ncwikicol.png -!! result -<p>External image: <img src="http://meta.wikimedia.org/upload/f/f1/Ncwikicol.png" alt="Ncwikicol.png" /> -</p> -!! end - -!! test -External image from https -!! input -External image from https: https://meta.wikimedia.org/upload/f/f1/Ncwikicol.png -!! result -<p>External image from https: <img src="https://meta.wikimedia.org/upload/f/f1/Ncwikicol.png" alt="Ncwikicol.png" /> -</p> -!! end - -!! test -Link to non-http image, no img tag -!! input -Link to non-http image, no img tag: ftp://example.com/test.jpg -!! result -<p>Link to non-http image, no img tag: <a href="ftp://example.com/test.jpg" class="external free" rel="nofollow">ftp://example.com/test.jpg</a> -</p> -!! end - -!! test -External links: terminating separator -!! input -Terminating separator: http://example.com/thing, -!! result -<p>Terminating separator: <a href="http://example.com/thing" class="external free" rel="nofollow">http://example.com/thing</a>, -</p> -!! end - -!! test -External links: intervening separator -!! input -Intervening separator: http://example.com/1,2,3 -!! result -<p>Intervening separator: <a href="http://example.com/1,2,3" class="external free" rel="nofollow">http://example.com/1,2,3</a> -</p> -!! end - -!! test -External links: old bug with URL in query -!! input -Old bug with URL in query: [http://example.com/thing?url=http://example.com link] -!! result -<p>Old bug with URL in query: <a href="http://example.com/thing?url=http://example.com" class="external text" rel="nofollow">link</a> -</p> -!! end - -!! test -External links: old URL-in-URL bug, mixed protocols -!! input -And again with mixed protocols: [ftp://example.com?url=http://example.com link] -!! result -<p>And again with mixed protocols: <a href="ftp://example.com?url=http://example.com" class="external text" rel="nofollow">link</a> -</p> -!!end - -!! test -External links: URL in text -!! input -URL in text: [http://example.com http://example.com] -!! result -<p>URL in text: <a href="http://example.com" class="external free" rel="nofollow">http://example.com</a> -</p> -!! end - -!! test -External links: Clickable images -!! input -ja-style clickable images: [http://example.com http://meta.wikimedia.org/upload/f/f1/Ncwikicol.png] -!! result -<p>ja-style clickable images: <a href="http://example.com" class="external text" rel="nofollow"><img src="http://meta.wikimedia.org/upload/f/f1/Ncwikicol.png" alt="Ncwikicol.png" /></a> -</p> -!!end - -!! test -External links: raw ampersand -!! input -Old & use: http://x&y -!! result -<p>Old & use: <a href="http://x&y" class="external free" rel="nofollow">http://x&y</a> -</p> -!! end - -!! test -External links: encoded ampersand -!! input -Old & use: http://x&y -!! result -<p>Old & use: <a href="http://x&y" class="external free" rel="nofollow">http://x&y</a> -</p> -!! end - -!! test -External links: encoded equals (bug 6102) -!! input -http://example.com/?foo=bar -!! result -<p><a href="http://example.com/?foo=bar" class="external free" rel="nofollow">http://example.com/?foo=bar</a> -</p> -!! end - -!! test -External links: [raw ampersand] -!! input -Old & use: [http://x&y] -!! result -<p>Old & use: <a href="http://x&y" class="external autonumber" rel="nofollow">[1]</a> -</p> -!! end - -!! test -External links: [encoded ampersand] -!! input -Old & use: [http://x&y] -!! result -<p>Old & use: <a href="http://x&y" class="external autonumber" rel="nofollow">[1]</a> -</p> -!! end - -!! test -External links: [encoded equals] (bug 6102) -!! input -[http://example.com/?foo=bar] -!! result -<p><a href="http://example.com/?foo=bar" class="external autonumber" rel="nofollow">[1]</a> -</p> -!! end - -!! test -External links: [IDN ignored character reference in hostname; strip it right off] -!! input -[http://e‌xample.com/] -!! result -<p><a href="http://example.com/" class="external autonumber" rel="nofollow">[1]</a> -</p> -!! end - -!! test -External links: IDN ignored character reference in hostname; strip it right off -!! input -http://e‌xample.com/ -!! result -<p><a href="http://example.com/" class="external free" rel="nofollow">http://example.com/</a> -</p> -!! end - -!! test -External links: www.jpeg.org (bug 554) -!! input -http://www.jpeg.org -!!result -<p><a href="http://www.jpeg.org" class="external free" rel="nofollow">http://www.jpeg.org</a> -</p> -!! end - -!! test -External links: URL within URL (original bug 2) -!! input -[http://www.unausa.org/newindex.asp?place=http://www.unausa.org/programs/mun.asp] -!! result -<p><a href="http://www.unausa.org/newindex.asp?place=http://www.unausa.org/programs/mun.asp" class="external autonumber" rel="nofollow">[1]</a> -</p> -!! end - -!! test -BUG 361: URL inside bracketed URL -!! input -[http://www.example.com/foo http://www.example.com/bar] -!! result -<p><a href="http://www.example.com/foo" class="external text" rel="nofollow">http://www.example.com/bar</a> -</p> -!! end - -!! test -BUG 361: URL within URL, not bracketed -!! input -http://www.example.com/foo?=http://www.example.com/bar -!! result -<p><a href="http://www.example.com/foo?=http://www.example.com/bar" class="external free" rel="nofollow">http://www.example.com/foo?=http://www.example.com/bar</a> -</p> -!! end - -!! test -BUG 289: ">"-token in URL-tail -!! input -http://www.example.com/<hello> -!! result -<p><a href="http://www.example.com/" class="external free" rel="nofollow">http://www.example.com/</a><hello> -</p> -!!end - -!! test -BUG 289: literal ">"-token in URL-tail -!! input -http://www.example.com/<b>html</b> -!! result -<p><a href="http://www.example.com/" class="external free" rel="nofollow">http://www.example.com/</a><b>html</b> -</p> -!!end - -!! test -BUG 289: ">"-token in bracketed URL -!! input -[http://www.example.com/<hello> stuff] -!! result -<p><a href="http://www.example.com/" class="external text" rel="nofollow"><hello> stuff</a> -</p> -!!end - -!! test -BUG 289: literal ">"-token in bracketed URL -!! input -[http://www.example.com/<b>html</b> stuff] -!! result -<p><a href="http://www.example.com/" class="external text" rel="nofollow"><b>html</b> stuff</a> -</p> -!!end - -!! test -BUG 289: literal double quote at end of URL -!! input -http://www.example.com/"hello" -!! result -<p><a href="http://www.example.com/" class="external free" rel="nofollow">http://www.example.com/</a>"hello" -</p> -!!end - -!! test -BUG 289: literal double quote in bracketed URL -!! input -[http://www.example.com/"hello" stuff] -!! result -<p><a href="http://www.example.com/" class="external text" rel="nofollow">"hello" stuff</a> -</p> -!!end - -!! test -External links: multiple legal whitespace is fine, Magnus. Don't break it please. (bug 5081) -!! input -[http://www.example.com test] -!! result -<p><a href="http://www.example.com" class="external text" rel="nofollow">test</a> -</p> -!! end - -!! test -External links: wiki links within external link (Bug 3695) -!! input -[http://example.com [[wikilink]] embedded in ext link] -!! result -<p><a href="http://example.com" class="external text" rel="nofollow"></a><a href="/index.php?title=Wikilink&action=edit&redlink=1" class="new" title="Wikilink (page does not exist)">wikilink</a><a href="http://example.com" class="external text" rel="nofollow"> embedded in ext link</a> -</p> -!! end - -!! test -BUG 787: Links with one slash after the url protocol are invalid -!! input -http:/example.com - -[http:/example.com title] -!! result -<p>http:/example.com -</p><p>[http:/example.com title] -</p> -!! end - -!! test -Bug 2702: Mismatched <i>, <b> and <a> tags are invalid -!! input -''[http://example.com text''] -[http://example.com '''text]''' -''Something [http://example.com in italic''] -''Something [http://example.com mixed''''', even bold]''' -'''''Now [http://example.com both'''''] -!! result -<p><a href="http://example.com" class="external text" rel="nofollow"><i>text</i></a> -<a href="http://example.com" class="external text" rel="nofollow"><b>text</b></a> -<i>Something </i><a href="http://example.com" class="external text" rel="nofollow"><i>in italic</i></a> -<i>Something </i><a href="http://example.com" class="external text" rel="nofollow"><i>mixed</i><b>, even bold</b></a> -<i><b>Now </b></i><a href="http://example.com" class="external text" rel="nofollow"><i><b>both</b></i></a> -</p> -!! end - - -!! test -Bug 4781: %26 in URL -!! input -http://www.example.com/?title=AT%26T -!! result -<p><a href="http://www.example.com/?title=AT%26T" class="external free" rel="nofollow">http://www.example.com/?title=AT%26T</a> -</p> -!! end - -!! test -Bug 4781, 5267: %26 in URL -!! input -http://www.example.com/?title=100%25_Bran -!! result -<p><a href="http://www.example.com/?title=100%25_Bran" class="external free" rel="nofollow">http://www.example.com/?title=100%25_Bran</a> -</p> -!! end - -!! test -Bug 4781, 5267: %28, %29 in URL -!! input -http://www.example.com/?title=Ben-Hur_%281959_film%29 -!! result -<p><a href="http://www.example.com/?title=Ben-Hur_%281959_film%29" class="external free" rel="nofollow">http://www.example.com/?title=Ben-Hur_%281959_film%29</a> -</p> -!! end - - -!! test -Bug 4781: %26 in autonumber URL -!! input -[http://www.example.com/?title=AT%26T] -!! result -<p><a href="http://www.example.com/?title=AT%26T" class="external autonumber" rel="nofollow">[1]</a> -</p> -!! end - -!! test -Bug 4781, 5267: %26 in autonumber URL -!! input -[http://www.example.com/?title=100%25_Bran] -!! result -<p><a href="http://www.example.com/?title=100%25_Bran" class="external autonumber" rel="nofollow">[1]</a> -</p> -!! end - -!! test -Bug 4781, 5267: %28, %29 in autonumber URL -!! input -[http://www.example.com/?title=Ben-Hur_%281959_film%29] -!! result -<p><a href="http://www.example.com/?title=Ben-Hur_%281959_film%29" class="external autonumber" rel="nofollow">[1]</a> -</p> -!! end - - -!! test -Bug 4781: %26 in bracketed URL -!! input -[http://www.example.com/?title=AT%26T link] -!! result -<p><a href="http://www.example.com/?title=AT%26T" class="external text" rel="nofollow">link</a> -</p> -!! end - -!! test -Bug 4781, 5267: %26 in bracketed URL -!! input -[http://www.example.com/?title=100%25_Bran link] -!! result -<p><a href="http://www.example.com/?title=100%25_Bran" class="external text" rel="nofollow">link</a> -</p> -!! end - -!! test -Bug 4781, 5267: %28, %29 in bracketed URL -!! input -[http://www.example.com/?title=Ben-Hur_%281959_film%29 link] -!! result -<p><a href="http://www.example.com/?title=Ben-Hur_%281959_film%29" class="external text" rel="nofollow">link</a> -</p> -!! end - -!! test -External link containing double-single-quotes in text '' (bug 4598 sanity check) -!! input -Some [http://example.com/ pretty ''italics'' and stuff]! -!! result -<p>Some <a href="http://example.com/" class="external text" rel="nofollow">pretty <i>italics</i> and stuff</a>! -</p> -!! end - -!! test -External link containing double-single-quotes in text embedded in italics (bug 4598 sanity check) -!! input -''Some [http://example.com/ pretty ''italics'' and stuff]!'' -!! result -<p><i>Some </i><a href="http://example.com/" class="external text" rel="nofollow"><i>pretty </i>italics<i> and stuff</i></a><i>!</i> -</p> -!! end - -!! test -External link containing double-single-quotes with no space separating the url from text in italics -!! input -[http://www.musee-picasso.fr/pages/page_id18528_u1l2.htm''La muerte de Casagemas'' (1901) en el sitio de [[Museo Picasso (París)|Museo Picasso]].] -!! result -<p><a href="http://www.musee-picasso.fr/pages/page_id18528_u1l2.htm" class="external text" rel="nofollow"><i>La muerte de Casagemas</i> (1901) en el sitio de <a href="/index.php?title=Museo_Picasso_(Par%C3%ADs)&action=edit&redlink=1" class="new" title="Museo Picasso (París) (page does not exist)">Museo Picasso</a>.</a> -</p> -!! end - -!! test -URL-encoding in URL functions (single parameter) -!! input -{{localurl:Some page|amp=&}} -!! result -<p>/index.php?title=Some_page&amp=& -</p> -!! end - -!! test -URL-encoding in URL functions (multiple parameters) -!! input -{{localurl:Some page|q=?&=&}} -!! result -<p>/index.php?title=Some_page&q=?&amp=& -</p> -!! end - -### -### Quotes -### - -!! test -Quotes -!! input -Normal text. '''Bold text.''' Normal text. ''Italic text.'' - -Normal text. '''''Bold italic text.''''' Normal text. -!!result -<p>Normal text. <b>Bold text.</b> Normal text. <i>Italic text.</i> -</p><p>Normal text. <i><b>Bold italic text.</b></i> Normal text. -</p> -!! end - - -!! test -Unclosed and unmatched quotes -!! input -'''''Bold italic text '''with bold deactivated''' in between.''''' - -'''''Bold italic text ''with italic deactivated'' in between.''''' - -'''Bold text.. - -..spanning two paragraphs (should not work).''' - -'''Bold tag left open - -''Italic tag left open - -Normal text. - -<!-- Unmatching number of opening, closing tags: --> -'''This year''''s election ''should'' beat '''last year''''s. - -''Tom'''s car is bigger than ''Susan'''s. -!! result -<p><i><b>Bold italic text </b>with bold deactivated<b> in between.</b></i> -</p><p><b><i>Bold italic text </i>with italic deactivated<i> in between.</i></b> -</p><p><b>Bold text..</b> -</p><p>..spanning two paragraphs (should not work). -</p><p><b>Bold tag left open</b> -</p><p><i>Italic tag left open</i> -</p><p>Normal text. -</p><p><b>This year'</b>s election <i>should</i> beat <b>last year'</b>s. -</p><p><i>Tom<b>s car is bigger than </b></i><b>Susan</b>s. -</p> -!! end - -### -### Tables -### -### some content taken from http://meta.wikimedia.org/wiki/MediaWiki_User%27s_Guide:_Using_tables -### - -# This should not produce <table></table> as <table><tr><td></td></tr></table> -# is the bare minimun required by the spec, see: -# http://www.w3.org/TR/xhtml-modularization/dtd_module_defs.html#a_module_Basic_Tables -!! test -A table with no data. -!! input -{||} -!! result -!! end - -# A table with nothing but a caption is invalid XHTML, we might want to render -# this as <p>caption</p> -!! test -A table with nothing but a caption -!! input -{| -|+ caption -|} -!! result -<table> -<caption> caption -</caption><tr><td></td></tr></table> - -!! end - -!! test -Simple table -!! input -{| -| 1 || 2 -|- -| 3 || 4 -|} -!! result -<table> -<tr> -<td> 1 </td> -<td> 2 -</td></tr> -<tr> -<td> 3 </td> -<td> 4 -</td></tr></table> - -!! end - -!! test -Multiplication table -!! input -{| border="1" cellpadding="2" -|+Multiplication table -|- -! × !! 1 !! 2 !! 3 -|- -! 1 -| 1 || 2 || 3 -|- -! 2 -| 2 || 4 || 6 -|- -! 3 -| 3 || 6 || 9 -|- -! 4 -| 4 || 8 || 12 -|- -! 5 -| 5 || 10 || 15 -|} -!! result -<table border="1" cellpadding="2"> -<caption>Multiplication table -</caption> -<tr> -<th> × </th> -<th> 1 </th> -<th> 2 </th> -<th> 3 -</th></tr> -<tr> -<th> 1 -</th> -<td> 1 </td> -<td> 2 </td> -<td> 3 -</td></tr> -<tr> -<th> 2 -</th> -<td> 2 </td> -<td> 4 </td> -<td> 6 -</td></tr> -<tr> -<th> 3 -</th> -<td> 3 </td> -<td> 6 </td> -<td> 9 -</td></tr> -<tr> -<th> 4 -</th> -<td> 4 </td> -<td> 8 </td> -<td> 12 -</td></tr> -<tr> -<th> 5 -</th> -<td> 5 </td> -<td> 10 </td> -<td> 15 -</td></tr></table> - -!! end - -!! test -Table rowspan -!! input -{| align=right border=1 -| Cell 1, row 1 -|rowspan=2| Cell 2, row 1 (and 2) -| Cell 3, row 1 -|- -| Cell 1, row 2 -| Cell 3, row 2 -|} -!! result -<table align="right" border="1"> -<tr> -<td> Cell 1, row 1 -</td> -<td rowspan="2"> Cell 2, row 1 (and 2) -</td> -<td> Cell 3, row 1 -</td></tr> -<tr> -<td> Cell 1, row 2 -</td> -<td> Cell 3, row 2 -</td></tr></table> - -!! end - -!! test -Nested table -!! input -{| border=1 -| α -| -{| bgcolor=#ABCDEF border=2 -|nested -|- -|table -|} -|the original table again -|} -!! result -<table border="1"> -<tr> -<td> α -</td> -<td> -<table bgcolor="#ABCDEF" border="2"> -<tr> -<td>nested -</td></tr> -<tr> -<td>table -</td></tr></table> -</td> -<td>the original table again -</td></tr></table> - -!! end - -!! test -Invalid attributes in table cell (bug 1830) -!! input -{| -|Cell:|broken -|} -!! result -<table> -<tr> -<td>broken -</td></tr></table> - -!! end - - -!! test -Table security: embedded pipes (http://lists.wikimedia.org/mailman/htdig/wikitech-l/2006-April/022293.html) -!! input -{| -| |[ftp://|x||]" onmouseover="alert(document.cookie)">test -!! result -<table> -<tr> -<td>[<a href="ftp://%7Cx" class="external free" rel="nofollow">ftp://%7Cx</a></td> -<td>]" onmouseover="alert(document.cookie)">test -</td> -</tr> -</table> - -!! end - - -### -### Internal links -### -!! test -Plain link, capitalized -!! input -[[Main Page]] -!! result -<p><a href="/wiki/Main_Page">Main Page</a> -</p> -!! end - -!! test -Plain link, uncapitalized -!! input -[[main Page]] -!! result -<p><a href="/wiki/Main_Page">main Page</a> -</p> -!! end - -!! test -Piped link -!! input -[[Main Page|The Main Page]] -!! result -<p><a href="/wiki/Main_Page" title="Main Page">The Main Page</a> -</p> -!! end - -!! test -Broken link -!! input -[[Zigzagzogzagzig]] -!! result -<p><a href="/index.php?title=Zigzagzogzagzig&action=edit&redlink=1" class="new" title="Zigzagzogzagzig (page does not exist)">Zigzagzogzagzig</a> -</p> -!! end - -!! test -Broken link with fragment -!! input -[[Zigzagzogzagzig#zug]] -!! result -<p><a href="/index.php?title=Zigzagzogzagzig&action=edit&redlink=1" class="new" title="Zigzagzogzagzig (page does not exist)">Zigzagzogzagzig#zug</a> -</p> -!! end - -!! test -Special page link with fragment -!! input -[[Special:Version#anchor]] -!! result -<p><a href="/wiki/Special:Version#anchor" title="Special:Version">Special:Version#anchor</a> -</p> -!! end - -!! test -Nonexistent special page link with fragment -!! input -[[Special:ThisNameWillHopefullyNeverBeUsed#anchor]] -!! result -<p><a href="/wiki/Special:ThisNameWillHopefullyNeverBeUsed" class="new" title="Special:ThisNameWillHopefullyNeverBeUsed (page does not exist)">Special:ThisNameWillHopefullyNeverBeUsed#anchor</a> -</p> -!! end - -!! test -Link with prefix -!! input -xxx[[main Page]], xxx[[Main Page]], Xxx[[main Page]] XXX[[main Page]], XXX[[Main Page]] -!! result -<p>xxx<a href="/wiki/Main_Page">main Page</a>, xxx<a href="/wiki/Main_Page">Main Page</a>, Xxx<a href="/wiki/Main_Page">main Page</a> XXX<a href="/wiki/Main_Page">main Page</a>, XXX<a href="/wiki/Main_Page">Main Page</a> -</p> -!! end - -!! test -Link with suffix -!! input -[[Main Page]]xxx, [[Main Page]]XXX, [[Main Page]]!!! -!! result -<p><a href="/wiki/Main_Page" title="Main Page">Main Pagexxx</a>, <a href="/wiki/Main_Page">Main Page</a>XXX, <a href="/wiki/Main_Page">Main Page</a>!!! -</p> -!! end - -!! test -Link with 3 brackets -!! input -[[[main page]]] -!! result -<p>[[[main page]]] -</p> -!! end - -!! test -Piped link with 3 brackets -!! input -[[[main page|the main page]]] -!! result -<p>[[[main page|the main page]]] -</p> -!! end - -!! test -Link with multiple pipes -!! input -[[Main Page|The|Main|Page]] -!! result -<p><a href="/wiki/Main_Page" title="Main Page">The|Main|Page</a> -</p> -!! end - -!! test -Link to namespaces -!! input -[[Talk:Parser testing]], [[Meta:Disclaimers]] -!! result -<p><a href="/index.php?title=Talk:Parser_testing&action=edit&redlink=1" class="new" title="Talk:Parser testing (page does not exist)">Talk:Parser testing</a>, <a href="/index.php?title=Meta:Disclaimers&action=edit&redlink=1" class="new" title="Meta:Disclaimers (page does not exist)">Meta:Disclaimers</a> -</p> -!! end - -!! test -Piped link to namespace -!! input -[[Meta:Disclaimers|The disclaimers]] -!! result -<p><a href="/index.php?title=Meta:Disclaimers&action=edit&redlink=1" class="new" title="Meta:Disclaimers (page does not exist)">The disclaimers</a> -</p> -!! end - -!! test -Link containing } -!! input -[[Usually caused by a typo (oops}]] -!! result -<p>[[Usually caused by a typo (oops}]] -</p> -!! end - -!! test -Link containing % (not as a hex sequence) -!! input -[[7% Solution]] -!! result -<p><a href="/index.php?title=7%25_Solution&action=edit&redlink=1" class="new" title="7% Solution (page does not exist)">7% Solution</a> -</p> -!! end - -!! test -Link containing % as a single hex sequence interpreted to char -!! input -[[7%25 Solution]] -!! result -<p><a href="/index.php?title=7%25_Solution&action=edit&redlink=1" class="new" title="7% Solution (page does not exist)">7% Solution</a> -</p> -!!end - -!! test -Link containing % as a double hex sequence interpreted to hex sequence -!! input -[[7%2525 Solution]] -!! result -<p>[[7%2525 Solution]] -</p> -!!end - -!! test -Link containing "#<" and "#>" % as a hex sequences- these are valid section anchors -Example for such a section: == < == -!! input -[[%23%3c]][[%23%3e]] -!! result -<p><a href="#.3C">#<</a><a href="#.3E">#></a> -</p> -!! end - -!! test -Link containing "<#" and ">#" as a hex sequences -!! input -[[%3c%23]][[%3e%23]] -!! result -<p>[[%3c%23]][[%3e%23]] -</p> -!! end - -!! test -Link containing double-single-quotes '' (bug 4598) -!! input -[[Lista d''e paise d''o munno]] -!! result -<p><a href="/index.php?title=Lista_d%27%27e_paise_d%27%27o_munno&action=edit&redlink=1" class="new" title="Lista d''e paise d''o munno (page does not exist)">Lista d''e paise d''o munno</a> -</p> -!! end - -!! test -Link containing double-single-quotes '' in text (bug 4598 sanity check) -!! input -Some [[Link|pretty ''italics'' and stuff]]! -!! result -<p>Some <a href="/index.php?title=Link&action=edit&redlink=1" class="new" title="Link (page does not exist)">pretty <i>italics</i> and stuff</a>! -</p> -!! end - -!! test -Link containing double-single-quotes '' in text embedded in italics (bug 4598 sanity check) -!! input -''Some [[Link|pretty ''italics'' and stuff]]! -!! result -<p><i>Some <a href="/index.php?title=Link&action=edit&redlink=1" class="new" title="Link (page does not exist)">pretty <i>italics</i> and stuff</a>!</i> -</p> -!! end - -!! test -Link with double quotes in title part (literal) and alternate part (interpreted) -!! input -[[File:Denys Savchenko ''Pentecoste''.jpg]] - -[[''Pentecoste'']] - -[[''Pentecoste''|Pentecoste]] - -[[''Pentecoste''|''Pentecoste'']] -!! result -<p><a href="/index.php?title=Special:Upload&wpDestFile=Denys_Savchenko_%27%27Pentecoste%27%27.jpg" class="new" title="File:Denys Savchenko ''Pentecoste''.jpg">File:Denys Savchenko <i>Pentecoste</i>.jpg</a> -</p><p><a href="/index.php?title=%27%27Pentecoste%27%27&action=edit&redlink=1" class="new" title="''Pentecoste'' (page does not exist)">''Pentecoste''</a> -</p><p><a href="/index.php?title=%27%27Pentecoste%27%27&action=edit&redlink=1" class="new" title="''Pentecoste'' (page does not exist)">Pentecoste</a> -</p><p><a href="/index.php?title=%27%27Pentecoste%27%27&action=edit&redlink=1" class="new" title="''Pentecoste'' (page does not exist)"><i>Pentecoste</i></a> -</p> -!! end - -!! test -Plain link to URL -!! input -[[http://www.example.com]] -!! result -<p>[<a href="http://www.example.com" class="external autonumber" rel="nofollow">[1]</a>] -</p> -!! end - -# I'm fairly sure the expected result here is wrong. -# We want these to be URL links, not pseudo-pages with URLs for titles.... -# However the current output is also pretty screwy. -# -# ---- -# I'm changing it to match the current output--it arguably makes more -# sense in the light of the test above. Old expected result was: -#<p>Piped link to URL: <a href="/index.php?title=Http://www.example.com&action=edit" class="new">an example URL</a> -#</p> -# But I think this test is bordering on "garbage in, garbage out" anyway. -# -- wtm -!! test -Piped link to URL -!! input -Piped link to URL: [[http://www.example.com|an example URL]] -!! result -<p>Piped link to URL: [<a href="http://www.example.com%7Can" class="external text" rel="nofollow">example URL</a>] -</p> -!! end - -!! test -BUG 2: [[page|http://url/]] should link to page, not http://url/ -!! input -[[Main Page|http://url/]] -!! result -<p><a href="/wiki/Main_Page" title="Main Page">http://url/</a> -</p> -!! end - -!! test -BUG 337: Escaped self-links should be bold -!! options -title=[[Bug462]] -!! input -[[Bug462]] [[Bug462]] -!! result -<p><strong class="selflink">Bug462</strong> <strong class="selflink">Bug462</strong> -</p> -!! end - -!! test -Self-link to section should not be bold -!! options -title=[[Main Page]] -!! input -[[Main Page#section]] -!! result -<p><a href="/wiki/Main_Page#section" title="Main Page">Main Page#section</a> -</p> -!! end - -!! article -00 -!! text -This is 00. -!! endarticle - -!!test -Self-link to numeric title -!!options -title=[[0]] -!!input -[[0]] -!!result -<p><strong class="selflink">0</strong> -</p> -!!end - -!!test -Link to numeric-equivalent title -!!options -title=[[0]] -!!input -[[00]] -!!result -<p><a href="/wiki/00">00</a> -</p> -!!end - -!! test -<nowiki> inside a link -!! input -[[Main<nowiki> Page</nowiki>]] [[Main Page|the main page <nowiki>[it's not very good]</nowiki>]] -!! result -<p>[[Main Page]] <a href="/wiki/Main_Page" title="Main Page">the main page [it's not very good]</a> -</p> -!! end - -!! test -Non-breaking spaces in title -!! input -[[ Main Page ]] -!! result -<p><a href="/wiki/Main_Page" title="Main Page"> Main Page </a> -</p> -!!end - - -### -### Interwiki links (see maintenance/interwiki.sql) -### - -!! test -Inline interwiki link -!! input -[[MeatBall:SoftSecurity]] -!! result -<p><a href="http://www.usemod.com/cgi-bin/mb.pl?SoftSecurity" class="extiw">MeatBall:SoftSecurity</a> -</p> -!! end - -!! test -Inline interwiki link with empty title (bug 2372) -!! input -[[MeatBall:]] -!! result -<p><a href="http://www.usemod.com/cgi-bin/mb.pl?" class="extiw">MeatBall:</a> -</p> -!! end - -!! test -Interwiki link encoding conversion (bug 1636) -!! input -*[[Wikipedia:ro:Olteniţa]] -*[[Wikipedia:ro:Olteniţa]] -!! result -<ul><li><a href="http://en.wikipedia.org/wiki/ro:Olteni%C5%A3a" class="extiw" title="wikipedia:ro:Olteniţa">Wikipedia:ro:Olteniţa</a> -</li><li><a href="http://en.wikipedia.org/wiki/ro:Olteni%C5%A3a" class="extiw" title="wikipedia:ro:Olteniţa">Wikipedia:ro:Olteniţa</a> -</li></ul> - -!! end - -!! test -Interwiki link with fragment (bug 2130) -!! input -[[MeatBall:SoftSecurity#foo]] -!! result -<p><a href="http://www.usemod.com/cgi-bin/mb.pl?SoftSecurity#foo" class="extiw" title="meatball:SoftSecurity">MeatBall:SoftSecurity#foo</a> -</p> -!! end - -!! test -Interlanguage link -!! input -Blah blah blah -[[zh:Chinese]] -!!result -<p>Blah blah blah -</p> -!! end - -!! test -Double interlanguage link -!! input -Blah blah blah -[[es:Spanish]] -[[zh:Chinese]] -!!result -<p>Blah blah blah -</p> -!! end - -!! test -Interlanguage link, with prefix links -!! options -language=ln -!! input -Blah blah blah -[[zh:Chinese]] -!!result -<p>Blah blah blah -</p> -!! end - -!! test -Double interlanguage link, with prefix links (bug 8897) -!! options -language=ln -!! input -Blah blah blah -[[es:Spanish]] -[[zh:Chinese]] -!!result -<p>Blah blah blah -</p> -!! end - - -## -## XHTML tidiness -### - -!! test -<br> to <br /> -!! input -1<br>2<br />3 -!! result -<p>1<br />2<br />3 -</p> -!! end - -!! test -Incorrecly removing closing slashes from correctly formed XHTML -!! input -<br style="clear:both;" /> -!! result -<p><br style="clear:both;" /> -</p> -!! end - -!! test -Failing to transform badly formed HTML into correct XHTML -!! input -<br clear=left> -<br clear=right> -<br clear=all> -!! result -<p><br clear="left" /> -<br clear="right" /> -<br clear="all" /> -</p> -!!end - -!! test -Horizontal ruler (should it add that extra space?) -!! input -<hr> -<hr > -foo <hr -> bar -!! result -<hr /> -<hr /> -foo <hr /> bar - -!! end - -### -### Block-level elements -### -!! test -Common list -!! input -*Common list -* item 2 -*item 3 -!! result -<ul><li>Common list -</li><li> item 2 -</li><li>item 3 -</li></ul> - -!! end - -!! test -Numbered list -!! input -#Numbered list -#item 2 -# item 3 -!! result -<ol><li>Numbered list -</li><li>item 2 -</li><li> item 3 -</li></ol> - -!! end - -!! test -Mixed list -!! input -*Mixed list -*# with numbers -** and bullets -*# and numbers -*bullets again -**bullet level 2 -***bullet level 3 -***#Number on level 4 -**bullet level 2 -**#Number on level 3 -**#Number on level 3 -*#number level 2 -*Level 1 -!! result -<ul><li>Mixed list -<ol><li> with numbers -</li></ol> -<ul><li> and bullets -</li></ul> -<ol><li> and numbers -</li></ol> -</li><li>bullets again -<ul><li>bullet level 2 -<ul><li>bullet level 3 -<ol><li>Number on level 4 -</li></ol> -</li></ul> -</li><li>bullet level 2 -<ol><li>Number on level 3 -</li><li>Number on level 3 -</li></ol> -</li></ul> -<ol><li>number level 2 -</li></ol> -</li><li>Level 1 -</li></ul> - -!! end - -!! test -List items are not parsed correctly following a <pre> block (bug 785) -!! input -* <pre>foo</pre> -* <pre>bar</pre> -* zar -!! result -<ul><li> <pre>foo</pre> -</li><li> <pre>bar</pre> -</li><li> zar -</li></ul> - -!! end - -### -### Magic Words -### - -!! test -Magic Word: {{CURRENTDAY}} -!! input -{{CURRENTDAY}} -!! result -<p>1 -</p> -!! end - -!! test -Magic Word: {{CURRENTDAY2}} -!! input -{{CURRENTDAY2}} -!! result -<p>01 -</p> -!! end - -!! test -Magic Word: {{CURRENTDAYNAME}} -!! input -{{CURRENTDAYNAME}} -!! result -<p>Thursday -</p> -!! end - -!! test -Magic Word: {{CURRENTDOW}} -!! input -{{CURRENTDOW}} -!! result -<p>4 -</p> -!! end - -!! test -Magic Word: {{CURRENTMONTH}} -!! input -{{CURRENTMONTH}} -!! result -<p>01 -</p> -!! end - -!! test -Magic Word: {{CURRENTMONTHABBREV}} -!! input -{{CURRENTMONTHABBREV}} -!! result -<p>Jan -</p> -!! end - -!! test -Magic Word: {{CURRENTMONTHNAME}} -!! input -{{CURRENTMONTHNAME}} -!! result -<p>January -</p> -!! end - -!! test -Magic Word: {{CURRENTMONTHNAMEGEN}} -!! input -{{CURRENTMONTHNAMEGEN}} -!! result -<p>January -</p> -!! end - -!! test -Magic Word: {{CURRENTTIME}} -!! input -{{CURRENTTIME}} -!! result -<p>00:02 -</p> -!! end - -!! test -Magic Word: {{CURRENTWEEK}} (@bug 4594) -!! input -{{CURRENTWEEK}} -!! result -<p>1 -</p> -!! end - -!! test -Magic Word: {{CURRENTYEAR}} -!! input -{{CURRENTYEAR}} -!! result -<p>1970 -</p> -!! end - -!! test -Magic Word: {{FULLPAGENAME}} -!! options -title=[[User:Ævar Arnfjörð Bjarmason]] -!! input -{{FULLPAGENAME}} -!! result -<p>User:Ævar Arnfjörð Bjarmason -</p> -!! end - -!! test -Magic Word: {{FULLPAGENAMEE}} -!! options -title=[[User:Ævar Arnfjörð Bjarmason]] -!! input -{{FULLPAGENAMEE}} -!! result -<p>User:%C3%86var_Arnfj%C3%B6r%C3%B0_Bjarmason -</p> -!! end - -!! test -Magic Word: {{NAMESPACE}} -!! options -title=[[User:Ævar Arnfjörð Bjarmason]] -!! input -{{NAMESPACE}} -!! result -<p>User -</p> -!! end - -!! test -Magic Word: {{NAMESPACEE}} -!! options -title=[[User:Ævar Arnfjörð Bjarmason]] -!! input -{{NAMESPACEE}} -!! result -<p>User -</p> -!! end - -!! test -Magic Word: {{NUMBEROFFILES}} -!! input -{{NUMBEROFFILES}} -!! result -<p>2 -</p> -!! end - -!! test -Magic Word: {{PAGENAME}} -!! options -title=[[User:Ævar Arnfjörð Bjarmason]] -!! input -{{PAGENAME}} -!! result -<p>Ævar Arnfjörð Bjarmason -</p> -!! end - -!! test -Magic Word: {{PAGENAMEE}} -!! options -title=[[User:Ævar Arnfjörð Bjarmason]] -!! input -{{PAGENAMEE}} -!! result -<p>%C3%86var_Arnfj%C3%B6r%C3%B0_Bjarmason -</p> -!! end - -!! test -Magic Word: {{REVISIONID}} -!! input -{{REVISIONID}} -!! result -<p>1337 -</p> -!! end - -!! test -Magic Word: {{SCRIPTPATH}} -!! input -{{SCRIPTPATH}} -!! result -<p>/ -</p> -!! end - -!! test -Magic Word: {{SERVER}} -!! input -{{SERVER}} -!! result -<p><a href="http://Britney-Spears" class="external free" rel="nofollow">http://Britney-Spears</a> -</p> -!! end - -!! test -Magic Word: {{SERVERNAME}} -!! input -{{SERVERNAME}} -!! result -<p>Britney-Spears -</p> -!! end - -!! test -Magic Word: {{SITENAME}} -!! input -{{SITENAME}} -!! result -<p>MediaWiki -</p> -!! end - -!! test -Namespace 1 {{ns:1}} -!! input -{{ns:1}} -!! result -<p>Talk -</p> -!! end - -!! test -Namespace 1 {{ns:01}} -!! input -{{ns:01}} -!! result -<p>Talk -</p> -!! end - -!! test -Namespace 0 {{ns:0}} (bug 4783) -!! input -{{ns:0}} -!! result - -!! end - -!! test -Namespace 0 {{ns:00}} (bug 4783) -!! input -{{ns:00}} -!! result - -!! end - -!! test -Namespace -1 {{ns:-1}} -!! input -{{ns:-1}} -!! result -<p>Special -</p> -!! end - -!! test -Namespace User {{ns:User}} -!! input -{{ns:User}} -!! result -<p>User -</p> -!! end - -!! test -Namespace User talk {{ns:User_talk}} -!! input -{{ns:User_talk}} -!! result -<p>User talk -</p> -!! end - -!! test -Namespace User talk {{ns:uSeR tAlK}} -!! input -{{ns:uSeR tAlK}} -!! result -<p>User talk -</p> -!! end - -!! test -Namespace File {{ns:File}} -!! input -{{ns:File}} -!! result -<p>File -</p> -!! end - -!! test -Namespace File {{ns:Image}} -!! input -{{ns:Image}} -!! result -<p>File -</p> -!! end - -!! test -Namespace (lang=de) Benutzer {{ns:User}} -!! options -language=de -!! input -{{ns:User}} -!! result -<p>Benutzer -</p> -!! end - -!! test -Namespace (lang=de) Benutzer Diskussion {{ns:3}} -!! options -language=de -!! input -{{ns:3}} -!! result -<p>Benutzer Diskussion -</p> -!! end - - -!! test -Urlencode -!! input -{{urlencode:hi world?!}} -{{urlencode:hi world?!|WIKI}} -{{urlencode:hi world?!|PATH}} -{{urlencode:hi world?!|QUERY}} -!! result -<p>hi+world%3F%21 -hi_world%3F! -hi%20world%3F%21 -hi+world%3F%21 -</p> -!! end - -### -### Magic links -### -!! test -Magic links: internal link to RFC (bug 479) -!! input -[[RFC 123]] -!! result -<p><a href="/index.php?title=RFC_123&action=edit&redlink=1" class="new" title="RFC 123 (page does not exist)">RFC 123</a> -</p> -!! end - -!! test -Magic links: RFC (bug 479) -!! input -RFC 822 -!! result -<p><a href="http://tools.ietf.org/html/rfc822" class="external mw-magiclink-rfc">RFC 822</a> -</p> -!! end - -!! test -Magic links: ISBN (bug 1937) -!! input -ISBN 0-306-40615-2 -!! result -<p><a href="/wiki/Special:BookSources/0306406152" class="internal mw-magiclink-isbn">ISBN 0-306-40615-2</a> -</p> -!! end - -!! test -Magic links: PMID incorrectly converts space to underscore -!! input -PMID 1234 -!! result -<p><a href="http://www.ncbi.nlm.nih.gov/pubmed/1234?dopt=Abstract" class="external mw-magiclink-pmid">PMID 1234</a> -</p> -!! end - -### -### Templates -#### - -!! test -Nonexistent template -!! input -{{thistemplatedoesnotexist}} -!! result -<p><a href="/index.php?title=Template:Thistemplatedoesnotexist&action=edit&redlink=1" class="new" title="Template:Thistemplatedoesnotexist (page does not exist)">Template:Thistemplatedoesnotexist</a> -</p> -!! end - -!! article -Template:test -!! text -This is a test template -!! endarticle - -!! test -Simple template -!! input -{{test}} -!! result -<p>This is a test template -</p> -!! end - -!! test -Template with explicit namespace -!! input -{{Template:test}} -!! result -<p>This is a test template -</p> -!! end - - -!! article -Template:paramtest -!! text -This is a test template with parameter {{{param}}} -!! endarticle - -!! test -Template parameter -!! input -{{paramtest|param=foo}} -!! result -<p>This is a test template with parameter foo -</p> -!! end - -!! article -Template:paramtestnum -!! text -[[{{{1}}}|{{{2}}}]] -!! endarticle - -!! test -Template unnamed parameter -!! input -{{paramtestnum|Main Page|the main page}} -!! result -<p><a href="/wiki/Main_Page" title="Main Page">the main page</a> -</p> -!! end - -!! article -Template:templatesimple -!! text -(test) -!! endarticle - -!! article -Template:templateredirect -!! text -#redirect [[Template:templatesimple]] -!! endarticle - -!! article -Template:templateasargtestnum -!! text -{{{{{1}}}}} -!! endarticle - -!! article -Template:templateasargtest -!! text -{{template{{{templ}}}}} -!! endarticle - -!! article -Template:templateasargtest2 -!! text -{{{{{templ}}}}} -!! endarticle - -!! test -Template with template name as unnamed argument -!! input -{{templateasargtestnum|templatesimple}} -!! result -<p>(test) -</p> -!! end - -!! test -Template with template name as argument -!! input -{{templateasargtest|templ=simple}} -!! result -<p>(test) -</p> -!! end - -!! test -Template with template name as argument (2) -!! input -{{templateasargtest2|templ=templatesimple}} -!! result -<p>(test) -</p> -!! end - -!! article -Template:templateasargtestdefault -!! text -{{{{{templ|templatesimple}}}}} -!! endarticle - -!! article -Template:templa -!! text -'''templ''' -!! endarticle - -!! test -Template with default value -!! input -{{templateasargtestdefault}} -!! result -<p>(test) -</p> -!! end - -!! test -Template with default value (value set) -!! input -{{templateasargtestdefault|templ=templa}} -!! result -<p><b>templ</b> -</p> -!! end - -!! test -Template redirect -!! input -{{templateredirect}} -!! result -<p>(test) -</p> -!! end - -!! test -Template with argument in separate line -!! input -{{ templateasargtest | - templ = simple }} -!! result -<p>(test) -</p> -!! end - -!! test -Template with complex template as argument -!! input -{{paramtest| - param ={{ templateasargtest | - templ = simple }}}} -!! result -<p>This is a test template with parameter (test) -</p> -!! end - -!! test -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 class="thumbinner" style="width:182px;"><a href="/index.php?title=Special:Upload&wpDestFile=Noimage.png" class="new" title="File:Noimage.png">File:Noimage.png</a> <div class="thumbcaption"><a href="/index.php?title=No_link&action=edit&redlink=1" class="new" title="No link (page does not exist)">link</a> <a href="/index.php?title=No_link&action=edit&redlink=1" class="new" title="No link (page does not exist)">caption</a></div></div></div> - -!! end - -!! article -Template:complextemplate -!! text -{{{1}}} {{paramtest| - param ={{{param}}}}} -!! endarticle - -!! test -Template with complex arguments -!! input -{{complextemplate| - param ={{ templateasargtest | - templ = simple }}|[[Template:complextemplate|link]]}} -!! result -<p><a href="/wiki/Template:Complextemplate" title="Template:Complextemplate">link</a> This is a test template with parameter (test) -</p> -!! end - -!! test -BUG 553: link with two variables in a piped link -!! input -{| -|[[{{{1}}}|{{{2}}}]] -|} -!! result -<table> -<tr> -<td>[[{{{1}}}|{{{2}}}]] -</td></tr></table> - -!! end - -!! test -Magic variable as template parameter -!! input -{{paramtest|param={{SITENAME}}}} -!! result -<p>This is a test template with parameter MediaWiki -</p> -!! end - -!! article -Template:linktest -!! text -[[{{{param}}}|link]] -!! endarticle - -!! test -Template parameter as link source -!! input -{{linktest|param=Main Page}} -!! result -<p><a href="/wiki/Main_Page" title="Main Page">link</a> -</p> -!! end - - -!!article -Template:paramtest2 -!! text -including another template, {{paramtest|param={{{arg}}}}} -!! endarticle - -!! test -Template passing argument to another template -!! input -{{paramtest2|arg='hmm'}} -!! result -<p>including another template, This is a test template with parameter 'hmm' -</p> -!! end - -!! article -Template:Linktest2 -!! text -Main Page -!! endarticle - -!! test -Template as link source -!! input -[[{{linktest2}}]] -!! result -<p><a href="/wiki/Main_Page">Main Page</a> -</p> -!! end - - -!! article -Template:loop1 -!! text -{{loop2}} -!! endarticle - -!! article -Template:loop2 -!! text -{{loop1}} -!! endarticle - -!! test -Template infinite loop -!! input -{{loop1}} -!! result -<p><span class="error">Template loop detected: <a href="/wiki/Template:Loop1">Template:Loop1</a></span> -</p> -!! end - -!! test -Template from main namespace -!! input -{{:Main Page}} -!! result -<p>blah blah -</p> -!! end - -!! article -Template:table -!! text -{| -| 1 || 2 -|- -| 3 || 4 -|} -!! endarticle - -!! test -BUG 529: Template with table, not included at beginning of line -!! input -foo {{table}} -!! result -<p>foo -</p> -<table> -<tr> -<td> 1 </td> -<td> 2 -</td></tr> -<tr> -<td> 3 </td> -<td> 4 -</td></tr></table> - -!! end - -!! test -BUG 523: Template shouldn't eat newline (or add an extra one before table) -!! input -foo -{{table}} -!! result -<p>foo -</p> -<table> -<tr> -<td> 1 </td> -<td> 2 -</td></tr> -<tr> -<td> 3 </td> -<td> 4 -</td></tr></table> - -!! end - -!! test -BUG 41: Template parameters shown as broken links -!! input -{{{parameter}}} -!! result -<p>{{{parameter}}} -</p> -!! end - - -!! article -Template:MSGNW test -!! text -''None'' of '''this''' should be -* interpreted - but rather passed unmodified -{{test}} -!! endarticle - -# hmm, fix this or just deprecate msgnw and document its behavior? -!! test -msgnw keyword -!! options -disabled -!! input -{{msgnw:MSGNW test}} -!! result -<p>''None'' of '''this''' should be -* interpreted - but rather passed unmodified -{{test}} -</p> -!! end - -!! test -int keyword -!! input -{{int:youhavenewmessages|lots of money|not!}} -!! result -<p>You have lots of money (not!). -</p> -!! end - -!! article -Template:Includes -!! text -Foo<noinclude>zar</noinclude><includeonly>bar</includeonly> -!! endarticle - -!! test -<includeonly> and <noinclude> being included -!! input -{{Includes}} -!! result -<p>Foobar -</p> -!! end - -!! article -Template:Includes2 -!! text -<onlyinclude>Foo</onlyinclude>bar -!! endarticle - -!! test -<onlyinclude> being included -!! input -{{Includes2}} -!! result -<p>Foo -</p> -!! end - - -!! article -Template:Includes3 -!! text -<onlyinclude>Foo</onlyinclude>bar<includeonly>zar</includeonly> -!! endarticle - -!! test -<onlyinclude> and <includeonly> being included -!! input -{{Includes3}} -!! result -<p>Foo -</p> -!! end - -!! test -<includeonly> and <noinclude> on a page -!! input -Foo<noinclude>zar</noinclude><includeonly>bar</includeonly> -!! result -<p>Foozar -</p> -!! end - -!! test -<onlyinclude> on a page -!! input -<onlyinclude>Foo</onlyinclude>bar -!! result -<p>Foobar -</p> -!! end - -!! article -Template:Includeonly section -!! text -<includeonly> -==Includeonly section== -</includeonly> -==Section T-1== -!!endarticle - -!! test -Bug 6563: Edit link generation for section shown by <includeonly> -!! input -{{includeonly section}} -!! result -<h2><span class="editsection">[<a href="/index.php?title=Template:Includeonly_section&action=edit&section=T-1" title="Template:Includeonly section">edit</a>]</span> <span class="mw-headline" id="Includeonly_section">Includeonly section</span></h2> -<h2><span class="editsection">[<a href="/index.php?title=Template:Includeonly_section&action=edit&section=T-2" title="Template:Includeonly section">edit</a>]</span> <span class="mw-headline" id="Section_T-1">Section T-1</span></h2> - -!! end - -# Uses same input as the contents of [[Template:Includeonly section]] -!! test -Bug 6563: Section extraction for section shown by <includeonly> -!! options -section=T-2 -!! input -<includeonly> -==Includeonly section== -</includeonly> -==Section T-2== -!! result -==Section T-2== -!! end - -!! test -Bug 6563: Edit link generation for section suppressed by <includeonly> -!! input -<includeonly> -==Includeonly section== -</includeonly> -==Section 1== -!! result -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: Section 1">edit</a>]</span> <span class="mw-headline" id="Section_1">Section 1</span></h2> - -!! end - -!! test -Bug 6563: Section extraction for section suppressed by <includeonly> -!! options -section=1 -!! input -<includeonly> -==Includeonly section== -</includeonly> -==Section 1== -!! result -==Section 1== -!! end - -### -### Pre-save transform tests -### -!! test -pre-save transform: subst: -!! options -PST -!! input -{{subst:test}} -!! result -This is a test template -!! end - -!! test -pre-save transform: normal template -!! options -PST -!! input -{{test}} -!! result -{{test}} -!! end - -!! test -pre-save transform: nonexistent template -!! options -PST -!! input -{{thistemplatedoesnotexist}} -!! result -{{thistemplatedoesnotexist}} -!! end - - -!! test -pre-save transform: subst magic variables -!! options -PST -!! input -{{subst:SITENAME}} -!! result -MediaWiki -!! end - -# This is bug 89, which I fixed. -- wtm -!! test -pre-save transform: subst: templates with parameters -!! options -pst -!! input -{{subst:paramtest|param="something else"}} -!! result -This is a test template with parameter "something else" -!! end - -!! article -Template:nowikitest -!! text -<nowiki>'''not wiki'''</nowiki> -!! endarticle - -!! test -pre-save transform: nowiki in subst (bug 1188) -!! options -pst -!! input -{{subst:nowikitest}} -!! result -<nowiki>'''not wiki'''</nowiki> -!! end - - -!! article -Template:commenttest -!! text -This template has <!-- a comment --> in it. -!! endarticle - -!! test -pre-save transform: comment in subst (bug 1936) -!! options -pst -!! input -{{subst:commenttest}} -!! result -This template has <!-- a comment --> in it. -!! end - -!! test -pre-save transform: unclosed tag -!! options -pst noxml -!! input -<nowiki>'''not wiki''' -!! result -<nowiki>'''not wiki''' -!! end - -!! test -pre-save transform: mixed tag case -!! options -pst noxml -!! input -<NOwiki>'''not wiki'''</noWIKI> -!! result -<NOwiki>'''not wiki'''</noWIKI> -!! end - -!! test -pre-save transform: unclosed comment in <nowiki> -!! options -pst noxml -!! input -wiki<nowiki>nowiki<!--nowiki</nowiki>wiki -!! result -wiki<nowiki>nowiki<!--nowiki</nowiki>wiki -!!end - -!! article -Template:dangerous -!!text -<span onmouseover="alert('crap')">Oh no</span> -!!endarticle - -!!test -(confirming safety of fix for subst bug 1936) -!! input -{{Template:dangerous}} -!! result -<p><span>Oh no</span> -</p> -!! end - -!! test -pre-save transform: comment containing gallery (bug 5024) -!! options -pst -!! input -<!-- <gallery>data</gallery> --> -!!result -<!-- <gallery>data</gallery> --> -!!end - -!! test -pre-save transform: comment containing extension -!! options -pst -!! input -<!-- <tag>data</tag> --> -!!result -<!-- <tag>data</tag> --> -!!end - -!! test -pre-save transform: comment containing nowiki -!! options -pst -!! input -<!-- <nowiki>data</nowiki> --> -!!result -<!-- <nowiki>data</nowiki> --> -!!end - -!! test -pre-save transform: comment containing math -!! options -pst -!! input -<!-- <math>data</math> --> -!!result -<!-- <math>data</math> --> -!!end - -!! test -pre-save transform: <noinclude> in subst (bug 3298) -!! options -pst -!! input -{{subst:Includes}} -!! result -Foobar -!! end - -!! test -pre-save transform: <onlyinclude> in subst (bug 3298) -!! options -pst -!! input -{{subst:Includes2}} -!! result -Foo -!! end - -!! article -Template:SubstTest -!!text -{{<includeonly>subst:</includeonly>Includes}} -!! endarticle - -!! article -Template:SafeSubstTest -!! text -{{<includeonly>safesubst:</includeonly>Includes}} -!! endarticle - -!! test -bug 22297: safesubst: works during PST -!! options -pst -!! input -{{subst:SafeSubstTest}}{{safesubst:SubstTest}} -!! result -FoobarFoobar -!! end - -!! test -bug 22297: safesubst: works during normal parse -!! input -{{SafeSubstTest}} -!! result -<p>Foobar -</p> -!! end - -!! test: -subst: does not work during normal parse -!! input -{{SubstTest}} -!! result -<p>{{subst:Includes}} -</p> -!! end - -!! test -pre-save transform: context links ("pipe trick") -!! options -pst -!! input -[[Article (context)|]] -[[Bar:Article|]] -[[:Bar:Article|]] -[[Bar:Article (context)|]] -[[:Bar:Article (context)|]] -[[|Article]] -[[|Article (context)]] -[[Bar:X (Y) Z|]] -[[:Bar:X (Y) Z|]] -!! result -[[Article (context)|Article]] -[[Bar:Article|Article]] -[[:Bar:Article|Article]] -[[Bar:Article (context)|Article]] -[[:Bar:Article (context)|Article]] -[[Article]] -[[Article (context)]] -[[Bar:X (Y) Z|X (Y) Z]] -[[:Bar:X (Y) Z|X (Y) Z]] -!! end - -!! test -pre-save transform: context links ("pipe trick") with interwiki prefix -!! options -pst -!! input -[[interwiki:Article|]] -[[:interwiki:Article|]] -[[interwiki:Bar:Article|]] -[[:interwiki:Bar:Article|]] -!! result -[[interwiki:Article|Article]] -[[:interwiki:Article|Article]] -[[interwiki:Bar:Article|Bar:Article]] -[[:interwiki:Bar:Article|Bar:Article]] -!! end - -!! test -pre-save transform: context links ("pipe trick") with parens in title -!! options -pst title=[[Somearticle (context)]] -!! input -[[|Article]] -!! result -[[Article (context)|Article]] -!! end - -!! test -pre-save transform: context links ("pipe trick") with comma in title -!! options -pst title=[[Someplace, Somewhere]] -!! input -[[|Otherplace]] -[[Otherplace, Elsewhere|]] -[[Otherplace, Elsewhere, Anywhere|]] -!! result -[[Otherplace, Somewhere|Otherplace]] -[[Otherplace, Elsewhere|Otherplace]] -[[Otherplace, Elsewhere, Anywhere|Otherplace]] -!! end - -!! test -pre-save transform: context links ("pipe trick") with parens and comma -!! options -pst title=[[Someplace (IGNORED), Somewhere]] -!! input -[[|Otherplace]] -[[Otherplace (place), Elsewhere|]] -!! result -[[Otherplace, Somewhere|Otherplace]] -[[Otherplace (place), Elsewhere|Otherplace]] -!! end - -!! test -pre-save transform: context links ("pipe trick") with comma and parens -!! options -pst title=[[Who, me? (context)]] -!! input -[[|Yes, you.]] -[[Me, Myself, and I (1937 song)|]] -!! result -[[Yes, you. (context)|Yes, you.]] -[[Me, Myself, and I (1937 song)|Me, Myself, and I]] -!! end - -!! test -pre-save transform: context links ("pipe trick") with namespace -!! options -pst title=[[Ns:Somearticle]] -!! input -[[|Article]] -!! result -[[Ns:Article|Article]] -!! end - -!! test -pre-save transform: context links ("pipe trick") with namespace and parens -!! options -pst title=[[Ns:Somearticle (context)]] -!! input -[[|Article]] -!! result -[[Ns:Article (context)|Article]] -!! end - -!! test -pre-save transform: context links ("pipe trick") with namespace and comma -!! options -pst title=[[Ns:Somearticle, Context, Whatever]] -!! input -[[|Article]] -!! result -[[Ns:Article, Context, Whatever|Article]] -!! end - -!! test -pre-save transform: context links ("pipe trick") with namespace, comma and parens -!! options -pst title=[[Ns:Somearticle, Context (context)]] -!! input -[[|Article]] -!! result -[[Ns:Article (context)|Article]] -!! end - -!! test -pre-save transform: context links ("pipe trick") with namespace, parens and comma -!! options -pst title=[[Ns:Somearticle (IGNORED), Context]] -!! input -[[|Article]] -!! result -[[Ns:Article, Context|Article]] -!! end - - -### -### Message transform tests -### -!! test -message transform: magic variables -!! options -msg -!! input -{{SITENAME}} -!! result -MediaWiki -!! end - -!! test -message transform: should not transform wiki markup -!! options -msg -!! input -''test'' -!! result -''test'' -!! end - -!! test -message transform: <noinclude> in transcluded template (bug 4926) -!! options -msg -!! input -{{Includes}} -!! result -Foobar -!! end - -!! test -message transform: <onlyinclude> in transcluded template (bug 4926) -!! options -msg -!! input -{{Includes2}} -!! result -Foo -!! end - -!! test -{{#special:}} page name, known -!! options -msg -!! input -{{#special:Recentchanges}} -!! result -Special:RecentChanges -!! end - -!! test -{{#special:}} page name with subpage, known -!! options -msg -!! input -{{#special:Recentchanges/param}} -!! result -Special:RecentChanges/param -!! end - -!! test -{{#special:}} page name, unknown -!! options -msg -!! input -{{#special:foobarnonexistent}} -!! result -No such special page -!! end - -### -### Images -### -!! test -Simple image -!! input -[[Image:foobar.jpg]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -Right-aligned image -!! input -[[Image:foobar.jpg|right]] -!! result -<div class="floatright"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a></div> - -!! end - -!! test -Simple image (using File: namespace, now canonical) -!! input -[[File:foobar.jpg]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -Image with caption -!! input -[[Image:foobar.jpg|right|Caption text]] -!! result -<div class="floatright"><a href="/wiki/File:Foobar.jpg" class="image" title="Caption text"><img alt="Caption text" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a></div> - -!! end - -!! test -Image with link parameter, wiki target -!! input -[[Image:foobar.jpg|link=Target page]] -!! result -<p><a href="/wiki/Target_page" title="Target page"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -Image with link parameter, URL target -!! input -[[Image:foobar.jpg|link=http://example.com/]] -!! result -<p><a href="http://example.com/"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -Image with empty link parameter -!! input -[[Image:foobar.jpg|link=]] -!! result -<p><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /> -</p> -!! end - -!! test -Image with link parameter (wiki target) and unnamed parameter -!! input -[[Image:foobar.jpg|link=Target page|Title]] -!! result -<p><a href="/wiki/Target_page" title="Title"><img alt="Title" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -Image with link parameter (URL target) and unnamed parameter -!! input -[[Image:foobar.jpg|link=http://example.com/|Title]] -!! result -<p><a href="http://example.com/" title="Title"><img alt="Title" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -Thumbnail image with link parameter -!! input -[[Image:foobar.jpg|thumb|link=http://example.com/|Title]] -!! result -<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="http://example.com/"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>Title</div></div></div> - -!! end - -!! test -Image with frame and link -!! input -[[Image:Foobar.jpg|frame|left|This is a test image [[Main Page]]]] -!! result -<div class="thumb tleft"><div class="thumbinner" style="width:1943px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" class="thumbimage" /></a> <div class="thumbcaption">This is a test image <a href="/wiki/Main_Page">Main Page</a></div></div></div> - -!! end - -!! test -Image with frame and link and explicit alt -!! input -[[Image:Foobar.jpg|frame|left|This is a test image [[Main Page]]|alt=Altitude]] -!! result -<div class="thumb tleft"><div class="thumbinner" style="width:1943px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Altitude" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" class="thumbimage" /></a> <div class="thumbcaption">This is a test image <a href="/wiki/Main_Page">Main Page</a></div></div></div> - -!! end - -!! test -Image with wiki markup in implicit alt -!! input -[[Image:Foobar.jpg|testing '''bold''' in alt]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image" title="testing bold in alt"><img alt="testing bold in alt" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -Image with wiki markup in explicit alt -!! input -[[Image:Foobar.jpg|alt=testing '''bold''' in alt]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="testing bold in alt" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -Link to image page- image page normally doesn't exists, hence edit link -Add test with existing image page -#<p><a href="/wiki/File:Test" title="Image:Test">Image:test</a> -!! input -[[:Image:test]] -!! result -<p><a href="/index.php?title=File:Test&action=edit&redlink=1" class="new" title="File:Test (page does not exist)">Image:test</a> -</p> -!! end - -!! test -bug 18784 Link to non-existent image page with caption should use caption as link text -!! input -[[:Image:test|caption]] -!! result -<p><a href="/index.php?title=File:Test&action=edit&redlink=1" class="new" title="File:Test (page does not exist)">caption</a> -</p> -!! end - -!! test -Frameless image caption with a free URL -!! input -[[Image:foobar.jpg|http://example.com]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image" title="http://example.com"><img alt="http://example.com" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -Thumbnail image caption with a free URL -!! input -[[Image:foobar.jpg|thumb|http://example.com]] -!! result -<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File: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" rel="nofollow">http://example.com</a></div></div></div> - -!! end - -!! test -Thumbnail image caption with a free URL and explicit alt -!! input -[[Image:foobar.jpg|thumb|http://example.com|alt=Alteration]] -!! result -<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Alteration" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File: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" rel="nofollow">http://example.com</a></div></div></div> - -!! end - -!! test -BUG 1887: A ISBN with a thumbnail -!! input -[[Image:foobar.jpg|thumb|ISBN 1235467890]] -!! result -<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div><a href="/wiki/Special:BookSources/1235467890" class="internal mw-magiclink-isbn">ISBN 1235467890</a></div></div></div> - -!! end - -!! test -BUG 1887: A RFC with a thumbnail -!! input -[[Image:foobar.jpg|thumb|This is RFC 12354]] -!! result -<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File: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://tools.ietf.org/html/rfc12354" class="external mw-magiclink-rfc">RFC 12354</a></div></div></div> - -!! end - -!! test -BUG 1887: A mailto link with a thumbnail -!! input -[[Image:foobar.jpg|thumb|Please mailto:nobody@example.com]] -!! result -<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File: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" rel="nofollow">mailto:nobody@example.com</a></div></div></div> - -!! end - -!! test -BUG 1887: A <math> with a thumbnail- we don't render math in the parsertests by default, -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 class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File: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 - -!! test -BUG 1887, part 2: A <math> with a thumbnail- math enabled -!! options -math -!! input -[[Image:foobar.jpg|thumb|<math>2+2</math>]] -!! result -<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File: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 - -# Pending resolution to bug 368 -!! test -BUG 648: Frameless image caption with a link -!! input -[[Image:foobar.jpg|text with a [[link]] in it]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image" title="text with a link in it"><img alt="text with a link in it" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -BUG 648: Frameless image caption with a link (suffix) -!! input -[[Image:foobar.jpg|text with a [[link]]foo in it]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image" title="text with a linkfoo in it"><img alt="text with a linkfoo in it" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -BUG 648: Frameless image caption with an interwiki link -!! input -[[Image:foobar.jpg|text with a [[MeatBall:Link]] in it]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image" title="text with a MeatBall:Link in it"><img alt="text with a MeatBall:Link in it" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -BUG 648: Frameless image caption with a piped interwiki link -!! input -[[Image:foobar.jpg|text with a [[MeatBall:Link|link]] in it]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image" title="text with a link in it"><img alt="text with a link in it" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -Escape HTML special chars in image alt text -!! input -[[Image:foobar.jpg|& < > "]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image" title="& < > ""><img alt="& < > "" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -BUG 499: Alt text should have Ӓ, not &1234; -!! input -[[Image:foobar.jpg|♀]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image" title="♀"><img alt="♀" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!! end - -!! test -Broken image caption with link -!! input -[[Image:Foobar.jpg|thumb|This is a broken caption. But [[Main Page|this]] is just an ordinary link. -!! result -<p>[[Image:Foobar.jpg|thumb|This is a broken caption. But <a href="/wiki/Main_Page" title="Main Page">this</a> is just an ordinary link. -</p> -!! end - -!! test -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 class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File: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="File:Icon.png">image</a> inside it!</div></div></div> - -!! end - -!! test -Image caption containing a newline -!! input -[[Image:Foobar.jpg|This -*is some text]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image" title="This *is some text"><img alt="This *is some text" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!!end - - -!! test -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 class="thumbinner" style="width:202px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="200" height="23" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File: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" rel="nofollow">irc</a> and <a href="https://example.com" class="external text" rel="nofollow">Secure</a> ext links in it.</div></div></div> - -!! end - -!! article -File:Barfoo.jpg -!! text -#REDIRECT [[File:Barfoo.jpg]] -!! endarticle - -!! test -Redirected image -!! input -[[Image:Barfoo.jpg]] -!! result -<p><a href="/wiki/File:Barfoo.jpg">File:Barfoo.jpg</a> -</p> -!! end - -!! test -Missing image with uploads disabled -!! options -wgEnableUploads=0 -!! input -[[Image:Foobaz.jpg]] -!! result -<p><a href="/wiki/File:Foobaz.jpg">File:Foobaz.jpg</a> -</p> -!! end - - -### -### Subpages -### -!! article -Subpage test/subpage -!! text -foo -!! endarticle - -!! test -Subpage link -!! options -subpage title=[[Subpage test]] -!! input -[[/subpage]] -!! result -<p><a href="/wiki/Subpage_test/subpage" title="Subpage test/subpage">/subpage</a> -</p> -!! end - -!! test -Subpage noslash link -!! options -subpage title=[[Subpage test]] -!!input -[[/subpage/]] -!! result -<p><a href="/wiki/Subpage_test/subpage" title="Subpage test/subpage">subpage</a> -</p> -!! end - -!! test -Disabled subpages -!! input -[[/subpage]] -!! result -<p><a href="/index.php?title=/subpage&action=edit&redlink=1" class="new" title="/subpage (page does not exist)">/subpage</a> -</p> -!! end - -!! test -BUG 561: {{/Subpage}} -!! options -subpage title=[[Page]] -!! input -{{/Subpage}} -!! result -<p><a href="/index.php?title=Page/Subpage&action=edit&redlink=1" class="new" title="Page/Subpage (page does not exist)">Page/Subpage</a> -</p> -!! end - -### -### Categories -### -!! article -Category:MediaWiki User's Guide -!! text -blah -!! endarticle - -!! test -Link to category -!! input -[[:Category:MediaWiki User's Guide]] -!! result -<p><a href="/wiki/Category:MediaWiki_User%27s_Guide">Category:MediaWiki User's Guide</a> -</p> -!! end - -!! test -Simple category -!! options -cat -!! input -[[Category:MediaWiki User's Guide]] -!! result -<a href="/wiki/Category:MediaWiki_User%27s_Guide" title="Category:MediaWiki User's Guide">MediaWiki User's Guide</a> -!! end - -!! test -PAGESINCATEGORY invalid title fatal (r33546 fix) -!! input -{{PAGESINCATEGORY:<bogus>}} -!! result -<p>0 -</p> -!! end - -### -### Inter-language links -### -!! test -Inter-language links -!! options -ill -!! input -[[es:Alimento]] -[[fr:Nourriture]] -[[zh:食品]] -!! result -es:Alimento fr:Nourriture zh:食品 -!! end - -### -### Sections -### -!! test -Basic section headings -!! input -== Headline 1 == -Some text - -==Headline 2== -More -===Smaller headline=== -Blah blah -!! result -<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" id="Headline_1"> Headline 1 </span></h2> -<p>Some text -</p> -<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" id="Headline_2">Headline 2</span></h2> -<p>More -</p> -<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" id="Smaller_headline">Smaller headline</span></h3> -<p>Blah blah -</p> -!! end - -!! test -Section headings with TOC -!! input -== Headline 1 == -=== Subheadline 1 === -===== Skipping a level ===== -====== Skipping a level ====== - -== Headline 2 == -Some text -===Another headline=== -!! result -<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div> -<ul> -<li class="toclevel-1 tocsection-1"><a href="#Headline_1"><span class="tocnumber">1</span> <span class="toctext">Headline 1</span></a> -<ul> -<li class="toclevel-2 tocsection-2"><a href="#Subheadline_1"><span class="tocnumber">1.1</span> <span class="toctext">Subheadline 1</span></a> -<ul> -<li class="toclevel-3 tocsection-3"><a href="#Skipping_a_level"><span class="tocnumber">1.1.1</span> <span class="toctext">Skipping a level</span></a> -<ul> -<li class="toclevel-4 tocsection-4"><a href="#Skipping_a_level_2"><span class="tocnumber">1.1.1.1</span> <span class="toctext">Skipping a level</span></a></li> -</ul> -</li> -</ul> -</li> -</ul> -</li> -<li class="toclevel-1 tocsection-5"><a href="#Headline_2"><span class="tocnumber">2</span> <span class="toctext">Headline 2</span></a> -<ul> -<li class="toclevel-2 tocsection-6"><a href="#Another_headline"><span class="tocnumber">2.1</span> <span class="toctext">Another headline</span></a></li> -</ul> -</li> -</ul> -</td></tr></table> -<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" id="Headline_1"> Headline 1 </span></h2> -<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" id="Subheadline_1"> Subheadline 1 </span></h3> -<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" id="Skipping_a_level"> Skipping a level </span></h5> -<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" id="Skipping_a_level_2"> Skipping a level </span></h6> -<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" id="Headline_2"> Headline 2 </span></h2> -<p>Some text -</p> -<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" id="Another_headline">Another headline</span></h3> - -!! end - -# perl -e 'print "="x$_," Level $_ heading","="x$_,"\n" for 1..10' -!! test -Handling of sections up to level 6 and beyond -!! input -= Level 1 Heading= -== Level 2 Heading== -=== Level 3 Heading=== -==== Level 4 Heading==== -===== Level 5 Heading===== -====== Level 6 Heading====== -======= Level 7 Heading======= -======== Level 8 Heading======== -========= Level 9 Heading========= -========== Level 10 Heading========== -!! result -<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div> -<ul> -<li class="toclevel-1 tocsection-1"><a href="#Level_1_Heading"><span class="tocnumber">1</span> <span class="toctext">Level 1 Heading</span></a> -<ul> -<li class="toclevel-2 tocsection-2"><a href="#Level_2_Heading"><span class="tocnumber">1.1</span> <span class="toctext">Level 2 Heading</span></a> -<ul> -<li class="toclevel-3 tocsection-3"><a href="#Level_3_Heading"><span class="tocnumber">1.1.1</span> <span class="toctext">Level 3 Heading</span></a> -<ul> -<li class="toclevel-4 tocsection-4"><a href="#Level_4_Heading"><span class="tocnumber">1.1.1.1</span> <span class="toctext">Level 4 Heading</span></a> -<ul> -<li class="toclevel-5 tocsection-5"><a href="#Level_5_Heading"><span class="tocnumber">1.1.1.1.1</span> <span class="toctext">Level 5 Heading</span></a> -<ul> -<li class="toclevel-6 tocsection-6"><a href="#Level_6_Heading"><span class="tocnumber">1.1.1.1.1.1</span> <span class="toctext">Level 6 Heading</span></a></li> -<li class="toclevel-6 tocsection-7"><a href="#.3D_Level_7_Heading.3D"><span class="tocnumber">1.1.1.1.1.2</span> <span class="toctext">= Level 7 Heading=</span></a></li> -<li class="toclevel-6 tocsection-8"><a href="#.3D.3D_Level_8_Heading.3D.3D"><span class="tocnumber">1.1.1.1.1.3</span> <span class="toctext">== Level 8 Heading==</span></a></li> -<li class="toclevel-6 tocsection-9"><a href="#.3D.3D.3D_Level_9_Heading.3D.3D.3D"><span class="tocnumber">1.1.1.1.1.4</span> <span class="toctext">=== Level 9 Heading===</span></a></li> -<li class="toclevel-6 tocsection-10"><a href="#.3D.3D.3D.3D_Level_10_Heading.3D.3D.3D.3D"><span class="tocnumber">1.1.1.1.1.5</span> <span class="toctext">==== Level 10 Heading====</span></a></li> -</ul> -</li> -</ul> -</li> -</ul> -</li> -</ul> -</li> -</ul> -</li> -</ul> -</td></tr></table> -<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" id="Level_1_Heading"> Level 1 Heading</span></h1> -<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" id="Level_2_Heading"> Level 2 Heading</span></h2> -<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" id="Level_3_Heading"> Level 3 Heading</span></h3> -<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" id="Level_4_Heading"> Level 4 Heading</span></h4> -<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" id="Level_5_Heading"> Level 5 Heading</span></h5> -<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" id="Level_6_Heading"> Level 6 Heading</span></h6> -<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" id=".3D_Level_7_Heading.3D">= Level 7 Heading=</span></h6> -<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" id=".3D.3D_Level_8_Heading.3D.3D">== Level 8 Heading==</span></h6> -<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" id=".3D.3D.3D_Level_9_Heading.3D.3D.3D">=== Level 9 Heading===</span></h6> -<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" id=".3D.3D.3D.3D_Level_10_Heading.3D.3D.3D.3D">==== Level 10 Heading====</span></h6> - -!! end - -!! test -TOC regression (bug 9764) -!! input -== title 1 == -=== title 1.1 === -==== title 1.1.1 ==== -=== title 1.2 === -== title 2 == -=== title 2.1 === -!! result -<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div> -<ul> -<li class="toclevel-1 tocsection-1"><a href="#title_1"><span class="tocnumber">1</span> <span class="toctext">title 1</span></a> -<ul> -<li class="toclevel-2 tocsection-2"><a href="#title_1.1"><span class="tocnumber">1.1</span> <span class="toctext">title 1.1</span></a> -<ul> -<li class="toclevel-3 tocsection-3"><a href="#title_1.1.1"><span class="tocnumber">1.1.1</span> <span class="toctext">title 1.1.1</span></a></li> -</ul> -</li> -<li class="toclevel-2 tocsection-4"><a href="#title_1.2"><span class="tocnumber">1.2</span> <span class="toctext">title 1.2</span></a></li> -</ul> -</li> -<li class="toclevel-1 tocsection-5"><a href="#title_2"><span class="tocnumber">2</span> <span class="toctext">title 2</span></a> -<ul> -<li class="toclevel-2 tocsection-6"><a href="#title_2.1"><span class="tocnumber">2.1</span> <span class="toctext">title 2.1</span></a></li> -</ul> -</li> -</ul> -</td></tr></table> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: title 1">edit</a>]</span> <span class="mw-headline" id="title_1"> title 1 </span></h2> -<h3><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: title 1.1">edit</a>]</span> <span class="mw-headline" id="title_1.1"> title 1.1 </span></h3> -<h4><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=3" title="Edit section: title 1.1.1">edit</a>]</span> <span class="mw-headline" id="title_1.1.1"> title 1.1.1 </span></h4> -<h3><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=4" title="Edit section: title 1.2">edit</a>]</span> <span class="mw-headline" id="title_1.2"> title 1.2 </span></h3> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=5" title="Edit section: title 2">edit</a>]</span> <span class="mw-headline" id="title_2"> title 2 </span></h2> -<h3><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=6" title="Edit section: title 2.1">edit</a>]</span> <span class="mw-headline" id="title_2.1"> title 2.1 </span></h3> - -!! end - -!! test -TOC with wgMaxTocLevel=3 (bug 6204) -!! options -wgMaxTocLevel=3 -!! input -== title 1 == -=== title 1.1 === -==== title 1.1.1 ==== -=== title 1.2 === -== title 2 == -=== title 2.1 === -!! result -<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div> -<ul> -<li class="toclevel-1 tocsection-1"><a href="#title_1"><span class="tocnumber">1</span> <span class="toctext">title 1</span></a> -<ul> -<li class="toclevel-2 tocsection-2"><a href="#title_1.1"><span class="tocnumber">1.1</span> <span class="toctext">title 1.1</span></a></li> -<li class="toclevel-2 tocsection-4"><a href="#title_1.2"><span class="tocnumber">1.2</span> <span class="toctext">title 1.2</span></a></li> -</ul> -</li> -<li class="toclevel-1 tocsection-5"><a href="#title_2"><span class="tocnumber">2</span> <span class="toctext">title 2</span></a> -<ul> -<li class="toclevel-2 tocsection-6"><a href="#title_2.1"><span class="tocnumber">2.1</span> <span class="toctext">title 2.1</span></a></li> -</ul> -</li> -</ul> -</td></tr></table> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: title 1">edit</a>]</span> <span class="mw-headline" id="title_1"> title 1 </span></h2> -<h3><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: title 1.1">edit</a>]</span> <span class="mw-headline" id="title_1.1"> title 1.1 </span></h3> -<h4><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=3" title="Edit section: title 1.1.1">edit</a>]</span> <span class="mw-headline" id="title_1.1.1"> title 1.1.1 </span></h4> -<h3><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=4" title="Edit section: title 1.2">edit</a>]</span> <span class="mw-headline" id="title_1.2"> title 1.2 </span></h3> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=5" title="Edit section: title 2">edit</a>]</span> <span class="mw-headline" id="title_2"> title 2 </span></h2> -<h3><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=6" title="Edit section: title 2.1">edit</a>]</span> <span class="mw-headline" id="title_2.1"> title 2.1 </span></h3> - -!! end - -!! test -TOC with wgMaxTocLevel=3 and two level four headings (bug 6204) -!! options -wgMaxTocLevel=3 -!! input -==Section 1== -===Section 1.1=== -====Section 1.1.1==== -====Section 1.1.1.1==== -==Section 2== -!! result -<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div> -<ul> -<li class="toclevel-1 tocsection-1"><a href="#Section_1"><span class="tocnumber">1</span> <span class="toctext">Section 1</span></a> -<ul> -<li class="toclevel-2 tocsection-2"><a href="#Section_1.1"><span class="tocnumber">1.1</span> <span class="toctext">Section 1.1</span></a></li> -</ul> -</li> -<li class="toclevel-1 tocsection-5"><a href="#Section_2"><span class="tocnumber">2</span> <span class="toctext">Section 2</span></a></li> -</ul> -</td></tr></table> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: Section 1">edit</a>]</span> <span class="mw-headline" id="Section_1">Section 1</span></h2> -<h3><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: Section 1.1">edit</a>]</span> <span class="mw-headline" id="Section_1.1">Section 1.1</span></h3> -<h4><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=3" title="Edit section: Section 1.1.1">edit</a>]</span> <span class="mw-headline" id="Section_1.1.1">Section 1.1.1</span></h4> -<h4><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=4" title="Edit section: Section 1.1.1.1">edit</a>]</span> <span class="mw-headline" id="Section_1.1.1.1">Section 1.1.1.1</span></h4> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=5" title="Edit section: Section 2">edit</a>]</span> <span class="mw-headline" id="Section_2">Section 2</span></h2> - -!! end - - -!! test -Resolving duplicate section names -!! input -== Foo bar == -== Foo bar == -!! result -<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" id="Foo_bar"> Foo bar </span></h2> -<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" id="Foo_bar_2"> Foo bar </span></h2> - -!! end - -!! test -Resolving duplicate section names with differing case (bug 10721) -!! input -== Foo bar == -== Foo Bar == -!! result -<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" id="Foo_bar"> Foo bar </span></h2> -<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" id="Foo_Bar_2"> Foo Bar </span></h2> - -!! end - -!! article -Template:sections -!! text -===Section 1=== -==Section 2== -!! endarticle - -!! test -Template with sections, __NOTOC__ -!! input -__NOTOC__ -==Section 0== -{{sections}} -==Section 4== -!! result -<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" id="Section_0">Section 0</span></h2> -<h3><span class="editsection">[<a href="/index.php?title=Template:Sections&action=edit&section=T-1" title="Template:Sections">edit</a>]</span> <span class="mw-headline" id="Section_1">Section 1</span></h3> -<h2><span class="editsection">[<a href="/index.php?title=Template:Sections&action=edit&section=T-2" title="Template:Sections">edit</a>]</span> <span class="mw-headline" id="Section_2">Section 2</span></h2> -<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" id="Section_4">Section 4</span></h2> - -!! end - -!! test -__NOEDITSECTION__ keyword -!! input -__NOEDITSECTION__ -==Section 1== -==Section 2== -!! result -<h2> <span class="mw-headline" id="Section_1">Section 1</span></h2> -<h2> <span class="mw-headline" id="Section_2">Section 2</span></h2> - -!! end - -!! test -Link inside a section heading -!! input -==Section with a [[Main Page|link]] in it== -!! result -<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" id="Section_with_a_link_in_it">Section with a <a href="/wiki/Main_Page" title="Main Page">link</a> in it</span></h2> - -!! end - -!! test -TOC regression (bug 12077) -!! input -__TOC__ -== title 1 == -=== title 1.1 === -== title 2 == -!! result -<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div> -<ul> -<li class="toclevel-1 tocsection-1"><a href="#title_1"><span class="tocnumber">1</span> <span class="toctext">title 1</span></a> -<ul> -<li class="toclevel-2 tocsection-2"><a href="#title_1.1"><span class="tocnumber">1.1</span> <span class="toctext">title 1.1</span></a></li> -</ul> -</li> -<li class="toclevel-1 tocsection-3"><a href="#title_2"><span class="tocnumber">2</span> <span class="toctext">title 2</span></a></li> -</ul> -</td></tr></table> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: title 1">edit</a>]</span> <span class="mw-headline" id="title_1"> title 1 </span></h2> -<h3><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: title 1.1">edit</a>]</span> <span class="mw-headline" id="title_1.1"> title 1.1 </span></h3> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=3" title="Edit section: title 2">edit</a>]</span> <span class="mw-headline" id="title_2"> title 2 </span></h2> - -!! end - -!! test -BUG 1219 URL next to image (good) -!! input -http://example.com [[Image:foobar.jpg]] -!! result -<p><a href="http://example.com" class="external free" rel="nofollow">http://example.com</a> <a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!!end - -!! test -Short headings with trailing space should match behaviour of Parser::doHeadings (bug 19910) -!! input -=== -The line above must have a trailing space! -=== <!-- ---> <!-- --> -But just in case it doesn't... -!! result -<h1><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: =">edit</a>]</span> <span class="mw-headline" id=".3D">=</span></h1> -<p>The line above must have a trailing space! -</p> -<h1><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: =">edit</a>]</span> <span class="mw-headline" id=".3D_2">=</span></h1> -<p>But just in case it doesn't... -</p> -!! end - -!! test -Header with special characters (bug 25462) -!! input -The tooltips shall not show entities to the user (ie. be double escaped) - -== text > text == -section 1 - -== text < text == -section 2 - -== text & text == -section 3 - -== text ' text == -section 4 - -== text " text == -section 5 -!! result -<p>The tooltips shall not show entities to the user (ie. be double escaped) -</p> -<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div> -<ul> -<li class="toclevel-1 tocsection-1"><a href="#text_.3E_text"><span class="tocnumber">1</span> <span class="toctext">text > text</span></a></li> -<li class="toclevel-1 tocsection-2"><a href="#text_.3C_text"><span class="tocnumber">2</span> <span class="toctext">text < text</span></a></li> -<li class="toclevel-1 tocsection-3"><a href="#text_.26_text"><span class="tocnumber">3</span> <span class="toctext">text & text</span></a></li> -<li class="toclevel-1 tocsection-4"><a href="#text_.27_text"><span class="tocnumber">4</span> <span class="toctext">text ' text</span></a></li> -<li class="toclevel-1 tocsection-5"><a href="#text_.22_text"><span class="tocnumber">5</span> <span class="toctext">text " text</span></a></li> -</ul> -</td></tr></table> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: text > text">edit</a>]</span> <span class="mw-headline" id="text_.3E_text"> text > text </span></h2> -<p>section 1 -</p> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=2" title="Edit section: text < text">edit</a>]</span> <span class="mw-headline" id="text_.3C_text"> text < text </span></h2> -<p>section 2 -</p> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=3" title="Edit section: text & text">edit</a>]</span> <span class="mw-headline" id="text_.26_text"> text & text </span></h2> -<p>section 3 -</p> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=4" title="Edit section: text ' text">edit</a>]</span> <span class="mw-headline" id="text_.27_text"> text ' text </span></h2> -<p>section 4 -</p> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=5" title="Edit section: text " text">edit</a>]</span> <span class="mw-headline" id="text_.22_text"> text " text </span></h2> -<p>section 5 -</p> -!! end - -!! test -BUG 1219 URL next to image (broken) -!! input -http://example.com[[Image:foobar.jpg]] -!! result -<p><a href="http://example.com" class="external free" rel="nofollow">http://example.com</a><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a> -</p> -!!end - -!! test -Bug 1186 news: in the middle of text -!! input -http://en.wikinews.org/wiki/Wikinews:Workplace -!! result -<p><a href="http://en.wikinews.org/wiki/Wikinews:Workplace" class="external free" rel="nofollow">http://en.wikinews.org/wiki/Wikinews:Workplace</a> -</p> -!!end - - -!! test -Namespaced link must have a title -!! input -[[Project:]] -!! result -<p>[[Project:]] -</p> -!!end - -!! test -Namespaced link must have a title (bad fragment version) -!! input -[[Project:#fragment]] -!! result -<p>[[Project:#fragment]] -</p> -!!end - - -!! test -div with no attributes -!! input -<div>HTML rocks</div> -!! result -<div>HTML rocks</div> - -!! end - -!! test -div with double-quoted attribute -!! input -<div id="rock">HTML rocks</div> -!! result -<div id="rock">HTML rocks</div> - -!! end - -!! test -div with single-quoted attribute -!! input -<div id='rock'>HTML rocks</div> -!! result -<div id="rock">HTML rocks</div> - -!! end - -!! test -div with unquoted attribute -!! input -<div id=rock>HTML rocks</div> -!! result -<div id="rock">HTML rocks</div> - -!! end - -!! test -div with illegal double attributes -!! input -<div align="center" align="right">HTML rocks</div> -!! result -<div align="right">HTML rocks</div> - -!!end - -!! test -HTML multiple attributes correction -!! input -<p class="error" class="awesome">Awesome!</p> -!! result -<p class="awesome">Awesome!</p> - -!!end - -!! test -Table multiple attributes correction -!! input -{| -!+ class="error" class="awesome"| status -|} -!! result -<table> -<tr> -<th class="awesome"> status -</th></tr></table> - -!!end - -!! test -DIV IN UPPERCASE -!! input -<DIV ALIGN="center">HTML ROCKS</DIV> -!! result -<div align="center">HTML ROCKS</div> - -!!end - - -!! test -text with amp in the middle of nowhere -!! input -Remember AT&T? -!!result -<p>Remember AT&T? -</p> -!! end - -!! test -text with character entity: eacute -!! input -I always thought é was a cute letter. -!! result -<p>I always thought é was a cute letter. -</p> -!! end - -!! test -text with undefined character entity: xacute -!! input -I always thought &xacute; was a cute letter. -!! result -<p>I always thought &xacute; was a cute letter. -</p> -!! end - - -### -### Media links -### - -!! test -Media link -!! input -[[Media:Foobar.jpg]] -!! result -<p><a href="http://example.com/images/3/3a/Foobar.jpg" class="internal" title="Foobar.jpg">Media:Foobar.jpg</a> -</p> -!! end - -!! test -Media link with text -!! input -[[Media:Foobar.jpg|A neat file to look at]] -!! result -<p><a href="http://example.com/images/3/3a/Foobar.jpg" class="internal" title="Foobar.jpg">A neat file to look at</a> -</p> -!! end - -# FIXME: this is still bad HTML tag nesting -!! test -Media link with nasty text -fixme: doBlockLevels won't wrap this in a paragraph because it contains a div -!! input -[[Media:Foobar.jpg|Safe Link<div style=display:none>" onmouseover="alert(document.cookie)" onfoo="</div>]] -!! result -<a href="http://example.com/images/3/3a/Foobar.jpg" class="internal" title="Foobar.jpg">Safe Link<div style="display:none">" onmouseover="alert(document.cookie)" onfoo="</div></a> - -!! end - -!! test -Media link to nonexistent file (bug 1702) -!! input -[[Media:No such.jpg]] -!! result -<p><a href="/index.php?title=Special:Upload&wpDestFile=No_such.jpg" class="new" title="No such.jpg">Media:No such.jpg</a> -</p> -!! end - -!! test -Image link to nonexistent file (bug 1850 - good) -!! input -[[Image:No such.jpg]] -!! result -<p><a href="/index.php?title=Special:Upload&wpDestFile=No_such.jpg" class="new" title="File:No such.jpg">File:No such.jpg</a> -</p> -!! end - -!! test -:Image link to nonexistent file (bug 1850 - bad) -!! input -[[:Image:No such.jpg]] -!! result -<p><a href="/index.php?title=File:No_such.jpg&action=edit&redlink=1" class="new" title="File:No such.jpg (page does not exist)">Image:No such.jpg</a> -</p> -!! end - - - -!! test -Character reference normalization in link text (bug 1938) -!! input -[[Main Page|this&that]] -!! result -<p><a href="/wiki/Main_Page" title="Main Page">this&that</a> -</p> -!!end - -!! article -אַ -!! text -Test for unicode normalization - -The page's name is U+05d0 U+05b7, with non-canonical form U+FB2E -!! endarticle - -!! test -(bug 19451) Links should refer to the normalized form. -!! input -[[אַ]] -[[אַ]] -[[אַ]] -[[אַ]] -[[אַ]] -!! result -<p><a href="/wiki/%D7%90%D6%B7" title="אַ">אַ</a> -<a href="/wiki/%D7%90%D6%B7" title="אַ">אַ</a> -<a href="/wiki/%D7%90%D6%B7" title="אַ">אַ</a> -<a href="/wiki/%D7%90%D6%B7" title="אַ">אַ</a> -<a href="/wiki/%D7%90%D6%B7">אַ</a> -</p> -!! end - -!! test -Empty attribute crash test (bug 2067) -!! input -<font color="">foo</font> -!! result -<p><font color="">foo</font> -</p> -!! end - -!! test -Empty attribute crash test single-quotes (bug 2067) -!! input -<font color=''>foo</font> -!! result -<p><font color="">foo</font> -</p> -!! end - -!! test -Attribute test: equals, then nothing -!! input -<font color=>foo</font> -!! result -<p><font>foo</font> -</p> -!! end - -!! test -Attribute test: unquoted value -!! input -<font color=x>foo</font> -!! result -<p><font color="x">foo</font> -</p> -!! end - -!! test -Attribute test: unquoted but illegal value (hash) -!! input -<font color=#x>foo</font> -!! result -<p><font color="#x">foo</font> -</p> -!! end - -!! test -Attribute test: no value -!! input -<font color>foo</font> -!! result -<p><font color="color">foo</font> -</p> -!! end - -!! test -Bug 2095: link with three closing brackets -!! input -[[Main Page]]] -!! result -<p><a href="/wiki/Main_Page">Main Page</a>] -</p> -!! end - -!! test -Bug 2095: link with pipe and three closing brackets -!! input -[[Main Page|link]]] -!! result -<p><a href="/wiki/Main_Page" title="Main Page">link</a>] -</p> -!! end - -!! test -Bug 2095: link with pipe and three closing brackets, version 2 -!! input -[[Main Page|[http://example.com/]]] -!! result -<p><a href="/wiki/Main_Page" title="Main Page">[http://example.com/]</a> -</p> -!! end - - -### -### Safety -### - -!! article -Template:Dangerous attribute -!! text -" onmouseover="alert(document.cookie) -!! endarticle - -!! article -Template:Dangerous style attribute -!! text -border-size: expression(alert(document.cookie)) -!! endarticle - -!! article -Template:Div style -!! text -<div style="float: right; {{{1}}}">Magic div</div> -!! endarticle - -!! test -Bug 2304: HTML attribute safety (safe template; regression bug 2309) -!! input -<div title="{{test}}"></div> -!! result -<div title="This is a test template"></div> - -!! end - -!! test -Bug 2304: HTML attribute safety (dangerous template; 2309) -!! input -<div title="{{dangerous attribute}}"></div> -!! result -<div title=""></div> - -!! end - -!! test -Bug 2304: HTML attribute safety (dangerous style template; 2309) -!! input -<div style="{{dangerous style attribute}}"></div> -!! result -<div style="/* insecure input */"></div> - -!! end - -!! test -Bug 2304: HTML attribute safety (safe parameter; 2309) -!! input -{{div style|width: 200px}} -!! result -<div style="float: right; width: 200px">Magic div</div> - -!! end - -!! test -Bug 2304: HTML attribute safety (unsafe parameter; 2309) -!! input -{{div style|width: expression(alert(document.cookie))}} -!! result -<div style="/* insecure input */">Magic div</div> - -!! end - -!! test -Bug 2304: HTML attribute safety (unsafe breakout parameter; 2309) -!! input -{{div style|"><script>alert(document.cookie)</script>}} -!! result -<div style="float: right;"><script>alert(document.cookie)</script>">Magic div</div> - -!! end - -!! test -Bug 2304: HTML attribute safety (unsafe breakout parameter 2; 2309) -!! input -{{div style|" ><script>alert(document.cookie)</script>}} -!! result -<div style="float: right;"><script>alert(document.cookie)</script>">Magic div</div> - -!! end - -!! test -Bug 2304: HTML attribute safety (link) -!! input -<div title="[[Main Page]]"></div> -!! result -<div title="[[Main Page]]"></div> - -!! end - -!! test -Bug 2304: HTML attribute safety (italics) -!! input -<div title="''foobar''"></div> -!! result -<div title="''foobar''"></div> - -!! end - -!! test -Bug 2304: HTML attribute safety (bold) -!! input -<div title="'''foobar'''"></div> -!! result -<div title="'''foobar'''"></div> - -!! end - - -!! test -Bug 2304: HTML attribute safety (ISBN) -!! input -<div title="ISBN 1234567890"></div> -!! result -<div title="ISBN 1234567890"></div> - -!! end - -!! test -Bug 2304: HTML attribute safety (RFC) -!! input -<div title="RFC 1234"></div> -!! result -<div title="RFC 1234"></div> - -!! end - -!! test -Bug 2304: HTML attribute safety (PMID) -!! input -<div title="PMID 1234567890"></div> -!! result -<div title="PMID 1234567890"></div> - -!! end - -!! test -Bug 2304: HTML attribute safety (web link) -!! input -<div title="http://example.com/"></div> -!! result -<div title="http://example.com/"></div> - -!! end - -!! test -Bug 2304: HTML attribute safety (named web link) -!! input -<div title="[http://example.com/ link]"></div> -!! result -<div title="[http://example.com/ link]"></div> - -!! end - -!! test -Bug 3244: HTML attribute safety (extension; safe) -!! input -<div style="<nowiki>background:blue</nowiki>"></div> -!! result -<div style="background:blue"></div> - -!! end - -!! test -Bug 3244: HTML attribute safety (extension; unsafe) -!! input -<div style="<nowiki>border-left:expression(alert(document.cookie))</nowiki>"></div> -!! result -<div style="/* insecure input */"></div> - -!! end - -!! test -Math section safety when disabled -!! input -<math><script>alert(document.cookies);</script></math> -!! result -<p><math><script>alert(document.cookies);</script></math> -</p> -!! end - -# More MSIE fun discovered by Tom Gilder - -!! test -MSIE CSS safety test: spurious slash -!! input -<div style="background-image:u\rl(javascript:alert('boo'))">evil</div> -!! result -<div style="/* insecure input */">evil</div> - -!! end - -!! test -MSIE CSS safety test: hex code -!! input -<div style="background-image:u\72l(javascript:alert('boo'))">evil</div> -!! result -<div style="/* insecure input */">evil</div> - -!! end - -!! test -MSIE CSS safety test: comment in url -!! input -<div style="background-image:u/**/rl(javascript:alert('boo'))">evil</div> -!! result -<div style="background-image:u rl(javascript:alert('boo'))">evil</div> - -!! end - -!! test -MSIE CSS safety test: comment in expression -!! input -<div style="background-image:expres/**/sion(alert('boo4'))">evil4</div> -!! result -<div style="background-image:expres sion(alert('boo4'))">evil4</div> - -!! end - - -!! test -Table attribute legitimate extension -!! input -{| -!+ style="<nowiki>color:blue</nowiki>"| status -|} -!! result -<table> -<tr> -<th style="color:blue"> status -</th></tr></table> - -!!end - -!! test -Table attribute safety -!! input -{| -!+ style="<nowiki>border-width:expression(0+alert(document.cookie))</nowiki>"| status -|} -!! result -<table> -<tr> -<th style="/* insecure input */"> status -</th></tr></table> - -!! end - -!! test -CSS line continuation 1 -!! input -<div style="background-image: u\ rl(test.jpg);"></div> -!! result -<div style="/* insecure input */"></div> - -!! end - -!! test -CSS line continuation 2 -!! input -<div style="background-image: u\ rl(test.jpg); "></div> -!! result -<div style="/* insecure input */"></div> - -!! end - -!! article -Template:Identity -!! text -{{{1}}} -!! endarticle - -!! test -Expansion of multi-line templates in attribute values (bug 6255) -!! input -<div style="background: {{identity|#00FF00}}">-</div> -!! result -<div style="background: #00FF00">-</div> - -!! end - - -!! test -Expansion of multi-line templates in attribute values (bug 6255 sanity check) -!! input -<div style="background: -#00FF00">-</div> -!! result -<div style="background: #00FF00">-</div> - -!! end - -!! test -Expansion of multi-line templates in attribute values (bug 6255 sanity check 2) -!! input -<div style="background: #00FF00">-</div> -!! result -<div style="background: #00FF00">-</div> - -!! end - -### -### Parser hooks (see maintenance/parserTestsParserHook.php for the <tag> extension) -### -!! test -Parser hook: empty input -!! input -<tag></tag> -!! result -<pre> -string(0) "" -array(0) { -} -</pre> - -!! end - -!! test -Parser hook: empty input using terminated empty elements -!! input -<tag/> -!! result -<pre> -NULL -array(0) { -} -</pre> - -!! end - -!! test -Parser hook: empty input using terminated empty elements (space before) -!! input -<tag /> -!! result -<pre> -NULL -array(0) { -} -</pre> - -!! end - -!! test -Parser hook: basic input -!! input -<tag>input</tag> -!! result -<pre> -string(5) "input" -array(0) { -} -</pre> - -!! end - - -!! test -Parser hook: case insensitive -!! input -<TAG>input</TAG> -!! result -<pre> -string(5) "input" -array(0) { -} -</pre> - -!! end - - -!! test -Parser hook: case insensitive, redux -!! input -<TaG>input</TAg> -!! result -<pre> -string(5) "input" -array(0) { -} -</pre> - -!! end - -!! test -Parser hook: nested tags -!! options -noxml -!! input -<tag><tag></tag></tag> -!! result -<pre> -string(5) "<tag>" -array(0) { -} -</pre></tag> - -!! end - -!! test -Parser hook: basic arguments -!! input -<tag width=200 height = "100" depth = '50' square></tag> -!! result -<pre> -string(0) "" -array(4) { - ["width"]=> - string(3) "200" - ["height"]=> - string(3) "100" - ["depth"]=> - string(2) "50" - ["square"]=> - string(6) "square" -} -</pre> - -!! end - -!! test -Parser hook: argument containing a forward slash (bug 5344) -!! input -<tag filename='/tmp/bla'></tag> -!! result -<pre> -string(0) "" -array(1) { - ["filename"]=> - string(8) "/tmp/bla" -} -</pre> - -!! end - -!! test -Parser hook: empty input using terminated empty elements (bug 2374) -!! input -<tag foo=bar/>text -!! result -<pre> -NULL -array(1) { - ["foo"]=> - string(3) "bar" -} -</pre>text - -!! end - -# </tag> should be output literally since there is no matching tag that begins it -!! test -Parser hook: basic arguments using terminated empty elements (bug 2374) -!! input -<tag width=200 height = "100" depth = '50' square/> -other stuff -</tag> -!! result -<pre> -NULL -array(4) { - ["width"]=> - string(3) "200" - ["height"]=> - string(3) "100" - ["depth"]=> - string(2) "50" - ["square"]=> - string(6) "square" -} -</pre> -<p>other stuff -</tag> -</p> -!! end - -### -### (see maintenance/parserTestsStaticParserHook.php for the <statictag> extension) -### - -!! test -Parser hook: static parser hook not inside a comment -!! input -<statictag>hello, world</statictag> -<statictag action=flush/> -!! result -<p>hello, world -</p> -!! end - - -!! test -Parser hook: static parser hook inside a comment -!! input -<!-- <statictag>hello, world</statictag> --> -<statictag action=flush/> -!! result -<p><br /> -</p> -!! end - -# Nested template calls; this case was broken by Parser.php rev 1.506, -# since reverted. - -!! article -Template:One-parameter -!! text -(My parameter is: {{{1}}}) -!! endarticle - -!! article -Template:Map-one-parameter -!! text -{{{{{1}}}|{{{2}}}}} -!! endarticle - -!! test -Nested template calls -!! input -{{Map-one-parameter|One-parameter|param}} -!! result -<p>(My parameter is: param) -</p> -!! end - - -### -### Sanitizer -### -!! test -Sanitizer: Closing of open tags -!! input -<s></s><table></table> -!! result -<s></s><table></table> - -!! end - -!! test -Sanitizer: Closing of open but not closed tags -!! input -<s>foo -!! result -<p><s>foo</s> -</p> -!! end - -!! test -Sanitizer: Closing of closed but not open tags -!! input -</s> -!! result -<p></s> -</p> -!! end - -!! test -Sanitizer: Closing of closed but not open table tags -!! input -Table not started</td></tr></table> -!! result -<p>Table not started</td></tr></table> -</p> -!! end - -!! test -Sanitizer: Escaping of spaces, multibyte characters, colons & other stuff in id="" -!! input -<span id="æ: v">byte</span>[[#æ: v|backlink]] -!! result -<p><span id=".C3.A6:_v">byte</span><a href="#.C3.A6:_v">backlink</a> -</p> -!! end - -!! test -Sanitizer: Validating the contents of the id attribute (bug 4515) -!! options -disabled -!! input -<br id=9 /> -!! result -Something, but definitely not <br id="9" />... -!! end - -!! test -Sanitizer: Validating id attribute uniqueness (bug 4515, bug 6301) -!! options -disabled -!! input -<br id="foo" /><br id="foo" /> -!! result -Something need to be done. foo-2 ? -!! end - -!! test -Language converter: output gets cut off unexpectedly (bug 5757) -!! options -language=zh -!! input -this bit is safe: }- - -but if we add a conversion instance: -{zh-cn:xxx;zh-tw:yyy}- - -then we get cut off here: }- - -all additional text is vanished -!! result -<p>this bit is safe: }- -</p><p>but if we add a conversion instance: xxx -</p><p>then we get cut off here: }- -</p><p>all additional text is vanished -</p> -!! end - -!! test -Self closed html pairs (bug 5487) -!! options -!! input -<center><font id="bug" />Centered text</center> -<div><font id="bug2" />In div text</div> -!! result -<center><font id="bug" />Centered text</center> -<div><font id="bug2" />In div text</div> - -!! end - -# -# -# - -!! test -Punctuation: nbsp before exclamation -!! input -C'est grave ! -!! result -<p>C'est grave ! -</p> -!! end - -!! test -Punctuation: CSS !important (bug 11874) -!! input -<div style="width:50% !important">important</div> -!! result -<div style="width:50% !important">important</div> - -!!end - -!! test -Punctuation: CSS ! important (bug 11874; with space after) -!! input -<div style="width:50% ! important">important</div> -!! result -<div style="width:50% ! important">important</div> - -!!end - - -!! test -HTML bullet list, closed tags (bug 5497) -!! input -<ul> -<li>One</li> -<li>Two</li> -</ul> -!! result -<ul> -<li>One</li> -<li>Two</li> -</ul> - -!! end - -!! test -HTML bullet list, unclosed tags (bug 5497) -!! options -disabled -!! input -<ul> -<li>One -<li>Two -</ul> -!! result -<ul> -<li>One -</li><li>Two -</li></ul> - -!! end - -!! test -HTML ordered list, closed tags (bug 5497) -!! input -<ol> -<li>One</li> -<li>Two</li> -</ol> -!! result -<ol> -<li>One</li> -<li>Two</li> -</ol> - -!! end - -!! test -HTML ordered list, unclosed tags (bug 5497) -!! options -disabled -!! input -<ol> -<li>One -<li>Two -</ol> -!! result -<ol> -<li>One -</li><li>Two -</li></ol> - -!! end - -!! test -HTML nested bullet list, closed tags (bug 5497) -!! input -<ul> -<li>One</li> -<li>Two: -<ul> -<li>Sub-one</li> -<li>Sub-two</li> -</ul> -</li> -</ul> -!! result -<ul> -<li>One</li> -<li>Two: -<ul> -<li>Sub-one</li> -<li>Sub-two</li> -</ul> -</li> -</ul> - -!! end - -!! test -HTML nested bullet list, open tags (bug 5497) -!! options -disabled -!! input -<ul> -<li>One -<li>Two: -<ul> -<li>Sub-one -<li>Sub-two -</ul> -</ul> -!! result -<ul> -<li>One -</li><li>Two: -<ul> -<li>Sub-one -</li><li>Sub-two -</li></ul> -</li></ul> - -!! end - -!! test -HTML nested ordered list, closed tags (bug 5497) -!! input -<ol> -<li>One</li> -<li>Two: -<ol> -<li>Sub-one</li> -<li>Sub-two</li> -</ol> -</li> -</ol> -!! result -<ol> -<li>One</li> -<li>Two: -<ol> -<li>Sub-one</li> -<li>Sub-two</li> -</ol> -</li> -</ol> - -!! end - -!! test -HTML nested ordered list, open tags (bug 5497) -!! options -disabled -!! input -<ol> -<li>One -<li>Two: -<ol> -<li>Sub-one -<li>Sub-two -</ol> -</ol> -!! result -<ol> -<li>One -</li><li>Two: -<ol> -<li>Sub-one -</li><li>Sub-two -</li></ol> -</li></ol> - -!! end - -!! test -HTML ordered list item with parameters oddity -!! input -<ol><li id="fragment">One</li></ol> -!! result -<ol><li id="fragment">One</li></ol> - -!! end - -!!test -bug 5918: autonumbering -!! input -[http://first/] [http://second] [ftp://ftp] - -ftp://inlineftp - -[mailto:enclosed@mail.tld With target] - -[mailto:enclosed@mail.tld] - -mailto:inline@mail.tld -!! result -<p><a href="http://first/" class="external autonumber" rel="nofollow">[1]</a> <a href="http://second" class="external autonumber" rel="nofollow">[2]</a> <a href="ftp://ftp" class="external autonumber" rel="nofollow">[3]</a> -</p><p><a href="ftp://inlineftp" class="external free" rel="nofollow">ftp://inlineftp</a> -</p><p><a href="mailto:enclosed@mail.tld" class="external text" rel="nofollow">With target</a> -</p><p><a href="mailto:enclosed@mail.tld" class="external autonumber" rel="nofollow">[4]</a> -</p><p><a href="mailto:inline@mail.tld" class="external free" rel="nofollow">mailto:inline@mail.tld</a> -</p> -!! end - - -# -# Security and HTML correctness -# From Nick Jenkins' fuzz testing -# - -!! test -Fuzz testing: Parser13 -!! input -{| -| http://a| -!! result -<table> -<tr> -<td> -</td> -</tr> -</table> - -!! end - -!! test -Fuzz testing: Parser14 -!! input -== onmouseover= == -http://__TOC__ -!! result -<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" id="onmouseover.3D"> onmouseover= </span></h2> -http://<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div> -<ul> -<li class="toclevel-1 tocsection-1"><a href="#onmouseover.3D"><span class="tocnumber">1</span> <span class="toctext">onmouseover=</span></a></li> -</ul> -</td></tr></table> - -!! end - -!! test -Fuzz testing: Parser14-table -!! input -==a== -{| STYLE=__TOC__ -!! result -<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" id="a">a</span></h2> -<table style="__TOC__"> -<tr><td></td></tr> -</table> - -!! end - -# Known to produce bogus xml (extra </td>) -!! test -Fuzz testing: Parser16 -!! options -noxml -!! input -{| -!https://|||||| -!! result -<table> -<tr> -<th>https://</th> -<th></th> -<th></th> -<th> -</td> -</tr> -</table> - -!! end - -!! test -Fuzz testing: Parser21 -!! input -{| -! irc://{{ftp://a" onmouseover="alert('hello world');" -| -!! result -<table> -<tr> -<th> <a href="irc://{{ftp://a" class="external free" rel="nofollow">irc://{{ftp://a</a>" onmouseover="alert('hello world');" -</th> -<td> -</td> -</tr> -</table> - -!! end - -!! test -Fuzz testing: Parser22 -!! input -http://===r:::https://b - -{| -!!result -<p><a href="http://===r:::https://b" class="external free" rel="nofollow">http://===r:::https://b</a> -</p> -<table> -<tr><td></td></tr> -</table> - -!! end - -# Known to produce bad XML for now -!! test -Fuzz testing: Parser24 -!! options -noxml -!! input -{| -{{{| -<u CLASS= -| {{{{SSSll!!!!!!!VVVV)]]][[Special:*xxxxxxx--><noinclude>}}}} > -<br style="onmouseover='alert(document.cookie);' " /> - -MOVE YOUR MOUSE CURSOR OVER THIS TEXT -| -!! result -<table> -{{{| -<u class="|">}}}} > -<br style="onmouseover='alert(document.cookie);'" /> - -MOVE YOUR MOUSE CURSOR OVER THIS TEXT -<tr> -<td></u> -</td> -</tr> -</table> - -!! end - -# Note: the current result listed for this is not what the original one was, -# but the original bug was JavaScript injection, which is fixed in any case. -# It's not clear that the original result listed was any more correct than the -# current one. Original result: -# <p>{{{| -# </p> -# <li class="||"> -# }}}blah" onmouseover="alert('hello world');" align="left"<b>MOVE MOUSE CURSOR OVER HERE</b> -!!test -Fuzz testing: Parser25 (bug 6055) -!! input -{{{ -| -<LI CLASS=|| - > -}}}blah" onmouseover="alert('hello world');" align="left"'''MOVE MOUSE CURSOR OVER HERE -!! result -<p><LI CLASS=blah" onmouseover="alert('hello world');" align="left"<b>MOVE MOUSE CURSOR OVER HERE</b> -</p> -!! end - -!!test -Fuzz testing: URL adjacent extension (with space, clean) -!! options -!! input -http://example.com <nowiki>junk</nowiki> -!! result -<p><a href="http://example.com" class="external free" rel="nofollow">http://example.com</a> junk -</p> -!!end - -!!test -Fuzz testing: URL adjacent extension (no space, dirty; nowiki) -!! options -!! input -http://example.com<nowiki>junk</nowiki> -!! result -<p><a href="http://example.com" class="external free" rel="nofollow">http://example.com</a>junk -</p> -!!end - -!!test -Fuzz testing: URL adjacent extension (no space, dirty; pre) -!! options -!! input -http://example.com<pre>junk</pre> -!! result -<a href="http://example.com" class="external free" rel="nofollow">http://example.com</a><pre>junk</pre> - -!!end - -!!test -Fuzz testing: image with bogus manual thumbnail -!!input -[[Image:foobar.jpg|thumbnail= ]] -!!result -<div class="thumb tright"><div class="thumbinner" style="width:1943px;">Error creating thumbnail: <div class="thumbcaption"></div></div></div> - -!!end - -!! test -Fuzz testing: encoded newline in generated HTML replacements (bug 6577) -!! input -<pre dir=" "></pre> -!! result -<pre dir=" "></pre> - -!! end - -!! test -Parsing optional HTML elements (Bug 6171) -!! options -!! input -<table> - <tr> - <td> Some tabular data</td> - <td> More tabular data ... - <td> And yet som tabular data</td> - </tr> -</table> -!! result -<table> - <tr> - <td> Some tabular data</td> - <td> More tabular data ... - </td><td> And yet som tabular data</td> - </tr> -</table> - -!! end - -!! test -Correct handling of <td>, <tr> (Bug 6171) -!! options -!! input -<table> - <tr> - <td> Some tabular data</td> - <td> More tabular data ...</td> - <td> And yet som tabular data</td> - </tr> -</table> -!! result -<table> - <tr> - <td> Some tabular data</td> - <td> More tabular data ...</td> - <td> And yet som tabular data</td> - </tr> -</table> - -!! end - - -!! test -Parsing crashing regression (fr:JavaScript) -!! input -</body></x> -!! result -<p></body></x> -</p> -!! end - -!! test -Inline wiki vs wiki block nesting -!! input -'''Bold paragraph - -New wiki paragraph -!! result -<p><b>Bold paragraph</b> -</p><p>New wiki paragraph -</p> -!! end - -!! test -Inline HTML vs wiki block nesting -!! options -disabled -!! input -<b>Bold paragraph - -New wiki paragraph -!! result -<p><b>Bold paragraph</b> -</p><p>New wiki paragraph -</p> -!! end - -# Original result was this: -# <p><b>bold</b><b>bold<i>bolditalics</i></b> -# </p> -# While that might be marginally more intuitive, maybe, the six-apostrophe -# construct is clearly pathological and the result stated here (which is what -# the parser actually does) is about as reasonable as anything. -!!test -Mixing markup for italics and bold -!! options -!! input -'''bold''''''bold''bolditalics''''' -!! result -<p>'<i>bold'</i><b>bold<i>bolditalics</i></b> -</p> -!! end - - -!! article -Xyzzyx -!! text -Article for special page transclusion test -!! endarticle - -!! test -Special page transclusion -!! options -!! input -{{Special:Prefixindex/Xyzzyx}} -!! result -<p><br /> -</p> -<table border="0" id="mw-prefixindex-list-table"><tr><td><a href="/wiki/Xyzzyx">Xyzzyx</a></td></tr></table> - -!! end - -!! test -Special page transclusion twice (bug 5021) -!! options -!! input -{{Special:Prefixindex/Xyzzyx}} -{{Special:Prefixindex/Xyzzyx}} -!! result -<p><br /> -</p> -<table border="0" id="mw-prefixindex-list-table"><tr><td><a href="/wiki/Xyzzyx">Xyzzyx</a></td></tr></table> -<p><br /> -</p> -<table border="0" id="mw-prefixindex-list-table"><tr><td><a href="/wiki/Xyzzyx">Xyzzyx</a></td></tr></table> - -!! 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&redlink=1" class="new" title="MediaWiki:Mainpagexxx (page does not exist)">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 -!! result -<p>= x = y -</p> -!! end - - -!! test -Section extraction test (section 0) -!! options -section=0 -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -start -!! end - -!! test -Section extraction test (section 1) -!! options -section=1 -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -==a== -===aa=== -====aaa==== -!! end - -!! test -Section extraction test (section 2) -!! options -section=2 -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -===aa=== -====aaa==== -!! end - -!! test -Section extraction test (section 3) -!! options -section=3 -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -====aaa==== -!! end - -!! test -Section extraction test (section 4) -!! options -section=4 -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -==b== -===ba=== -===bb=== -====bba==== -===bc=== -!! end - -!! test -Section extraction test (section 5) -!! options -section=5 -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -===ba=== -!! end - -!! test -Section extraction test (section 6) -!! options -section=6 -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -===bb=== -====bba==== -!! end - -!! test -Section extraction test (section 7) -!! options -section=7 -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -====bba==== -!! end - -!! test -Section extraction test (section 8) -!! options -section=8 -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -===bc=== -!! end - -!! test -Section extraction test (section 9) -!! options -section=9 -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -==c== -===ca=== -!! end - -!! test -Section extraction test (section 10) -!! options -section=10 -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -===ca=== -!! end - -!! test -Section extraction test (nonexistent section 11) -!! options -section=11 -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -!! end - -!! test -Section extraction test with bogus heading (section 1) -!! options -section=1 -!! input -==a== -==bogus== not a legal section -==b== -!! result -==a== -==bogus== not a legal section -!! end - -!! test -Section extraction test with bogus heading (section 2) -!! options -section=2 -!! input -==a== -==bogus== not a legal section -==b== -!! result -==b== -!! end - -!! test -Section extraction test with comment after heading (section 1) -!! options -section=1 -!! input -==a== -==b== <!-- --> -==c== -!! result -==a== -!! end - -!! test -Section extraction test with comment after heading (section 2) -!! options -section=2 -!! input -==a== -==b== <!-- --> -==c== -!! result -==b== <!-- --> -!! end - -!! test -Section extraction test with bogus <nowiki> heading (section 1) -!! options -section=1 -!! input -==a== -==bogus== <nowiki>not a legal section</nowiki> -==b== -!! result -==a== -==bogus== <nowiki>not a legal section</nowiki> -!! end - -!! test -Section extraction test with bogus <nowiki> heading (section 2) -!! options -section=2 -!! input -==a== -==bogus== <nowiki>not a legal section</nowiki> -==b== -!! result -==b== -!! end - - -# Formerly testing for bug 2587, now resolved by the use of unmarked sections -# instead of respecting commented sections -!! test -Section extraction prefixed by comment (section 1) -!! options -section=1 -!! input -<!-- -->==sec1== -==sec2== -!!result -==sec2== -!!end - -!! test -Section extraction prefixed by comment (section 2) -!! options -section=2 -!! input -<!-- -->==sec1== -==sec2== -!!result - -!!end - - -# Formerly testing for bug 2607, now resolved by the use of unmarked sections -# instead of respecting HTML-style headings -!! test -Section extraction, mixed wiki and html (section 1) -!! options -section=1 -!! input -<h2>unmarked</h2> -unmarked -==1== -one -==2== -two -!! result -==1== -one -!! end - -!! test -Section extraction, mixed wiki and html (section 2) -!! options -section=2 -!! input -<h2>unmarked</h2> -unmarked -==1== -one -==2== -two -!! result -==2== -two -!! end - - -# Formerly testing for bug 3342 -!! test -Section extraction, heading surrounded by <noinclude> -!! options -section=1 -!! input -<noinclude>==unmarked==</noinclude> -==marked== -!! result -==marked== -!!end - -# Test behaviour of bug 19910 -!! test -Sectiion with all-equals -!! options -section=2 -!! input -=== -The line above must have a trailing space -=== <!-- ---> <!-- --> -But just in case it doesn't... -!! result -=== <!-- ---> <!-- --> -But just in case it doesn't... -!! end - -!! test -Section replacement test (section 0) -!! options -replace=0,"xxx" -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -xxx - -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! end - -!! test -Section replacement test (section 1) -!! options -replace=1,"xxx" -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -start -xxx - -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! end - -!! test -Section replacement test (section 2) -!! options -replace=2,"xxx" -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -start -==a== -xxx - -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! end - -!! test -Section replacement test (section 3) -!! options -replace=3,"xxx" -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -start -==a== -===aa=== -xxx - -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! end - -!! test -Section replacement test (section 4) -!! options -replace=4,"xxx" -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -start -==a== -===aa=== -====aaa==== -xxx - -==c== -===ca=== -!! end - -!! test -Section replacement test (section 5) -!! options -replace=5,"xxx" -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -start -==a== -===aa=== -====aaa==== -==b== -xxx - -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! end - -!! test -Section replacement test (section 6) -!! options -replace=6,"xxx" -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -xxx - -===bc=== -==c== -===ca=== -!! end - -!! test -Section replacement test (section 7) -!! options -replace=7,"xxx" -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -xxx - -===bc=== -==c== -===ca=== -!! end - -!! test -Section replacement test (section 8) -!! options -replace=8,"xxx" -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -xxx - -==c== -===ca=== -!!end - -!! test -Section replacement test (section 9) -!! options -replace=9,"xxx" -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -xxx -!! end - -!! test -Section replacement test (section 10) -!! options -replace=10,"xxx" -!! input -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -===ca=== -!! result -start -==a== -===aa=== -====aaa==== -==b== -===ba=== -===bb=== -====bba==== -===bc=== -==c== -xxx -!! end - -!! test -Section replacement test with initial whitespace (bug 13728) -!! options -replace=2,"xxx" -!! input - Preformatted initial line -==a== -===a=== -!! result - Preformatted initial line -==a== -xxx -!! end - - -!! test -Section extraction, heading followed by pre with 20 spaces (bug 6398) -!! options -section=1 -!! input -==a== - a -!! result -==a== - a -!! end - -!! test -Section extraction, heading followed by pre with 19 spaces (bug 6398 sanity check) -!! options -section=1 -!! input -==a== - a -!! result -==a== - a -!! end - - -!! test -Section extraction, <pre> around bogus header (bug 10309) -!! options -noxml section=2 -!! input -== Section One == -<pre> -======= -</pre> - -== Section Two == -stuff -!! result -== Section Two == -stuff -!! end - -!! test -Section replacement, <pre> around bogus header (bug 10309) -!! options -noxml replace=2,"xxx" -!! input -== Section One == -<pre> -======= -</pre> - -== Section Two == -stuff -!! result -== Section One == -<pre> -======= -</pre> - -xxx -!! end - - - -!! test -Handling of 
 in URLs -!! input -**irc://
a -!! result -<ul><li><ul><li><a href="irc://%0Aa" class="external free" rel="nofollow">irc://%0Aa</a> -</li></ul> -</li></ul> - -!!end - -!! test -5 quotes, code coverage +1 line -!! input -''''' -!! result -!! end - -!! test -Special:Search page linking. -!! input -{{Special:search}} -!! result -<p><a href="/wiki/Special:Search">Special:Search</a> -</p> -!! end - -!! test -Say the magic word -!! input -* {{PAGENAME}} -* {{BASEPAGENAME}} -* {{SUBPAGENAME}} -* {{SUBPAGENAMEE}} -* {{BASEPAGENAME}} -* {{BASEPAGENAMEE}} -* {{TALKPAGENAME}} -* {{TALKPAGENAMEE}} -* {{SUBJECTPAGENAME}} -* {{SUBJECTPAGENAMEE}} -* {{NAMESPACEE}} -* {{NAMESPACE}} -* {{TALKSPACE}} -* {{TALKSPACEE}} -* {{SUBJECTSPACE}} -* {{SUBJECTSPACEE}} -* {{Dynamic|{{NUMBEROFUSERS}}|{{NUMBEROFPAGES}}|{{CURRENTVERSION}}|{{CONTENTLANGUAGE}}|{{DIRECTIONMARK}}|{{CURRENTTIMESTAMP}}|{{NUMBEROFARTICLES}}}} -!! result -<ul><li> Parser test -</li><li> Parser test -</li><li> Parser test -</li><li> Parser_test -</li><li> Parser test -</li><li> Parser_test -</li><li> Talk:Parser test -</li><li> Talk:Parser_test -</li><li> Parser test -</li><li> Parser_test -</li><li> -</li><li> -</li><li> Talk -</li><li> Talk -</li><li> -</li><li> -</li><li> <a href="/index.php?title=Template:Dynamic&action=edit&redlink=1" class="new" title="Template:Dynamic (page does not exist)">Template:Dynamic</a> -</li></ul> - -!! end -### Note: Above tests excludes the "{{NUMBEROFADMINS}}" magic word because it generates a MySQL error when included. - -!! test -Gallery -!! input -<gallery> -image1.png | -image2.gif||||| - -image3| -image4 |300px| centre - image5.svg| http:///////// -[[x|xx]]]] -* image6 -</gallery> -!! result -<ul class="gallery"> - <li class="gallerybox" style="width: 155px"><div style="width: 155px"> - <div style="height: 150px;">Image1.png</div> - <div class="gallerytext"> - </div> - </div></li> - <li class="gallerybox" style="width: 155px"><div style="width: 155px"> - <div style="height: 150px;">Image2.gif</div> - <div class="gallerytext"> -<p>|||| -</p> - </div> - </div></li> - <li class="gallerybox" style="width: 155px"><div style="width: 155px"> - <div style="height: 150px;">Image3</div> - <div class="gallerytext"> - </div> - </div></li> - <li class="gallerybox" style="width: 155px"><div style="width: 155px"> - <div style="height: 150px;">Image4</div> - <div class="gallerytext"> -<p>300px| centre -</p> - </div> - </div></li> - <li class="gallerybox" style="width: 155px"><div style="width: 155px"> - <div style="height: 150px;">Image5.svg</div> - <div class="gallerytext"> -<p><a href="http://///////" class="external free" rel="nofollow">http://///////</a> -</p> - </div> - </div></li> - <li class="gallerybox" style="width: 155px"><div style="width: 155px"> - <div style="height: 150px;">* image6</div> - <div class="gallerytext"> - </div> - </div></li> -</ul> - -!! end - -!! test -Gallery (with options) -!! input -<gallery widths='60px' heights='40px' perrow='2' caption='Foo [[Main Page]]' > -File:Nonexistant.jpg|caption -File:Nonexistant.jpg -image:foobar.jpg|some '''caption''' [[Main Page]] -image:foobar.jpg -</gallery> -!! result -<ul class="gallery" style="max-width: 206px;_width: 206px;"> - <li class='gallerycaption'>Foo <a href="/wiki/Main_Page">Main Page</a></li> - <li class="gallerybox" style="width: 95px"><div style="width: 95px"> - <div style="height: 70px;">Nonexistant.jpg</div> - <div class="gallerytext"> -<p>caption -</p> - </div> - </div></li> - <li class="gallerybox" style="width: 95px"><div style="width: 95px"> - <div style="height: 70px;">Nonexistant.jpg</div> - <div class="gallerytext"> - </div> - </div></li> - <li class="gallerybox" style="width: 95px"><div style="width: 95px"> - <div class="thumb" style="width: 90px; height: 70px;"><div style="margin:31px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="60" height="7" /></a></div></div> - <div class="gallerytext"> -<p>some <b>caption</b> <a href="/wiki/Main_Page">Main Page</a> -</p> - </div> - </div></li> - <li class="gallerybox" style="width: 95px"><div style="width: 95px"> - <div class="thumb" style="width: 90px; height: 70px;"><div style="margin:31px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="60" height="7" /></a></div></div> - <div class="gallerytext"> - </div> - </div></li> -</ul> - -!! end - -!! test -gallery (with showfilename option) -!! input -<gallery showfilename> -File:Nonexistant.jpg|caption -File:Nonexistant.jpg -image:foobar.jpg|some '''caption''' [[Main Page]] -File:Foobar.jpg -</gallery> -!! result -<ul class="gallery"> - <li class="gallerybox" style="width: 155px"><div style="width: 155px"> - <div style="height: 150px;">Nonexistant.jpg</div> - <div class="gallerytext"> -<p><a href="/wiki/File:Nonexistant.jpg" title="File:Nonexistant.jpg">Nonexistant.jpg</a><br /> -caption -</p> - </div> - </div></li> - <li class="gallerybox" style="width: 155px"><div style="width: 155px"> - <div style="height: 150px;">Nonexistant.jpg</div> - <div class="gallerytext"> -<p><a href="/wiki/File:Nonexistant.jpg" title="File:Nonexistant.jpg">Nonexistant.jpg</a><br /> -</p> - </div> - </div></li> - <li class="gallerybox" style="width: 155px"><div style="width: 155px"> - <div class="thumb" style="width: 150px; height: 150px;"><div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="120" height="14" /></a></div></div> - <div class="gallerytext"> -<p><a href="/wiki/File:Foobar.jpg" title="File:Foobar.jpg">Foobar.jpg</a><br /> -some <b>caption</b> <a href="/wiki/Main_Page">Main Page</a> -</p> - </div> - </div></li> - <li class="gallerybox" style="width: 155px"><div style="width: 155px"> - <div class="thumb" style="width: 150px; height: 150px;"><div style="margin:68px auto;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="120" height="14" /></a></div></div> - <div class="gallerytext"> -<p><a href="/wiki/File:Foobar.jpg" title="File:Foobar.jpg">Foobar.jpg</a><br /> -</p> - </div> - </div></li> -</ul> - -!! end - -!! test -HTML Hex character encoding (spells the word "JavaScript") -!! input -JavaScript -!! result -<p>JavaScript -</p> -!! end - -!! test -__FORCETOC__ override -!! input -__NEWSECTIONLINK__ -__FORCETOC__ -!! result -<p><br /> -</p> -!! end - -!! test -ISBN code coverage -!! input -ISBN 978-0-1234-56 789 -!! result -<p><a href="/wiki/Special:BookSources/9780123456" class="internal mw-magiclink-isbn">ISBN 978-0-1234-56</a> 789 -</p> -!! end - -!! test -ISBN followed by 5 spaces -!! input -ISBN -!! result -<p>ISBN -</p> -!! end - -!! test -Double ISBN -!! input -ISBN ISBN 1234567890 -!! result -<p>ISBN <a href="/wiki/Special:BookSources/1234567890" class="internal mw-magiclink-isbn">ISBN 1234567890</a> -</p> -!! end - -!! test -Bug 22905: <abbr> followed by ISBN followed by </a> -!! input -<abbr>(fr)</abbr> ISBN 2753300917 [http://www.example.com example.com] -!! result -<p><abbr>(fr)</abbr> <a href="/wiki/Special:BookSources/2753300917" class="internal mw-magiclink-isbn">ISBN 2753300917</a> <a href="http://www.example.com" class="external text" rel="nofollow">example.com</a> -</p> -!! end - -!! test -Double RFC -!! input -RFC RFC 1234 -!! result -<p>RFC <a href="http://tools.ietf.org/html/rfc1234" class="external mw-magiclink-rfc">RFC 1234</a> -</p> -!! end - -!! test -Double RFC with a wiki link -!! input -RFC [[RFC 1234]] -!! result -<p>RFC <a href="/index.php?title=RFC_1234&action=edit&redlink=1" class="new" title="RFC 1234 (page does not exist)">RFC 1234</a> -</p> -!! end - -!! test -RFC code coverage -!! input -RFC 983 987 -!! result -<p><a href="http://tools.ietf.org/html/rfc983" class="external mw-magiclink-rfc">RFC 983</a> 987 -</p> -!! end - -!! test -Centre-aligned image -!! input -[[Image:foobar.jpg|centre]] -!! result -<div class="center"><div class="floatnone"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a></div></div> - -!!end - -!! test -None-aligned image -!! input -[[Image:foobar.jpg|none]] -!! result -<div class="floatnone"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a></div> - -!!end - -!! test -Width + Height sized image (using px) (height is ignored) -!! input -[[Image:foobar.jpg|640x480px]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="640" height="73" /></a> -</p> -!!end - -!! test -Width-sized image (using px, no following whitespace) -!! input -[[Image:foobar.jpg|640px]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="640" height="73" /></a> -</p> -!!end - -!! test -Width-sized image (using px, with following whitespace - test regression from r39467) -!! input -[[Image:foobar.jpg|640px ]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="640" height="73" /></a> -</p> -!!end - -!! test -Width-sized image (using px, with preceding whitespace - test regression from r39467) -!! input -[[Image:foobar.jpg| 640px]] -!! result -<p><a href="/wiki/File:Foobar.jpg" class="image"><img alt="Foobar.jpg" src="http://example.com/images/3/3a/Foobar.jpg" width="640" height="73" /></a> -</p> -!!end - -!! test -Another italics / bold test -!! input - ''' ''x' -!! result -<pre>'<i> </i>x' -</pre> -!!end - -# Note the results may be incorrect, as parserTest output included this: -# XML error: Mismatched tag at byte 6120: -# ...<dd> </dt></dl> </dd... -!! test -dt/dd/dl test -!! options -disabled -!! input -:;;;:: -!! result -<dl><dd><dl><dt><dl><dt><dl><dt><dl><dd><dl><dd> -</dd></dl> -</dd></dl> -</dt></dl> -</dt></dl> -</dt></dl> -</dd></dl> - -!!end - - -# Images with the "|" character in external URLs in comment tags; Eats half the comment, leaves unmatched "</a>" tag. -!! test -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/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File: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=%7Cleft%7C&param2=%7Cx" class="external text" 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&redlink=1" class="new" title="Subpage test/L1/L2 (page does not exist)">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&redlink=1" class="new" title="Subpage test/L1/L2 (page does not exist)">Subpage test/L1/L2</a> -</p> -!! end - - - -!! test -Parents of subpages, two levels up -!! options -subpage title=[[Subpage test/L1/L2/L3]] -!! input -[[../../|L1]]2 - -[[../../|L1]]l -!! result -<p><a href="/index.php?title=Subpage_test/L1&action=edit&redlink=1" class="new" title="Subpage test/L1 (page does not exist)">L1</a>2 -</p><p><a href="/index.php?title=Subpage_test/L1&action=edit&redlink=1" class="new" title="Subpage test/L1 (page does not exist)">L1l</a> -</p> -!! end - -!! test -Parents of subpages, two levels up, without trailing slash or name. -!! options -subpage title=[[Subpage test/L1/L2/L3]] -!! input -[[../..]] -!! result -<p>[[../..]] -</p> -!! end - -!! test -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&redlink=1" class="new" title="Subpage test/L1//// (page does not exist)">///</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 -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&redlink=1" class="new" title="Template:QUERTY (page does not exist)">Template:QUERTY</a> -</p> -!! end - -# This isn't needed for XHTML conformance, but would be handy as a fallback security measure -!! test -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 -<h2><span class="editsection">[<a href="/index.php?title=MediaWiki:Fake&action=edit&section=T-1" title="MediaWiki:Fake">edit</a>]</span> <span class="mw-headline" id="header">header</span></h2> - -!! end - - -!! test -Out-of-order TOC heading levels -!! input -==2== -======6====== -===3=== -=1= -=====5===== -==2== -!! result -<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>Contents</h2></div> -<ul> -<li class="toclevel-1 tocsection-1"><a href="#2"><span class="tocnumber">1</span> <span class="toctext">2</span></a> -<ul> -<li class="toclevel-2 tocsection-2"><a href="#6"><span class="tocnumber">1.1</span> <span class="toctext">6</span></a></li> -<li class="toclevel-2 tocsection-3"><a href="#3"><span class="tocnumber">1.2</span> <span class="toctext">3</span></a></li> -</ul> -</li> -<li class="toclevel-1 tocsection-4"><a href="#1"><span class="tocnumber">2</span> <span class="toctext">1</span></a> -<ul> -<li class="toclevel-2 tocsection-5"><a href="#5"><span class="tocnumber">2.1</span> <span class="toctext">5</span></a></li> -<li class="toclevel-2 tocsection-6"><a href="#2_2"><span class="tocnumber">2.2</span> <span class="toctext">2</span></a></li> -</ul> -</li> -</ul> -</td></tr></table> -<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" id="2">2</span></h2> -<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" id="6">6</span></h6> -<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" id="3">3</span></h3> -<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" id="1">1</span></h1> -<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" id="5">5</span></h5> -<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" id="2_2">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="/wiki/Special:BookSources/9290170328" class="internal mw-magiclink-isbn">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="/wiki/Special:BookSources/1234567890" class="internal mw-magiclink-isbn">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="/wiki/Special:BookSources/1234567890" class="internal mw-magiclink-isbn">ISBN 1-234-56789-0</a> - 2006 -</p><p><a href="/wiki/Special:BookSources/1234567890" class="internal mw-magiclink-isbn">ISBN 1 234 56789 0</a> - 2006 -</p> -!! end - - -!! test -anchorencode -!! input -{{anchorencode:foo bar©#%n}} -!! result -<p>foo_bar.C2.A9.23.25n -</p> -!! end - -!! test -anchorencode trims spaces -!! input -{{anchorencode: __pretty__please__}} -!! result -<p>pretty_please -</p> -!! end - -!! test -anchorencode deals with links -!! input -{{anchorencode: [[hello|world]] [[hi]]}} -!! result -<p>world_hi -</p> -!! end - -!! test -anchorencode deals with templates -!! input -{{anchorencode: {{Foo}} }} -!! result -<p>FOO -</p> -!! end - -!! test -anchorencode encodes like the TOC generator: (bug 18431) -!! input -=== _ +:.3A%3A&&]] === -{{anchorencode: _ +:.3A%3A&&]] }} -__NOEDITSECTION__ -!! result -<h3> <span class="mw-headline" id=".2B:.3A.253A.26.26.5D.5D"> _ +:.3A%3A&&]] </span></h3> -<p>.2B:.3A.253A.26.26.5D.5D -</p> -!! end - -# Expected output in the following test is not necessarily expected (there -# should probably be <p> tags inside the <blockquote> in the output) -- it's -# only testing for well-formedness. -!! test -Bug 6200: blockquotes and paragraph formatting -!! input -<blockquote> -foo -</blockquote> - -bar - - baz -!! result -<blockquote> -foo -</blockquote> -<p>bar -</p> -<pre>baz -</pre> -!! 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">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/%D0%9A%D0%B0%D1%82%D0%B5%D0%B3%D0%BE%D1%80%D0%B8%D1%98%D0%B0:MediaWiki_User%27s_Guide" title="Категорија: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="/wiki/Main_Page">Main Page</a> -</p> -!! end - - -!! test --{}- tags within headlines (within html for parserConvert()) -!! options -language=sr variant=sr-ec -!! input -== -{Naslov}- == -!! result -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Уреди део: Naslov">уреди</a>]</span> <span class="mw-headline" id="-.7BNaslov.7D-"> 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 -Explicit session-wise language variant mapping (A flag and - flag) -!! options -language=zh variant=zh-tw -!! input -Taiwan is not China. -But -{A|zh:China;zh-tw:Taiwan}- is China, -(This-{-|zh:China;zh-tw:Taiwan}- should be stripped!) -and -{China}- is China. -!! result -<p>Taiwan is not China. -But Taiwan is Taiwan, -(This should be stripped!) -and China is China. -</p> -!! end - -!! test -Explicit session-wise language variant mapping (H flag for hide) -!! options -language=zh variant=zh-tw -!! input -(This-{H|zh:China;zh-tw:Taiwan}- should be stripped!) -Taiwan is China. -!! result -<p>(This should be stripped!) -Taiwan is Taiwan. -</p> -!! end - -!! test -Adding explicit conversion rule for title (T flag) -!! options -language=zh variant=zh-tw showtitle -!! input -Should be stripped-{T|zh:China;zh-tw:Taiwan}-! -!! result -Taiwan -<p>Should be stripped! -</p> -!! end - -!! test -Testing that changing the language variant here in the tests actually works -!! options -language=zh variant=zh showtitle -!! input -Should be stripped-{T|zh:China;zh-tw:Taiwan}-! -!! result -China -<p>Should be stripped! -</p> -!! end - -!! test -Bug 24072: more test on conversion rule for title -!! options -language=zh variant=zh-tw showtitle -!! input -This should be stripped-{T|zh:China;zh-tw:Taiwan}-! -This won't take interferes with the title rule-{H|zh:Beijing;zh-tw:Taipei}-. -!! result -Taiwan -<p>This should be stripped! -This won't take interferes with the title rule. -</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 -Nested using of manual convert syntax -!! options -language=zh variant=zh-hk -!! input -Nested: -{zh-hans:Hi -{zh-cn:China;zh-sg:Singapore;}-;zh-hant:Hello -{zh-tw:Taiwan;zh-hk:H-{ong}- K-{}-ong;}-;}-! -!! result -<p>Nested: Hello Hong Kong! -</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 - -!! test -Unclosed language converter markup "-{" -!! options -language=sr -!! input --{T|hello -!! result -<p>-{T|hello -</p> -!! end - -!! test -Don't convert raw rule "-{R|=>}-" to "=>" -!! options -language=sr -!! input --{R|=>}- -!! result -<p>=> -</p> -!!end - -!!article -Template:Bullet -!!text -* Bar -!!endarticle - -!! test -Bug 529: Uncovered bullet -!! input -* Foo {{bullet}} -!! result -<ul><li> Foo -</li><li> Bar -</li></ul> - -!! end - -!! test -Bug 529: Uncovered table already at line-start -!! input -x - -{{table}} -y -!! result -<p>x -</p> -<table> -<tr> -<td> 1 </td> -<td> 2 -</td></tr> -<tr> -<td> 3 </td> -<td> 4 -</td></tr></table> -<p>y -</p> -!! end - -!! test -Bug 529: Uncovered bullet in parser function result -!! input -* Foo {{lc:{{bullet}} }} -!! result -<ul><li> Foo -</li><li> bar -</li></ul> - -!! end - -!! test -Bug 5678: Double-parsed template argument -!! input -{{lc:{{{1}}}|hello}} -!! result -<p>{{{1}}} -</p> -!! end - -!! test -Bug 5678: Double-parsed template invocation -!! input -{{lc:{{paramtest {{!}} param = hello }} }} -!! result -<p>{{paramtest | param = hello }} -</p> -!! end - -!! test -Case insensitivity of parser functions for non-ASCII characters (bug 8143) -!! options -language=cs -title=[[Main Page]] -!! input -{{PRVNÍVELKÉ:ěščř}} -{{prvnívelké:ěščř}} -{{PRVNÍMALÉ:ěščř}} -{{prvnímalé:ěščř}} -{{MALÁ:ěščř}} -{{malá:ěščř}} -{{VELKÁ:ěščř}} -{{velká:ěščř}} -!! result -<p>Ěščř -Ěščř -ěščř -ěščř -ěščř -ěščř -ĚŠČŘ -ĚŠČŘ -</p> -!! end - -!! test -Morwen/13: Unclosed link followed by heading -!! input -[[link -==heading== -!! result -<p>[[link -</p> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: heading">edit</a>]</span> <span class="mw-headline" id="heading">heading</span></h2> - -!! end - -!! test -HHP2.1: Heuristics for headings in preprocessor parenthetical structures -!! input -{{foo| -=heading= -!! result -<p>{{foo| -</p> -<h1> <span class="mw-headline" id="heading">heading</span></h1> - -!! end - -!! test -HHP2.2: Heuristics for headings in preprocessor parenthetical structures -!! input -{{foo| -==heading== -!! result -<p>{{foo| -</p> -<h2><span class="editsection">[<a href="/index.php?title=Parser_test&action=edit&section=1" title="Edit section: heading">edit</a>]</span> <span class="mw-headline" id="heading">heading</span></h2> - -!! end - -!! test -Tildes in comments -!! options -pst -!! input -<!-- ~~~~ --> -!! result -<!-- ~~~~ --> -!! end - -!! test -Paragraphs inside divs (no extra line breaks) -!! input -<div>Line one - -Line two</div> -!! result -<div>Line one -Line two</div> - -!! end - -!! test -Paragraphs inside divs (extra line break on open) -!! input -<div> -Line one - -Line two</div> -!! result -<div> -<p>Line one -</p> -Line two</div> - -!! end - -!! test -Paragraphs inside divs (extra line break on close) -!! input -<div>Line one - -Line two -</div> -!! result -<div>Line one -<p>Line two -</p> -</div> - -!! end - -!! test -Paragraphs inside divs (extra line break on open and close) -!! input -<div> -Line one - -Line two -</div> -!! result -<div> -<p>Line one -</p><p>Line two -</p> -</div> - -!! end - -!! test -Nesting tags, paragraphs on lines which begin with <div> -!! options -disabled -!! input -<div></div><strong>A -B</strong> -!! result -<div></div> -<p><strong>A -B</strong> -</p> -!! end - -# Bug 6200: <blockquote> should behave like <div> with respect to line breaks -!! test -Bug 6200: paragraphs inside blockquotes (no extra line breaks) -!! options -disabled -!! input -<blockquote>Line one - -Line two</blockquote> -!! result -<blockquote>Line one -Line two</blockquote> - -!! end - -!! test -Bug 6200: paragraphs inside blockquotes (extra line break on open) -!! options -disabled -!! input -<blockquote> -Line one - -Line two</blockquote> -!! result -<blockquote> -<p>Line one -</p> -Line two</blockquote> - -!! end - -!! test -Bug 6200: paragraphs inside blockquotes (extra line break on close) -!! options -disabled -!! input -<blockquote>Line one - -Line two -</blockquote> -!! result -<blockquote>Line one -<p>Line two -</p> -</blockquote> - -!! end - -!! test -Bug 6200: paragraphs inside blockquotes (extra line break on open and close) -!! options -disabled -!! input -<blockquote> -Line one - -Line two -</blockquote> -!! result -<blockquote> -<p>Line one -</p><p>Line two -</p> -</blockquote> - -!! end - -!! test -Paragraphs inside blockquotes/divs (no extra line breaks) -!! input -<blockquote><div>Line one - -Line two</div></blockquote> -!! result -<blockquote><div>Line one -Line two</div></blockquote> - -!! end - -!! test -Paragraphs inside blockquotes/divs (extra line break on open) -!! input -<blockquote><div> -Line one - -Line two</div></blockquote> -!! result -<blockquote><div> -<p>Line one -</p> -Line two</div></blockquote> - -!! end - -!! test -Paragraphs inside blockquotes/divs (extra line break on close) -!! input -<blockquote><div>Line one - -Line two -</div></blockquote> -!! result -<blockquote><div>Line one -<p>Line two -</p> -</div></blockquote> - -!! end - -!! test -Paragraphs inside blockquotes/divs (extra line break on open and close) -!! input -<blockquote><div> -Line one - -Line two -</div></blockquote> -!! result -<blockquote><div> -<p>Line one -</p><p>Line two -</p> -</div></blockquote> - -!! end - -!! test -Interwiki links trounced by replaceExternalLinks after early LinkHolderArray expansion -!! options -wgLinkHolderBatchSize=0 -!! input -[[meatball:1]] -[[meatball:2]] -[[meatball:3]] -!! result -<p><a href="http://www.usemod.com/cgi-bin/mb.pl?1" class="extiw">meatball:1</a> -<a href="http://www.usemod.com/cgi-bin/mb.pl?2" class="extiw">meatball:2</a> -<a href="http://www.usemod.com/cgi-bin/mb.pl?3" class="extiw">meatball:3</a> -</p> -!! end - -!! test -Free external link invading image caption -!! input -[[Image:Foobar.jpg|thumb|http://x|hello]] -!! result -<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/3/3a/Foobar.jpg" width="180" height="20" class="thumbimage" /></a> <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.jpg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>hello</div></div></div> - -!! end - -!! test -Bug 15196: localised external link numbers -!! options -language=fa -!! input -[http://en.wikipedia.org/] -!! result -<p><a href="http://en.wikipedia.org/" class="external autonumber" rel="nofollow">[۱]</a> -</p> -!! end - -!! test -Multibyte character in padleft -!! input -{{padleft:-Hello|7|Æ}} -!! result -<p>Æ-Hello -</p> -!! end - -!! test -Multibyte character in padright -!! input -{{padright:Hello-|7|Æ}} -!! result -<p>Hello-Æ -</p> -!! end - -!! test -Formatted date -!! config -wgUseDynamicDates=1 -!! input -[[2009-03-24]] -!! result -<p><span class="mw-formatted-date" title="2009-03-24"><a href="/index.php?title=2009&action=edit&redlink=1" class="new" title="2009 (page does not exist)">2009</a>-<a href="/index.php?title=March_24&action=edit&redlink=1" class="new" title="March 24 (page does not exist)">03-24</a></span> -</p> -!!end - -!!test -formatdate parser function -!!input -{{#formatdate:2009-03-24}} -!! result -<p><span class="mw-formatted-date" title="2009-03-24">2009-03-24</span> -</p> -!! end - -!!test -formatdate parser function, with default format -!!input -{{#formatdate:2009-03-24|mdy}} -!! result -<p><span class="mw-formatted-date" title="2009-03-24">March 24, 2009</span> -</p> -!! end - -!! test -Linked date with autoformatting disabled -!! config -wgUseDynamicDates=false -!! input -[[2009-03-24]] -!! result -<p><a href="/index.php?title=2009-03-24&action=edit&redlink=1" class="new" title="2009-03-24 (page does not exist)">2009-03-24</a> -</p> -!! end - -!! test -Spacing of numbers in formatted dates -!! input -{{#formatdate:January 15}} -!! result -<p><span class="mw-formatted-date" title="01-15">January 15</span> -</p> -!! end - -!! test -Spacing of numbers in formatted dates (linked) -!! config -wgUseDynamicDates=true -!! input -[[January 15]] -!! result -<p><span class="mw-formatted-date" title="01-15"><a href="/index.php?title=January_15&action=edit&redlink=1" class="new" title="January 15 (page does not exist)">January 15</a></span> -</p> -!! end - -# -# -# - -# -# Edit comments -# - -!! test -Edit comment with link -!! options -comment -!! input -I like the [[Main Page]] a lot -!! result -I like the <a href="/wiki/Main_Page">Main Page</a> a lot -!!end - -!! test -Edit comment with link and link text -!! options -comment -!! input -I like the [[Main Page|best pages]] a lot -!! result -I like the <a href="/wiki/Main_Page" title="Main Page">best pages</a> a lot -!!end - -!! test -Edit comment with link and link text with suffix -!! options -comment -!! input -I like the [[Main Page|best page]]s a lot -!! result -I like the <a href="/wiki/Main_Page" title="Main Page">best pages</a> a lot -!!end - -!! test -Edit comment with section link (non-local, eg in history list) -!! options -comment title=[[Main Page]] -!! input -/* External links */ removed bogus entries -!! result -<span class="autocomment"><a href="/wiki/Main_Page#External_links" title="Main Page">→</a>External links: </span> removed bogus entries -!!end - -!! test -Edit comment with section link (local, eg in diff view) -!! options -comment local title=[[Main Page]] -!! input -/* External links */ removed bogus entries -!! result -<span class="autocomment"><a href="#External_links">→</a>External links: </span> removed bogus entries -!!end - -!! test -Edit comment with subpage link (bug 14080) -!! options -comment -subpage -title=[[Subpage test]] -!! input -Poked at a [[/subpage]] here... -!! result -Poked at a <a href="/wiki/Subpage_test/subpage" title="Subpage test/subpage">/subpage</a> here... -!!end - -!! test -Edit comment with subpage link and link text (bug 14080) -!! options -comment -subpage -title=[[Subpage test]] -!! input -Poked at a [[/subpage|neat little page]] here... -!! result -Poked at a <a href="/wiki/Subpage_test/subpage" title="Subpage test/subpage">neat little page</a> here... -!!end - -!! test -Edit comment with bogus subpage link in non-subpage NS (bug 14080) -!! options -comment -title=[[Subpage test]] -!! input -Poked at a [[/subpage]] here... -!! result -Poked at a <a href="/index.php?title=/subpage&action=edit&redlink=1" class="new" title="/subpage (page does not exist)">/subpage</a> here... -!!end - -!! test -Edit comment with bare anchor link (local, as on diff) -!! options -comment -local -title=[[Main Page]] -!!input -[[#section]] -!! result -<a href="#section">#section</a> -!! end - -!! test -Edit comment with bare anchor link (non-local, as on history) -!! options -comment -title=[[Main Page]] -!!input -[[#section]] -!! result -<a href="/wiki/Main_Page#section" title="Main Page">#section</a> -!! end - -!! test -Space normalisation on autocomment (bug 22784) -!! options -comment -title=[[Main Page]] -!!input -/* __hello__world__ */ -!! result -<span class="autocomment"><a href="/wiki/Main_Page#hello_world" title="Main Page">→</a>__hello__world__</span> -!! end - -!! test -Bad images - basic functionality -!! input -[[File:Bad.jpg]] -!! result -!! end - -!! test -Bad images - bug 16039: text after bad image disappears -!! input -Foo bar -[[File:Bad.jpg]] -Bar foo -!! result -<p>Foo bar -</p><p>Bar foo -</p> -!! end - -!! test -Verify that displaytitle works (bug #22501) no displaytitle -!! options -showtitle -!! config -wgAllowDisplayTitle=true -wgRestrictDisplayTitle=false -!! input -this is not the the title -!! result -Parser test -<p>this is not the the title -</p> -!! end - -!! test -Verify that displaytitle works (bug #22501) RestrictDisplayTitle=false -!! options -showtitle -title=[[Screen]] -!! config -wgAllowDisplayTitle=true -wgRestrictDisplayTitle=false -!! input -this is not the the title -{{DISPLAYTITLE:whatever}} -!! result -whatever -<p>this is not the the title -</p> -!! end - -!! test -Verify that displaytitle works (bug #22501) RestrictDisplayTitle=true mismatch -!! options -showtitle -title=[[Screen]] -!! config -wgAllowDisplayTitle=true -wgRestrictDisplayTitle=true -!! input -this is not the the title -{{DISPLAYTITLE:whatever}} -!! result -Screen -<p>this is not the the title -</p> -!! end - -!! test -Verify that displaytitle works (bug #22501) RestrictDisplayTitle=true matching -!! options -showtitle -title=[[Screen]] -!! config -wgAllowDisplayTitle=true -wgRestrictDisplayTitle=true -!! input -this is not the the title -{{DISPLAYTITLE:screen}} -!! result -screen -<p>this is not the the title -</p> -!! end - -!! test -Verify that displaytitle works (bug #22501) AllowDisplayTitle=false -!! options -showtitle -title=[[Screen]] -!! config -wgAllowDisplayTitle=false -!! input -this is not the the title -{{DISPLAYTITLE:screen}} -!! result -Screen -<p>this is not the the title -<a href="/index.php?title=Template:DISPLAYTITLE:screen&action=edit&redlink=1" class="new" title="Template:DISPLAYTITLE:screen (page does not exist)">Template:DISPLAYTITLE:screen</a> -</p> -!! end - -!! test -Verify that displaytitle works (bug #22501) AllowDisplayTitle=false no DISPLAYTITLE -!! options -showtitle -title=[[Screen]] -!! config -wgAllowDisplayTitle=false -!! input -this is not the the title -!! result -Screen -<p>this is not the the title -</p> -!! end - -!! test -preload: check <noinclude> and <includeonly> -!! options -preload -!! input -Hello <noinclude>cruel</noinclude><includeonly>kind</includeonly> world. -!! result -Hello kind world. -!! end - -!! test -preload: check <onlyinclude> -!! options -preload -!! input -Goodbye <onlyinclude>Hello world</onlyinclude> -!! result -Hello world -!! end - -!! test -preload: can pass tags through if we want to -!! options -preload -!! input -<includeonly><</includeonly>includeonly>Hello world<includeonly><</includeonly>/includeonly> -!! result -<includeonly>Hello world</includeonly> -!! end - -!! test -preload: check that it doesn't try to do tricks -!! options -preload -!! input -* <!-- Hello --> ''{{world}}'' {{<includeonly>subst:</includeonly>How are you}}{{ {{{|safesubst:}}} #if:1|2|3}} -!! result -* <!-- Hello --> ''{{world}}'' {{subst:How are you}}{{ {{{|safesubst:}}} #if:1|2|3}} -!! end - -!! test -Play a bit with r67090 and bug 3158 -!! options -disabled -!! input -<div style="width:50% !important"> </div> -<div style="width:50% !important"> </div> -<div style="width:50% !important"> </div> -<div style="border : solid;"> </div> -!! result -<div style="width:50% !important"> </div> -<div style="width:50% !important"> </div> -<div style="width:50% !important"> </div> -<div style="border : solid;"> </div> - -!! end - -!! test -HTML5 data attributes -!! input -<span data-foo="bar">Baz</span> -<p data-abc-def_hij="">Quuz</p> -!! result -<p><span data-foo="bar">Baz</span> -</p> -<p data-abc-def_hij="">Quuz</p> - -!! end - - -TODO: -more images -more tables -math -character entities -and much more -Try for 100% code coverage diff --git a/maintenance/tests/parser/parserTestsParserHook.php b/maintenance/tests/parser/parserTestsParserHook.php deleted file mode 100644 index 6387208a..00000000 --- a/maintenance/tests/parser/parserTestsParserHook.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -/** - * A basic extension that's used by the parser tests to test whether input and - * arguments are passed to extensions properly. - * - * Copyright © 2005, 2006 Ævar Arnfjörð Bjarmason - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - * @ingroup Maintenance - * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com> - */ - -class ParserTestParserHook { - - static function setup( &$parser ) { - $parser->setHook( 'tag', array( __CLASS__, 'hook' ) ); - - return true; - } - - static function hook( $in, $argv ) { - ob_start(); - var_dump( - $in, - $argv - ); - $ret = ob_get_clean(); - - return "<pre>\n$ret</pre>"; - } -} diff --git a/maintenance/tests/parser/parserTestsStaticParserHook.php b/maintenance/tests/parser/parserTestsStaticParserHook.php deleted file mode 100644 index 72f82276..00000000 --- a/maintenance/tests/parser/parserTestsStaticParserHook.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -/** - * A basic extension that's used by the parser tests to test whether the parser - * calls extensions when they're called inside comments, it shouldn't do that - * - * Copyright © 2005, 2006 Ævar Arnfjörð Bjarmason - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - * @ingroup Maintenance - * @author Ævar Arnfjörð Bjarmason <avarab@gmail.com> - */ - -class ParserTestStaticParserHook { - static function setup( &$parser ) { - $parser->setHook( 'statictag', array( __CLASS__, 'hook' ) ); - - return true; - } - - static function hook( $in, $argv, $parser ) { - if ( ! count( $argv ) ) { - $parser->static_tag_buf = $in; - return ''; - } else if ( count( $argv ) === 1 && isset( $argv['action'] ) - && $argv['action'] === 'flush' && $in === null ) - { - // Clear the buffer, we probably don't need to - if ( isset( $parser->static_tag_buf ) ) { - $tmp = $parser->static_tag_buf; - } else { - $tmp = ''; - } - $parser->static_tag_buf = null; - return $tmp; - } else - // wtf? - return - "\nCall this extension as <statictag>string</statictag> or as" . - " <statictag action=flush/>, not in any other way.\n" . - "text: " . var_export( $in, true ) . "\n" . - "argv: " . var_export( $argv, true ) . "\n"; - } -} diff --git a/maintenance/tests/parserTests.php b/maintenance/tests/parserTests.php deleted file mode 100644 index 7793e6b8..00000000 --- a/maintenance/tests/parserTests.php +++ /dev/null @@ -1,92 +0,0 @@ -<?php -/** - * MediaWiki parser test suite - * - * Copyright © 2004 Brion Vibber <brion@pobox.com> - * http://www.mediawiki.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - * @ingroup Maintenance - */ - -$options = array( 'quick', 'color', 'quiet', 'help', 'show-output', 'record', 'run-disabled' ); -$optionsWithArgs = array( 'regex', 'seed', 'setversion' ); - -require_once( dirname( __FILE__ ) . '/../commandLine.inc' ); - -if ( isset( $options['help'] ) ) { - echo <<<ENDS -MediaWiki $wgVersion parser test suite -Usage: php parserTests.php [options...] - -Options: - --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[=yes|no] Override terminal detection and force color output on or off - use wgCommandLineDarkBg = true; if your term is dark - --regex Only run tests whose descriptions which match given regex - --file=<testfile> Run test cases from a custom file instead of parserTests.txt - --record Record tests in database - --compare Compare with recorded results, without updating the database. - --setversion When using --record, set the version string to use (useful - with git-svn so that you can get the exact revision) - --keep-uploads Re-use the same upload directory for each test, don't delete it - --fuzz Do a fuzz test instead of a normal test - --seed <n> Start the fuzz test from the specified seed - --help Show this help message - --run-disabled run disabled tests - --upload Upload test results to remote wiki (per \$wgParserTestRemote) - -ENDS; - exit( 0 ); -} - -# Cases of weird db corruption were encountered when running tests on earlyish -# versions of SQLite -if ( $wgDBtype == 'sqlite' ) { - $db = wfGetDB( DB_MASTER ); - $version = $db->getServerVersion(); - if ( version_compare( $version, '3.6' ) < 0 ) { - die( "Parser tests require SQLite version 3.6 or later, you have $version\n" ); - } -} - -# There is a convention that the parser should never -# refer to $wgTitle directly, but instead use the title -# passed to it. -$wgTitle = Title::newFromText( 'Parser test script do not use' ); -$tester = new ParserTest($options); - -if ( isset( $options['file'] ) ) { - $files = array( $options['file'] ); -} else { - // Default parser tests and any set from extensions or local config - $files = $wgParserTestFiles; -} - -# Print out software version to assist with locating regressions -$version = SpecialVersion::getVersion(); -echo( "This is MediaWiki version {$version}.\n\n" ); - -if ( isset( $options['fuzz'] ) ) { - $tester->fuzzTest( $files ); -} else { - $ok = $tester->runTestsFromFiles( $files ); - exit ( $ok ? 0 : 1 ); -} diff --git a/maintenance/tests/selenium/Selenium.php b/maintenance/tests/selenium/Selenium.php deleted file mode 100644 index ecf7f9ec..00000000 --- a/maintenance/tests/selenium/Selenium.php +++ /dev/null @@ -1,190 +0,0 @@ -<?php -/** - * Selenium connector - * This is implemented as a singleton. - */ - -require( 'Testing/Selenium.php' ); - -class Selenium { - protected static $_instance = null; - - public $isStarted = false; - public $tester; - - protected $port; - protected $host; - protected $browser; - protected $browsers; - protected $logger; - protected $user; - protected $pass; - protected $timeout = 30000; - protected $verbose; - protected $junitlogfile; //processed by phpUnderControl - protected $runagainstgrid = false; - - /** - * @todo this shouldn't have to be static - */ - static protected $url; - - /** - * Override parent - */ - public function __construct() { - /** - * @todo this is an ugly hack to make information available to - * other tests. It should be fixed. - */ - if ( null === self::$_instance ) { - self::$_instance = $this; - } else { - throw new MWException( "Already have one Selenium instance." ); - } - } - - public function start() { - $this->tester = new Testing_Selenium( $this->browser, self::$url, $this->host, - $this->port, $this->timeout ); - if ( method_exists( $this->tester, "setVerbose" ) ) $this->tester->setVerbose( $this->verbose ); - - $this->tester->start(); - $this->isStarted = true; - } - - public function stop() { - $this->tester->stop(); - $this->tester = null; - $this->isStarted = false; - } - - public function login() { - if ( strlen( $this->user ) == 0 ) { - return; - } - $this->open( self::$url . '/index.php?title=Special:Userlogin' ); - $this->type( 'wpName1', $this->user ); - $this->type( 'wpPassword1', $this->pass ); - $this->click( "//input[@id='wpLoginAttempt']" ); - $this->waitForPageToLoad( 10000 ); - - // after login we redirect to the main page. So check whether the "Prefernces" top menu item exists - $value = $this->isElementPresent( "//li[@id='pt-preferences']" ); - - if ( $value != true ) { - throw new Testing_Selenium_Exception( "Login Failed" ); - } - - } - - public static function getInstance() { - if ( null === self::$_instance ) { - throw new MWException( "No instance set yet" ); - } - - return self::$_instance; - } - - public function loadPage( $title, $action ) { - $this->open( self::$url . '/index.php?title=' . $title . '&action=' . $action ); - } - - public function setLogger( $logger ) { - $this->logger = $logger; - } - - public function getLogger( ) { - return $this->logger; - } - - public function log( $message ) { - $this->logger->write( $message ); - } - - public function setUrl( $url ) { - self::$url = $url; - } - - static public function getUrl() { - return self::$url; - } - - public function setPort( $port ) { - $this->port = $port; - } - - public function getPort() { - return $this->port; - } - - public function setUser( $user ) { - $this->user = $user; - } - - // Function to get username - public function getUser() { - return $this->user; - } - - - public function setPass( $pass ) { - $this->pass = $pass; - } - - //add function to get password - public function getPass( ) { - return $this->pass; - } - - - public function setHost( $host ) { - $this->host = $host; - } - - public function setVerbose( $verbose ) { - $this->verbose = $verbose; - } - - public function setAvailableBrowsers( $availableBrowsers ) { - $this->browsers = $availableBrowsers; - } - - public function setJUnitLogfile( $junitlogfile ) { - $this->junitlogfile = $junitlogfile; - } - - public function getJUnitLogfile( ) { - return $this->junitlogfile; - } - - public function setRunAgainstGrid( $runagainstgrid ) { - $this->runagainstgrid = $runagainstgrid; - } - - public function setBrowser( $b ) { - if ($this->runagainstgrid) { - $this->browser = $b; - return true; - } - if ( !isset( $this->browsers[$b] ) ) { - throw new MWException( "Invalid Browser: $b.\n" ); - } - - $this->browser = $this->browsers[$b]; - } - - public function getAvailableBrowsers() { - return $this->browsers; - } - - public function __call( $name, $args ) { - $t = call_user_func_array( array( $this->tester, $name ), $args ); - return $t; - } - - // Prevent external cloning - protected function __clone() { } - // Prevent external construction - // protected function __construct() {} -} diff --git a/maintenance/tests/selenium/SeleniumConfig.php b/maintenance/tests/selenium/SeleniumConfig.php deleted file mode 100644 index ca69b1f0..00000000 --- a/maintenance/tests/selenium/SeleniumConfig.php +++ /dev/null @@ -1,88 +0,0 @@ -<?php -if ( !defined( 'SELENIUMTEST' ) ) { - die( 1 ); -} - -class SeleniumConfig { - - /* - * Retreives the Selenium configuration values from an ini file. - * See sample config file in selenium_settings.ini.sample - * - */ - - public static function getSeleniumSettings ( &$seleniumSettings, - &$seleniumBrowsers, - &$seleniumTestSuites, - $seleniumConfigFile = null ) { - if ( strlen( $seleniumConfigFile ) == 0 ) { - global $wgSeleniumConfigFile; - if ( isset( $wgSeleniumConfigFile ) ) $seleniumConfigFile = $wgSeleniumConfigFile ; - } - - if ( strlen( $seleniumConfigFile ) == 0 || !file_exists( $seleniumConfigFile ) ) { - throw new MWException( "Unable to read local Selenium Settings from " . $seleniumConfigFile . "\n" ); - } - - if ( !defined( 'PHP_VERSION_ID' ) || - ( PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3 ) ) { - $configArray = self::parse_5_2_ini_file( $seleniumConfigFile ); - } else { - $configArray = parse_ini_file( $seleniumConfigFile, true ); - } - if ( $configArray === false ) { - throw new MWException( "Error parsing " . $seleniumConfigFile . "\n" ); - } - - if ( array_key_exists( 'SeleniumSettings', $configArray) ) { - wfSuppressWarnings(); - //we may need to change how this is set. But for now leave it in the ini file - $seleniumBrowsers = $configArray['SeleniumSettings']['browsers']; - - $seleniumSettings['host'] = $configArray['SeleniumSettings']['host']; - $seleniumSettings['port'] = $configArray['SeleniumSettings']['port']; - $seleniumSettings['wikiUrl'] = $configArray['SeleniumSettings']['wikiUrl']; - $seleniumSettings['username'] = $configArray['SeleniumSettings']['username']; - $seleniumSettings['userPassword'] = $configArray['SeleniumSettings']['userPassword']; - $seleniumSettings['testBrowser'] = $configArray['SeleniumSettings']['testBrowser']; - $seleniumSettings['startserver'] = $configArray['SeleniumSettings']['startserver']; - $seleniumSettings['stopserver'] = $configArray['SeleniumSettings']['stopserver']; - $seleniumSettings['seleniumserverexecpath'] = $configArray['SeleniumSettings']['seleniumserverexecpath']; - $seleniumSettings['jUnitLogFile'] = $configArray['SeleniumSettings']['jUnitLogFile']; - $seleniumSettings['runAgainstGrid'] = $configArray['SeleniumSettings']['runAgainstGrid']; - - wfRestoreWarnings(); - } - if ( array_key_exists( 'SeleniumTests', $configArray) ) { - wfSuppressWarnings(); - $seleniumTestSuites = $configArray['SeleniumTests']['testSuite']; - wfRestoreWarnings(); - } - return true; - } - - private static function parse_5_2_ini_file ( $ConfigFile ) { - - $configArray = parse_ini_file( $ConfigFile, true ); - if ( $configArray === false ) return false; - - // PHP 5.2 ini files have [browsers] and [testSuite] sections - // to get around lack of support for array keys. It then - // inserts the section arrays into the appropriate places in - // the SeleniumSettings and SeleniumTests arrays. - - if ( isset( $configArray['browsers'] ) ) { - $configArray['SeleniumSettings']['browsers'] = $configArray['browsers']; - unset ( $configArray['browsers'] ); - } - - if ( isset( $configArray['testSuite'] ) ) { - $configArray['SeleniumTests']['testSuite'] = $configArray['testSuite']; - unset ( $configArray['testSuite'] ); - } - - return $configArray; - - } - -} diff --git a/maintenance/tests/selenium/SeleniumLoader.php b/maintenance/tests/selenium/SeleniumLoader.php deleted file mode 100644 index 8d5e7713..00000000 --- a/maintenance/tests/selenium/SeleniumLoader.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php - -class SeleniumLoader { - static function load() { - require_once( 'Testing/Selenium.php' ); - require_once( 'PHPUnit/Framework.php' ); - require_once( 'PHPUnit/Extensions/SeleniumTestCase.php' ); - } -} diff --git a/maintenance/tests/selenium/SeleniumServerManager.php b/maintenance/tests/selenium/SeleniumServerManager.php deleted file mode 100644 index ae5ea682..00000000 --- a/maintenance/tests/selenium/SeleniumServerManager.php +++ /dev/null @@ -1,239 +0,0 @@ -<?php -/** - * Selenium server manager - * - * @file - * @ingroup Maintenance - * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com> - * http://citizendium.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @addtogroup Maintenance - */ - -class SeleniumServerManager { - private $SeleniumStartServer = false; - private $OS = ''; - private $SeleniumServerPid = 'NaN'; - private $SeleniumServerPort = 4444; - private $SeleniumServerStartTimeout = 10; // 10 secs. - private $SeleniumServerExecPath; - - public function __construct( $startServer, - $serverPort, - $serverExecPath ) { - $this->OS = (string) PHP_OS; - if ( isset( $startServer ) ) - $this->SeleniumStartServer = $startServer; - if ( isset( $serverPort ) ) - $this->SeleniumServerPort = $serverPort; - if ( isset( $serverExecPath ) ) - $this->SeleniumServerExecPath = $serverExecPath; - return; - } - - // Getters for certain private attributes. No setters, since they - // should not change after the manager object is created. - - public function getSeleniumStartServer() { - return $this->SeleniumStartServer; - } - - public function getSeleniumServerPort() { - return $this->SeleniumServerPort; - } - - public function getSeleniumServerPid() { - return $this->SeleniumServerPid; - } - - // Changing value of SeleniumStartServer allows starting server after - // creation of the class instance. Only allow setting SeleniumStartServer - // to true, since after server is started, it is shut down by stop(). - - public function setSeleniumStartServer( $startServer ) { - if ( $startServer == true ) $this->SeleniumStartServer = true; - } - - // return values are: 1) started - server started, 2) failed - - // server not started, 3) running - instructed to start server, but - // server already running - - public function start() { - - if ( !$this->SeleniumStartServer ) return 'failed'; - - // commented out cases are untested - - switch ( $this->OS ) { - case "Linux": -# case' CYGWIN_NT-5.1': - case 'Darwin': -# case 'FreeBSD': -# case 'HP-UX': -# case 'IRIX64': -# case 'NetBSD': -# case 'OpenBSD': -# case 'SunOS': -# case 'Unix': - // *nix based OS - return $this->startServerOnUnix(); - break; - case "Windows": - case "WIN32": - case "WINNT": - // Windows - return $this->startServerOnWindows(); - break; - default: - // An untested OS - return 'failed'; - break; - } - } - - public function stop() { - - // commented out cases are untested - - switch ( $this->OS ) { - case "Linux": -# case' CYGWIN_NT-5.1': - case 'Darwin': -# case 'FreeBSD': -# case 'HP-UX': -# case 'IRIX64': -# case 'NetBSD': -# case 'OpenBSD': -# case 'SunOS': -# case 'Unix': - // *nix based OS - return $this->stopServerOnUnix(); - break; - case "Windows": - case "WIN32": - case "WINNT": - // Windows - return $this->stopServerOnWindows(); - break; - default: - // An untested OS - return 'failed'; - break; - } - } - - private function startServerOnUnix() { - - $output = array(); - $user = $_ENV['USER']; - // @fixme this should be a little more generalized :) - if (PHP_OS == 'Darwin') { - // Mac OS X's ps barfs on the 'w' param, but doesn't need it. - $ps = "ps -U %s"; - } else { - // Good on Linux - $ps = "ps -U %s w"; - } - $psCommand = sprintf($ps, escapeshellarg($user)); - exec($psCommand . " | grep -i selenium-server", $output); - - // Start server. If there is already a server running, - // return running. - - if ( isset( $this->SeleniumServerExecPath ) ) { - $found = 0; - foreach ( $output as $string ) { - $found += preg_match( - '~^(.*)java(.+)-jar(.+)selenium-server~', - $string ); - } - if ( $found == 0 ) { - - // Didn't find the selenium server. Start it up. - // First set up comamand line suffix. - // NB: $! is pid of last job run in background - // The echo guarentees it is put into $op when - // the exec command is run. - - $commandSuffix = ' > /dev/null 2>&1'. ' & echo $!'; - $portText = ' -port ' . $this->SeleniumServerPort; - $command = "java -jar " . - escapeshellarg($this->SeleniumServerExecPath) . - $portText . $commandSuffix; - exec($command ,$op); - $pid = (int)$op[0]; - if ( $pid != "" ) - $this->SeleniumServerPid = $pid; - else { - $this->SeleniumServerPid = 'NaN'; - // Server start failed. - return 'failed'; - } - // Wait for the server to startup and listen - // on its port. Note: this solution kinda - // stinks, since it uses a wait loop - dnessett - - wfSuppressWarnings(); - for ( $cnt = 1; - $cnt <= $this->SeleniumServerStartTimeout; - $cnt++ ) { - $fp = fsockopen ( 'localhost', - $this->SeleniumServerPort, - $errno, $errstr, 0 ); - if ( !$fp ) { - sleep( 1 ); - continue; - // Server start succeeded. - } else { - fclose ( $fp ); - return 'started'; - } - } - wfRestoreWarnings(); - echo ( "Starting Selenium server timed out.\n" ); - return 'failed'; - } - // server already running. - else return 'running'; - - } - // No Server execution path defined. - return 'failed'; - } - - private function startServerOnWindows() { - // Unimplemented. - return 'failed'; - } - - private function stopServerOnUnix() { - - if ( !empty( $this->SeleniumServerPid ) && - $this->SeleniumServerPid != 'NaN' ) { - exec( "kill -9 " . $this->SeleniumServerPid ); - return 'stopped'; - } - else return 'failed'; - } - - private function stopServerOnWindows() { - // Unimplemented. - return 'failed'; - - } -} diff --git a/maintenance/tests/selenium/SeleniumTestCase.php b/maintenance/tests/selenium/SeleniumTestCase.php deleted file mode 100644 index 11e1b192..00000000 --- a/maintenance/tests/selenium/SeleniumTestCase.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php -class SeleniumTestCase extends PHPUnit_Framework_TestCase { // PHPUnit_Extensions_SeleniumTestCase - protected $selenium; - - public function setUp() { - set_time_limit( 60 ); - $this->selenium = Selenium::getInstance(); - } - - public function tearDown() { - - } - - public function __call( $method, $args ) { - return call_user_func_array( array( $this->selenium, $method ), $args ); - } - - public function assertSeleniumAttributeEquals( $attribute, $value ) { - $attr = $this->getAttribute( $attribute ); - $this->assertEquals( $attr, $value ); - } - - public function assertSeleniumHTMLContains( $element, $text ) { - $innerHTML = $this->getText( $element ); - // or assertContains - $this->assertRegExp( "/$text/", $innerHTML ); - } - -//Common Funtions Added for Selenium Tests - - public function getExistingPage(){ - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->type("searchInput", "new" ); - $this->click("searchGoButton"); - $this->waitForPageToLoad("30000"); - } - - public function getNewPage($pageName){ - - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->type("searchInput", $pageName ); - $this->click("searchGoButton"); - $this->waitForPageToLoad("30000"); - $this->click("link=".$pageName); - $this->waitForPageToLoad("600000"); - - - } - // Loading the mediawiki editor - public function loadWikiEditor(){ - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - } - - // Clear the content of the mediawiki editor - public function clearWikiEditor(){ - $this->type("wpTextbox1", ""); - } - - // Click on the 'Show preview' button of the mediawiki editor - public function clickShowPreviewBtn(){ - $this->click("wpPreview"); - } - - // Click on the 'Save Page' button of the mediawiki editor - public function clickSavePageBtn(){ - $this->click("wpSave"); - } - - // Click on the 'Edit' link - public function clickEditLink(){ - $this->click("link=Edit"); - $this->waitForPageToLoad("30000"); - } - - public function deletePage($pageName){ - $isLinkPresent = False; - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->click("link=Log out"); - $this->waitForPageToLoad( "30000" ); - $this->click( "link=Log in / create account" ); - $this->waitForPageToLoad( "30000" ); - $this->type( "wpName1", "nadeesha" ); - $this->type( "wpPassword1", "12345" ); - $this->click( "wpLoginAttempt" ); - $this->waitForPageToLoad( "30000" ); - $this->type( "searchInput", $pageName ); - $this->click( "searchGoButton"); - $this->waitForPageToLoad( "30000" ); - - $this->click( "link=Delete" ); - $this->waitForPageToLoad( "30000" ); - $this->click( "wpConfirmB" ); - $this->waitForPageToLoad( "30000" ); - - } - - - -} diff --git a/maintenance/tests/selenium/SeleniumTestConsoleLogger.php b/maintenance/tests/selenium/SeleniumTestConsoleLogger.php deleted file mode 100644 index b6f5496c..00000000 --- a/maintenance/tests/selenium/SeleniumTestConsoleLogger.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -class SeleniumTestConsoleLogger { - public function __construct() { - // Prepare testsuite for immediate output - @ini_set( 'zlib.output_compression', 0 ); - @ini_set( 'implicit_flush', 1 ); - for ( $i = 0; $i < ob_get_level(); $i++ ) { - ob_end_flush(); - } - ob_implicit_flush( 1 ); - } - - public function write( $message, $mode = false ) { - $out = ''; - // if ( $mode == SeleniumTestSuite::RESULT_OK ) $out .= '<font color="green">'; - $out .= htmlentities( $message ); - // if ( $mode == SeleniumTestSuite::RESULT_OK ) $out .= '</font>'; - if ( $mode != SeleniumTestSuite::CONTINUE_LINE ) { - $out .= "\n"; - } - - echo $out; - } -} diff --git a/maintenance/tests/selenium/SeleniumTestHTMLLogger.php b/maintenance/tests/selenium/SeleniumTestHTMLLogger.php deleted file mode 100644 index 21332cf0..00000000 --- a/maintenance/tests/selenium/SeleniumTestHTMLLogger.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -class SeleniumTestHTMLLogger { - public function setHeaders() { - global $wgOut; - $wgOut->addHeadItem( 'selenium', '<style type="text/css"> - .selenium pre { - overflow-x: auto; /* Use horizontal scroller if needed; for Firefox 2, not needed in Firefox 3 */ - white-space: pre-wrap; /* css-3 */ - white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */ - white-space: -pre-wrap; /* Opera 4-6 */ - white-space: -o-pre-wrap; /* Opera 7 */ - /* width: 99%; */ - word-wrap: break-word; /* Internet Explorer 5.5+ */ - } - .selenium-success { color: green } - </style>' ); - } - - public function write( $message, $mode = false ) { - global $wgOut; - $out = ''; - if ( $mode == SeleniumTestSuite::RESULT_OK ) { - $out .= '<span class="selenium-success">'; - } - $out .= htmlspecialchars( $message ); - if ( $mode == SeleniumTestSuite::RESULT_OK ) { - $out .= '</span>'; - } - if ( $mode != SeleniumTestSuite::CONTINUE_LINE ) { - $out .= '<br />'; - } - - $wgOut->addHTML( $out ); - } -} diff --git a/maintenance/tests/selenium/SeleniumTestListener.php b/maintenance/tests/selenium/SeleniumTestListener.php deleted file mode 100644 index 9436f672..00000000 --- a/maintenance/tests/selenium/SeleniumTestListener.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php - -class SeleniumTestListener implements PHPUnit_Framework_TestListener { - private $logger; - private $tests_ok = 0; - private $tests_failed = 0; - - public function __construct( $loggerInstance ) { - $this->logger = $loggerInstance; - } - - public function addError( PHPUnit_Framework_Test $test, Exception $e, $time ) { - $this->logger->write( 'Error: ' . $e->getMessage() ); - $this->tests_failed++; - } - - public function addFailure( PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time ) - { - $this->logger->write( 'Failed: ' . $e->getMessage() ); - $this->tests_failed++; - } - - public function addIncompleteTest( PHPUnit_Framework_Test $test, Exception $e, $time ) - { - $this->logger->write( 'Incomplete.' ); - $this->tests_failed++; - } - - public function addSkippedTest( PHPUnit_Framework_Test $test, Exception $e, $time ) - { - $this->logger->write( 'Skipped.' ); - $this->tests_failed++; - } - - public function startTest( PHPUnit_Framework_Test $test ) { - $this->logger->write( - 'Testing ' . $test->getName() . ' ... ', - SeleniumTestSuite::CONTINUE_LINE - ); - } - - public function endTest( PHPUnit_Framework_Test $test, $time ) { - if ( !$test->hasFailed() ) { - $this->logger->write( 'OK', SeleniumTestSuite::RESULT_OK ); - $this->tests_ok++; - } - } - - public function startTestSuite( PHPUnit_Framework_TestSuite $suite ) { - $this->logger->write( 'Testsuite ' . $suite->getName() . ' started.' ); - $this->tests_ok = 0; - $this->tests_failed = 0; - } - - public function endTestSuite( PHPUnit_Framework_TestSuite $suite ) { - $this->logger->write('Testsuite ' . $suite->getName() . ' ended.' ); - if ( $this->tests_ok > 0 || $this->tests_failed > 0 ) { - $this->logger->write( ' OK: ' . $this->tests_ok . ' Failed: ' . $this->tests_failed ); - } - $this->tests_ok = 0; - $this->tests_failed = 0; - } - - public function statusMessage( $message ) { - $this->logger->write( $message ); - } -} - diff --git a/maintenance/tests/selenium/SeleniumTestSuite.php b/maintenance/tests/selenium/SeleniumTestSuite.php deleted file mode 100644 index ba178051..00000000 --- a/maintenance/tests/selenium/SeleniumTestSuite.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -abstract class SeleniumTestSuite extends PHPUnit_Framework_TestSuite { - private $selenium; - private $isSetUp = false; - private $loginBeforeTests = true; - - // Do not add line break after test output - const CONTINUE_LINE = 1; - const RESULT_OK = 2; - const RESULT_ERROR = 3; - - public abstract function addTests(); - - public function setUp() { - // Hack because because PHPUnit version 3.0.6 which is on prototype does not - // run setUp as part of TestSuite::run - if ( $this->isSetUp ) { - return; - } - $this->isSetUp = true; - $this->selenium = Selenium::getInstance(); - $this->selenium->start(); - $this->selenium->open( $this->selenium->getUrl() . '/index.php?setupTestSuite=' . $this->getName() ); - if ( $this->loginBeforeTests ) { - $this->login(); - } - } - - public function tearDown() { - $this->selenium->open( $this->selenium->getUrl() . '/index.php?clearTestSuite=' . $this->getName() ); - $this->selenium->stop(); - } - - public function login() { - $this->selenium->login(); - } - - public function loadPage( $title, $action ) { - $this->selenium->loadPage( $title, $action ); - } - - protected function setLoginBeforeTests( $loginBeforeTests = true ) { - $this->loginBeforeTests = $loginBeforeTests; - } -} diff --git a/maintenance/tests/selenium/data/Wikipedia-logo-v2-de.png b/maintenance/tests/selenium/data/Wikipedia-logo-v2-de.png Binary files differdeleted file mode 100644 index 70385243..00000000 --- a/maintenance/tests/selenium/data/Wikipedia-logo-v2-de.png +++ /dev/null diff --git a/maintenance/tests/selenium/selenium_settings.ini.php52.sample b/maintenance/tests/selenium/selenium_settings.ini.php52.sample deleted file mode 100644 index ad21037e..00000000 --- a/maintenance/tests/selenium/selenium_settings.ini.php52.sample +++ /dev/null @@ -1,23 +0,0 @@ -[browsers] - -firefox = "*firefox" -iexploreproxy = "*iexploreproxy" -chrome = "*chrome" - -[SeleniumSettings] - -host = "localhost" -port = "4444" -wikiUrl = "http://localhost/mediawiki/latest_trunk/trunk/phase3" -username = "Wikiadmin" -userPassword = "Wikiadminpw" -testBrowser = "firefox" -startserver = -stopserver = -jUnitLogFile = -runAgainstGrid = false - -[testSuite] - -SimpleSeleniumTestSuite = "maintenance/tests/selenium/suites/SimpleSeleniumTestSuite.php" -WikiEditorTestSuite = "extensions/WikiEditor/selenium/WikiEditorTestSuite.php" diff --git a/maintenance/tests/selenium/selenium_settings.ini.sample b/maintenance/tests/selenium/selenium_settings.ini.sample deleted file mode 100644 index bacc0a90..00000000 --- a/maintenance/tests/selenium/selenium_settings.ini.sample +++ /dev/null @@ -1,32 +0,0 @@ -[SeleniumSettings] - -; Set up the available browsers that Selenium can control. -browsers[firefox] = "*firefox" -browsers[iexplorer] = "*iexploreproxy" -browsers[chrome] = "*chrome" - -; The simple configurations above usually work on Linux, but Windows and -; Mac OS X hosts may need to specify a full path: -;browsers[firefox] = "*firefox /Applications/Firefox.app/Contents/MacOS/firefox-bin" -;browsers[firefox] = "*firefox C:\Program Files\Mozilla Firefox\firefox.exe" - -host = "localhost" -port = "4444" -wikiUrl = "http://localhost/deployment" -username = "wikiuser" -userPassword = "wikipass" -testBrowser = "firefox" -startserver = -stopserver = -jUnitLogFile = -runAgainstGrid = false - -; To let the test runner start and stop the selenium server, it needs the full -; path to selenium-server.jar from the selenium-remote-control package. -seleniumserverexecpath = "/opt/local/selenium-remote-control-1.0.3/selenium-server-1.0.3/selenium-server.jar" - -[SeleniumTests] - -testSuite[SimpleSeleniumTestSuite] = "maintenance/tests/selenium/suites/SimpleSeleniumTestSuite.php" -testSuite[WikiEditorTestSuite] = "extensions/WikiEditor/selenium/WikiEditorTestSuite.php" - diff --git a/maintenance/tests/selenium/selenium_settings_grid.ini.sample b/maintenance/tests/selenium/selenium_settings_grid.ini.sample deleted file mode 100644 index eca60b0a..00000000 --- a/maintenance/tests/selenium/selenium_settings_grid.ini.sample +++ /dev/null @@ -1,14 +0,0 @@ -[SeleniumSettings] - -host = "grid.tesla.usability.wikimedia.org" -port = "4444" -wikiUrl = "http://208.80.152.253:5001" -username = "wikiuser" -userPassword = "wikipass" -testBrowser = "Safari on OS X Snow Leopard" -jUnitLogFile = -runAgainstGrid = true - -[testSuite] - -SimpleSeleniumTestSuite = "maintenance/tests/selenium/suites/SimpleSeleniumTestSuite.php"
\ No newline at end of file diff --git a/maintenance/tests/selenium/suites/AddContentToNewPageTestCase.php b/maintenance/tests/selenium/suites/AddContentToNewPageTestCase.php deleted file mode 100644 index dd4bc005..00000000 --- a/maintenance/tests/selenium/suites/AddContentToNewPageTestCase.php +++ /dev/null @@ -1,182 +0,0 @@ -<?php - -/** - * Selenium server manager - * - * @file - * @ingroup Maintenance - * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com> - * http://citizendium.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @addtogroup Maintenance - * - */ - - -class AddContentToNewPageTestCase extends SeleniumTestCase { - - - // Add bold text and verify output - public function testAddBoldText() { - - $this->getExistingPage(); - $this->clickEditLink(); - $this->loadWikiEditor(); - $this->clearWikiEditor(); - $this->click( "//*[@id='mw-editbutton-bold']" ); - $this->clickShowPreviewBtn(); - $this->waitForPageToLoad( "600000" ); - - // Verify bold text displayed on mediawiki preview - $this->assertTrue($this->isElementPresent( "//div[@id='wikiPreview']/p/b" )); - $this->assertTrue($this->isTextPresent( "Bold text" )); - } - - // Add italic text and verify output - public function testAddItalicText() { - - $this->getExistingPage(); - $this->clickEditLink(); - $this->loadWikiEditor(); - $this->clearWikiEditor(); - $this->click( "//*[@id='mw-editbutton-italic']" ); - $this->clickShowPreviewBtn(); - $this->waitForPageToLoad( "600000" ); - - // Verify italic text displayed on mediawiki preview - $this->assertTrue($this->isElementPresent("//div[@id='wikiPreview']/p/i")); - $this->assertTrue($this->isTextPresent( "Italic text" )); - } - - // Add internal link for a new page and verify output in the preview - public function testAddInternalLinkNewPage() { - $this->getExistingPage(); - $this->clickEditLink(); - $this->loadWikiEditor(); - $this->clearWikiEditor(); - $this->click( "//*[@id='mw-editbutton-link']" ); - $this->clickShowPreviewBtn(); - $this->waitForPageToLoad( "600000" ); - - // Verify internal link displayed on mediawiki preview - $source = $this->getText( "//*[@id='wikiPreview']/p/a" ); - $correct = strstr( $source, "Link title" ); - $this->assertEquals( $correct, true ); - - $this->click( "link=Link title" ); - $this->waitForPageToLoad( "600000" ); - - // Verify internal link open as a new page - editing mode - $source = $this->getText( "firstHeading" ); - $correct = strstr( $source, "Editing Link title" ); - $this->assertEquals( $correct, true ); - } - - // Add external link and verify output in the preview - public function testAddExternalLink() { - $this->getExistingPage(); - $this->clickEditLink(); - $this->loadWikiEditor(); - $this->clearWikiEditor(); - $this->click( "//*[@id='mw-editbutton-extlink']" ); - $this->type( "wpTextbox1", "[http://www.google.com Google]" ); - $this->clickShowPreviewBtn(); - $this->waitForPageToLoad( "600000" ); - - // Verify external links displayed on mediawiki preview - $source = $this->getText( "//*[@id='wikiPreview']/p/a" ); - $correct = strstr( $source, "Google" ); - $this->assertEquals( $correct, true ); - - $this->click( "link=Google" ); - $this->waitForPageToLoad( "600000" ); - - // Verify external link opens - $source = $this->getTitle(); - $correct = strstr( $source, "Google" ); - $this->assertEquals( $correct, true); - } - - // Add level 2 headline and verify output in the preview - public function testAddLevel2HeadLine() { - $blnElementPresent = False; - $blnTextPresent = False; - $this->getExistingPage(); - $this->clickEditLink(); - $this->loadWikiEditor(); - $this->clearWikiEditor(); - $this->click( "mw-editbutton-headline" ); - $this->clickShowPreviewBtn(); - $this->waitForPageToLoad( "600000" ); - $this->assertTrue($this->isElementPresent( "//div[@id='wikiPreview']/h2" )); - - // Verify level 2 headline displayed on mediawiki preview - $source = $this->getText( "//*[@id='Headline_text']" ); - $correct = strstr( $source, "Headline text" ); - $this->assertEquals( $correct, true ); - } - - // Add text with ignore wiki format and verify output the preview - public function testAddNoWikiFormat() { - $this->getExistingPage(); - $this->clickEditLink(); - $this->loadWikiEditor(); - $this->clearWikiEditor(); - $this->click( "//*[@id='mw-editbutton-nowiki']" ); - $this->clickShowPreviewBtn(); - $this->waitForPageToLoad( "600000" ); - - // Verify ignore wiki format text displayed on mediawiki preview - $source = $this->getText( "//div[@id='wikiPreview']/p" ); - $correct = strstr( $source, "Insert non-formatted text here" ); - $this->assertEquals( $correct, true ); - } - - // Add signature and verify output in the preview - public function testAddUserSignature() { - $this->getExistingPage(); - $this->clickEditLink(); - $this->loadWikiEditor(); - $this->clearWikiEditor(); - $this->click( "mw-editbutton-signature" ); - $this->clickShowPreviewBtn(); - $this->waitForPageToLoad( "600000" ); - - // Verify signature displayed on mediawiki preview - $source = $this->getText( "//*[@id='wikiPreview']/p/a" ); - $username = $this->getText( "//*[@id='pt-userpage']/a" ); - $correct = strstr( $source, $username ); - $this->assertEquals( $correct, true ); - } - - // Add horizontal line and verify output in the preview - public function testHorizontalLine() { - $this->getExistingPage(); - $this->clickEditLink(); - $this->loadWikiEditor(); - $this->clearWikiEditor(); - $this->click( "mw-editbutton-hr" ); - - $this->clickShowPreviewBtn(); - $this->waitForPageToLoad( "600000" ); - - // Verify horizontal line displayed on mediawiki preview - $this->assertTrue( $this->isElementPresent( "//div[@id='wikiPreview']/hr" )); - $this->deletePage( "new" ); - } -} diff --git a/maintenance/tests/selenium/suites/AddNewPageTestCase.php b/maintenance/tests/selenium/suites/AddNewPageTestCase.php deleted file mode 100644 index 423f2a2c..00000000 --- a/maintenance/tests/selenium/suites/AddNewPageTestCase.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php - -/** - * Selenium server manager - * - * @file - * @ingroup Maintenance - * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com> - * http://citizendium.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @addtogroup Maintenance - * - */ - - -class AddNewPageTestCase extends SeleniumTestCase { - - // Verify adding a new page - public function testAddNewPage() { - $newPage = "new"; - $displayName = "New"; - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->type( "searchInput", $newPage ); - $this->click( "searchGoButton" ); - $this->waitForPageToLoad( "600000" ); - - // Verify 'Search results' text available - $source = $this->gettext( "firstHeading" ); - $correct = strstr( $source, "Search results" ); - $this->assertEquals( $correct, true); - - // Verify 'Create the page "<page name>" on this wiki' text available - $source = $this->gettext( "//div[@id='bodyContent']/div[4]/p/b" ); - $correct = strstr ( $source, "Create the page \"New\" on this wiki!" ); - $this->assertEquals( $correct, true ); - - $this->click( "link=".$displayName ); - $this->waitForPageToLoad( "600000" ); - - $this->assertTrue($this->isElementPresent( "link=Create" )); - $this->type( "wpTextbox1", "add new test page" ); - $this->click( "wpSave" ); - - // Verify new page added - $source = $this->gettext( "firstHeading" ); - $correct = strstr ( $source, $displayName ); - $this->assertEquals( $correct, true ); - } -} diff --git a/maintenance/tests/selenium/suites/CreateAccountTestCase.php b/maintenance/tests/selenium/suites/CreateAccountTestCase.php deleted file mode 100644 index 1cfda2e0..00000000 --- a/maintenance/tests/selenium/suites/CreateAccountTestCase.php +++ /dev/null @@ -1,114 +0,0 @@ -<?php - -/** - * Selenium server manager - * - * @file - * @ingroup Maintenance - * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com> - * http://citizendium.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @addtogroup Maintenance - * - */ - -Class CreateAccountTestCase extends SeleniumTestCase { - - // Change these values before run the test - private $userName = "yourname4000"; - private $password = "yourpass4000"; - - // Verify 'Log in/create account' link existance in Main page. - public function testMainPageLink() { - - $this->click( "link=Log out" ); - $this->waitForPageToLoad( "30000" ); - - $this->open( $this->getUrl().'/index.php?title=Main_Page' ); - $this->assertTrue($this->isElementPresent( "link=Log in / create account" )); - } - - // Verify 'Create an account' link existance in 'Log in / create account' Page. - public function testCreateAccountPageLink() { - - $this->click( "link=Log out" ); - $this->waitForPageToLoad( "30000" ); - - $this->open( $this->getUrl().'/index.php?title=Main_Page' ); - - // click Log in / create account link to open Log in / create account' page - $this->click( "link=Log in / create account" ); - $this->waitForPageToLoad( "30000" ); - $this->assertTrue($this->isElementPresent( "link=Create an account" )); - } - - // Verify Create account - public function testCreateAccount() { - - $this->click( "link=Log out" ); - $this->waitForPageToLoad( "30000" ); - - $this->open( $this->getUrl().'/index.php?title=Main_Page' ); - - $this->click( "link=Log in / create account" ); - $this->waitForPageToLoad( "30000" ); - - $this->click( "link=Create an account" ); - $this->waitForPageToLoad( "30000" ); - - // Verify for blank user name - $this->type( "wpName2", "" ); - $this->click( "wpCreateaccount" ); - $this->waitForPageToLoad( "30000" ); - $this->assertEquals( "Login error\n You have not specified a valid user name.", - $this->getText( "//div[@id='bodyContent']/div[4]" )); - - // Verify for invalid user name - $this->type( "wpName2", "@" ); - $this->click("wpCreateaccount" ); - $this->waitForPageToLoad( "30000" ); - $this->assertEquals( "Login error\n You have not specified a valid user name.", - $this->getText( "//div[@id='bodyContent']/div[4]" )); - - // start of test for blank password - $this->type( "wpName2", $this->userName); - $this->type( "wpPassword2", "" ); - $this->click( "wpCreateaccount" ); - $this->waitForPageToLoad( "30000" ); - $this->assertEquals( "Login error\n Passwords must be at least 1 character.", - $this->getText("//div[@id='bodyContent']/div[4]" )); - - $this->type( "wpName2", $this->userName ); - $this->type( "wpPassword2", $this->password ); - $this->click( "wpCreateaccount" ); - $this->waitForPageToLoad( "30000" ); - $this->assertEquals( "Login error\n The passwords you entered do not match.", - $this->getText( "//div[@id='bodyContent']/div[4]" )); - - $this->type( "wpName2", $this->userName ); - $this->type( "wpPassword2", $this->password ); - $this->type( "wpRetype", $this->password ); - $this->click( "wpCreateaccount" ); - $this->waitForPageToLoad( "30000 "); - - // Verify successful account creation for valid combination of 'Username', 'Password', 'Retype password' - $this->assertEquals( "Welcome, ".ucfirst( $this->userName )."!", - $this->getText( "Welcome,_".ucfirst( $this->userName )."!" )); - } -} - diff --git a/maintenance/tests/selenium/suites/DeletePageAdminTestCase.php b/maintenance/tests/selenium/suites/DeletePageAdminTestCase.php deleted file mode 100644 index 40628986..00000000 --- a/maintenance/tests/selenium/suites/DeletePageAdminTestCase.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php - -/** - * Selenium server manager - * - * @file - * @ingroup Maintenance - * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com> - * http://citizendium.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @addtogroup Maintenance - * - */ - - -class DeletePageAdminTestCase extends SeleniumTestCase { - - // Verify adding a new page - public function testDeletePage() { - - - $newPage = "new"; - $displayName = "New"; - - $this->open( $this->getUrl().'/index.php?title=Main_Page' ); - - $this->type( "searchInput", $newPage ); - $this->click( "searchGoButton" ); - $this->waitForPageToLoad( "30000" ); - $this->click( "link=".$displayName ); - $this->waitForPageToLoad( "60000" ); - $this->type( "wpTextbox1", $newPage." text" ); - $this->click( "wpSave" ); - - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->click( "link=Log out" ); - $this->waitForPageToLoad( "30000" ); - $this->click( "link=Log in / create account" ); - $this->waitForPageToLoad( "30000" ); - - $this->type( "wpName1", $this->selenium->getUser() ); - $this->type( "wpPassword1", $this->selenium->getPass() ); - $this->click( "wpLoginAttempt" ); - $this->waitForPageToLoad( "30000" ); - $this->type( "searchInput", "new" ); - $this->click( "searchGoButton"); - $this->waitForPageToLoad( "30000" ); - - // Verify 'Delete' link displayed - $source = $this->gettext( "link=Delete" ); - $correct = strstr ( $source, "Delete" ); - $this->assertEquals($correct, true ); - - $this->click( "link=Delete" ); - $this->waitForPageToLoad( "30000" ); - - // Verify 'Delete' button available - $this->assertTrue($this->isElementPresent( "wpConfirmB" )); - - $this->click( "wpConfirmB" ); - $this->waitForPageToLoad( "30000" ); - - // Verify 'Action complete' text displayed - $source = $this->gettext( "firstHeading" ); - $correct = strstr ( $source, "Action complete" ); - $this->assertEquals( $correct, true ); - - // Verify '<Page Name> has been deleted. See deletion log for a record of recent deletions.' text displayed - $source = $this->gettext( "//div[@id='bodyContent']/p[1]" ); - $correct = strstr ( $source, "\"New\" has been deleted. See deletion log for a record of recent deletions." ); - $this->assertEquals( $correct, true ); - } -} diff --git a/maintenance/tests/selenium/suites/EmailPasswordTestCase.php b/maintenance/tests/selenium/suites/EmailPasswordTestCase.php deleted file mode 100644 index 8356f43a..00000000 --- a/maintenance/tests/selenium/suites/EmailPasswordTestCase.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php - -/** - * Selenium server manager - * - * @file - * @ingroup Maintenance - * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com> - * http://citizendium.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @addtogroup Maintenance - * - */ - -class EmailPasswordTestCase extends SeleniumTestCase { - - // change user name for each and every test (with in 24 hours) - private $userName = "test1"; - - public function testEmailPasswordButton() { - - $this->click( "link=Log out" ); - $this->waitForPageToLoad( "30000" ); - - $this->open( $this->getUrl().'/index.php?title=Main_Page' ); - - // click Log in / create account link to open Log in / create account' page - $this->click( "link=Log in / create account" ); - $this->waitForPageToLoad( "30000" ); - $this->assertTrue($this->isElementPresent( "wpMailmypassword" )); - } - - // Verify Email password functionality - public function testEmailPasswordMessages() { - - $this->click( "link=Log out" ); - $this->waitForPageToLoad( "30000" ); - - $this->open( $this->getUrl().'/index.php?title=Main_Page' ); - - // click Log in / create account link to open Log in / create account' page - $this->click( "link=Log in / create account" ); - $this->waitForPageToLoad( "30000" ); - - $this->type( "wpName1", "" ); - $this->click( "wpMailmypassword" ); - $this->waitForPageToLoad( "30000" ); - $this->assertEquals( "Login error\n You have not specified a valid user name.", - $this->getText("//div[@id='bodyContent']/div[4]")); - - $this->type( "wpName1", $this->userName ); - $this->click( "wpMailmypassword" ); - $this->waitForPageToLoad( "30000" ); - - // Can not run on localhost - $this->assertEquals( "A new password has been sent to the e-mail address registered for ".ucfirst($this->userName).". Please log in again after you receive it.", - $this->getText("//div[@id='bodyContent']/div[4]" )); - - $this->type( "wpName1", $this->userName ); - $this->click( "wpMailmypassword" ); - $this->waitForPageToLoad( "30000" ); - $this->assertEquals( "Login error\n A password reminder has already been sent, within the last 24 hours. To prevent abuse, only one password reminder will be sent per 24 hours.", - $this->getText( "//div[@id='bodyContent']/div[4]" )); - } -} - diff --git a/maintenance/tests/selenium/suites/MediaWikExtraTestSuite.php b/maintenance/tests/selenium/suites/MediaWikExtraTestSuite.php deleted file mode 100644 index 205cb332..00000000 --- a/maintenance/tests/selenium/suites/MediaWikExtraTestSuite.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -class MediaWikExtraTestSuite extends SeleniumTestSuite { - public function setUp() { - $this->setLoginBeforeTests( true ); - parent::setUp(); - } - public function addTests() { - $testFiles = array( - 'maintenance/tests/selenium/suites/MyContributionsTestCase.php', - 'maintenance/tests/selenium/suites/MyWatchListTestCase.php', - 'maintenance/tests/selenium/suites/UserPreferencesTestCase.php', - 'maintenance/tests/selenium/suites/MovePageTestCase.php', - 'maintenance/tests/selenium/suites/PageSearchTestCase.php', - 'maintenance/tests/selenium/suites/EmailPasswordTestCase.php', - 'maintenance/tests/selenium/suites/CreateAccountTestCase.php' - ); - parent::addTestFiles( $testFiles ); - } -} diff --git a/maintenance/tests/selenium/suites/MediaWikiEditorConfig.php b/maintenance/tests/selenium/suites/MediaWikiEditorConfig.php deleted file mode 100644 index 2803117d..00000000 --- a/maintenance/tests/selenium/suites/MediaWikiEditorConfig.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * Selenium server manager - * - * @file - * @ingroup Maintenance - * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com> - * http://citizendium.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @addtogroup Maintenance - * - */ - -class MediaWikiEditorConfig { - - public static function getSettings(&$includeFiles, &$globalConfigs) { - $includes = array( - //files that needed to be included would go here - 'maintenance/tests/selenium/suites/MediaWikiCommonFunction.php' - ); - $configs = array( - 'wgPageLoadTime' => "600000" - ); - $includeFiles = array_merge( $includeFiles, $includes ); - $globalConfigs = array_merge( $globalConfigs, $configs); - return true; - } -} - - - diff --git a/maintenance/tests/selenium/suites/MediaWikiEditorTestSuite.php b/maintenance/tests/selenium/suites/MediaWikiEditorTestSuite.php deleted file mode 100644 index 06046365..00000000 --- a/maintenance/tests/selenium/suites/MediaWikiEditorTestSuite.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -class MediaWikiEditorTestSuite extends SeleniumTestSuite { - public function setUp() { - $this->setLoginBeforeTests( true ); - parent::setUp(); - } - public function addTests() { - $testFiles = array( - 'maintenance/tests/selenium/suites/AddNewPageTestCase.php', - 'maintenance/tests/selenium/suites/AddContentToNewPageTestCase.php', - 'maintenance/tests/selenium/suites/PreviewPageTestCase.php', - 'maintenance/tests/selenium/suites/SavePageTestCase.php', - ); - parent::addTestFiles( $testFiles ); - } -} - diff --git a/maintenance/tests/selenium/suites/MediawikiCoreSmokeTestCase.php b/maintenance/tests/selenium/suites/MediawikiCoreSmokeTestCase.php deleted file mode 100644 index 7b9525af..00000000 --- a/maintenance/tests/selenium/suites/MediawikiCoreSmokeTestCase.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php -/* - * Stub of tests be need as part of the hack-a-ton - */ -class MediawikiCoreSmokeTestCase extends SeleniumTestCase { - public function testUserLogin() { - - } - - public function testChangeUserPreference() { - - } - - /* - * TODO: generalize this test to be reusable for different skins - */ - public function testCreateNewPageVector() { - - } - - /* - * TODO: generalize this test to be reusable for different skins - */ - public function testEditExistingPageVector() { - - } - - /* - * TODO: generalize this test to be reusable for different skins - */ - public function testCreateNewPageMonobook() { - - } - - /* - * TODO: generalize this test to be reusable for different skins - */ - public function testEditExistingPageMonobook() { - - } - - public function testImageUpload() { - $this->login(); - $this->open( $this->getUrl() . - '/index.php?title=Special:Upload' ); - $this->type( 'wpUploadFile', dirname( __FILE__ ) . - "\\..\\data\\Wikipedia-logo-v2-de.png" ); - $this->check( 'wpIgnoreWarning' ); - $this->click( 'wpUpload' ); - $this->waitForPageToLoad( 30000 ); - - $this->assertSeleniumHTMLContains( - '//h1[@class="firstHeading"]', "Wikipedia-logo-v2-de.png" ); - - /* - $this->open( $this->getUrl() . '/index.php?title=Image:' - . ucfirst( $this->filename ) . '&action=delete' ); - $this->type( 'wpReason', 'Remove test file' ); - $this->click( 'mw-filedelete-submit' ); - $this->waitForPageToLoad( 10000 ); - - // Todo: This message is localized - $this->assertSeleniumHTMLContains( '//div[@id="bodyContent"]/p', - ucfirst( $this->filename ) . '.*has been deleted.' ); - */ - } - - -} diff --git a/maintenance/tests/selenium/suites/MediawikiCoreSmokeTestSuite.php b/maintenance/tests/selenium/suites/MediawikiCoreSmokeTestSuite.php deleted file mode 100644 index 76287b23..00000000 --- a/maintenance/tests/selenium/suites/MediawikiCoreSmokeTestSuite.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -/* - * Stubs for now. We're going to start populating this test. - */ -class MediawikiCoreSmokeTestSuite extends SeleniumTestSuite -{ - public function setUp() { - $this->setLoginBeforeTests( false ); - parent::setUp(); - } - public function addTests() { - $testFiles = array( - 'maintenance/tests/selenium/suites/MediawikiCoreSmokeTestCase.php' - ); - parent::addTestFiles( $testFiles ); - } - - -} diff --git a/maintenance/tests/selenium/suites/MovePageTestCase.php b/maintenance/tests/selenium/suites/MovePageTestCase.php deleted file mode 100644 index d4d3b1f2..00000000 --- a/maintenance/tests/selenium/suites/MovePageTestCase.php +++ /dev/null @@ -1,117 +0,0 @@ -<?php - -/** - * Selenium server manager - * - * @file - * @ingroup Maintenance - * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com> - * http://citizendium.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @addtogroup Maintenance - * - */ - -class MovePageTestCase extends SeleniumTestCase { - - // Verify move(rename) wiki page - public function testMovePage() { - - $newPage = "mypage99"; - $displayName = "Mypage99"; - - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->type( "searchInput", $newPage ); - $this->click( "searchGoButton" ); - $this->waitForPageToLoad( "30000" ); - $this->click( "link=".$displayName ); - $this->waitForPageToLoad( "60000" ); - $this->type( "wpTextbox1", $newPage." text" ); - $this->click( "wpSave" ); - $this->waitForPageToLoad( "60000" ); - - // Verify link 'Move' available - $this->assertTrue($this->isElementPresent( "link=Move" )); - - $this->click( "link=Move" ); - $this->waitForPageToLoad( "30000" ); - - // Verify correct page name displayed under 'Move Page' field - $this->assertEquals($displayName, - $this->getText("//table[@id='mw-movepage-table']/tbody/tr[1]/td[2]/strong/a")); - $movePageName = $this->getText( "//table[@id='mw-movepage-table']/tbody/tr[1]/td[2]/strong/a" ); - - // Verify 'To new title' field has current page name as the default name - $newTitle = $this->getValue( "wpNewTitle" ); - $correct = strstr( $movePageName , $newTitle ); - $this->assertEquals( $correct, true ); - - $this->type( "wpNewTitle", $displayName ); - $this->click( "wpMove" ); - $this->waitForPageToLoad( "30000" ); - - // Verify warning message for the same source and destination titles - $this->assertEquals( "Source and destination titles are the same; cannot move a page over itself.", - $this->getText("//div[@id='bodyContent']/p[4]/strong" )); - - // Verify warning message for the blank title - $this->type( "wpNewTitle", "" ); - $this->click( "wpMove" ); - $this->waitForPageToLoad( "30000" ); - - // Verify warning message for the blank title - $this->assertEquals( "The requested page title was invalid, empty, or an incorrectly linked inter-language or inter-wiki title. It may contain one or more characters which cannot be used in titles.", - $this->getText( "//div[@id='bodyContent']/p[4]/strong" )); - - // Verify warning messages for the invalid titles - $this->type( "wpNewTitle", "# < > [ ] | { }" ); - $this->click( "wpMove" ); - $this->waitForPageToLoad( "30000" ); - $this->assertEquals( "The requested page title was invalid, empty, or an incorrectly linked inter-language or inter-wiki title. It may contain one or more characters which cannot be used in titles.", - $this->getText( "//div[@id='bodyContent']/p[4]/strong" )); - - $this->type( "wpNewTitle", $displayName."move" ); - $this->click( "wpMove" ); - $this->waitForPageToLoad( "30000" ); - - // Verify move success message displayed correctly - $this->assertEquals( "\"".$displayName."\" has been moved to \"".$displayName."move"."\"", - $this->getText( "//div[@id='bodyContent']/p[1]/b" )); - - $this->type( "searchInput", $newPage."move" ); - $this->click( "searchGoButton" ); - $this->waitForPageToLoad( "30000" ); - - // Verify search using new page name - $this->assertEquals( $displayName."move", $this->getText( "firstHeading" )); - - $this->type( "searchInput", $newPage ); - $this->click( "searchGoButton" ); - $this->waitForPageToLoad( "30000" ); - - // Verify search using old page name - $redirectPageName = $this->getText( "//*[@id='contentSub']" ); - $this->assertEquals( "(Redirected from ".$displayName.")" , $redirectPageName ); - - // newpage delete - $this->deletePage( $newPage."move" ); - $this->deletePage( $newPage ); - } -} - diff --git a/maintenance/tests/selenium/suites/MyContributionsTestCase.php b/maintenance/tests/selenium/suites/MyContributionsTestCase.php deleted file mode 100644 index 95011c3b..00000000 --- a/maintenance/tests/selenium/suites/MyContributionsTestCase.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php - -/** - * Selenium server manager - * - * @file - * @ingroup Maintenance - * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com> - * http://citizendium.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @addtogroup Maintenance - * - */ - -class MyContributionsTestCase extends SeleniumTestCase { - - // Verify user contributions - public function testRecentChangesAvailability() { - - $newPage = "mypage999"; - $displayName = "Mypage999"; - - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - - $this->type( "searchInput", $newPage); - $this->click( "searchGoButton" ); - $this->waitForPageToLoad( "60000" ); - $this->click( "link=".$displayName ); - $this->waitForPageToLoad( "600000" ); - $this->type( "wpTextbox1", $newPage." text" ); - $this->click( "wpSave" ); - $this->waitForPageToLoad( "60000" ); - - // Verify My contributions Link available - $this->assertTrue($this->isElementPresent( "link=My contributions" )); - - $this->click( "link=My contributions" ); - $this->waitForPageToLoad( "30000" ); - - // Verify recent page adding available on My Contributions list - $this->assertEquals( $displayName, $this->getText( "link=".$displayName )); - - $this->type( "searchInput", $newPage ); - $this->click( "searchGoButton" ); - $this->waitForPageToLoad( "30000" ); - - $this->click( "link=Edit" ); - $this->waitForPageToLoad( "30000" ); - $this->type( "wpTextbox1", $newPage." text changed" ); - $this->click( "wpSave" ); - $this->waitForPageToLoad( "30000" ); - $this->click( "link=My contributions" ); - $this->waitForPageToLoad( "30000" ); - - // Verify recent page changes available on My Contributions - $this->assertTrue($this->isTextPresent($displayName." (top)")); - $this->deletePage($newPage); - } -} - diff --git a/maintenance/tests/selenium/suites/MyWatchListTestCase.php b/maintenance/tests/selenium/suites/MyWatchListTestCase.php deleted file mode 100644 index 150c1f51..00000000 --- a/maintenance/tests/selenium/suites/MyWatchListTestCase.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php - -/** - * Selenium server manager - * - * @file - * @ingroup Maintenance - * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com> - * http://citizendium.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @addtogroup Maintenance - * - */ - - -class MyWatchListTestCase extends SeleniumTestCase { - - // Verify user watchlist - public function testMyWatchlist() { - - $newPage = "mypage"; - $displayName = "Mypage"; - $wikiText = "watch page"; - - $this->open( $this->getUrl().'/index.php?title=Main_Page' ); - - $this->type( "searchInput", $newPage ); - $this->click( "searchGoButton" ); - $this->waitForPageToLoad( "30000" ); - $this->click( "link=".$displayName ); - $this->waitForPageToLoad( "600000" ); - - $this->click( "wpWatchthis" ); - $this->type( "wpTextbox1",$wikiText ); - $this->click( "wpSave" ); - $this->waitForPageToLoad( "30000" ); - - // Verify link 'My Watchlist' available - $this->assertTrue( $this->isElementPresent( "link=My watchlist" ) ); - - $this->click( "link=My watchlist" ); - $this->waitForPageToLoad( "30000" ); - - // Verify newly added page to the watchlist is available - $watchList = $this->getText( "//*[@id='bodyContent']" ); - $this->assertContains( $displayName, $watchList ); - - $this->type( "searchInput", $newPage ); - $this->click( "searchGoButton" ); - $this->waitForPageToLoad( "30000" ); - $this->click("link=Edit"); - $this->waitForPageToLoad( "30000" ); - $this->click( "wpWatchthis" ); - $this->click( "wpSave" ); - $this->deletePage( $newPage ); - } -} - diff --git a/maintenance/tests/selenium/suites/PageDeleteTestSuite.php b/maintenance/tests/selenium/suites/PageDeleteTestSuite.php deleted file mode 100644 index 2e535c11..00000000 --- a/maintenance/tests/selenium/suites/PageDeleteTestSuite.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -class PageDeleteTestSuite extends SeleniumTestSuite { - public function setUp() { - $this->setLoginBeforeTests( true ); - parent::setUp(); - } - public function addTests() { - $testFiles = array( - 'maintenance/tests/selenium/suites/DeletePageAdminTestCase.php' - ); - parent::addTestFiles( $testFiles ); - } - - -} diff --git a/maintenance/tests/selenium/suites/PageSearchTestCase.php b/maintenance/tests/selenium/suites/PageSearchTestCase.php deleted file mode 100644 index e139f7a0..00000000 --- a/maintenance/tests/selenium/suites/PageSearchTestCase.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php - -/** - * Selenium server manager - * - * @file - * @ingroup Maintenance - * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com> - * http://citizendium.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @addtogroup Maintenance - * - */ - -class PageSearchTestCase extends SeleniumTestCase { - - // Verify the functionality of the 'Go' button - public function testPageSearchBtnGo() { - - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->type( "searchInput", "calcey qa" ); - $this->click( "searchGoButton" ); - $this->waitForPageToLoad( "600000" ); - - // Verify no page matched with the entered search text - $source = $this->gettext( "//div[@id='bodyContent']/div[4]/p/b" ); - $correct = strstr ( $source, "Create the page \"Calcey qa\" on this wiki!" ); - $this->assertEquals( $correct, true ); - - $this->click( "link=Calcey qa" ); - $this->waitForPageToLoad( "600000" ); - - $this->type( "wpTextbox1", "Calcey QA team" ); - $this->click( "wpSave" ); - $this->waitForPageToLoad( "600000" ); - - } - - // Verify the functionality of the 'Search' button - public function testPageSearchBtnSearch() { - - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->type( "searchInput", "Calcey web" ); - $this->click( "mw-searchButton" ); - $this->waitForPageToLoad( "30000" ); - - // Verify no page is available as the search text - $source = $this->gettext( "//div[@id='bodyContent']/div[4]/p[2]/b" ); - $correct = strstr ( $source, "Create the page \"Calcey web\" on this wiki!" ); - $this->assertEquals( $correct, true ); - - $this->click( "link=Calcey web" ); - $this->waitForPageToLoad( "600000" ); - - $this->type( "wpTextbox1", "Calcey web team" ); - $this->click( "wpSave" ); - $this->waitForPageToLoad( "600000" ); - - // Verify saved page is opened when the exact page name is given - $this->type( "searchInput", "Calcey web" ); - $this->click( "mw-searchButton" ); - $this->waitForPageToLoad( "30000" ); - - // Verify exact page matched with the entered search text using 'Search' button - $source = $this->getText( "//*[@id='bodyContent']/div[4]/p/b" ); - $correct = strstr( $source, "There is a page named \"Calcey web\" on this wiki." ); - $this->assertEquals( $correct, true ); - - // Verify resutls available when partial page name is entered as the search text - $this->type( "searchInput", "Calcey" ); - $this->click( "mw-searchButton" ); - $this->waitForPageToLoad( "30000" ); - - // Verify text avaialble in the search result under the page titles - if($this->isElementPresent( "Page_title_matches" )) { - $textPageTitle = $this->getText( "//*[@id='bodyContent']/div[4]/ul[1]/li[1]/div[1]/a" ); - $this->assertContains( 'Calcey', $textPageTitle ); - } - - // Verify text avaialble in the search result under the page text - if($this->isElementPresent( "Page_text_matches" )) { - $textPageText = $this->getText( "//*[@id='bodyContent']/div[4]/ul[2]/li[2]/div[2]/span" ); - $this->assertContains( 'Calcey', $textPageText ); - } - $this->deletePage("Calcey QA"); - $this->deletePage("Calcey web"); - } -} diff --git a/maintenance/tests/selenium/suites/PreviewPageTestCase.php b/maintenance/tests/selenium/suites/PreviewPageTestCase.php deleted file mode 100644 index b704bf39..00000000 --- a/maintenance/tests/selenium/suites/PreviewPageTestCase.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * Selenium server manager - * - * @file - * @ingroup Maintenance - * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com> - * http://citizendium.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @addtogroup Maintenance - * - */ - -class PreviewPageTestCase extends SeleniumTestCase { - - // Verify adding a new page - public function testPreviewPage() { - $wikiText = "Adding this page to test the \n Preview button functionality"; - $newPage = "Test Preview Page"; - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->getNewPage( $newPage ); - $this->type( "wpTextbox1", $wikiText."" ); - $this->assertTrue($this->isElementPresent( "//*[@id='wpPreview']" )); - - $this->click( "wpPreview" ); - - // Verify saved page available - $source = $this->gettext( "firstHeading" ); - $correct = strstr( $source, "Test Preview Page" ); - $this->assertEquals( $correct, true); - - // Verify page content previewed succesfully - $contentOfPreviewPage = $this->getText( "//*[@id='content']" ); - $this->assertContains( $wikiText, $contentOfPreviewPage ); - } -} diff --git a/maintenance/tests/selenium/suites/SavePageTestCase.php b/maintenance/tests/selenium/suites/SavePageTestCase.php deleted file mode 100644 index 7f1a6924..00000000 --- a/maintenance/tests/selenium/suites/SavePageTestCase.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -/** - * Selenium server manager - * - * @file - * @ingroup Maintenance - * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com> - * http://citizendium.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @addtogroup Maintenance - * - */ - -class SavePageTestCase extends SeleniumTestCase { - - // Verify adding a new page - public function testSavePage() { - $wikiText = "Adding this page to test the Save button functionality"; - $newPage = "Test Save Page"; - - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->getNewPage($newPage); - $this->type("wpTextbox1", $wikiText); - - // verify 'Save' button available - $this->assertTrue($this->isElementPresent( "wpSave" )); - $this->click( "wpSave" ); - - // Verify saved page available - $source = $this->gettext( "firstHeading" ); - $correct = strstr( $source, "Test Save Page" ); - - // Verify Saved page name displayed correctly - $this->assertEquals( $correct, true ); - - // Verify page content saved succesfully - $contentOfSavedPage = $this->getText( "//*[@id='content']" ); - $this->assertContains( $wikiText, $contentOfSavedPage ); - $this->deletePage( $newPage ); - } -} diff --git a/maintenance/tests/selenium/suites/SimpleSeleniumConfig.php b/maintenance/tests/selenium/suites/SimpleSeleniumConfig.php deleted file mode 100644 index cffa83c4..00000000 --- a/maintenance/tests/selenium/suites/SimpleSeleniumConfig.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php -class SimpleSeleniumConfig { - - public static function getSettings(&$includeFiles, &$globalConfigs) { - $includes = array( - //files that needed to be included would go here - ); - $configs = array( - 'wgDefaultSkin' => 'chick' - ); - $includeFiles = array_merge( $includeFiles, $includes ); - $globalConfigs = array_merge( $globalConfigs, $configs); - return true; - } -}
\ No newline at end of file diff --git a/maintenance/tests/selenium/suites/SimpleSeleniumTestCase.php b/maintenance/tests/selenium/suites/SimpleSeleniumTestCase.php deleted file mode 100644 index 8f01b437..00000000 --- a/maintenance/tests/selenium/suites/SimpleSeleniumTestCase.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php -/* - * This test case is part of the SimpleSeleniumTestSuite. - * Configuration for these tests are dosumented as part of SimpleSeleniumTestSuite.php - */ -class SimpleSeleniumTestCase extends SeleniumTestCase { - public function testBasic() { - $this->open( $this->getUrl() . - '/index.php?title=Selenium&action=edit' ); - $this->type( "wpTextbox1", "This is a basic test" ); - $this->click( "wpPreview" ); - $this->waitForPageToLoad( 10000 ); - - // check result - $source = $this->getText( "//div[@id='wikiPreview']/p" ); - $correct = strstr( $source, "This is a basic test" ); - $this->assertEquals( $correct, true ); - } - - /* - * All this test really does is verify that a global var was set. - * It depends on $wgDefaultSkin = 'chick'; being set - */ - public function testGlobalVariableForDefaultSkin() { - $this->open( $this->getUrl() . '/index.php?&action=purge' ); - $bodyClass = $this->getAttribute( "//body/@class" ); - $this-> assertContains('skin-chick', $bodyClass, 'Chick skin not set'); - } - -} diff --git a/maintenance/tests/selenium/suites/SimpleSeleniumTestSuite.php b/maintenance/tests/selenium/suites/SimpleSeleniumTestSuite.php deleted file mode 100644 index a04f33ed..00000000 --- a/maintenance/tests/selenium/suites/SimpleSeleniumTestSuite.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php -/* - * Sample test suite. - * Two ways to configure MW for these tests - * 1) If you are running multiple test suites, add the following in LocalSettings.php - * require_once("maintenance/tests/selenium/SimpleSeleniumConfig.php"); - * $wgSeleniumTestConfigs['SimpleSeleniumTestSuite'] = 'SimpleSeleniumConfig::getSettings'; - * OR - * 2) Add the following to your Localsettings.php - * $wgDefaultSkin = 'chick'; - */ -class SimpleSeleniumTestSuite extends SeleniumTestSuite -{ - public function setUp() { - $this->setLoginBeforeTests( false ); - parent::setUp(); - } - public function addTests() { - $testFiles = array( - 'maintenance/tests/selenium/suites/SimpleSeleniumTestCase.php' - ); - parent::addTestFiles( $testFiles ); - } - - -} diff --git a/maintenance/tests/selenium/suites/UserPreferencesTestCase.php b/maintenance/tests/selenium/suites/UserPreferencesTestCase.php deleted file mode 100644 index 12824307..00000000 --- a/maintenance/tests/selenium/suites/UserPreferencesTestCase.php +++ /dev/null @@ -1,179 +0,0 @@ -<?php - -/** - * Selenium server manager - * - * @file - * @ingroup Maintenance - * Copyright (C) 2010 Dan Nessett <dnessett@yahoo.com> - * http://citizendium.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @addtogroup Maintenance - * - */ - -class UserPreferencesTestCase extends SeleniumTestCase { - - // Verify user information - public function testUserInfoDisplay() { - - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->click( "link=My preferences" ); - $this->waitForPageToLoad( "30000" ); - - // Verify correct username displayed in User Preferences - $this->assertEquals( $this->getText( "//li[@id='pt-userpage']/a" ), - $this->getText( "//table[@id='mw-htmlform-info']/tbody/tr[1]/td[2]" )); - - // Verify existing Signature Displayed correctly - $this->assertEquals( $this->selenium->getUser(), - $this->getTable( "mw-htmlform-signature.0.1" ) ); - } - - // Verify change password - public function testChangePassword() { - - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->click( "link=My preferences" ); - $this->waitForPageToLoad( "30000" ); - - $this->click( "link=Change password" ); - $this->waitForPageToLoad( "30000" ); - - $this->type( "wpPassword", "12345" ); - $this->type( "wpNewPassword", "54321" ); - $this->type( "wpRetype", "54321" ); - $this->click( "//input[@value='Change password']" ); - $this->waitForPageToLoad( "30000" ); - - $this->assertEquals( "Preferences", $this->getText( "firstHeading" )); - - $this->click( "link=Change password" ); - $this->waitForPageToLoad( "30000" ); - - $this->type( "wpPassword", "54321" ); - $this->type( "wpNewPassword", "12345" ); - $this->type( "wpRetype", "12345" ); - $this->click( "//input[@value='Change password']" ); - $this->waitForPageToLoad( "30000" ); - $this->assertEquals( "Preferences", $this->getText( "firstHeading" )); - - $this->click( "link=Change password" ); - $this->waitForPageToLoad( "30000" ); - - $this->type( "wpPassword", "54321" ); - $this->type( "wpNewPassword", "12345" ); - $this->type( "wpRetype", "12345" ); - $this->click( "//input[@value='Change password']" ); - $this->waitForPageToLoad( "30000" ); - } - - // Verify successful preferences save - public function testSuccessfullSave() { - - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->click( "link=My preferences" ); - $this->waitForPageToLoad( "30000" ); - - $this->type( "mw-input-realname", "Test User" ); - $this->click( "prefcontrol" ); - $this->waitForPageToLoad( "30000" ); - - // Verify "Your preferences have been saved." message - $this->assertEquals( "Your preferences have been saved.", - $this->getText( "//div[@id='bodyContent']/div[4]/strong/p" )); - $this->type( "mw-input-realname", "" ); - $this->click( "prefcontrol" ); - $this->waitForPageToLoad( "30000" ); - - } - - // Verify change signature - public function testChangeSignature() { - - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->click( "link=My preferences" ); - $this->waitForPageToLoad( "30000" ); - - $this->type( "mw-input-nickname", "TestSignature" ); - $this->click( "prefcontrol" ); - $this->waitForPageToLoad( "30000" ); - - // Verify change user signature - $this->assertEquals( "TestSignature", $this->getText( "link=TestSignature" )); - $this->type( "mw-input-nickname", "Test" ); - $this->click( "prefcontrol" ); - $this->waitForPageToLoad("30000"); - } - - // Verify change date format - public function testChangeDateFormatTimeZone() { - - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - - $this->click( "link=My preferences" ); - $this->waitForPageToLoad( "30000" ); - $this->click( "link=Date and time" ); - $this->waitForPageToLoad( "30000" ); - - $this->click( "mw-input-date-dmy" ); - $this->select( "mw-input-timecorrection", "label=Asia/Colombo" ); - $this->click( "prefcontrol" ); - $this->waitForPageToLoad( "30000" ); - - // Verify Date format and time zome saved - $this->assertEquals( "Your preferences have been saved.", - $this->getText( "//div[@id='bodyContent']/div[4]/strong/p" )); - } - - // Verify restoring all default settings - public function testSetAllDefault() { - - $this->open( $this->getUrl() . - '/index.php?title=Main_Page&action=edit' ); - $this->click( "link=My preferences" ); - $this->waitForPageToLoad( "30000" ); - - // Verify restoring all default settings - $this->assertEquals( "Restore all default settings", - $this->getText( "link=Restore all default settings" )); - - $this->click("//*[@id='preferences']/div/a"); - $this->waitForPageToLoad("30000"); - - // Verify 'This can not be undone' warning message displayed - $this->assertTrue($this->isElementPresent("//input[@value='Restore all default settings']")); - - // Verify 'Restore all default settings' button available - $this->assertEquals("You can use this page to reset your preferences to the site defaults. This cannot be undone.", - $this->getText("//div[@id='bodyContent']/p")); - - $this->click("//input[@value='Restore all default settings']"); - $this->waitForPageToLoad("30000"); - - // Verify preferences saved successfully - $this->assertEquals("Your preferences have been saved.", - $this->getText("//div[@id='bodyContent']/div[4]/strong/p")); - } -} - diff --git a/maintenance/tests/testHelpers.inc b/maintenance/tests/testHelpers.inc deleted file mode 100644 index 94ca6abc..00000000 --- a/maintenance/tests/testHelpers.inc +++ /dev/null @@ -1,652 +0,0 @@ -<?php - -/** - * @ingroup Maintenance - * - * Set of classes to help with test output and such. Right now pretty specific - * to the parser tests but could be more useful one day :) - * - * @todo Fixme: Make this more generic - */ - -class AnsiTermColorer { - function __construct() { - } - - /** - * Return ANSI terminal escape code for changing text attribs/color - * - * @param $color String: semicolon-separated list of attribute/color codes - * @return String - */ - public function color( $color ) { - global $wgCommandLineDarkBg; - - $light = $wgCommandLineDarkBg ? "1;" : "0;"; - - return "\x1b[{$light}{$color}m"; - } - - /** - * Return ANSI terminal escape code for restoring default text attributes - * - * @return String - */ - public function reset() { - return $this->color( 0 ); - } -} - -/* A colour-less terminal */ -class DummyTermColorer { - public function color( $color ) { - return ''; - } - - public function reset() { - return ''; - } -} - -class TestRecorder { - var $parent; - var $term; - - function __construct( $parent ) { - $this->parent = $parent; - $this->term = $parent->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 ) . "ALL TESTS PASSED!"; - } else { - $failed = $total - $success ; - print $this->term->color( 31 ) . "$failed tests failed!"; - } - - print $this->term->reset() . "\n"; - - return ( $success == $total ); - } -} - -class DbTestPreviewer extends TestRecorder { - protected $lb; // /< Database load balancer - protected $db; // /< Database connection to the main DB - protected $curRun; // /< run ID number for the current run - protected $prevRun; // /< run ID number for the previous run, if any - protected $results; // /< Result array - - /** - * This should be called before the table prefix is changed - */ - function __construct( $parent ) { - parent::__construct( $parent ); - - $this->lb = wfGetLBFactory()->newMainLB(); - // This connection will have the wiki's table prefix, not parsertest_ - $this->db = $this->lb->getConnection( DB_MASTER ); - } - - /** - * Set up result recording; insert a record for the run with the date - * and all that fun stuff - */ - function start() { - parent::start(); - - if ( ! $this->db->tableExists( 'testrun' ) - or ! $this->db->tableExists( 'testitem' ) ) - { - print "WARNING> `testrun` table not found in database.\n"; - $this->prevRun = false; - } else { - // We'll make comparisons against the previous run later... - $this->prevRun = $this->db->selectField( 'testrun', 'MAX(tr_id)' ); - } - - $this->results = array(); - } - - function record( $test, $result ) { - parent::record( $test, $result ); - $this->results[$test] = $result; - } - - function report() { - if ( $this->prevRun ) { - // f = fail, p = pass, n = nonexistent - // codes show before then after - $table = array( - 'fp' => 'previously failing test(s) now PASSING! :)', - 'pn' => 'previously PASSING test(s) removed o_O', - 'np' => 'new PASSING test(s) :)', - - 'pf' => 'previously passing test(s) now FAILING! :(', - 'fn' => 'previously FAILING test(s) removed O_o', - 'nf' => 'new FAILING test(s) :(', - 'ff' => 'still FAILING test(s) :(', - ); - - $prevResults = array(); - - $res = $this->db->select( 'testitem', array( 'ti_name', 'ti_success' ), - array( 'ti_run' => $this->prevRun ), __METHOD__ ); - - foreach ( $res as $row ) { - if ( !$this->parent->regex - || preg_match( "/{$this->parent->regex}/i", $row->ti_name ) ) - { - $prevResults[$row->ti_name] = $row->ti_success; - } - } - - $combined = array_keys( $this->results + $prevResults ); - - # Determine breakdown by change type - $breakdown = array(); - foreach ( $combined as $test ) { - if ( !isset( $prevResults[$test] ) ) { - $before = 'n'; - } elseif ( $prevResults[$test] == 1 ) { - $before = 'p'; - } else /* if ( $prevResults[$test] == 0 )*/ { - $before = 'f'; - } - - if ( !isset( $this->results[$test] ) ) { - $after = 'n'; - } elseif ( $this->results[$test] == 1 ) { - $after = 'p'; - } else /*if ( $this->results[$test] == 0 ) */ { - $after = 'f'; - } - - $code = $before . $after; - - if ( isset( $table[$code] ) ) { - $breakdown[$code][$test] = $this->getTestStatusInfo( $test, $after ); - } - } - - # Write out results - foreach ( $table as $code => $label ) { - if ( !empty( $breakdown[$code] ) ) { - $count = count( $breakdown[$code] ); - printf( "\n%4d %s\n", $count, $label ); - - foreach ( $breakdown[$code] as $differing_test_name => $statusInfo ) { - print " * $differing_test_name [$statusInfo]\n"; - } - } - } - } else { - print "No previous test runs to compare against.\n"; - } - - print "\n"; - parent::report(); - } - - /** - * Returns a string giving information about when a test last had a status change. - * Could help to track down when regressions were introduced, as distinct from tests - * which have never passed (which are more change requests than regressions). - */ - private function getTestStatusInfo( $testname, $after ) { - // If we're looking at a test that has just been removed, then say when it first appeared. - if ( $after == 'n' ) { - $changedRun = $this->db->selectField ( 'testitem', - 'MIN(ti_run)', - array( 'ti_name' => $testname ), - __METHOD__ ); - $appear = $this->db->selectRow ( 'testrun', - array( 'tr_date', 'tr_mw_version' ), - array( 'tr_id' => $changedRun ), - __METHOD__ ); - - return "First recorded appearance: " - . date( "d-M-Y H:i:s", strtotime ( $appear->tr_date ) ) - . ", " . $appear->tr_mw_version; - } - - // Otherwise, this test has previous recorded results. - // See when this test last had a different result to what we're seeing now. - $conds = array( - 'ti_name' => $testname, - 'ti_success' => ( $after == 'f' ? "1" : "0" ) ); - - if ( $this->curRun ) { - $conds[] = "ti_run != " . $this->db->addQuotes ( $this->curRun ); - } - - $changedRun = $this->db->selectField ( 'testitem', 'MAX(ti_run)', $conds, __METHOD__ ); - - // If no record of ever having had a different result. - if ( is_null ( $changedRun ) ) { - if ( $after == "f" ) { - return "Has never passed"; - } else { - return "Has never failed"; - } - } - - // Otherwise, we're looking at a test whose status has changed. - // (i.e. it used to work, but now doesn't; or used to fail, but is now fixed.) - // In this situation, give as much info as we can as to when it changed status. - $pre = $this->db->selectRow ( 'testrun', - array( 'tr_date', 'tr_mw_version' ), - array( 'tr_id' => $changedRun ), - __METHOD__ ); - $post = $this->db->selectRow ( 'testrun', - array( 'tr_date', 'tr_mw_version' ), - array( "tr_id > " . $this->db->addQuotes ( $changedRun ) ), - __METHOD__, - array( "LIMIT" => 1, "ORDER BY" => 'tr_id' ) - ); - - if ( $post ) { - $postDate = date( "d-M-Y H:i:s", strtotime ( $post->tr_date ) ) . ", {$post->tr_mw_version}"; - } else { - $postDate = 'now'; - } - - return ( $after == "f" ? "Introduced" : "Fixed" ) . " between " - . date( "d-M-Y H:i:s", strtotime ( $pre->tr_date ) ) . ", " . $pre->tr_mw_version - . " and $postDate"; - - } - - /** - * Commit transaction and clean up for result recording - */ - function end() { - $this->lb->commitMasterChanges(); - $this->lb->closeAll(); - parent::end(); - } - -} - -class DbTestRecorder extends DbTestPreviewer { - var $version; - - /** - * Set up result recording; insert a record for the run with the date - * and all that fun stuff - */ - function start() { - global $wgDBtype; - $this->db->begin(); - - if ( ! $this->db->tableExists( 'testrun' ) - or ! $this->db->tableExists( 'testitem' ) ) - { - print "WARNING> `testrun` table not found in database. Trying to create table.\n"; - $this->db->sourceFile( $this->db->patchPath( 'patch-testrun.sql' ) ); - echo "OK, resuming.\n"; - } - - parent::start(); - - $this->db->insert( 'testrun', - array( - 'tr_date' => $this->db->timestamp(), - 'tr_mw_version' => $this->version, - 'tr_php_version' => phpversion(), - 'tr_db_version' => $this->db->getServerVersion(), - 'tr_uname' => php_uname() - ), - __METHOD__ ); - if ( $wgDBtype === 'postgres' ) { - $this->curRun = $this->db->currentSequenceValue( 'testrun_id_seq' ); - } else { - $this->curRun = $this->db->insertId(); - } - } - - /** - * Record an individual test item's success or failure to the db - * - * @param $test String - * @param $result Boolean - */ - 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__ ); - } -} - -class RemoteTestRecorder extends TestRecorder { - function start() { - parent::start(); - - $this->results = array(); - $this->ping( 'running' ); - } - - function record( $test, $result ) { - parent::record( $test, $result ); - $this->results[$test] = (bool)$result; - } - - function end() { - $this->ping( 'complete', $this->results ); - parent::end(); - } - - /** - * Inform a CodeReview instance that we've started or completed a test run... - * - * @param $status string: "running" - tell it we've started - * "complete" - provide test results array - * "abort" - something went horribly awry - * @param $results array of test name => true/false - */ - function ping( $status, $results = false ) { - global $wgParserTestRemote, $IP; - - $remote = $wgParserTestRemote; - $revId = SpecialVersion::getSvnRevision( $IP ); - $jsonResults = FormatJson::encode( $results ); - - if ( !$remote ) { - print "Can't do remote upload without configuring \$wgParserTestRemote!\n"; - exit( 1 ); - } - - // Generate a hash MAC to validate our credentials - $message = array( - $remote['repo'], - $remote['suite'], - $revId, - $status, - ); - - if ( $status == "complete" ) { - $message[] = $jsonResults; - } - $hmac = hash_hmac( "sha1", implode( "|", $message ), $remote['secret'] ); - - $postData = array( - 'action' => 'codetestupload', - 'format' => 'json', - 'repo' => $remote['repo'], - 'suite' => $remote['suite'], - 'rev' => $revId, - 'status' => $status, - 'hmac' => $hmac, - ); - - if ( $status == "complete" ) { - $postData['results'] = $jsonResults; - } - - $response = $this->post( $remote['api-url'], $postData ); - - if ( $response === false ) { - print "CodeReview info upload failed to reach server.\n"; - exit( 1 ); - } - - $responseData = FormatJson::decode( $response, true ); - - if ( !is_array( $responseData ) ) { - print "CodeReview API response not recognized...\n"; - wfDebug( "Unrecognized CodeReview API response: $response\n" ); - exit( 1 ); - } - - if ( isset( $responseData['error'] ) ) { - $code = $responseData['error']['code']; - $info = $responseData['error']['info']; - print "CodeReview info upload failed: $code $info\n"; - exit( 1 ); - } - } - - function post( $url, $data ) { - return Http::post( $url, array( 'postData' => $data ) ); - } -} - -class TestFileIterator implements Iterator { - private $file; - private $fh; - private $parserTest; /* An instance of ParserTest (parserTests.php) or MediaWikiParserTest (phpunit) */ - private $index = 0; - private $test; - private $lineNum; - private $eof; - - function __construct( $file, $parserTest = null ) { - global $IP; - - $this->file = $file; - $this->fh = fopen( $this->file, "rt" ); - - if ( !$this->fh ) { - wfDie( "Couldn't open file '$file'\n" ); - } - - $this->parserTest = $parserTest; - - if ( $this->parserTest ) { - $this->parserTest->showRunFile( wfRelativePath( $this->file, $IP ) ); - } - - $this->lineNum = $this->index = 0; - } - - function rewind() { - if ( fseek( $this->fh, 0 ) ) { - wfDie( "Couldn't fseek to the start of '$this->file'\n" ); - } - - $this->index = -1; - $this->lineNum = 0; - $this->eof = false; - $this->next(); - - return true; - } - - function current() { - return $this->test; - } - - function key() { - return $this->index; - } - - function next() { - if ( $this->readNextTest() ) { - $this->index++; - return true; - } else { - $this->eof = true; - } - } - - function valid() { - return $this->eof != true; - } - - function readNextTest() { - $data = array(); - $section = null; - - while ( false !== ( $line = fgets( $this->fh ) ) ) { - $this->lineNum++; - $matches = array(); - - if ( preg_match( '/^!!\s*(\w+)/', $line, $matches ) ) { - $section = strtolower( $matches[1] ); - - if ( $section == 'endarticle' ) { - if ( !isset( $data['text'] ) ) { - wfDie( "'endarticle' without 'text' at line {$this->lineNum} of $this->file\n" ); - } - - if ( !isset( $data['article'] ) ) { - wfDie( "'endarticle' without 'article' at line {$this->lineNum} of $this->file\n" ); - } - - if ( $this->parserTest ) { - $this->parserTest->addArticle( ParserTest::chomp( $data['article'] ), $data['text'], $this->lineNum ); - } else {wfDie("JAJA"); - ParserTest::addArticle( $data['article'], $data['text'], $this->lineNum ); - } - $data = array(); - $section = null; - - continue; - } - - if ( $section == 'endhooks' ) { - if ( !isset( $data['hooks'] ) ) { - wfDie( "'endhooks' without 'hooks' at line {$this->lineNum} of $this->file\n" ); - } - - foreach ( explode( "\n", $data['hooks'] ) as $line ) { - $line = trim( $line ); - - if ( $line ) { - if ( $this->parserTest && !$this->parserTest->requireHook( $line ) ) { - return false; - } - } - } - - $data = array(); - $section = null; - - continue; - } - - if ( $section == 'endfunctionhooks' ) { - if ( !isset( $data['functionhooks'] ) ) { - wfDie( "'endfunctionhooks' without 'functionhooks' at line {$this->lineNum} of $this->file\n" ); - } - - foreach ( explode( "\n", $data['functionhooks'] ) as $line ) { - $line = trim( $line ); - - if ( $line ) { - if ( $this->parserTest && !$this->parserTest->requireFunctionHook( $line ) ) { - return false; - } - } - } - - $data = array(); - $section = null; - - continue; - } - - if ( $section == 'end' ) { - if ( !isset( $data['test'] ) ) { - wfDie( "'end' without 'test' at line {$this->lineNum} of $this->file\n" ); - } - - if ( !isset( $data['input'] ) ) { - wfDie( "'end' without 'input' at line {$this->lineNum} of $this->file\n" ); - } - - if ( !isset( $data['result'] ) ) { - wfDie( "'end' without 'result' at line {$this->lineNum} of $this->file\n" ); - } - - if ( !isset( $data['options'] ) ) { - $data['options'] = ''; - } - - if ( !isset( $data['config'] ) ) - $data['config'] = ''; - - if ( $this->parserTest - && ( ( preg_match( '/\\bdisabled\\b/i', $data['options'] ) && !$this->parserTest->runDisabled ) - || !preg_match( "/" . $this->parserTest->regex . "/i", $data['test'] ) ) ) { - # disabled test - $data = array(); - $section = null; - - continue; - } - - global $wgUseTeX; - - if ( $this->parserTest && - preg_match( '/\\bmath\\b/i', $data['options'] ) && !$wgUseTeX ) { - # don't run math tests if $wgUseTeX is set to false in LocalSettings - $data = array(); - $section = null; - - continue; - } - - if ( $this->parserTest ) { - $this->test = array( - 'test' => ParserTest::chomp( $data['test'] ), - 'input' => ParserTest::chomp( $data['input'] ), - 'result' => ParserTest::chomp( $data['result'] ), - 'options' => ParserTest::chomp( $data['options'] ), - 'config' => ParserTest::chomp( $data['config'] ) ); - } else { - $this->test['test'] = $data['test']; - } - - return true; - } - - if ( isset ( $data[$section] ) ) { - wfDie( "duplicate section '$section' at line {$this->lineNum} of $this->file\n" ); - } - - $data[$section] = ''; - - continue; - } - - if ( $section ) { - $data[$section] .= $line; - } - } - - return false; - } -} diff --git a/maintenance/undelete.php b/maintenance/undelete.php index 50bf791a..7213d096 100644 --- a/maintenance/undelete.php +++ b/maintenance/undelete.php @@ -2,6 +2,21 @@ /** * Undelete a page by fetching it from the archive table * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance */ @@ -12,16 +27,16 @@ class Undelete extends Maintenance { public function __construct() { parent::__construct(); $this->mDescription = "Undelete a page"; - $this->addOption( 'u', 'The user to perform the undeletion', false, true ); - $this->addOption( 'r', 'The reason to undelete', false, true ); + $this->addOption( 'user', 'The user to perform the undeletion', false, true, 'u' ); + $this->addOption( 'reason', 'The reason to undelete', false, true, 'r' ); $this->addArg( 'pagename', 'Page to undelete' ); } public function execute() { global $wgUser; - $user = $this->getOption( 'u', 'Command line script' ); - $reason = $this->getOption( 'r', '' ); + $user = $this->getOption( 'user', 'Command line script' ); + $reason = $this->getOption( 'reason', '' ); $pageName = $this->getArg(); $title = Title::newFromText( $pageName ); diff --git a/maintenance/update.php b/maintenance/update.php index e3941a3c..e4a594f6 100644 --- a/maintenance/update.php +++ b/maintenance/update.php @@ -5,6 +5,21 @@ * This is used when the database schema is modified and we need to apply patches. * It is kept compatible with php 4 parsing so that it can give out a meaningful error. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @todo document * @ingroup Maintenance @@ -28,6 +43,7 @@ class UpdateMediaWiki extends Maintenance { $this->addOption( 'quick', 'Skip 5 second countdown before starting' ); $this->addOption( 'doshared', 'Also update shared tables' ); $this->addOption( 'nopurge', 'Do not purge the objectcache table after updates' ); + $this->addOption( 'force', 'Override when $wgMiserMode disables this script' ); } function getDbType() { @@ -60,7 +76,13 @@ class UpdateMediaWiki extends Maintenance { } function execute() { - global $wgVersion, $wgTitle, $wgLang; + global $wgVersion, $wgTitle, $wgLang, $wgMiserMode; + + if( $wgMiserMode && !$this->hasOption( 'force' ) ) { + $this->error( "Do not run update.php on this wiki. If you're seeing this you should\n" + . "probably ask for some help in performing your schema updates.\n\n" + . "If you know what you are doing, you can continue with --force", true ); + } $wgLang = Language::factory( 'en' ); $wgTitle = Title::newFromText( "MediaWiki database updater" ); @@ -88,7 +110,7 @@ class UpdateMediaWiki extends Maintenance { $shared = $this->hasOption( 'doshared' ); - $updates = array('core','extensions'); + $updates = array( 'core', 'extensions', 'stats' ); if( !$this->hasOption('nopurge') ) { $updates[] = 'purge'; } diff --git a/maintenance/updateArticleCount.php b/maintenance/updateArticleCount.php index b0dceb58..dbbfb80c 100644 --- a/maintenance/updateArticleCount.php +++ b/maintenance/updateArticleCount.php @@ -27,9 +27,6 @@ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); class UpdateArticleCount extends Maintenance { - // Content namespaces - private $namespaces; - public function __construct() { parent::__construct(); $this->mDescription = "Count of the number of articles and update the site statistics table"; @@ -37,64 +34,21 @@ class UpdateArticleCount extends Maintenance { } public function execute() { - global $wgContentNamespaces; - $this->namespaces = $wgContentNamespaces; $this->output( "Counting articles..." ); - $result = $this->count(); - if ( $result !== false ) { - $this->output( "found {$result}.\n" ); - if ( $this->hasOption( 'update' ) ) { - $this->output( "Updating site statistics table... " ); - $dbw = wfGetDB( DB_MASTER ); - $dbw->update( 'site_stats', array( 'ss_good_articles' => $result ), array( 'ss_row_id' => 1 ), __METHOD__ ); - $this->output( "done.\n" ); - } else { - $this->output( "To update the site statistics table, run the script with the --update option.\n" ); - } + $counter = new SiteStatsInit( false ); + $result = $counter->articles(); + + $this->output( "found {$result}.\n" ); + if ( $this->hasOption( 'update' ) ) { + $this->output( "Updating site statistics table... " ); + $dbw = wfGetDB( DB_MASTER ); + $dbw->update( 'site_stats', array( 'ss_good_articles' => $result ), array( 'ss_row_id' => 1 ), __METHOD__ ); + $this->output( "done.\n" ); } else { - $this->output( "failed.\n" ); + $this->output( "To update the site statistics table, run the script with the --update option.\n" ); } } - - /** - * Produce a comma-delimited set of namespaces - * Includes paranoia - * - * @return string - */ - private function makeNsSet() { - foreach ( $this->namespaces as $namespace ) - $namespaces[] = intval( $namespace ); - return implode( ', ', $namespaces ); - } - - /** - * Produce SQL for the query - * - * @param $dbr Database handle - * @return string - */ - private function makeSql( $dbr ) { - list( $page, $pagelinks ) = $dbr->tableNamesN( 'page', 'pagelinks' ); - $nsset = $this->makeNsSet(); - return "SELECT COUNT(DISTINCT page_id) AS pagecount " . - "FROM $page, $pagelinks " . - "WHERE pl_from=page_id and page_namespace IN ( $nsset ) " . - "AND page_is_redirect = 0 AND page_len > 0"; - } - - /** - * Count the number of valid content pages in the wiki - * - * @return mixed Integer, or false if there's a problem - */ - private function count() { - $dbr = wfGetDB( DB_SLAVE ); - $res = $dbr->query( $this->makeSql( $dbr ), __METHOD__ ); - $row = $dbr->fetchObject( $res ); - return $row ? $row->pagecount : false; - } } $maintClass = "UpdateArticleCount"; diff --git a/maintenance/updateCollation.php b/maintenance/updateCollation.php index e890bce7..7e8a7ee0 100644 --- a/maintenance/updateCollation.php +++ b/maintenance/updateCollation.php @@ -1,5 +1,24 @@ <?php /** + * Script will find all rows in the categorylinks table whose collation is + * out-of-date (cl_collation != $wgCategoryCollation) and repopulate cl_sortkey + * using the page title and cl_sortkey_prefix. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance * @author Aryeh Gregor (Simetrical) @@ -10,7 +29,8 @@ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); class UpdateCollation extends Maintenance { - const BATCH_SIZE = 50; + const BATCH_SIZE = 50; // Number of rows to process in one batch + const SYNC_INTERVAL = 20; // Wait for slaves after this many batches public function __construct() { parent::__construct(); @@ -25,33 +45,32 @@ TEXT; $this->addOption( 'force', 'Run on all rows, even if the collation is ' . 'supposed to be up-to-date.' ); - } - - public function syncDBs() { - $lb = wfGetLB(); - // bug 27975 - Don't try to wait for slaves if there are none - // Prevents permission error when getting master position - if ( $lb->getServerCount() > 1 ) { - $dbw = $lb->getConnection( DB_MASTER ); - $pos = $dbw->getMasterPos(); - $lb->waitForAll( $pos ); - } + $this->addOption( 'previous-collation', 'Set the previous value of ' . + '$wgCategoryCollation here to speed up this script, especially if your ' . + 'categorylinks table is large. This will only update rows with that ' . + 'collation, though, so it may miss out-of-date rows with a different, ' . + 'even older collation.', false, true ); } public function execute() { global $wgCategoryCollation, $wgMiserMode; - $dbw = wfGetDB( DB_MASTER ); + $dbw = $this->getDB( DB_MASTER ); $force = $this->getOption( 'force' ); - $options = array( 'LIMIT' => self::BATCH_SIZE ); + $options = array( 'LIMIT' => self::BATCH_SIZE, 'STRAIGHT_JOIN' ); if ( $force ) { $options['ORDER BY'] = 'cl_from, cl_to'; $collationConds = array(); } else { - $collationConds = array( 0 => - 'cl_collation != ' . $dbw->addQuotes( $wgCategoryCollation ) ); + if ( $this->hasOption( 'previous-collation' ) ) { + $collationConds['cl_collation'] = $this->getOption( 'previous-collation' ); + } else { + $collationConds = array( 0 => + 'cl_collation != ' . $dbw->addQuotes( $wgCategoryCollation ) + ); + } if ( !$wgMiserMode ) { $count = $dbw->selectField( @@ -70,10 +89,10 @@ TEXT; } $count = 0; - $row = false; + $batchCount = 0; $batchConds = array(); do { - $this->output( 'Processing next ' . self::BATCH_SIZE . ' rows... '); + $this->output( "Selecting next " . self::BATCH_SIZE . " rows..." ); $res = $dbw->select( array( 'categorylinks', 'page' ), array( 'cl_from', 'cl_to', 'cl_sortkey_prefix', 'cl_collation', @@ -83,6 +102,7 @@ TEXT; __METHOD__, $options ); + $this->output( " processing..." ); $dbw->begin(); foreach ( $res as $row ) { @@ -136,7 +156,11 @@ TEXT; $count += $res->numRows(); $this->output( "$count done.\n" ); - $this->syncDBs(); + if ( ++$batchCount % self::SYNC_INTERVAL == 0 ) { + $this->output( "Waiting for slaves ... " ); + wfWaitForSlaves(); + $this->output( "done\n" ); + } } while ( $res->numRows() == self::BATCH_SIZE ); } } diff --git a/maintenance/updateDoubleWidthSearch.php b/maintenance/updateDoubleWidthSearch.php index bfbc441f..61545f8d 100644 --- a/maintenance/updateDoubleWidthSearch.php +++ b/maintenance/updateDoubleWidthSearch.php @@ -43,8 +43,7 @@ class UpdateDoubleWidthSearch extends Maintenance { $dbw = wfGetDB( DB_MASTER ); if ( $dbw->getType() !== 'mysql' ) { - $this->output( "This change is only needed on MySQL, quitting.\n" ); - exit( 1 ); + $this->error( "This change is only needed on MySQL, quitting.\n", true ); } $res = $this->findRows( $dbw ); diff --git a/maintenance/updateRestrictions.php b/maintenance/updateRestrictions.php index c815f4b9..ffbdb2ba 100644 --- a/maintenance/updateRestrictions.php +++ b/maintenance/updateRestrictions.php @@ -64,7 +64,7 @@ class UpdateRestrictions extends Maintenance { // old old format should be treated as edit/move restriction $oldRestrictions["edit"] = trim( $temp[0] ); $oldRestrictions["move"] = trim( $temp[0] ); - } else if ( $temp[1] ) { + } elseif ( $temp[1] ) { $oldRestrictions[$temp[0]] = trim( $temp[1] ); } } @@ -96,7 +96,7 @@ class UpdateRestrictions extends Maintenance { } $blockStart += $this->mBatchSize - 1; $blockEnd += $this->mBatchSize - 1; - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } $this->output( "...removing dead rows from page_restrictions\n" ); // Kill any broken rows from previous imports diff --git a/maintenance/updateSearchIndex.php b/maintenance/updateSearchIndex.php index 97863101..eed3571c 100644 --- a/maintenance/updateSearchIndex.php +++ b/maintenance/updateSearchIndex.php @@ -55,11 +55,10 @@ class UpdateSearchIndex extends Maintenance { # We can safely delete the file when we're done though. $start = file_get_contents( 'searchUpdate.pos' ); unlink( 'searchUpdate.pos' ); + } elseif( is_readable( $posFile ) ) { + $start = file_get_contents( $posFile ); } else { - $start = @file_get_contents( $posFile ); - if ( !$start ) { - $start = wfTimestamp( TS_MW, time() - 86400 ); - } + $start = wfTimestamp( TS_MW, time() - 86400 ); } $lockTime = $this->getOption( 'l', 20 ); @@ -70,10 +69,10 @@ class UpdateSearchIndex extends Maintenance { fwrite( $file, $end ); fclose( $file ); } else { - $this->output( "*** Couldn't write to the $posFile!\n" ); + $this->error( "*** Couldn't write to the $posFile!\n" ); } } else { - $this->output( "*** Couldn't write to the $posFile!\n" ); + $this->error( "*** Couldn't write to the $posFile!\n" ); } } diff --git a/maintenance/updateSpecialPages.php b/maintenance/updateSpecialPages.php index 3e5df982..ddf1601b 100644 --- a/maintenance/updateSpecialPages.php +++ b/maintenance/updateSpecialPages.php @@ -33,8 +33,8 @@ class UpdateSpecialPages extends Maintenance { } public function execute() { - global $IP, $wgOut, $wgSpecialPageCacheUpdates, $wgQueryPages, $wgQueryCacheLimit, $wgDisableQueryPageUpdate; - $wgOut->disable(); + global $IP, $wgSpecialPageCacheUpdates, $wgQueryPages, $wgQueryCacheLimit, $wgDisableQueryPageUpdate; + $dbw = wfGetDB( DB_MASTER ); foreach ( $wgSpecialPageCacheUpdates as $special => $call ) { @@ -58,14 +58,15 @@ class UpdateSpecialPages extends Maintenance { } $this->output( sprintf( "completed in %.2fs\n", $seconds ) ); # Wait for the slave to catch up - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } // This is needed to initialise $wgQueryPages require_once( "$IP/includes/QueryPage.php" ); foreach ( $wgQueryPages as $page ) { - @list( $class, $special, $limit ) = $page; + list( $class, $special ) = $page; + $limit = isset( $page[2] ) ? $page[2] : null; # --list : just show the name of pages if ( $this->hasOption( 'list' ) ) { @@ -78,16 +79,20 @@ class UpdateSpecialPages extends Maintenance { continue; } - $specialObj = SpecialPage::getPage( $special ); + $specialObj = SpecialPageFactory::getPage( $special ); if ( !$specialObj ) { $this->output( "No such special page: $special\n" ); exit; } - if ( !class_exists( $class ) ) { - $file = $specialObj->getFile(); - require_once( $file ); + if ( $specialObj instanceof QueryPage ) { + $queryPage = $specialObj; + } else { + if ( !class_exists( $class ) ) { + $file = $specialObj->getFile(); + require_once( $file ); + } + $queryPage = new $class; } - $queryPage = new $class; if ( !$this->hasOption( 'only' ) || $this->getOption( 'only' ) == $queryPage->getName() ) { $this->output( sprintf( '%-30s ', $special ) ); @@ -126,7 +131,7 @@ class UpdateSpecialPages extends Maintenance { $dbw->commit(); } # Wait for the slave to catch up - wfWaitForSlaves( 5 ); + wfWaitForSlaves(); } else { $this->output( "cheap, skipped\n" ); } diff --git a/maintenance/upgrade1_5.php b/maintenance/upgrade1_5.php index e919c62e..730e7637 100644 --- a/maintenance/upgrade1_5.php +++ b/maintenance/upgrade1_5.php @@ -9,6 +9,21 @@ * much older versions, etc. * Run this, FOLLOWED BY update.php, for upgrading from 1.4.5 release to 1.5. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance */ @@ -86,7 +101,6 @@ class FiveUpgrade extends Maintenance { $this->cleanupSwaps = array(); $this->emailAuth = false; # don't preauthenticate emails - $this->maxLag = 10; # if slaves are lagged more than 10 secs, wait $this->step = $this->getOption( 'step', null ); } @@ -125,12 +139,10 @@ class FiveUpgrade extends Maintenance { * @access private */ function streamConnection() { - global $wgDBtype; - $timeout = 3600 * 24; $db = $this->newConnection(); $db->bufferResults( false ); - if ( $wgDBtype == 'mysql' ) { + if ( $db->getType() == 'mysql' ) { $db->query( "SET net_read_timeout=$timeout" ); $db->query( "SET net_write_timeout=$timeout" ); } @@ -303,10 +315,16 @@ class FiveUpgrade extends Maintenance { */ function insertChunk( &$chunk ) { // Give slaves a chance to catch up - wfWaitForSlaves( $this->maxLag ); + wfWaitForSlaves(); $this->dbw->insert( $this->chunkTable, $chunk, $this->chunkFunction, $this->chunkOptions ); } + /** + * Helper function for copyTable array_filter + */ + static private function notUpgradeNull( $x ) { + return $x !== MW_UPGRADE_NULL; + } /** * Copy and transcode a table to table_temp. @@ -336,8 +354,7 @@ class FiveUpgrade extends Maintenance { $this->setChunkScale( 100, $numRecords, $name_temp, __METHOD__ ); // Pull all records from the second, streaming database connection. - $sourceFields = array_keys( array_filter( $fields, - create_function( '$x', 'return $x !== MW_UPGRADE_NULL;' ) ) ); + $sourceFields = array_keys( array_filter( $fields, 'FiveUpgrade::notUpgradeNull' ) ); $result = $this->dbr->select( $name, $sourceFields, '', diff --git a/maintenance/userDupes.inc b/maintenance/userDupes.inc index 7c2bca1c..31bae8ed 100644 --- a/maintenance/userDupes.inc +++ b/maintenance/userDupes.inc @@ -1,23 +1,25 @@ <?php -# Copyright (C) 2005 Brion Vibber <brion@pobox.com> -# http://www.mediawiki.org/ -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# http://www.gnu.org/copyleft/gpl.html - /** + * Helper class for update.php and upgrade1_5.php. + * + * Copyright © 2005 Brion Vibber <brion@pobox.com> + * http://www.mediawiki.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance */ @@ -172,7 +174,7 @@ class UserDupes { * @access private */ function newSchema() { - return class_exists( 'Revision' ); + return MWInit::classExists( 'Revision' ); } /** diff --git a/maintenance/userOptions.inc b/maintenance/userOptions.inc index deb12bc8..6edef9d9 100644 --- a/maintenance/userOptions.inc +++ b/maintenance/userOptions.inc @@ -1,5 +1,22 @@ <?php /** + * Helper class for userOptions.php script. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance */ diff --git a/maintenance/userOptions.php b/maintenance/userOptions.php index 597c6d45..6e1681c3 100644 --- a/maintenance/userOptions.php +++ b/maintenance/userOptions.php @@ -6,6 +6,21 @@ * * Made on an original idea by Fooey (freenode) * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file * @ingroup Maintenance * @author Ashar Voultoiz <hashar at free dot fr> diff --git a/maintenance/users.sql b/maintenance/users.sql deleted file mode 100644 index 1db32ae2..00000000 --- a/maintenance/users.sql +++ /dev/null @@ -1,12 +0,0 @@ --- SQL script to create required database users with proper --- access rights. This is run from the installation script --- which replaces the password variables with their values --- from local settings. --- - -GRANT ALL PRIVILEGES ON `{$wgDBname}`.* - TO '{$wgDBuser}'@'%' IDENTIFIED BY '{$wgDBpassword}'; -GRANT ALL PRIVILEGES ON `{$wgDBname}`.* - TO '{$wgDBuser}'@localhost IDENTIFIED BY '{$wgDBpassword}'; -GRANT ALL PRIVILEGES ON `{$wgDBname}`.* - TO '{$wgDBuser}'@localhost.localdomain IDENTIFIED BY '{$wgDBpassword}'; diff --git a/maintenance/waitForSlave.php b/maintenance/waitForSlave.php index 0bf01d6d..720ca288 100644 --- a/maintenance/waitForSlave.php +++ b/maintenance/waitForSlave.php @@ -1,5 +1,7 @@ <?php /** + * Script to wait until slave lag goes under a certain value. + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or diff --git a/maintenance/wikipedia-interwiki.sql b/maintenance/wikipedia-interwiki.sql index f683439e..6a213166 100644 --- a/maintenance/wikipedia-interwiki.sql +++ b/maintenance/wikipedia-interwiki.sql @@ -250,7 +250,6 @@ REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local) VALUES ('tl','http://tl.wikipedia.org/wiki/$1',1), ('tn','http://tn.wikipedia.org/wiki/$1',1), ('to','http://to.wikipedia.org/wiki/$1',1), -('tokipona','http://tokipona.wikipedia.org/wiki/$1',1), ('tpi','http://tpi.wikipedia.org/wiki/$1',1), ('tr','http://tr.wikipedia.org/wiki/$1',1), ('ts','http://ts.wikipedia.org/wiki/$1',1), diff --git a/maintenance/wiktionary-interwiki.sql b/maintenance/wiktionary-interwiki.sql index 3dc62e5e..f55235b1 100644 --- a/maintenance/wiktionary-interwiki.sql +++ b/maintenance/wiktionary-interwiki.sql @@ -159,7 +159,6 @@ REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local) VALUES ('tl','http://tl.wiktionary.org/wiki/$1',1), ('tn','http://tn.wiktionary.org/wiki/$1',1), ('to','http://to.wiktionary.org/wiki/$1',1), -('tokipona','http://tokipona.wiktionary.org/wiki/$1',1), ('tpi','http://tpi.wiktionary.org/wiki/$1',1), ('tr','http://tr.wiktionary.org/wiki/$1',1), ('ts','http://ts.wiktionary.org/wiki/$1',1), |