summaryrefslogtreecommitdiff
path: root/maintenance
diff options
context:
space:
mode:
Diffstat (limited to 'maintenance')
-rw-r--r--maintenance/7zip.inc18
-rw-r--r--maintenance/Doxyfile11
-rw-r--r--maintenance/Maintenance.php355
-rw-r--r--maintenance/Site.php19
-rw-r--r--maintenance/addwiki.php39
-rw-r--r--maintenance/archives/patch-archive_ar_revid.sql3
-rw-r--r--maintenance/archives/patch-image_reditects.sql0
-rw-r--r--maintenance/archives/patch-math.sql28
-rw-r--r--maintenance/archives/patch-page_no_title_convert.sql0
-rw-r--r--maintenance/archives/patch-profiling.sql2
-rw-r--r--maintenance/archives/patch-up_property.sql4
-rw-r--r--maintenance/archives/patch-uploadstash.sql49
-rw-r--r--maintenance/archives/patch-user-newtalk-timestamp-null.sql1
-rw-r--r--maintenance/archives/patch-user_email_index.sql1
-rw-r--r--maintenance/archives/patch-user_former_groups.sql9
-rw-r--r--maintenance/archives/patch-user_last_timestamp.sql2
-rw-r--r--maintenance/archives/upgradeLogging.php21
-rw-r--r--maintenance/backup.inc56
-rw-r--r--maintenance/backupPrefetch.inc25
-rw-r--r--maintenance/benchmarks/Benchmarker.php23
-rw-r--r--maintenance/benchmarks/bench_HTTP_HTTPS.php21
-rw-r--r--maintenance/benchmarks/bench_delete_truncate.php78
-rw-r--r--maintenance/benchmarks/bench_if_switch.php88
-rw-r--r--maintenance/benchmarks/bench_strtr_str_replace.php50
-rw-r--r--maintenance/benchmarks/bench_wfIsWindows.php21
-rw-r--r--maintenance/benchmarks/benchmarkPurge.php (renamed from maintenance/benchmarkPurge.php)7
-rw-r--r--maintenance/cdb.php119
-rw-r--r--maintenance/changePassword.php11
-rw-r--r--maintenance/checkBadRedirects.php8
-rw-r--r--maintenance/checkSyntax.php20
-rw-r--r--maintenance/cleanupImages.php2
-rw-r--r--maintenance/cleanupSpam.php1
-rw-r--r--maintenance/cleanupTable.inc3
-rw-r--r--maintenance/cleanupUploadStash.php75
-rw-r--r--maintenance/commandLine.inc22
-rw-r--r--maintenance/compareParsers.php142
-rw-r--r--maintenance/convertLinks.php4
-rw-r--r--maintenance/convertUserOptions.php2
-rw-r--r--maintenance/createAndPromote.php10
-rw-r--r--maintenance/deleteArchivedFiles.inc9
-rw-r--r--maintenance/deleteArchivedRevisions.inc2
-rw-r--r--maintenance/deleteBatch.php10
-rw-r--r--maintenance/deleteDefaultMessages.php4
-rw-r--r--maintenance/deleteOldRevisions.php1
-rw-r--r--maintenance/deleteOrphanedRevisions.php2
-rw-r--r--maintenance/deleteSelfExternals.php2
-rw-r--r--maintenance/doMaintenance.php47
-rw-r--r--maintenance/dumpBackup.php23
-rw-r--r--maintenance/dumpInterwiki.php64
-rw-r--r--maintenance/dumpIterator.php167
-rw-r--r--maintenance/dumpTextPass.php397
-rw-r--r--maintenance/dumpUploads.php2
-rw-r--r--maintenance/edit.php22
-rw-r--r--maintenance/eval.php24
-rw-r--r--maintenance/fetchText.php3
-rw-r--r--maintenance/findHooks.php (renamed from maintenance/findhooks.php)35
-rw-r--r--maintenance/fixDoubleRedirects.php120
-rw-r--r--maintenance/fixExtLinksProtocolRelative.php81
-rw-r--r--maintenance/fixSlaveDesync.php4
-rw-r--r--maintenance/formatInstallDoc.php54
-rw-r--r--maintenance/fuzz-tester.php115
-rw-r--r--maintenance/gearman/gearmanWorker.php2
-rw-r--r--maintenance/generateSitemap.php33
-rw-r--r--maintenance/hiphop/compiler.conf5
-rw-r--r--maintenance/hiphop/extra-files35
-rw-r--r--maintenance/hiphop/make308
-rw-r--r--maintenance/hiphop/run-server75
-rw-r--r--maintenance/hiphop/server.conf30
-rw-r--r--maintenance/httpSessionDownload.php57
-rw-r--r--maintenance/ibm_db2/patch-categorylinks-better-collation.sql21
-rw-r--r--maintenance/ibm_db2/patch-change_tag-indexes.sql5
-rw-r--r--maintenance/ibm_db2/patch-change_tag.sql8
-rw-r--r--maintenance/ibm_db2/patch-change_tag_summary.sql7
-rw-r--r--maintenance/ibm_db2/patch-change_valid_tag.sql3
-rw-r--r--maintenance/ibm_db2/patch-cl_collation-field.sql1
-rw-r--r--maintenance/ibm_db2/patch-cl_sortkey_prefix-field.sql1
-rw-r--r--maintenance/ibm_db2/patch-cl_type-field.sql1
-rw-r--r--maintenance/ibm_db2/patch-external_user.sql7
-rw-r--r--maintenance/ibm_db2/patch-ipb_allow_usertalk.sql23
-rw-r--r--maintenance/ibm_db2/patch-iw_api-field.sql1
-rw-r--r--maintenance/ibm_db2/patch-iw_api_and_wikiid.sql8
-rw-r--r--maintenance/ibm_db2/patch-iw_wikiid-field.sql1
-rw-r--r--maintenance/ibm_db2/patch-iwlinks.sql7
-rw-r--r--maintenance/ibm_db2/patch-l10n_cache.sql8
-rw-r--r--maintenance/ibm_db2/patch-log_search-rename-index.sql8
-rw-r--r--maintenance/ibm_db2/patch-log_search.sql8
-rw-r--r--maintenance/ibm_db2/patch-log_user_text.sql17
-rw-r--r--maintenance/ibm_db2/patch-module_deps.sql6
-rw-r--r--maintenance/ibm_db2/patch-msg_resource.sql8
-rw-r--r--maintenance/ibm_db2/patch-msg_resource_links.sql6
-rw-r--r--maintenance/ibm_db2/patch-rd_interwiki.sql8
-rw-r--r--maintenance/ibm_db2/patch-ss_active_users.sql11
-rw-r--r--maintenance/ibm_db2/patch-ul_value.sql3
-rw-r--r--maintenance/ibm_db2/patch-uq61_msg_resource_links.sql7
-rw-r--r--maintenance/ibm_db2/patch-uq81_msg_resource.sql7
-rw-r--r--maintenance/ibm_db2/patch-uq96_module_deps.sql7
-rw-r--r--maintenance/ibm_db2/patch-user_properties.sql10
-rw-r--r--maintenance/ibm_db2/tables.sql78
-rw-r--r--maintenance/importDump.php162
-rw-r--r--maintenance/importImages.inc18
-rw-r--r--maintenance/importImages.php29
-rw-r--r--maintenance/importSiteScripts.php76
-rw-r--r--maintenance/importTextFile.php15
-rw-r--r--maintenance/importUseModWiki.php533
-rw-r--r--maintenance/importUseModWikipedia.php892
-rw-r--r--maintenance/initEditCount.php2
-rw-r--r--maintenance/initStats.php2
-rw-r--r--maintenance/install-utils.inc31
-rw-r--r--maintenance/install.php14
-rw-r--r--maintenance/jsparse.php72
-rw-r--r--maintenance/lag.php17
-rw-r--r--maintenance/language/StatOutputs.php25
-rw-r--r--maintenance/language/checkDupeMessages.php28
-rw-r--r--maintenance/language/checkExtensions.php15
-rw-r--r--maintenance/language/checkLanguage.inc23
-rw-r--r--maintenance/language/checkLanguage.php15
-rw-r--r--maintenance/language/function-list.php15
-rw-r--r--maintenance/language/generateCollationData.php11
-rw-r--r--maintenance/language/generateNormalizerData.php21
-rw-r--r--maintenance/language/languages.inc19
-rw-r--r--maintenance/language/messageTypes.inc228
-rw-r--r--maintenance/language/messages.inc394
-rw-r--r--maintenance/language/rebuildLanguage.php26
-rw-r--r--maintenance/language/transstat.php20
-rw-r--r--maintenance/language/validate.php17
-rw-r--r--maintenance/language/writeMessagesArray.inc15
-rw-r--r--maintenance/mcc.php17
-rw-r--r--maintenance/mergeMessageFileList.php23
-rw-r--r--maintenance/migrateUserGroup.php2
-rw-r--r--maintenance/minify.php18
-rw-r--r--maintenance/moveBatch.php2
-rw-r--r--maintenance/mssql/tables.sql12
-rw-r--r--maintenance/mwdocgen.php51
-rw-r--r--maintenance/namespaceDupes.php13
-rw-r--r--maintenance/nextJobDB.php85
-rw-r--r--maintenance/nukePage.php1
-rw-r--r--maintenance/oracle/alterSharedConstraints.php90
-rw-r--r--maintenance/oracle/archives/patch-config.sql8
-rw-r--r--maintenance/oracle/archives/patch-up_property.sql3
-rw-r--r--maintenance/oracle/archives/patch-uploadstash.sql25
-rw-r--r--maintenance/oracle/archives/patch-user_email_index.sql4
-rw-r--r--maintenance/oracle/archives/patch-user_former_groups.sql9
-rw-r--r--maintenance/oracle/archives/patch_rebuild_dupfunc.sql146
-rw-r--r--maintenance/oracle/tables.sql103
-rw-r--r--maintenance/orphans.php2
-rw-r--r--maintenance/ourusers.php15
-rw-r--r--maintenance/populateCategory.php20
-rw-r--r--maintenance/populateLogSearch.php10
-rw-r--r--maintenance/populateLogUsertext.php4
-rw-r--r--maintenance/populateParentId.php2
-rw-r--r--maintenance/populateRevisionLength.php4
-rw-r--r--maintenance/populateSha1.php4
-rw-r--r--maintenance/postgres/archives/patch-user_former_groups.sql5
-rw-r--r--maintenance/postgres/compare_schemas.pl6
-rw-r--r--maintenance/postgres/mediawiki_mysql2postgres.pl26
-rw-r--r--maintenance/postgres/tables.sql41
-rw-r--r--maintenance/preprocessDump.php86
-rw-r--r--maintenance/preprocessorFuzzTest.php35
-rw-r--r--maintenance/protect.php2
-rw-r--r--maintenance/proxy_check.php54
-rw-r--r--maintenance/purgeList.php78
-rw-r--r--maintenance/purgeOldText.inc16
-rw-r--r--maintenance/purgeParserCache.php43
-rw-r--r--maintenance/purgeStaleMemcachedText.php34
-rw-r--r--maintenance/reassignEdits.php4
-rw-r--r--maintenance/rebuildFileCache.php25
-rw-r--r--maintenance/rebuildImages.php13
-rw-r--r--maintenance/rebuildInterwiki.php34
-rw-r--r--maintenance/rebuildLocalisationCache.php15
-rw-r--r--maintenance/rebuildall.php3
-rw-r--r--maintenance/rebuildtextindex.php9
-rw-r--r--maintenance/refreshImageMetadata.php199
-rw-r--r--maintenance/refreshLinks.php88
-rw-r--r--maintenance/resetUserTokens.php73
-rw-r--r--maintenance/runBatchedQuery.php5
-rw-r--r--maintenance/runJobs.php13
-rw-r--r--maintenance/sqlite.inc21
-rw-r--r--maintenance/sqlite.php9
-rw-r--r--maintenance/sqlite/archives/patch-archive_kill_ar_page_revid.sql3
-rw-r--r--maintenance/sqlite/archives/patch-rename-iwl_prefix.sql2
-rw-r--r--maintenance/stats.php44
-rw-r--r--maintenance/storage/checkStorage.php16
-rw-r--r--maintenance/storage/compressOld.inc300
-rw-r--r--maintenance/storage/compressOld.php398
-rw-r--r--maintenance/storage/fixBug20757.php23
-rw-r--r--maintenance/storage/moveToExternal.php17
-rw-r--r--maintenance/storage/recompressTracked.php22
-rw-r--r--maintenance/storage/resolveStubs.php20
-rw-r--r--maintenance/storage/storageTypeStats.php19
-rw-r--r--maintenance/storage/testCompression.php20
-rw-r--r--maintenance/storage/trackBlobs.php26
-rw-r--r--maintenance/tables.sql92
-rw-r--r--maintenance/tests/.svnignore6
-rw-r--r--maintenance/tests/RunSeleniumTests.php220
-rw-r--r--maintenance/tests/parser/ExtraParserTests.txtbin1261 -> 0 bytes
-rw-r--r--maintenance/tests/parser/parserTest.inc1305
-rw-r--r--maintenance/tests/parser/parserTests.txt8315
-rw-r--r--maintenance/tests/parser/parserTestsParserHook.php46
-rw-r--r--maintenance/tests/parser/parserTestsStaticParserHook.php58
-rw-r--r--maintenance/tests/parserTests.php92
-rw-r--r--maintenance/tests/selenium/Selenium.php190
-rw-r--r--maintenance/tests/selenium/SeleniumConfig.php88
-rw-r--r--maintenance/tests/selenium/SeleniumLoader.php9
-rw-r--r--maintenance/tests/selenium/SeleniumServerManager.php239
-rw-r--r--maintenance/tests/selenium/SeleniumTestCase.php103
-rw-r--r--maintenance/tests/selenium/SeleniumTestConsoleLogger.php25
-rw-r--r--maintenance/tests/selenium/SeleniumTestHTMLLogger.php36
-rw-r--r--maintenance/tests/selenium/SeleniumTestListener.php68
-rw-r--r--maintenance/tests/selenium/SeleniumTestSuite.php46
-rw-r--r--maintenance/tests/selenium/data/Wikipedia-logo-v2-de.pngbin21479 -> 0 bytes
-rw-r--r--maintenance/tests/selenium/selenium_settings.ini.php52.sample23
-rw-r--r--maintenance/tests/selenium/selenium_settings.ini.sample32
-rw-r--r--maintenance/tests/selenium/selenium_settings_grid.ini.sample14
-rw-r--r--maintenance/tests/selenium/suites/AddContentToNewPageTestCase.php182
-rw-r--r--maintenance/tests/selenium/suites/AddNewPageTestCase.php65
-rw-r--r--maintenance/tests/selenium/suites/CreateAccountTestCase.php114
-rw-r--r--maintenance/tests/selenium/suites/DeletePageAdminTestCase.php89
-rw-r--r--maintenance/tests/selenium/suites/EmailPasswordTestCase.php81
-rw-r--r--maintenance/tests/selenium/suites/MediaWikExtraTestSuite.php20
-rw-r--r--maintenance/tests/selenium/suites/MediaWikiEditorConfig.php47
-rw-r--r--maintenance/tests/selenium/suites/MediaWikiEditorTestSuite.php18
-rw-r--r--maintenance/tests/selenium/suites/MediawikiCoreSmokeTestCase.php69
-rw-r--r--maintenance/tests/selenium/suites/MediawikiCoreSmokeTestSuite.php19
-rw-r--r--maintenance/tests/selenium/suites/MovePageTestCase.php117
-rw-r--r--maintenance/tests/selenium/suites/MyContributionsTestCase.php76
-rw-r--r--maintenance/tests/selenium/suites/MyWatchListTestCase.php73
-rw-r--r--maintenance/tests/selenium/suites/PageDeleteTestSuite.php16
-rw-r--r--maintenance/tests/selenium/suites/PageSearchTestCase.php105
-rw-r--r--maintenance/tests/selenium/suites/PreviewPageTestCase.php53
-rw-r--r--maintenance/tests/selenium/suites/SavePageTestCase.php58
-rw-r--r--maintenance/tests/selenium/suites/SimpleSeleniumConfig.php15
-rw-r--r--maintenance/tests/selenium/suites/SimpleSeleniumTestCase.php30
-rw-r--r--maintenance/tests/selenium/suites/SimpleSeleniumTestSuite.php26
-rw-r--r--maintenance/tests/selenium/suites/UserPreferencesTestCase.php179
-rw-r--r--maintenance/tests/testHelpers.inc652
-rw-r--r--maintenance/undelete.php23
-rw-r--r--maintenance/update.php26
-rw-r--r--maintenance/updateArticleCount.php66
-rw-r--r--maintenance/updateCollation.php62
-rw-r--r--maintenance/updateDoubleWidthSearch.php3
-rw-r--r--maintenance/updateRestrictions.php4
-rw-r--r--maintenance/updateSearchIndex.php11
-rw-r--r--maintenance/updateSpecialPages.php25
-rw-r--r--maintenance/upgrade1_5.php31
-rw-r--r--maintenance/userDupes.inc40
-rw-r--r--maintenance/userOptions.inc17
-rw-r--r--maintenance/userOptions.php15
-rw-r--r--maintenance/users.sql12
-rw-r--r--maintenance/waitForSlave.php2
-rw-r--r--maintenance/wikipedia-interwiki.sql1
-rw-r--r--maintenance/wiktionary-interwiki.sql1
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 &#1234; 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 &#1234; 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
deleted file mode 100644
index 66b8032d..00000000
--- a/maintenance/tests/parser/ExtraParserTests.txt
+++ /dev/null
Binary files differ
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>--&gt;
-</p>
-!! end
-
-!! test
-Comment semantics: nesting
-!! input
-<!--<!-- no, we're not going to do anything fancy here -->-->
-!! result
-<p>--&gt;
-</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>
-&lt;b&gt;
-&lt;cite&gt;
-&lt;em&gt;
-</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>
-&lt;nowiki&gt;
-</pre>
-<pre>
-
-</pre>
-<pre>&lt;nowiki&gt;Foo&lt;/nowiki&gt;</pre>
-
-!! end
-
-!! test
-<nowiki> and <pre> preference (first one wins)
-!! input
-<pre>
-<nowiki>
-</pre>
-</nowiki>
-</pre>
-
-<nowiki>
-<pre>
-<nowiki>
-</pre>
-</nowiki>
-</pre>
-
-!! result
-<pre>
-&lt;nowiki&gt;
-</pre>
-<p>&lt;/nowiki&gt;
-&lt;/pre&gt;
-</p><p>
-&lt;pre&gt;
-&lt;nowiki&gt;
-&lt;/pre&gt;
-
-&lt;/pre&gt;
-</p>
-!! end
-
-
-###
-### Definition lists
-###
-!! test
-Simple definition
-!! input
-; name : Definition
-!! result
-<dl><dt> name&#160;</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>&#160;</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&amp;action=edit&amp;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&#160;: 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&#160;</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 &amp; use: http://x&y
-!! result
-<p>Old &amp; use: <a href="http://x&amp;y" class="external free" rel="nofollow">http://x&amp;y</a>
-</p>
-!! end
-
-!! test
-External links: encoded ampersand
-!! input
-Old &amp; use: http://x&amp;y
-!! result
-<p>Old &amp; use: <a href="http://x&amp;y" class="external free" rel="nofollow">http://x&amp;y</a>
-</p>
-!! end
-
-!! test
-External links: encoded equals (bug 6102)
-!! input
-http://example.com/?foo&#61;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 &amp; use: [http://x&y]
-!! result
-<p>Old &amp; use: <a href="http://x&amp;y" class="external autonumber" rel="nofollow">[1]</a>
-</p>
-!! end
-
-!! test
-External links: [encoded ampersand]
-!! input
-Old &amp; use: [http://x&amp;y]
-!! result
-<p>Old &amp; use: <a href="http://x&amp;y" class="external autonumber" rel="nofollow">[1]</a>
-</p>
-!! end
-
-!! test
-External links: [encoded equals] (bug 6102)
-!! input
-[http://example.com/?foo&#61;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&zwnj;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&zwnj;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>&lt;hello&gt;
-</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">&lt;hello&gt; 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&amp;action=edit&amp;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)&amp;action=edit&amp;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;amp=&amp;
-</p>
-!! end
-
-!! test
-URL-encoding in URL functions (multiple parameters)
-!! input
-{{localurl:Some page|q=?&amp=&}}
-!! result
-<p>/index.php?title=Some_page&amp;q=?&amp;amp=&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
-|-
-! &times; !! 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> &times; </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
-| &alpha;
-|
-{| bgcolor=#ABCDEF border=2
-|nested
-|-
-|table
-|}
-|the original table again
-|}
-!! result
-<table border="1">
-<tr>
-<td> &alpha;
-</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)"&gt;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;redlink=1" class="new" title="Talk:Parser testing (page does not exist)">Talk:Parser testing</a>, <a href="/index.php?title=Meta:Disclaimers&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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">#&lt;</a><a href="#.3E">#&gt;</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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;wpDestFile=Denys_Savchenko_%27%27Pentecoste%27%27.jpg" class="new" title="File:Denys Savchenko &#39;&#39;Pentecoste&#39;&#39;.jpg">File:Denys Savchenko <i>Pentecoste</i>.jpg</a>
-</p><p><a href="/index.php?title=%27%27Pentecoste%27%27&amp;action=edit&amp;redlink=1" class="new" title="''Pentecoste'' (page does not exist)">''Pentecoste''</a>
-</p><p><a href="/index.php?title=%27%27Pentecoste%27%27&amp;action=edit&amp;redlink=1" class="new" title="''Pentecoste'' (page does not exist)">Pentecoste</a>
-</p><p><a href="/index.php?title=%27%27Pentecoste%27%27&amp;action=edit&amp;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&amp;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
-[[Bu&#103;462]] [[Bug462]]
-!! result
-<p><strong class="selflink">Bu&#103;462</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
-[[&nbsp; Main &nbsp; Page &nbsp;]]
-!! result
-<p><a href="/wiki/Main_Page" title="Main Page">&nbsp; Main &nbsp; Page &nbsp;</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&#0355;a]]
-*[[Wikipedia:ro:Olteni&#355;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&#355;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&#355;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;wpDestFile=Noimage.png" class="new" title="File:Noimage.png">File:Noimage.png</a> <div class="thumbcaption"><a href="/index.php?title=No_link&amp;action=edit&amp;redlink=1" class="new" title="No link (page does not exist)">link</a> <a href="/index.php?title=No_link&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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 &lt;math&gt; 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>&lt;math&gt;2+2&lt;/math&gt;</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="&amp; &lt; &gt; &quot;"><img alt="&amp; &lt; &gt; &quot;" src="http://example.com/images/3/3a/Foobar.jpg" width="1941" height="220" /></a>
-</p>
-!! end
-
-!! test
-BUG 499: Alt text should have &#1234;, not &amp;1234;
-!! input
-[[Image:foobar.jpg|&#9792;]]
-!! 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&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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:&#39135;&#21697;]]
-!! 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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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 &gt; text</span></a></li>
-<li class="toclevel-1 tocsection-2"><a href="#text_.3C_text"><span class="tocnumber">2</span> <span class="toctext">text &lt; text</span></a></li>
-<li class="toclevel-1 tocsection-3"><a href="#text_.26_text"><span class="tocnumber">3</span> <span class="toctext">text &amp; 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&amp;action=edit&amp;section=1" title="Edit section: text > text">edit</a>]</span> <span class="mw-headline" id="text_.3E_text"> text &gt; text </span></h2>
-<p>section 1
-</p>
-<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Edit section: text &lt; text">edit</a>]</span> <span class="mw-headline" id="text_.3C_text"> text &lt; text </span></h2>
-<p>section 2
-</p>
-<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;section=3" title="Edit section: text &amp; text">edit</a>]</span> <span class="mw-headline" id="text_.26_text"> text &amp; text </span></h2>
-<p>section 3
-</p>
-<h2><span class="editsection">[<a href="/index.php?title=Parser_test&amp;action=edit&amp;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&amp;action=edit&amp;section=5" title="Edit section: text &quot; 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&amp;T?
-</p>
-!! end
-
-!! test
-text with character entity: eacute
-!! input
-I always thought &eacute; was a cute letter.
-!! result
-<p>I always thought &eacute; 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 &amp;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&lt;div style="display:none"&gt;" onmouseover="alert(document.cookie)" onfoo="&lt;/div&gt;</a>
-
-!! end
-
-!! test
-Media link to nonexistent file (bug 1702)
-!! input
-[[Media:No such.jpg]]
-!! result
-<p><a href="/index.php?title=Special:Upload&amp;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&amp;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&amp;action=edit&amp;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&amp;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
-[[&#xFB2E;]]
-[[&#x5d0;&#x5b7;]]
-[[&#x5d0;ַ]]
-[[א&#x5b7;]]
-[[אַ]]
-!! result
-<p><a href="/wiki/%D7%90%D6%B7" title="אַ">&#xfb2e;</a>
-<a href="/wiki/%D7%90%D6%B7" title="אַ">&#x5d0;&#x5b7;</a>
-<a href="/wiki/%D7%90%D6%B7" title="אַ">&#x5d0;ַ</a>
-<a href="/wiki/%D7%90%D6%B7" title="אַ">א&#x5b7;</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;">&lt;script&gt;alert(document.cookie)&lt;/script&gt;"&gt;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;">&lt;script&gt;alert(document.cookie)&lt;/script&gt;"&gt;Magic div</div>
-
-!! end
-
-!! test
-Bug 2304: HTML attribute safety (link)
-!! input
-<div title="[[Main Page]]"></div>
-!! result
-<div title="&#91;&#91;Main Page]]"></div>
-
-!! end
-
-!! test
-Bug 2304: HTML attribute safety (italics)
-!! input
-<div title="''foobar''"></div>
-!! result
-<div title="&#39;&#39;foobar&#39;&#39;"></div>
-
-!! end
-
-!! test
-Bug 2304: HTML attribute safety (bold)
-!! input
-<div title="'''foobar'''"></div>
-!! result
-<div title="&#39;&#39;&#39;foobar&#39;&#39;&#39;"></div>
-
-!! end
-
-
-!! test
-Bug 2304: HTML attribute safety (ISBN)
-!! input
-<div title="ISBN 1234567890"></div>
-!! result
-<div title="&#73;SBN 1234567890"></div>
-
-!! end
-
-!! test
-Bug 2304: HTML attribute safety (RFC)
-!! input
-<div title="RFC 1234"></div>
-!! result
-<div title="&#82;FC 1234"></div>
-
-!! end
-
-!! test
-Bug 2304: HTML attribute safety (PMID)
-!! input
-<div title="PMID 1234567890"></div>
-!! result
-<div title="&#80;MID 1234567890"></div>
-
-!! end
-
-!! test
-Bug 2304: HTML attribute safety (web link)
-!! input
-<div title="http://example.com/"></div>
-!! result
-<div title="http&#58;//example.com/"></div>
-
-!! end
-
-!! test
-Bug 2304: HTML attribute safety (named web link)
-!! input
-<div title="[http://example.com/ link]"></div>
-!! result
-<div title="&#91;http&#58;//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>&lt;math&gt;&lt;script&gt;alert(document.cookies);&lt;/script&gt;&lt;/math&gt;
-</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(&#39;boo&#39;))">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(&#39;boo4&#39;))">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\&#10;rl(test.jpg);"></div>
-!! result
-<div style="/* insecure input */"></div>
-
-!! end
-
-!! test
-CSS line continuation 2
-!! input
-<div style="background-image: u\&#13;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: &#10;#00FF00">-</div>
-!! result
-<div style="background: &#10;#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>&lt;/tag&gt;
-
-!! 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
-&lt;/tag&gt;
-</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>&lt;/s&gt;
-</p>
-!! end
-
-!! test
-Sanitizer: Closing of closed but not open table tags
-!! input
-Table not started</td></tr></table>
-!! result
-<p>Table not started&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
-</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>&lt;font id="bug" /&gt;Centered text</center>
-<div>&lt;font id="bug2" /&gt;In div text</div>
-
-!! end
-
-#
-#
-#
-
-!! test
-Punctuation: nbsp before exclamation
-!! input
-C'est grave !
-!! result
-<p>C'est grave&#160;!
-</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&amp;action=edit&amp;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&amp;action=edit&amp;section=1" title="Edit section: a">edit</a>]</span> <span class="mw-headline" id="a">a</span></h2>
-<table style="&#95;_TOC&#95;_">
-<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="&#124;">}}}} &gt;
-<br style="onmouseover=&#39;alert(document.cookie);&#39;" />
-
-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="&#124;&#124;">
-# }}}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>&lt;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="&#10;"></pre>
-!! result
-<pre dir="&#10;"></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>&lt;/body&gt;&lt;/x&gt;
-</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&amp;action=edit&amp;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 &#x0A; in URLs
-!! input
-**irc://&#x0A;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&amp;action=edit&amp;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
-&#x4A;&#x061;&#x0076;&#x00061;&#x000053;&#x0000063;&#114;&#x0000069;&#00000112;&#x0000000074;
-!! result
-<p>&#x4a;&#x61;&#x76;&#x61;&#x53;&#x63;&#114;&#x69;&#112;&#x74;
-</p>
-!! end
-
-!! test
-__FORCETOC__ override
-!! input
-__NEWSECTIONLINK__
-__FORCETOC__
-!! result
-<p><br />
-</p>
-!! end
-
-!! test
-ISBN code coverage
-!! input
-ISBN 978-0-1234-56&#x20;789
-!! result
-<p><a href="/wiki/Special:BookSources/9780123456" class="internal mw-magiclink-isbn">ISBN 978-0-1234-56</a>&#x20;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&amp;action=edit&amp;redlink=1" class="new" title="RFC 1234 (page does not exist)">RFC 1234</a>
-</p>
-!! end
-
-!! test
-RFC code coverage
-!! input
-RFC 983&#x20;987
-!! result
-<p><a href="http://tools.ietf.org/html/rfc983" class="external mw-magiclink-rfc">RFC 983</a>&#x20;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|&param2=|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&amp;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>&lt;html&gt;&lt;script&gt;alert(1);&lt;/script&gt;&lt;/html&gt;
-</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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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////&amp;action=edit&amp;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 &#160;</dt><dd> def
-</dd><dt> title&#160;</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>&#91;&#91;:Template:Msg&#93;&#93;
-</p>
-!! end
-
-!! test
-RAW magic word
-!! input
-{{RAW:QUERTY}}
-!! result
-<p><a href="/index.php?title=Template:QUERTY&amp;action=edit&amp;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>&gt;&lt;&gt;
-</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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&&amp;]] ===
-{{anchorencode: _ +:.3A%3A&&amp;]] }}
-__NOEDITSECTION__
-!! result
-<h3> <span class="mw-headline" id=".2B:.3A.253A.26.26.5D.5D"> _ +:.3A%3A&amp;&amp;]] </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&amp;action=edit&amp;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|=&gt;}-" to "=>"
-!! options
-language=sr
-!! input
--{R|=&gt;}-
-!! result
-<p>=&gt;
-</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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;redlink=1" class="new" title="2009 (page does not exist)">2009</a>-<a href="/index.php?title=March_24&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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&amp;action=edit&amp;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">&nbsp;</div>
-<div style="width:50%&nbsp;!important">&nbsp;</div>
-<div style="width:50%&#160;!important">&nbsp;</div>
-<div style="border : solid;">&nbsp;</div>
-!! result
-<div style="width:50% !important">&nbsp;</div>
-<div style="width:50% !important">&nbsp;</div>
-<div style="width:50% !important">&nbsp;</div>
-<div style="border&#160;: solid;">&nbsp;</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
deleted file mode 100644
index 70385243..00000000
--- a/maintenance/tests/selenium/data/Wikipedia-logo-v2-de.png
+++ /dev/null
Binary files differ
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),