From c1f9b1f7b1b77776192048005dcc66dcf3df2bfb Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Sat, 27 Dec 2014 15:41:37 +0100 Subject: Update to MediaWiki 1.24.1 --- maintenance/7zip.inc | 4 +- maintenance/Maintenance.php | 275 +- maintenance/README | 5 +- maintenance/archives/patch-drop-rc_cur_time.sql | 2 + .../archives/patch-fa_major_mime-chemical.sql | 3 + maintenance/archives/patch-hitcounter.sql | 2 +- maintenance/archives/patch-il_from_namespace.sql | 4 + .../archives/patch-img_major_mime-chemical.sql | 3 + .../patch-logging_user_text_time_index.sql | 1 + .../patch-logging_user_text_type_time_index.sql | 1 + maintenance/archives/patch-mimesearch-indexes.sql | 2 +- .../archives/patch-oi_major_mime-chemical.sql | 3 + maintenance/archives/patch-page_lang.sql | 2 + maintenance/archives/patch-page_links_updated.sql | 2 + maintenance/archives/patch-pl_from_namespace.sql | 4 + maintenance/archives/patch-pp_sortkey.sql | 8 + maintenance/archives/patch-profiling.sql | 2 +- maintenance/archives/patch-rc_source.sql | 16 + maintenance/archives/patch-restructure.sql | 146 - maintenance/archives/patch-tl_from_namespace.sql | 4 + maintenance/archives/patch-uploadstash.sql | 4 +- .../archives/patch-user_password_expire.sql | 3 + maintenance/archives/patch-val_ip.sql | 4 - maintenance/archives/patch-validate.sql | 13 - ...-watchlist-user-notificationtimestamp-index.sql | 4 + maintenance/archives/upgradeLogging.php | 6 +- maintenance/attachLatest.php | 14 +- maintenance/backup.inc | 185 +- maintenance/backupPrefetch.inc | 74 +- maintenance/backupTextPass.inc | 210 +- maintenance/benchmarks/Benchmarker.php | 21 +- maintenance/benchmarks/bench_HTTP_HTTPS.php | 7 +- maintenance/benchmarks/bench_delete_truncate.php | 9 +- maintenance/benchmarks/bench_if_switch.php | 89 +- maintenance/benchmarks/bench_strtr_str_replace.php | 11 +- maintenance/benchmarks/bench_utf8_title_check.php | 29 +- maintenance/benchmarks/bench_wfBaseConvert.php | 12 +- maintenance/benchmarks/bench_wfIsWindows.php | 12 +- maintenance/benchmarks/benchmarkHooks.php | 8 +- maintenance/benchmarks/benchmarkParse.php | 174 + maintenance/benchmarks/benchmarkPurge.php | 11 +- maintenance/cdb.php | 15 +- maintenance/checkBadRedirects.php | 2 +- maintenance/checkImages.php | 4 +- maintenance/checkLess.php | 48 +- maintenance/checkSyntax.php | 63 +- maintenance/checkUsernames.php | 2 +- maintenance/cleanupAncientTables.php | 9 +- maintenance/cleanupCaps.php | 5 +- maintenance/cleanupImages.php | 20 +- maintenance/cleanupRemovedModules.php | 10 +- maintenance/cleanupSpam.php | 32 +- maintenance/cleanupTitles.php | 10 +- maintenance/cleanupUploadStash.php | 52 +- maintenance/cleanupWatchlist.php | 14 +- maintenance/commandLine.inc | 7 + maintenance/compareParserCache.php | 104 + maintenance/compareParsers.php | 52 +- maintenance/convertLinks.php | 124 +- maintenance/convertUserOptions.php | 48 +- maintenance/copyFileBackend.php | 49 +- maintenance/copyJobQueue.php | 11 +- maintenance/createAndPromote.php | 14 +- maintenance/cssjanus/COPYING | 13 - maintenance/cssjanus/LICENSE | 202 - maintenance/cssjanus/README | 91 - maintenance/cssjanus/cssjanus.py | 574 --- maintenance/cssjanus/csslex.py | 114 - maintenance/deleteArchivedFiles.inc | 14 +- maintenance/deleteArchivedFiles.php | 1 + maintenance/deleteArchivedRevisions.inc | 3 +- maintenance/deleteArchivedRevisions.php | 6 +- maintenance/deleteBatch.php | 14 +- maintenance/deleteDefaultMessages.php | 3 +- maintenance/deleteEqualMessages.php | 25 +- maintenance/deleteImageMemcached.php | 8 +- maintenance/deleteOrphanedRevisions.php | 7 +- maintenance/deleteRevision.php | 59 +- maintenance/dev/includes/php.sh | 2 +- maintenance/dev/includes/router.php | 4 +- maintenance/dictionary/mediawiki.dic | 160 +- maintenance/doMaintenance.php | 28 +- maintenance/dumpBackup.php | 4 +- maintenance/dumpIterator.php | 22 +- maintenance/dumpLinks.php | 6 +- maintenance/dumpSisterSites.php | 2 +- maintenance/dumpTextPass.php | 5 +- maintenance/dumpUploads.php | 6 +- maintenance/edit.php | 21 +- maintenance/eraseArchivedFile.php | 2 +- maintenance/eval.php | 22 +- maintenance/fetchText.php | 20 +- maintenance/findHooks.php | 133 +- maintenance/findMissingFiles.php | 115 + maintenance/fixDoubleRedirects.php | 17 +- maintenance/fixExtLinksProtocolRelative.php | 15 +- maintenance/fixSlaveDesync.php | 46 +- maintenance/fixTimestamps.php | 9 +- maintenance/fixUserRegistration.php | 14 +- maintenance/fuzz-tester.php | 2692 ------------- maintenance/generateJsonI18n.php | 287 ++ maintenance/generateSitemap.php | 120 +- maintenance/getConfiguration.php | 12 +- maintenance/getSlaveServer.php | 1 + maintenance/getText.php | 5 +- maintenance/importDump.php | 37 +- maintenance/importImages.inc | 33 +- maintenance/importImages.php | 124 +- maintenance/importSiteScripts.php | 4 +- maintenance/importTextFile.php | 110 - maintenance/initEditCount.php | 2 +- maintenance/initSiteStats.php | 23 +- maintenance/install.php | 99 +- maintenance/interwiki.list | 89 +- maintenance/interwiki.sql | 90 +- maintenance/jsduck/CustomTags.rb | 116 + maintenance/jsduck/MetaTags.rb | 69 - maintenance/jsduck/categories.json | 55 +- maintenance/jsduck/config.json | 47 +- maintenance/jsduck/eg-iframe.html | 154 +- maintenance/lag.php | 4 +- maintenance/language/StatOutputs.php | 47 +- maintenance/language/checkDupeMessages.php | 3 +- maintenance/language/checkLanguage.inc | 262 +- maintenance/language/countMessages.php | 72 - maintenance/language/generateCollationData.php | 19 +- maintenance/language/generateNormalizerData.php | 159 - maintenance/language/generateNormalizerDataAr.php | 133 + maintenance/language/generateNormalizerDataMl.php | 69 + maintenance/language/generateUtf8Case.php | 129 + maintenance/language/langmemusage.php | 2 +- maintenance/language/languages.inc | 209 +- maintenance/language/listVariants.php | 73 + maintenance/language/messageTypes.inc | 872 ---- maintenance/language/messages.inc | 4239 -------------------- maintenance/language/rebuildLanguage.php | 126 - maintenance/language/transstat.php | 41 +- maintenance/language/validate.php | 64 - maintenance/language/writeMessagesArray.inc | 283 -- maintenance/language/zhtable/Makefile.py | 70 +- .../language/zhtable/trad2simp_supp_unset.manual | 0 maintenance/language/zhtable/tradphrases.manual | 1 + maintenance/locking/LockServerDaemon.php | 640 --- maintenance/mctest.php | 19 +- maintenance/mergeMessageFileList.php | 23 +- maintenance/minify.php | 3 +- maintenance/moveBatch.php | 10 +- maintenance/mssql/archives/named_constraints.sql | 38 + .../archives/patch-fa_major_mime-chemical.sql | 4 + .../archives/patch-img_major_mime-chemical.sql | 4 + .../archives/patch-oi_major_mime-chemical.sql | 4 + .../mssql/archives/patch-page_page_lang.sql | 1 + .../mssql/archives/patch-user_password_expires.sql | 1 + maintenance/mssql/tables.sql | 1592 +++++--- maintenance/mssql/update-keys.sql | 31 + maintenance/mwdocgen.php | 4 +- maintenance/mwjsduck-gen | 10 +- maintenance/namespaceDupes.php | 101 +- maintenance/nextJobDB.php | 119 - maintenance/nukeNS.php | 2 +- maintenance/oracle/alterSharedConstraints.php | 38 +- .../patch-logging_user_text_time_index.sql | 4 + .../patch-logging_user_text_type_time_index.sql | 4 + .../oracle/archives/patch-page-page_lang.sql | 3 + .../oracle/archives/patch-page_links_updated.sql | 4 + maintenance/oracle/archives/patch-rc_source.sql | 3 + .../oracle/archives/patch-user_password_expire.sql | 3 + .../oracle/archives/patch_16_17_schema_changes.sql | 2 +- maintenance/oracle/tables.sql | 21 +- maintenance/oracle/update-keys.sql | 29 + maintenance/orphans.php | 36 +- maintenance/pageExists.php | 54 + maintenance/parse.php | 22 +- maintenance/patchSql.php | 5 +- maintenance/populateBacklinkNamespace.php | 97 + maintenance/populateBloomCache.php | 78 + maintenance/populateCategory.php | 39 +- maintenance/populateFilearchiveSha1.php | 1 + maintenance/populateImageSha1.php | 25 +- maintenance/populateLogSearch.php | 12 +- maintenance/populateLogUsertext.php | 2 + maintenance/populateParentId.php | 15 +- maintenance/populateRecentChangesSource.php | 107 + maintenance/populateRevisionLength.php | 111 +- maintenance/populateRevisionSha1.php | 32 +- maintenance/postgres/mediawiki_mysql2postgres.pl | 3 +- maintenance/postgres/tables.sql | 29 +- maintenance/postgres/update-keys.sql | 29 + maintenance/preprocessDump.php | 8 +- maintenance/preprocessorFuzzTest.php | 23 +- maintenance/pruneFileCache.php | 8 +- maintenance/purgeChangedFiles.php | 18 +- maintenance/purgeChangedPages.php | 4 +- maintenance/purgeList.php | 13 +- maintenance/purgeOldText.inc | 78 - maintenance/purgeParserCache.php | 5 +- maintenance/reassignEdits.php | 53 +- maintenance/rebuildFileCache.php | 17 +- maintenance/rebuildImages.php | 20 +- maintenance/rebuildLocalisationCache.php | 10 +- maintenance/rebuildall.php | 6 +- maintenance/rebuildrecentchanges.php | 97 +- maintenance/rebuildtextindex.php | 10 +- maintenance/refreshImageMetadata.php | 65 +- maintenance/refreshLinks.php | 24 +- maintenance/removeUnusedAccounts.php | 16 +- maintenance/renderDump.php | 5 +- maintenance/resetUserTokens.php | 22 +- maintenance/resources/update-oojs-ui.sh | 95 + maintenance/resources/update-oojs.sh | 53 + maintenance/rollbackEdits.php | 14 +- maintenance/runBatchedQuery.php | 4 +- maintenance/runJobs.php | 122 +- maintenance/runScript.php | 64 + maintenance/showCacheStats.php | 41 +- maintenance/showJobs.php | 11 +- maintenance/showSiteStats.php | 7 +- maintenance/sql.php | 35 +- maintenance/sqlite.inc | 8 +- maintenance/sqlite.php | 8 +- maintenance/sqlite/archives/initial-indexes.sql | 4 +- .../sqlite/archives/patch-drop-rc_cur_time.sql | 45 + .../sqlite/archives/patch-page-page_lang.sql | 3 + maintenance/storage/checkStorage.php | 68 +- maintenance/storage/compressOld.php | 99 +- maintenance/storage/fixBug20757.php | 18 +- maintenance/storage/orphanStats.php | 17 +- maintenance/storage/recompressTracked.php | 73 +- maintenance/storage/resolveStubs.php | 13 +- maintenance/storage/testCompression.php | 6 +- maintenance/storage/trackBlobs.php | 4 +- maintenance/syncFileBackend.php | 31 +- maintenance/tables.sql | 73 +- maintenance/term/MWTerm.php | 6 +- maintenance/undelete.php | 2 +- maintenance/update-keys.sql | 29 + maintenance/update.php | 53 +- maintenance/updateArticleCount.php | 10 +- maintenance/updateCollation.php | 20 +- maintenance/updateDoubleWidthSearch.php | 8 +- maintenance/updateRestrictions.php | 15 +- maintenance/updateSearchIndex.php | 14 +- maintenance/updateSpecialPages.php | 76 +- maintenance/userDupes.inc | 38 +- maintenance/userOptions.inc | 45 +- maintenance/userOptions.php | 2 +- maintenance/waitForSlave.php | 1 + maintenance/wrapOldPasswords.php | 126 + 248 files changed, 6826 insertions(+), 13368 deletions(-) create mode 100644 maintenance/archives/patch-drop-rc_cur_time.sql create mode 100644 maintenance/archives/patch-fa_major_mime-chemical.sql create mode 100644 maintenance/archives/patch-il_from_namespace.sql create mode 100644 maintenance/archives/patch-img_major_mime-chemical.sql create mode 100644 maintenance/archives/patch-logging_user_text_time_index.sql create mode 100644 maintenance/archives/patch-logging_user_text_type_time_index.sql create mode 100644 maintenance/archives/patch-oi_major_mime-chemical.sql create mode 100644 maintenance/archives/patch-page_lang.sql create mode 100644 maintenance/archives/patch-page_links_updated.sql create mode 100644 maintenance/archives/patch-pl_from_namespace.sql create mode 100644 maintenance/archives/patch-pp_sortkey.sql create mode 100644 maintenance/archives/patch-rc_source.sql delete mode 100644 maintenance/archives/patch-restructure.sql create mode 100644 maintenance/archives/patch-tl_from_namespace.sql create mode 100644 maintenance/archives/patch-user_password_expire.sql delete mode 100644 maintenance/archives/patch-val_ip.sql delete mode 100644 maintenance/archives/patch-validate.sql create mode 100644 maintenance/archives/patch-watchlist-user-notificationtimestamp-index.sql create mode 100644 maintenance/benchmarks/benchmarkParse.php create mode 100644 maintenance/compareParserCache.php delete mode 100644 maintenance/cssjanus/COPYING delete mode 100644 maintenance/cssjanus/LICENSE delete mode 100644 maintenance/cssjanus/README delete mode 100644 maintenance/cssjanus/cssjanus.py delete mode 100644 maintenance/cssjanus/csslex.py create mode 100644 maintenance/findMissingFiles.php delete mode 100644 maintenance/fuzz-tester.php create mode 100644 maintenance/generateJsonI18n.php delete mode 100644 maintenance/importTextFile.php create mode 100644 maintenance/jsduck/CustomTags.rb delete mode 100644 maintenance/jsduck/MetaTags.rb delete mode 100644 maintenance/language/countMessages.php delete mode 100644 maintenance/language/generateNormalizerData.php create mode 100644 maintenance/language/generateNormalizerDataAr.php create mode 100644 maintenance/language/generateNormalizerDataMl.php create mode 100644 maintenance/language/generateUtf8Case.php create mode 100644 maintenance/language/listVariants.php delete mode 100644 maintenance/language/messageTypes.inc delete mode 100644 maintenance/language/messages.inc delete mode 100644 maintenance/language/rebuildLanguage.php delete mode 100644 maintenance/language/validate.php delete mode 100644 maintenance/language/writeMessagesArray.inc delete mode 100644 maintenance/language/zhtable/trad2simp_supp_unset.manual delete mode 100644 maintenance/locking/LockServerDaemon.php create mode 100644 maintenance/mssql/archives/named_constraints.sql create mode 100644 maintenance/mssql/archives/patch-fa_major_mime-chemical.sql create mode 100644 maintenance/mssql/archives/patch-img_major_mime-chemical.sql create mode 100644 maintenance/mssql/archives/patch-oi_major_mime-chemical.sql create mode 100644 maintenance/mssql/archives/patch-page_page_lang.sql create mode 100644 maintenance/mssql/archives/patch-user_password_expires.sql create mode 100644 maintenance/mssql/update-keys.sql delete mode 100644 maintenance/nextJobDB.php create mode 100644 maintenance/oracle/archives/patch-logging_user_text_time_index.sql create mode 100644 maintenance/oracle/archives/patch-logging_user_text_type_time_index.sql create mode 100644 maintenance/oracle/archives/patch-page-page_lang.sql create mode 100644 maintenance/oracle/archives/patch-page_links_updated.sql create mode 100644 maintenance/oracle/archives/patch-rc_source.sql create mode 100644 maintenance/oracle/archives/patch-user_password_expire.sql create mode 100644 maintenance/oracle/update-keys.sql create mode 100644 maintenance/pageExists.php create mode 100644 maintenance/populateBacklinkNamespace.php create mode 100644 maintenance/populateBloomCache.php create mode 100644 maintenance/populateRecentChangesSource.php create mode 100644 maintenance/postgres/update-keys.sql delete mode 100644 maintenance/purgeOldText.inc create mode 100644 maintenance/resources/update-oojs-ui.sh create mode 100644 maintenance/resources/update-oojs.sh create mode 100644 maintenance/runScript.php create mode 100644 maintenance/sqlite/archives/patch-drop-rc_cur_time.sql create mode 100644 maintenance/sqlite/archives/patch-page-page_lang.sql create mode 100644 maintenance/update-keys.sql create mode 100644 maintenance/wrapOldPasswords.php (limited to 'maintenance') diff --git a/maintenance/7zip.inc b/maintenance/7zip.inc index 590cad23..751a1311 100644 --- a/maintenance/7zip.inc +++ b/maintenance/7zip.inc @@ -3,7 +3,7 @@ * 7z stream wrapper * * Copyright © 2005 Brion Vibber - * http://www.mediawiki.org/ + * https://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 @@ -36,6 +36,7 @@ class SevenZipStream { private function stripPath( $path ) { $prefix = 'mediawiki.compress.7z://'; + return substr( $path, strlen( $prefix ) ); } @@ -91,4 +92,5 @@ class SevenZipStream { return fseek( $this->stream, $offset, $whence ); } } + stream_wrapper_register( 'mediawiki.compress.7z', 'SevenZipStream' ); diff --git a/maintenance/Maintenance.php b/maintenance/Maintenance.php index 30e93c90..8d30df4c 100644 --- a/maintenance/Maintenance.php +++ b/maintenance/Maintenance.php @@ -49,7 +49,6 @@ $maintClass = false; * @ingroup Maintenance */ abstract class Maintenance { - /** * Constants for DB access type * @see Maintenance::getDbType() @@ -115,11 +114,11 @@ abstract class Maintenance { public $fileHandle; /** - * List of all the core maintenance scripts. This is added - * to scripts added by extensions in $wgMaintenanceScripts - * and returned by getMaintenanceScripts() + * Accessible via getConfig() + * + * @var Config */ - protected static $mCoreScripts = null; + private $config; /** * Default constructor. Children should call this *first* if implementing @@ -141,9 +140,16 @@ abstract class Maintenance { * as a standalone class? It checks that the call stack only includes this * function and "requires" (meaning was called from the file scope) * - * @return Boolean + * @return bool */ public static function shouldExecute() { + global $wgCommandLineMode; + + if ( !function_exists( 'debug_backtrace' ) ) { + // If someone has a better idea... + return $wgCommandLineMode; + } + $bt = debug_backtrace(); $count = count( $bt ); if ( $count < 2 ) { @@ -158,6 +164,7 @@ abstract class Maintenance { return false; // previous calls should all be "requires" } } + return true; } @@ -170,14 +177,22 @@ abstract class Maintenance { * Add a parameter to the script. Will be displayed on --help * with the associated description * - * @param $name String: the name of the param (help, version, etc) - * @param $description String: the description of the param to show on --help - * @param $required Boolean: is the param required? - * @param $withArg Boolean: is an argument required with this option? - * @param $shortName String: character to use as short name + * @param string $name The name of the param (help, version, etc) + * @param string $description The description of the param to show on --help + * @param bool $required Is the param required? + * @param bool $withArg Is an argument required with this option? + * @param string $shortName Character to use as short name */ - protected function addOption( $name, $description, $required = false, $withArg = false, $shortName = false ) { - $this->mParams[$name] = array( 'desc' => $description, 'require' => $required, 'withArg' => $withArg, 'shortName' => $shortName ); + 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; } @@ -185,8 +200,8 @@ abstract class Maintenance { /** * Checks to see if a particular param exists. - * @param $name String: the name of the param - * @return Boolean + * @param string $name The name of the param + * @return bool */ protected function hasOption( $name ) { return isset( $this->mOptions[$name] ); @@ -194,9 +209,9 @@ abstract class Maintenance { /** * Get an option, or return the default - * @param $name String: the name of the param - * @param $default Mixed: anything you want, default null - * @return Mixed + * @param string $name The name of the param + * @param mixed $default Anything you want, default null + * @return mixed */ protected function getOption( $name, $default = null ) { if ( $this->hasOption( $name ) ) { @@ -204,15 +219,16 @@ abstract class Maintenance { } else { // Set it so we don't have to provide the default again $this->mOptions[$name] = $default; + return $this->mOptions[$name]; } } /** * Add some args that are needed - * @param $arg String: name of the arg, like 'start' - * @param $description String: short description of the arg - * @param $required Boolean: is this required? + * @param string $arg Name of the arg, like 'start' + * @param string $description Short description of the arg + * @param bool $required Is this required? */ protected function addArg( $arg, $description, $required = true ) { $this->mArgList[] = array( @@ -224,7 +240,7 @@ abstract class Maintenance { /** * Remove an option. Useful for removing options that won't be used in your script. - * @param $name String: the option to remove. + * @param string $name The option to remove. */ protected function deleteOption( $name ) { unset( $this->mParams[$name] ); @@ -232,7 +248,7 @@ abstract class Maintenance { /** * Set the description text. - * @param $text String: the text of the description + * @param string $text The text of the description */ protected function addDescription( $text ) { $this->mDescription = $text; @@ -240,8 +256,8 @@ abstract class Maintenance { /** * Does a given argument exist? - * @param $argId Integer: the integer value (from zero) for the arg - * @return Boolean + * @param int $argId The integer value (from zero) for the arg + * @return bool */ protected function hasArg( $argId = 0 ) { return isset( $this->mArgs[$argId] ); @@ -249,8 +265,8 @@ abstract class Maintenance { /** * Get an argument. - * @param $argId Integer: the integer value (from zero) for the arg - * @param $default Mixed: the default if it doesn't exist + * @param int $argId The integer value (from zero) for the arg + * @param mixed $default The default if it doesn't exist * @return mixed */ protected function getArg( $argId = 0, $default = null ) { @@ -259,7 +275,7 @@ abstract class Maintenance { /** * Set the batch size. - * @param $s Integer: the number of operations to do in a batch + * @param int $s The number of operations to do in a batch */ protected function setBatchSize( $s = 0 ) { $this->mBatchSize = $s; @@ -281,7 +297,7 @@ abstract class Maintenance { /** * Get the script's name - * @return String + * @return string */ public function getName() { return $this->mSelf; @@ -289,10 +305,9 @@ abstract class Maintenance { /** * Return input from stdin. - * @param $len Integer: the number of bytes to read. If null, - * just return the handle. Maintenance::STDIN_ALL returns - * the full length - * @return Mixed + * @param int $len The number of bytes to read. If null, just return the handle. + * Maintenance::STDIN_ALL returns the full length + * @return mixed */ protected function getStdin( $len = null ) { if ( $len == Maintenance::STDIN_ALL ) { @@ -304,6 +319,7 @@ abstract class Maintenance { } $input = fgets( $f, $len ); fclose( $f ); + return rtrim( $input ); } @@ -317,9 +333,8 @@ abstract class Maintenance { /** * Throw some output to the user. Scripts can call this with no fears, * as we handle all --quiet stuff here - * @param $out String: the text to show to the user - * @param $channel Mixed: unique identifier for the channel. See - * function outputChanneled. + * @param string $out The text to show to the user + * @param mixed $channel Unique identifier for the channel. See function outputChanneled. */ protected function output( $out, $channel = null ) { if ( $this->mQuiet ) { @@ -337,8 +352,8 @@ 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 Int: if > 0, go ahead and die out using this int as the code + * @param string $err The error to display + * @param int $die If > 0, go ahead and die out using this int as the code */ protected function error( $err, $die = 0 ) { $this->outputChanneled( false ); @@ -370,13 +385,14 @@ abstract class Maintenance { * Message outputter with channeled message support. Messages on the * same channel are concatenated, but any intervening messages in another * channel start a new line. - * @param $msg String: the message without trailing newline - * @param $channel string Channel identifier or null for no + * @param string $msg The message without trailing newline + * @param string $channel Channel identifier or null for no * channel. Channel comparison uses ===. */ public function outputChanneled( $msg, $channel = null ) { if ( $msg === false ) { $this->cleanupChanneled(); + return; } @@ -404,7 +420,7 @@ abstract class Maintenance { * Maintenance::DB_NONE - For no DB access at all * Maintenance::DB_STD - For normal DB access, default * Maintenance::DB_ADMIN - For admin DB access - * @return Integer + * @return int */ public function getDbType() { return Maintenance::DB_STD; @@ -422,10 +438,14 @@ abstract class Maintenance { $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' ); - $this->addOption( 'memory-limit', 'Set a specific memory limit for the script, "max" for no limit or "default" to avoid changing it' ); + $this->addOption( + 'memory-limit', + 'Set a specific memory limit for the script, ' + . '"max" for no limit or "default" to avoid changing it' + ); $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 ); + "http://en.wikipedia.org. This is sometimes necessary because " . + "server name detection may fail in command line scripts.", false, true ); $this->addOption( 'profiler', 'Set to "text" or "trace" to show profiling output', false, true ); # Save generic options to display them separately in help @@ -444,12 +464,32 @@ abstract class Maintenance { $this->mDependantParameters = array_diff_key( $this->mParams, $this->mGenericParameters ); } + /** + * @since 1.24 + * @return Config + */ + public function getConfig() { + if ( $this->config === null ) { + $this->config = ConfigFactory::getDefaultInstance()->makeConfig( 'main' ); + } + + return $this->config; + } + + /** + * @since 1.24 + * @param Config $config + */ + public function setConfig( Config $config ) { + $this->config = $config; + } + /** * Run a child maintenance script. Pass all of the current arguments * to it. - * @param $maintClass String: a name of a child maintenance class - * @param $classFile String: full path of where the child is - * @return Maintenance child + * @param string $maintClass A name of a child maintenance class + * @param string $classFile Full path of where the child is + * @return Maintenance */ public function runChild( $maintClass, $classFile = null ) { // Make sure the class is loaded first @@ -470,6 +510,7 @@ abstract class Maintenance { if ( !is_null( $this->mDb ) ) { $child->setDB( $this->mDb ); } + return $child; } @@ -570,9 +611,9 @@ abstract class Maintenance { * $mOptions becomes an array with keys set to the option names * $mArgs becomes a zero-based array containing the non-option arguments * - * @param $self String The name of the script, if any - * @param $opts Array An array of options, in form of key=>value - * @param $args Array An array of command line arguments + * @param string $self The name of the script, if any + * @param array $opts An array of options, in form of key=>value + * @param array $args An array of command line arguments */ public function loadParamsAndArgs( $self = null, $opts = null, $args = null ) { # If we were given opts or args, set those and return early @@ -594,6 +635,7 @@ abstract class Maintenance { # it's run again and again if ( $this->mInputLoaded ) { $this->loadSpecialVars(); + return; } @@ -639,8 +681,9 @@ abstract class Maintenance { } } elseif ( substr( $arg, 0, 1 ) == '-' ) { # Short options - for ( $p = 1; $p < strlen( $arg ); $p++ ) { - $option = $arg { $p }; + $argLength = strlen( $arg ); + for ( $p = 1; $p < $argLength; $p++ ) { + $option = $arg[$p]; if ( !isset( $this->mParams[$option] ) && isset( $this->mShortParamsMap[$option] ) ) { $option = $this->mShortParamsMap[$option]; } @@ -715,14 +758,14 @@ abstract class Maintenance { /** * Maybe show the help. - * @param $force boolean Whether to force the help to show, default false + * @param bool $force Whether to force the help to show, default false */ protected function maybeHelp( $force = false ) { if ( !$force && !$this->hasOption( 'help' ) ) { return; } - $screenWidth = 80; // TODO: Caculate this! + $screenWidth = 80; // TODO: Calculate this! $tab = " "; $descWidth = $screenWidth - ( 2 * strlen( $tab ) ); @@ -766,7 +809,7 @@ abstract class Maintenance { } $this->output( wordwrap( "$tab--$par: " . $info['desc'], $descWidth, - "\n$tab$tab" ) . "\n" + "\n$tab$tab" ) . "\n" ); } $this->output( "\n" ); @@ -781,13 +824,12 @@ abstract class Maintenance { } $this->output( wordwrap( "$tab--$par: " . $info['desc'], $descWidth, - "\n$tab$tab" ) . "\n" + "\n$tab$tab" ) . "\n" ); } $this->output( "\n" ); } - // Script specific parameters not defined on construction by // Maintenance::addDefaultParams() $scriptSpecificParams = array_diff_key( @@ -806,7 +848,7 @@ abstract class Maintenance { } $this->output( wordwrap( "$tab--$par: " . $info['desc'], $descWidth, - "\n$tab$tab" ) . "\n" + "\n$tab$tab" ) . "\n" ); } $this->output( "\n" ); @@ -881,7 +923,12 @@ abstract class Maintenance { $this->afterFinalSetup(); $wgShowSQLErrors = true; + + // @codingStandardsIgnoreStart Allow error supppression. wfSuppressWarnings() + // is not avaiable. @set_time_limit( 0 ); + // @codingStandardsIgnoreStart + $this->adjustMemoryLimit(); // Per-script profiling; useful for debugging @@ -916,7 +963,7 @@ abstract class Maintenance { /** * Generic setup for most installs. Returns the location of LocalSettings - * @return String + * @return string */ public function loadSettings() { global $wgCommandLineMode, $IP; @@ -939,16 +986,17 @@ abstract class Maintenance { if ( !is_readable( $settingsFile ) ) { $this->error( "A copy of your installation's LocalSettings.php\n" . - "must exist and be readable in the source directory.\n" . - "Use --conf to specify it.", true ); + "must exist and be readable in the source directory.\n" . + "Use --conf to specify it.", true ); } $wgCommandLineMode = true; + return $settingsFile; } /** * Support function for cleaning up redundant text records - * @param $delete Boolean: whether or not to actually delete the records + * @param bool $delete Whether or not to actually delete the records * @author Rob Church */ public function purgeRedundantText( $delete = true ) { @@ -1008,52 +1056,6 @@ abstract class Maintenance { return __DIR__; } - /** - * Get the list of available maintenance scripts. Note - * that if you call this _before_ calling doMaintenance - * you won't have any extensions in it yet - * @return Array - */ - public static function getMaintenanceScripts() { - global $wgMaintenanceScripts; - return $wgMaintenanceScripts + self::getCoreScripts(); - } - - /** - * Return all of the core maintenance scripts - * @return array - */ - protected static function getCoreScripts() { - if ( !self::$mCoreScripts ) { - $paths = array( - __DIR__, - __DIR__ . '/language', - __DIR__ . '/storage', - ); - self::$mCoreScripts = array(); - foreach ( $paths as $p ) { - $handle = opendir( $p ); - while ( ( $file = readdir( $handle ) ) !== false ) { - if ( $file == 'Maintenance.php' ) { - continue; - } - $file = $p . '/' . $file; - if ( is_dir( $file ) || !strpos( $file, '.php' ) || - ( strpos( file_get_contents( $file ), '$maintClass' ) === false ) ) { - continue; - } - require $file; - $vars = get_defined_vars(); - if ( array_key_exists( 'maintClass', $vars ) ) { - self::$mCoreScripts[$vars['maintClass']] = $file; - } - } - closedir( $handle ); - } - } - return self::$mCoreScripts; - } - /** * Returns a database to be used by current maintenance script. It can be set by setDB(). * If not set, wfGetDB() will be used. @@ -1072,7 +1074,7 @@ abstract class Maintenance { /** * Sets database object to be returned by getDB(). * - * @param $db DatabaseBase: Database object to be used + * @param DatabaseBase $db Database object to be used */ public function setDB( &$db ) { $this->mDb = $db; @@ -1080,7 +1082,7 @@ abstract class Maintenance { /** * Lock the search index - * @param &$db DatabaseBase object + * @param DatabaseBase &$db */ private function lockSearchindex( &$db ) { $write = array( 'searchindex' ); @@ -1090,7 +1092,7 @@ abstract class Maintenance { /** * Unlock the tables - * @param &$db DatabaseBase object + * @param DatabaseBase &$db */ private function unlockSearchindex( &$db ) { $db->unlockTables( __CLASS__ . '::' . __METHOD__ ); @@ -1099,7 +1101,7 @@ abstract class Maintenance { /** * Unlock and lock again * Since the lock is low-priority, queued reads will be able to complete - * @param &$db DatabaseBase object + * @param DatabaseBase &$db */ private function relockSearchindex( &$db ) { $this->unlockSearchindex( $db ); @@ -1108,10 +1110,10 @@ 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 DatabaseBase object - * @param $results + * @param int $maxLockTime The maximum time to keep the search index locked. + * @param string $callback The function that will update the function. + * @param DatabaseBase $dbw + * @param array $results */ public function updateSearchIndex( $maxLockTime, $callback, $dbw, $results ) { $lockTime = time(); @@ -1142,13 +1144,12 @@ abstract class Maintenance { $this->unlockSearchindex( $dbw ); $this->output( "\n" ); } - } /** * Update the searchindex table for a given pageid - * @param $dbw DatabaseBase a database write handle - * @param $pageId Integer: the page ID to update. + * @param DatabaseBase $dbw A database write handle + * @param int $pageId The page ID to update. * @return null|string */ public function updateSearchIndexForPage( $dbw, $pageId ) { @@ -1164,6 +1165,7 @@ abstract class Maintenance { $u->doUpdate(); $this->output( "\n" ); } + return $title; } @@ -1172,7 +1174,7 @@ abstract class Maintenance { * We default as considering stdin a tty (for nice readline methods) * but treating stout as not a tty to avoid color codes * - * @param $fd int File descriptor + * @param int $fd File descriptor * @return bool */ public static function posix_isatty( $fd ) { @@ -1185,8 +1187,8 @@ abstract class Maintenance { /** * Prompt the console for input - * @param $prompt String what to begin the line with, like '> ' - * @return String response + * @param string $prompt What to begin the line with, like '> ' + * @return string Response */ public static function readconsole( $prompt = '> ' ) { static $isatty = null; @@ -1210,14 +1212,15 @@ abstract class Maintenance { return false; } $resp = trim( $st ); + return $resp; } } /** * Emulate readline() - * @param $prompt String what to begin the line with, like '> ' - * @return String + * @param string $prompt What to begin the line with, like '> ' + * @return string */ private static function readlineEmulation( $prompt ) { $bash = Installer::locateExecutableInDefaultPaths( array( 'bash' ) ); @@ -1245,6 +1248,7 @@ abstract class Maintenance { return false; } print $prompt; + return fgets( STDIN, 1024 ); } } @@ -1254,6 +1258,7 @@ abstract class Maintenance { */ class FakeMaintenance extends Maintenance { protected $mSelf = "FakeMaintenanceScript"; + public function execute() { return; } @@ -1274,10 +1279,11 @@ abstract class LoggedUpdateMaintenance extends Maintenance { $db = $this->getDB( DB_MASTER ); $key = $this->getUpdateKey(); - if ( !$this->hasOption( 'force' ) && - $db->selectRow( 'updatelog', '1', array( 'ul_key' => $key ), __METHOD__ ) ) - { + if ( !$this->hasOption( 'force' ) + && $db->selectRow( 'updatelog', '1', array( 'ul_key' => $key ), __METHOD__ ) + ) { $this->output( "..." . $this->updateSkippedMessage() . "\n" ); + return true; } @@ -1285,44 +1291,45 @@ abstract class LoggedUpdateMaintenance extends Maintenance { return false; } - if ( - $db->insert( 'updatelog', array( 'ul_key' => $key ), __METHOD__, 'IGNORE' ) ) - { + 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 + * @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 + * @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 + * @return bool */ abstract protected function doDBUpdates(); /** * Get the update key name to go in the update log table - * @return String + * @return string */ abstract protected function getUpdateKey(); } diff --git a/maintenance/README b/maintenance/README index 5cb6f5f5..30bb6adb 100644 --- a/maintenance/README +++ b/maintenance/README @@ -51,7 +51,7 @@ installations. edit.php Edit a page to change its content - findhooks.php + findHooks.php Find hooks that aren't documented in docs/hooks.txt importDump.php @@ -60,9 +60,6 @@ installations. importImages.php Import images into the wiki - importTextFile.php - Import the contents of a text file into a wiki page - moveBatch.php Move a batch of pages diff --git a/maintenance/archives/patch-drop-rc_cur_time.sql b/maintenance/archives/patch-drop-rc_cur_time.sql new file mode 100644 index 00000000..f1bc9e8b --- /dev/null +++ b/maintenance/archives/patch-drop-rc_cur_time.sql @@ -0,0 +1,2 @@ +-- rc_cur_time is no longer used, delete the field +ALTER TABLE /*$wgDBprefix*/recentchanges DROP COLUMN rc_cur_time; \ No newline at end of file diff --git a/maintenance/archives/patch-fa_major_mime-chemical.sql b/maintenance/archives/patch-fa_major_mime-chemical.sql new file mode 100644 index 00000000..be9b0ff5 --- /dev/null +++ b/maintenance/archives/patch-fa_major_mime-chemical.sql @@ -0,0 +1,3 @@ +ALTER TABLE /*$wgDBprefix*/filearchive + CHANGE fa_major_mime fa_major_mime ENUM('unknown','application','audio','image','text','video','message','model','multipart','chemical'); + diff --git a/maintenance/archives/patch-hitcounter.sql b/maintenance/archives/patch-hitcounter.sql index c87c9592..2d698f68 100644 --- a/maintenance/archives/patch-hitcounter.sql +++ b/maintenance/archives/patch-hitcounter.sql @@ -6,4 +6,4 @@ CREATE TABLE /*$wgDBprefix*/hitcounter ( hc_id INTEGER UNSIGNED NOT NULL -) ENGINE=HEAP MAX_ROWS=25000; +) ENGINE=MEMORY MAX_ROWS=25000; diff --git a/maintenance/archives/patch-il_from_namespace.sql b/maintenance/archives/patch-il_from_namespace.sql new file mode 100644 index 00000000..4c858f44 --- /dev/null +++ b/maintenance/archives/patch-il_from_namespace.sql @@ -0,0 +1,4 @@ +ALTER TABLE /*_*/imagelinks + ADD COLUMN il_from_namespace int NOT NULL default 0; + +CREATE INDEX /*i*/il_backlinks_namespace ON /*_*/imagelinks (il_to,il_from_namespace,il_from); \ No newline at end of file diff --git a/maintenance/archives/patch-img_major_mime-chemical.sql b/maintenance/archives/patch-img_major_mime-chemical.sql new file mode 100644 index 00000000..4bde446e --- /dev/null +++ b/maintenance/archives/patch-img_major_mime-chemical.sql @@ -0,0 +1,3 @@ +ALTER TABLE /*$wgDBprefix*/image + CHANGE img_major_mime img_major_mime ENUM('unknown','application','audio','image','text','video','message','model','multipart','chemical'); + diff --git a/maintenance/archives/patch-logging_user_text_time_index.sql b/maintenance/archives/patch-logging_user_text_time_index.sql new file mode 100644 index 00000000..06f29861 --- /dev/null +++ b/maintenance/archives/patch-logging_user_text_time_index.sql @@ -0,0 +1 @@ +CREATE INDEX /*i*/log_user_text_time ON /*_*/logging (log_user_text, log_timestamp); diff --git a/maintenance/archives/patch-logging_user_text_type_time_index.sql b/maintenance/archives/patch-logging_user_text_type_time_index.sql new file mode 100644 index 00000000..2801bc86 --- /dev/null +++ b/maintenance/archives/patch-logging_user_text_type_time_index.sql @@ -0,0 +1 @@ +CREATE INDEX /*i*/log_user_text_type_time ON /*_*/logging (log_user_text, log_type, log_timestamp); diff --git a/maintenance/archives/patch-mimesearch-indexes.sql b/maintenance/archives/patch-mimesearch-indexes.sql index bd348c46..8d9426ea 100644 --- a/maintenance/archives/patch-mimesearch-indexes.sql +++ b/maintenance/archives/patch-mimesearch-indexes.sql @@ -1,4 +1,4 @@ --- Add indexes to the mime types in image for use on Special:MIMEsearch, +-- Add indexes to the MIME types in image for use on Special:MIMEsearch, -- changes a query like -- -- SELECT img_name FROM image WHERE img_major_mime = "image" AND img_minor_mime = "svg"; diff --git a/maintenance/archives/patch-oi_major_mime-chemical.sql b/maintenance/archives/patch-oi_major_mime-chemical.sql new file mode 100644 index 00000000..e3b4552d --- /dev/null +++ b/maintenance/archives/patch-oi_major_mime-chemical.sql @@ -0,0 +1,3 @@ +ALTER TABLE /*$wgDBprefix*/oldimage + CHANGE oi_major_mime oi_major_mime ENUM('unknown','application','audio','image','text','video','message','model','multipart','chemical'); + diff --git a/maintenance/archives/patch-page_lang.sql b/maintenance/archives/patch-page_lang.sql new file mode 100644 index 00000000..c792b4ad --- /dev/null +++ b/maintenance/archives/patch-page_lang.sql @@ -0,0 +1,2 @@ +ALTER TABLE /*$wgDBprefix*/page + ADD page_lang varbinary(35) DEFAULT NULL; diff --git a/maintenance/archives/patch-page_links_updated.sql b/maintenance/archives/patch-page_links_updated.sql new file mode 100644 index 00000000..18d9e2d9 --- /dev/null +++ b/maintenance/archives/patch-page_links_updated.sql @@ -0,0 +1,2 @@ +ALTER TABLE /*$wgDBprefix*/page + ADD page_links_updated varbinary(14) NULL default NULL; diff --git a/maintenance/archives/patch-pl_from_namespace.sql b/maintenance/archives/patch-pl_from_namespace.sql new file mode 100644 index 00000000..2f7ff046 --- /dev/null +++ b/maintenance/archives/patch-pl_from_namespace.sql @@ -0,0 +1,4 @@ +ALTER TABLE /*_*/pagelinks + ADD COLUMN pl_from_namespace int NOT NULL default 0; + +CREATE INDEX /*i*/pl_backlinks_namespace ON /*_*/pagelinks (pl_namespace,pl_title,pl_from_namespace,pl_from); diff --git a/maintenance/archives/patch-pp_sortkey.sql b/maintenance/archives/patch-pp_sortkey.sql new file mode 100644 index 00000000..b13b6055 --- /dev/null +++ b/maintenance/archives/patch-pp_sortkey.sql @@ -0,0 +1,8 @@ +-- Add a 'sortkey' field to page_props so pages can be efficiently +-- queried by the numeric value of a property. + +ALTER TABLE /*_*/page_props + ADD pp_sortkey float DEFAULT NULL; + +CREATE UNIQUE INDEX /*i*/pp_propname_sortkey_page + ON /*_*/page_props ( pp_propname, pp_sortkey, pp_page ); diff --git a/maintenance/archives/patch-profiling.sql b/maintenance/archives/patch-profiling.sql index 0a0e4e1a..6ad16224 100644 --- a/maintenance/archives/patch-profiling.sql +++ b/maintenance/archives/patch-profiling.sql @@ -7,6 +7,6 @@ CREATE TABLE /*_*/profiling ( pf_memory float NOT NULL default 0, pf_name varchar(255) NOT NULL default '', pf_server varchar(30) NOT NULL default '' -) ENGINE=HEAP; +) ENGINE=MEMORY; 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-rc_source.sql b/maintenance/archives/patch-rc_source.sql new file mode 100644 index 00000000..7dedd745 --- /dev/null +++ b/maintenance/archives/patch-rc_source.sql @@ -0,0 +1,16 @@ +-- first step of migrating recentchanges rc_type to rc_source +ALTER TABLE /*$wgDBprefix*/recentchanges + ADD rc_source varbinary(16) NOT NULL default ''; + +-- Populate rc_source field with the data from rc_type +-- Large wiki's might prefer the PopulateRecentChangeSource maintenance +-- script to batch updates into groups rather than all at once. +UPDATE /*$wgDBprefix*/recentchanges + SET rc_source = CASE + WHEN rc_type = 0 THEN 'mw.edit' + WHEN rc_type = 1 THEN 'mw.new' + WHEN rc_type = 3 THEN 'mw.log' + WHEN rc_type = 5 THEN 'mw.external' + ELSE '' + END +WHERE rc_source = ''; diff --git a/maintenance/archives/patch-restructure.sql b/maintenance/archives/patch-restructure.sql deleted file mode 100644 index a5bc3e52..00000000 --- a/maintenance/archives/patch-restructure.sql +++ /dev/null @@ -1,146 +0,0 @@ --- The Great Restructuring of October 2004 --- Creates 'page', 'revision' tables and transforms the classic --- cur+old into a separate page+revision+text structure. --- --- The pre-conversion 'old' table is renamed to 'text' and used --- without internal restructuring to avoid rebuilding the entire --- table. (This can be done separately if desired.) --- --- The pre-conversion 'cur' table is now redundant and can be --- discarded when done. - -CREATE TABLE /*$wgDBprefix*/page ( - page_id int unsigned NOT NULL auto_increment, - page_namespace tinyint NOT NULL, - page_title varchar(255) binary NOT NULL, - page_restrictions tinyblob NOT NULL, - page_counter bigint unsigned NOT NULL default '0', - page_is_redirect tinyint unsigned NOT NULL default '0', - page_is_new tinyint unsigned NOT NULL default '0', - page_random real unsigned NOT NULL, - page_touched binary(14) NOT NULL default '', - page_latest int unsigned NOT NULL, - page_len int unsigned NOT NULL, - - PRIMARY KEY page_id (page_id), - UNIQUE INDEX name_title (page_namespace,page_title), - INDEX (page_random), - INDEX (page_len) -); - -CREATE TABLE /*$wgDBprefix*/revision ( - rev_id int unsigned NOT NULL auto_increment, - rev_page int unsigned NOT NULL, - rev_comment tinyblob NOT NULL, - rev_user int unsigned NOT NULL default '0', - rev_user_text varchar(255) binary NOT NULL default '', - rev_timestamp binary(14) NOT NULL default '', - rev_minor_edit tinyint unsigned NOT NULL default '0', - rev_deleted tinyint unsigned NOT NULL default '0', - - PRIMARY KEY rev_page_id (rev_page, rev_id), - UNIQUE INDEX rev_id (rev_id), - INDEX rev_timestamp (rev_timestamp), - INDEX page_timestamp (rev_page,rev_timestamp), - INDEX user_timestamp (rev_user,rev_timestamp), - INDEX usertext_timestamp (rev_user_text,rev_timestamp) -); - --- If creating new 'text' table it would look like this: --- --- CREATE TABLE /*$wgDBprefix*/text ( --- old_id int(8) unsigned NOT NULL auto_increment, --- old_text mediumtext NOT NULL, --- old_flags tinyblob NOT NULL, --- --- PRIMARY KEY old_id (old_id) --- ); - - --- Lock! -LOCK TABLES /*$wgDBprefix*/page WRITE, /*$wgDBprefix*/revision WRITE, /*$wgDBprefix*/old WRITE, /*$wgDBprefix*/cur WRITE; - --- Save the last old_id value for later -SELECT (@maxold:=MAX(old_id)) FROM /*$wgDBprefix*/old; - --- First, copy all current entries into the old table. -INSERT - INTO /*$wgDBprefix*/old - (old_namespace, - old_title, - old_text, - old_comment, - old_user, - old_user_text, - old_timestamp, - old_minor_edit, - old_flags) - SELECT - cur_namespace, - cur_title, - cur_text, - cur_comment, - cur_user, - cur_user_text, - cur_timestamp, - cur_minor_edit, - '' - FROM /*$wgDBprefix*/cur; - --- Now, copy all old data except the text into revisions -INSERT - INTO /*$wgDBprefix*/revision - (rev_id, - rev_page, - rev_comment, - rev_user, - rev_user_text, - rev_timestamp, - rev_minor_edit) - SELECT - old_id, - cur_id, - old_comment, - old_user, - old_user_text, - old_timestamp, - old_minor_edit - FROM /*$wgDBprefix*/old,/*$wgDBprefix*/cur - WHERE old_namespace=cur_namespace - AND old_title=cur_title; - --- And, copy the cur data into page -INSERT - INTO /*$wgDBprefix*/page - (page_id, - page_namespace, - page_title, - page_restrictions, - page_counter, - page_is_redirect, - page_is_new, - page_random, - page_touched, - page_latest) - SELECT - cur_id, - cur_namespace, - cur_title, - cur_restrictions, - cur_counter, - cur_is_redirect, - cur_is_new, - cur_random, - cur_touched, - rev_id - FROM /*$wgDBprefix*/cur,/*$wgDBprefix*/revision - WHERE cur_id=rev_page - AND rev_timestamp=cur_timestamp - AND rev_id > @maxold; - -UNLOCK TABLES; - --- Keep the old table around as the text store. --- Its extra fields will be ignored, but trimming them is slow --- so we won't bother doing it for now. -ALTER TABLE /*$wgDBprefix*/old RENAME TO /*$wgDBprefix*/text; diff --git a/maintenance/archives/patch-tl_from_namespace.sql b/maintenance/archives/patch-tl_from_namespace.sql new file mode 100644 index 00000000..8d6c76b8 --- /dev/null +++ b/maintenance/archives/patch-tl_from_namespace.sql @@ -0,0 +1,4 @@ +ALTER TABLE /*_*/templatelinks + ADD COLUMN tl_from_namespace int NOT NULL default 0; + +CREATE INDEX /*i*/tl_backlinks_namespace ON /*_*/templatelinks (tl_namespace,tl_title,tl_from_namespace,tl_from); diff --git a/maintenance/archives/patch-uploadstash.sql b/maintenance/archives/patch-uploadstash.sql index 14eaeab0..c1d93ef3 100644 --- a/maintenance/archives/patch-uploadstash.sql +++ b/maintenance/archives/patch-uploadstash.sql @@ -26,10 +26,10 @@ CREATE TABLE /*_*/uploadstash ( us_status varchar(50) not null, - -- file properties from File::getPropsFromPath. these may prove unnecessary. + -- file properties from FSFile::getProps(). these may prove unnecessary. -- us_size int unsigned NOT NULL, - -- this hash comes from File::sha1Base36(), and is 31 characters + -- this hash comes from FSFile::getSha1Base36(), 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 diff --git a/maintenance/archives/patch-user_password_expire.sql b/maintenance/archives/patch-user_password_expire.sql new file mode 100644 index 00000000..3e716d33 --- /dev/null +++ b/maintenance/archives/patch-user_password_expire.sql @@ -0,0 +1,3 @@ +-- For setting a password expiration date for users +ALTER TABLE /*$wgDBprefix*/user + ADD COLUMN user_password_expires varbinary(14) DEFAULT NULL; diff --git a/maintenance/archives/patch-val_ip.sql b/maintenance/archives/patch-val_ip.sql deleted file mode 100644 index 9214218d..00000000 --- a/maintenance/archives/patch-val_ip.sql +++ /dev/null @@ -1,4 +0,0 @@ --- Column added 2005-05-24 - -ALTER TABLE /*$wgDBprefix*/validate - ADD COLUMN val_ip varchar(20) NOT NULL default ''; diff --git a/maintenance/archives/patch-validate.sql b/maintenance/archives/patch-validate.sql deleted file mode 100644 index 9701083c..00000000 --- a/maintenance/archives/patch-validate.sql +++ /dev/null @@ -1,13 +0,0 @@ --- For article validation - -DROP TABLE IF EXISTS /*$wgDBprefix*/validate; -CREATE TABLE /*$wgDBprefix*/validate ( - `val_user` int(11) NOT NULL default '0', - `val_page` int(11) unsigned NOT NULL default '0', - `val_revision` int(11) unsigned NOT NULL default '0', - `val_type` int(11) unsigned NOT NULL default '0', - `val_value` int(11) default '0', - `val_comment` varchar(255) NOT NULL default '', - `val_ip` varchar(20) NOT NULL default '', - KEY `val_user` (`val_user`,`val_revision`) -) /*$wgDBTableOptions*/; diff --git a/maintenance/archives/patch-watchlist-user-notificationtimestamp-index.sql b/maintenance/archives/patch-watchlist-user-notificationtimestamp-index.sql new file mode 100644 index 00000000..22ae44f1 --- /dev/null +++ b/maintenance/archives/patch-watchlist-user-notificationtimestamp-index.sql @@ -0,0 +1,4 @@ +-- +-- Creates the wl_user_notificationtimestamp index for the watchlist table +-- +CREATE INDEX /*i*/wl_user_notificationtimestamp ON /*_*/watchlist (wl_user, wl_notificationtimestamp); diff --git a/maintenance/archives/upgradeLogging.php b/maintenance/archives/upgradeLogging.php index 0749bbf6..aeadc93d 100644 --- a/maintenance/archives/upgradeLogging.php +++ b/maintenance/archives/upgradeLogging.php @@ -52,6 +52,7 @@ class UpdateLogging { if ( $this->dbw->tableExists( 'logging_pre_1_10' ) ) { echo "This script has already been run to completion\n"; + return; } @@ -124,6 +125,8 @@ EOT; /** * Copy all rows from $srcTable to $dstTable + * @param string $srcTable + * @param string $dstTable */ function sync( $srcTable, $dstTable ) { $batchSize = 1000; @@ -158,7 +161,7 @@ EOT; $srcRes = $this->dbw->select( $srcTable, '*', $conds, __METHOD__, array( 'LIMIT' => $batchSize, 'ORDER BY' => 'log_timestamp' ) ); - if ( ! $srcRes->numRows() ) { + if ( !$srcRes->numRows() ) { # All done break; } @@ -205,6 +208,7 @@ EOT; } } } + return $numRowsCopied; } } diff --git a/maintenance/attachLatest.php b/maintenance/attachLatest.php index 2cf277fe..fba6b92d 100644 --- a/maintenance/attachLatest.php +++ b/maintenance/attachLatest.php @@ -3,7 +3,7 @@ * Corrects wrong values in the `page_latest` field in the database. * * Copyright © 2005 Brion Vibber - * http://www.mediawiki.org/ + * https://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 @@ -33,19 +33,24 @@ require_once __DIR__ . '/Maintenance.php'; * @ingroup Maintenance */ class AttachLatest extends Maintenance { - public function __construct() { parent::__construct(); $this->addOption( "fix", "Actually fix the entries, will dry run otherwise" ); + $this->addOption( "regenerate-all", + "Regenerate the page_latest field for all records in table page" ); $this->mDescription = "Fix page_latest entries in the page table"; } public function execute() { $this->output( "Looking for pages with page_latest set to 0...\n" ); $dbw = wfGetDB( DB_MASTER ); + $conds = array( 'page_latest' => 0 ); + if ( $this->hasOption( 'regenerate-all' ) ) { + $conds = ''; + } $result = $dbw->select( 'page', array( 'page_id', 'page_namespace', 'page_title' ), - array( 'page_latest' => 0 ), + $conds, __METHOD__ ); $n = 0; @@ -64,7 +69,8 @@ class AttachLatest extends Maintenance { $revision = Revision::loadFromTimestamp( $dbw, $title, $latestTime ); if ( is_null( $revision ) ) { - $this->output( wfWikiID() . " $pageId [[$name]] latest time $latestTime, can't find revision id\n" ); + $this->output( wfWikiID() + . " $pageId [[$name]] latest time $latestTime, can't find revision id\n" ); continue; } $id = $revision->getId(); diff --git a/maintenance/backup.inc b/maintenance/backup.inc index 3dc94c88..222c538b 100644 --- a/maintenance/backup.inc +++ b/maintenance/backup.inc @@ -3,7 +3,7 @@ * Base classes for database dumpers * * Copyright © 2005 Brion Vibber - * http://www.mediawiki.org/ + * https://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 @@ -37,28 +37,30 @@ class DumpDBZip2Output extends DumpPipeOutput { * @ingroup Dump Maintenance */ class BackupDumper { - var $reportingInterval = 100; - var $reporting = true; - var $pageCount = 0; - var $revCount = 0; - var $server = null; // use default - var $pages = null; // all pages - var $skipHeader = false; // don't output and - var $skipFooter = false; // don't output - var $startId = 0; - var $endId = 0; - var $revStartId = 0; - var $revEndId = 0; - 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; - - var $outputTypes = array(), $filterTypes = array(); + public $reporting = true; + public $pages = null; // all pages + public $skipHeader = false; // don't output and + public $skipFooter = false; // don't output + public $startId = 0; + public $endId = 0; + public $revStartId = 0; + public $revEndId = 0; + public $dumpUploads = false; + public $dumpUploadFileContents = false; + + protected $reportingInterval = 100; + protected $pageCount = 0; + protected $revCount = 0; + protected $server = null; // use default + protected $sink = null; // Output filters + protected $lastTime = 0; + protected $pageCountLast = 0; + protected $revCountLast = 0; + + protected $outputTypes = array(); + protected $filterTypes = array(); + + protected $ID = 0; /** * The dependency-injected database to use. @@ -69,11 +71,12 @@ class BackupDumper { */ protected $forcedDb = null; - /** - * @var LoadBalancer - */ + /** @var LoadBalancer */ protected $lb; + // @todo Unused? + private $stubText = false; // include rev_text_id instead of text; for 2-pass dump + function __construct( $args ) { $this->stderr = fopen( "php://stderr", "wt" ); @@ -92,16 +95,16 @@ class BackupDumper { } /** - * @param $name String - * @param $class String: name of output filter plugin class + * @param string $name + * @param string $class Name of output filter plugin class */ function registerOutput( $name, $class ) { $this->outputTypes[$name] = $class; } /** - * @param $name String - * @param $class String: name of filter plugin class + * @param string $name + * @param string $class Name of filter plugin class */ function registerFilter( $name, $class ) { $this->filterTypes[$name] = $class; @@ -110,9 +113,9 @@ class BackupDumper { /** * Load a plugin and register it * - * @param $class String: name of plugin class; must have a static 'register' - * method that takes a BackupDumper as a parameter. - * @param $file String: full or relative path to the PHP file to load, or empty + * @param string $class Name of plugin class; must have a static 'register' + * method that takes a BackupDumper as a parameter. + * @param string $file Full or relative path to the PHP file to load, or empty */ function loadPlugin( $class, $file ) { if ( $file != '' ) { @@ -123,8 +126,8 @@ class BackupDumper { } /** - * @param $args Array - * @return Array + * @param array $args + * @return array */ function processArgs( $args ) { $sink = null; @@ -132,50 +135,53 @@ class BackupDumper { foreach ( $args as $arg ) { $matches = array(); if ( preg_match( '/^--(.+?)(?:=(.+?)(?::(.+?))?)?$/', $arg, $matches ) ) { - @list( /* $full */ , $opt, $val, $param ) = $matches; + wfSuppressWarnings(); + list( /* $full */, $opt, $val, $param ) = $matches; + wfRestoreWarnings(); + switch ( $opt ) { - case "plugin": - $this->loadPlugin( $val, $param ); - break; - case "output": - if ( !is_null( $sink ) ) { - $sinks[] = $sink; - } - if ( !isset( $this->outputTypes[$val] ) ) { - $this->fatalError( "Unrecognized output sink type '$val'" ); - } - $type = $this->outputTypes[$val]; - $sink = new $type( $param ); - break; - case "filter": - if ( is_null( $sink ) ) { - $sink = new DumpOutput(); - } - if ( !isset( $this->filterTypes[$val] ) ) { - $this->fatalError( "Unrecognized filter type '$val'" ); - } - $type = $this->filterTypes[$val]; - $filter = new $type( $sink, $param ); - - // references are lame in php... - unset( $sink ); - $sink = $filter; - - break; - case "report": - $this->reportingInterval = intval( $val ); - break; - case "server": - $this->server = $val; - break; - case "force-normal": - if ( !function_exists( 'utf8_normalize' ) ) { - $this->fatalError( "UTF-8 normalization extension not loaded. " . - "Install or remove --force-normal parameter to use slower code." ); - } - break; - default: - $this->processOption( $opt, $val, $param ); + case "plugin": + $this->loadPlugin( $val, $param ); + break; + case "output": + if ( !is_null( $sink ) ) { + $sinks[] = $sink; + } + if ( !isset( $this->outputTypes[$val] ) ) { + $this->fatalError( "Unrecognized output sink type '$val'" ); + } + $type = $this->outputTypes[$val]; + $sink = new $type( $param ); + break; + case "filter": + if ( is_null( $sink ) ) { + $sink = new DumpOutput(); + } + if ( !isset( $this->filterTypes[$val] ) ) { + $this->fatalError( "Unrecognized filter type '$val'" ); + } + $type = $this->filterTypes[$val]; + $filter = new $type( $sink, $param ); + + // references are lame in php... + unset( $sink ); + $sink = $filter; + + break; + case "report": + $this->reportingInterval = intval( $val ); + break; + case "server": + $this->server = $val; + break; + case "force-normal": + if ( !function_exists( 'utf8_normalize' ) ) { + $this->fatalError( "UTF-8 normalization extension not loaded. " . + "Install or remove --force-normal parameter to use slower code." ); + } + break; + default: + $this->processOption( $opt, $val, $param ); } } } @@ -223,8 +229,8 @@ class BackupDumper { } else { $exporter->allLogs(); } - # Page dumps: all or by page ID range } elseif ( is_null( $this->pages ) ) { + # Page dumps: all or by page ID range if ( $this->startId || $this->endId ) { $exporter->pagesByRange( $this->startId, $this->endId ); } elseif ( $this->revStartId || $this->revEndId ) { @@ -232,8 +238,8 @@ class BackupDumper { } else { $exporter->allPages(); } - # Dump of specific pages } else { + # Dump of specific pages $exporter->pagesByName( $this->pages ); } @@ -248,7 +254,7 @@ class BackupDumper { * Initialise starting time and maximum revision count. * We'll make ETA calculations based an progress, assuming relatively * constant per-revision rate. - * @param $history Integer: WikiExporter::CURRENT or WikiExporter::FULL + * @param int $history WikiExporter::CURRENT or WikiExporter::FULL */ function initProgress( $history = WikiExporter::FULL ) { $table = ( $history == WikiExporter::CURRENT ) ? 'page' : 'revision'; @@ -276,7 +282,7 @@ class BackupDumper { } $this->lb = wfGetLBFactory()->newMainLB(); - $db = $this->lb->getConnection( DB_SLAVE, 'backup' ); + $db = $this->lb->getConnection( DB_SLAVE, 'dump' ); // Discourage the server from disconnecting us if it takes a long time // to read out the big ol' batch query. @@ -289,9 +295,8 @@ class BackupDumper { * Force the dump to use the provided database connection for database * operations, wherever possible. * - * @param $db DatabaseBase|null: (Optional) the database connection to - * use. If null, resort to use the globally provided ways to - * get database connections. + * @param DatabaseBase|null $db (Optional) the database connection to use. If null, resort to + * use the globally provided ways to get database connections. */ function setDb( DatabaseBase $db = null ) { $this->forcedDb = $db; @@ -305,6 +310,7 @@ class BackupDumper { function backupServer() { global $wgDBserver; + return $this->server ? $this->server : $wgDBserver; @@ -352,8 +358,13 @@ class BackupDumper { $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->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; } diff --git a/maintenance/backupPrefetch.inc b/maintenance/backupPrefetch.inc index 04352b9b..7bfb7345 100644 --- a/maintenance/backupPrefetch.inc +++ b/maintenance/backupPrefetch.inc @@ -3,7 +3,7 @@ * Helper class for the --prefetch option of dumpTextPass.php * * Copyright © 2005 Brion Vibber - * http://www.mediawiki.org/ + * https://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 @@ -40,21 +40,20 @@ * @ingroup Maintenance */ class BaseDump { - var $reader = null; - var $atEnd = false; - var $atPageEnd = false; - var $lastPage = 0; - var $lastRev = 0; - var $infiles = null; - - function BaseDump( $infile ) { + protected $reader = null; + protected $atEnd = false; + protected $atPageEnd = false; + protected $lastPage = 0; + protected $lastRev = 0; + protected $infiles = null; + + public function __construct( $infile ) { $this->infiles = explode( ';', $infile ); $this->reader = new XMLReader(); $infile = array_shift( $this->infiles ); if ( defined( 'LIBXML_PARSEHUGE' ) ) { $this->reader->open( $infile, null, LIBXML_PARSEHUGE ); - } - else { + } else { $this->reader->open( $infile ); } } @@ -64,9 +63,9 @@ class BaseDump { * from the dump stream. May return null if the page is * unavailable. * - * @param $page Integer: ID number of page to read - * @param $rev Integer: ID number of revision to read - * @return string or null + * @param int $page ID number of page to read + * @param int $rev ID number of revision to read + * @return string|null */ function prefetch( $page, $rev ) { $page = intval( $page ); @@ -76,18 +75,24 @@ class BaseDump { $this->nextPage(); } if ( $this->lastPage > $page || $this->atEnd ) { - $this->debug( "BaseDump::prefetch already past page $page looking for rev $rev [$this->lastPage, $this->lastRev]" ); + $this->debug( "BaseDump::prefetch already past page $page " + . "looking for rev $rev [$this->lastPage, $this->lastRev]" ); + return null; } while ( $this->lastRev < $rev && !$this->atEnd && !$this->atPageEnd ) { - $this->debug( "BaseDump::prefetch at page $this->lastPage, rev $this->lastRev, looking for $page, $rev" ); + $this->debug( "BaseDump::prefetch at page $this->lastPage, rev $this->lastRev, " + . "looking for $page, $rev" ); $this->nextRev(); } if ( $this->lastRev == $rev && !$this->atEnd ) { $this->debug( "BaseDump::prefetch hit on $page, $rev [$this->lastPage, $this->lastRev]" ); + return $this->nextText(); } else { - $this->debug( "BaseDump::prefetch already past rev $rev on page $page [$this->lastPage, $this->lastRev]" ); + $this->debug( "BaseDump::prefetch already past rev $rev on page $page " + . "[$this->lastPage, $this->lastRev]" ); + return null; } } @@ -137,13 +142,14 @@ class BaseDump { */ function nextText() { $this->skipTo( 'text' ); + return strval( $this->nodeContents() ); } /** * @access private - * @param $name string - * @param $parent string + * @param string $name + * @param string $parent * @return bool|null */ function skipTo( $name, $parent = 'page' ) { @@ -151,16 +157,20 @@ class BaseDump { return false; } while ( $this->reader->read() ) { - if ( $this->reader->nodeType == XMLReader::ELEMENT && - $this->reader->name == $name ) { + if ( $this->reader->nodeType == XMLReader::ELEMENT + && $this->reader->name == $name + ) { return true; } - if ( $this->reader->nodeType == XMLReader::END_ELEMENT && - $this->reader->name == $parent ) { + if ( $this->reader->nodeType == XMLReader::END_ELEMENT + && $this->reader->name == $parent + ) { $this->debug( "BaseDump::skipTo found searching for <$name>" ); + return false; } } + return $this->close(); } @@ -169,7 +179,7 @@ class BaseDump { * Fetches text contents of the current element, assuming * no sub-elements or such scary things. * - * @return String + * @return string * @access private */ function nodeContents() { @@ -182,15 +192,16 @@ class BaseDump { $buffer = ""; while ( $this->reader->read() ) { switch ( $this->reader->nodeType ) { - case XMLReader::TEXT: -// case XMLReader::WHITESPACE: - case XMLReader::SIGNIFICANT_WHITESPACE: - $buffer .= $this->reader->value; - break; - case XMLReader::END_ELEMENT: - return $buffer; + case XMLReader::TEXT: + //case XMLReader::WHITESPACE: + case XMLReader::SIGNIFICANT_WHITESPACE: + $buffer .= $this->reader->value; + break; + case XMLReader::END_ELEMENT: + return $buffer; } } + return $this->close(); } @@ -201,6 +212,7 @@ class BaseDump { function close() { $this->reader->close(); $this->atEnd = true; + return null; } } diff --git a/maintenance/backupTextPass.inc b/maintenance/backupTextPass.inc index c515c6fe..5f776373 100644 --- a/maintenance/backupTextPass.inc +++ b/maintenance/backupTextPass.inc @@ -3,7 +3,7 @@ * BackupDumper that postprocesses XML dumps from dumpBackup.php to add page text * * Copyright (C) 2005 Brion Vibber - * http://www.mediawiki.org/ + * https://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 @@ -30,59 +30,60 @@ require_once __DIR__ . '/backup.inc'; * @ingroup Maintenance */ 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; + public $prefetch = null; - var $maxFailures = 5; - var $maxConsecutiveFailedTextRetrievals = 200; - var $failureTimeout = 5; // Seconds to sleep after db failure + // 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) + public $maxTimeAllowed = 0; // 0 = no limit - var $php = "php"; - var $spawn = false; + protected $input = "php://stdin"; + protected $history = WikiExporter::FULL; + protected $fetchCount = 0; + protected $prefetchCount = 0; + protected $prefetchCountLast = 0; + protected $fetchCountLast = 0; + + protected $maxFailures = 5; + protected $maxConsecutiveFailedTextRetrievals = 200; + protected $failureTimeout = 5; // Seconds to sleep after db failure + + protected $php = "php"; + protected $spawn = false; /** * @var bool|resource */ - var $spawnProc = false; + protected $spawnProc = false; /** * @var bool|resource */ - var $spawnWrite = false; + protected $spawnWrite = false; /** * @var bool|resource */ - var $spawnRead = false; + protected $spawnRead = false; /** * @var bool|resource */ - var $spawnErr = false; + protected $spawnErr = false; - var $xmlwriterobj = false; + protected $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(); + protected $timeExceeded = false; + protected $firstPageWritten = false; + protected $lastPageWritten = false; + protected $checkpointJustWritten = false; + protected $checkpointFiles = array(); /** * @var DatabaseBase */ protected $db; - /** * Drop the database connection $this->db and try to get a new one. * @@ -103,6 +104,7 @@ class TextPassDumper extends BackupDumper { if ( $this->forcedDb !== null ) { $this->db = $this->forcedDb; + return; } @@ -120,19 +122,19 @@ class TextPassDumper extends BackupDumper { try { $this->lb = wfGetLBFactory()->newMainLB(); } catch ( Exception $e ) { - throw new MWException( __METHOD__ . " rotating DB failed to obtain new load balancer (" . $e->getMessage() . ")" ); + throw new MWException( __METHOD__ + . " rotating DB failed to obtain new load balancer (" . $e->getMessage() . ")" ); } - // 2. The Connection, through the load balancer. try { - $this->db = $this->lb->getConnection( DB_SLAVE, 'backup' ); + $this->db = $this->lb->getConnection( DB_SLAVE, 'dump' ); } catch ( Exception $e ) { - throw new MWException( __METHOD__ . " rotating DB failed to obtain new database (" . $e->getMessage() . ")" ); + throw new MWException( __METHOD__ + . " rotating DB failed to obtain new database (" . $e->getMessage() . ")" ); } } - function initProgress( $history = WikiExporter::FULL ) { parent::initProgress(); $this->timeOfCheckpoint = $this->startTime; @@ -184,31 +186,31 @@ class TextPassDumper extends BackupDumper { $url = $this->processFileOpt( $val, $param ); switch ( $opt ) { - case 'prefetch': - require_once "$IP/maintenance/backupPrefetch.inc"; - $this->prefetch = new BaseDump( $url ); - break; - 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 ) { - $this->php = $val; - } - break; + case 'prefetch': + require_once "$IP/maintenance/backupPrefetch.inc"; + $this->prefetch = new BaseDump( $url ); + break; + 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 ) { + $this->php = $val; + } + break; } } @@ -234,6 +236,7 @@ class TextPassDumper extends BackupDumper { $newFileURIs[] = $newURI; } $val = implode( ';', $newFileURIs ); + return $val; } @@ -243,6 +246,7 @@ class TextPassDumper extends BackupDumper { function showReport() { if ( !$this->prefetch ) { parent::showReport(); + return; } @@ -279,14 +283,19 @@ class TextPassDumper extends BackupDumper { } $pageRatePart = $this->pageCountPart / $deltaPart; $revRatePart = $this->revCountPart / $deltaPart; - } else { $fetchRatePart = '-'; $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), %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->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; @@ -299,35 +308,43 @@ class TextPassDumper extends BackupDumper { } function checkIfTimeExceeded() { - if ( $this->maxTimeAllowed && ( $this->lastTime - $this->timeOfCheckpoint > $this->maxTimeAllowed ) ) { + if ( $this->maxTimeAllowed + && ( $this->lastTime - $this->timeOfCheckpoint > $this->maxTimeAllowed ) + ) { return true; } + return false; } function finalOptionCheck() { - if ( ( $this->checkpointFiles && ! $this->maxTimeAllowed ) || - ( $this->maxTimeAllowed && !$this->checkpointFiles ) ) { + 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" ); + $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" ); + throw new MWException( "Option checkpointfile must contain two '%s' " + . "for substitution of first and last pageids, count is $count instead, " + . "file is $checkpointFile.\n" ); } } 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" ); + throw new MWException( "One checkpointfile must be specified " + . "for each output option, if maxtime is used.\n" ); } } } /** * @throws MWException Failure to parse XML input - * @return true + * @param string $input + * @return bool */ function readDump( $input ) { $this->buffer = ""; @@ -341,7 +358,11 @@ class TextPassDumper extends BackupDumper { $parser = xml_parser_create( "UTF-8" ); xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false ); - xml_set_element_handler( $parser, array( &$this, 'startElement' ), array( &$this, 'endElement' ) ); + xml_set_element_handler( + $parser, + array( &$this, 'startElement' ), + array( &$this, 'endElement' ) + ); xml_set_character_data_handler( $parser, array( &$this, 'characterData' ) ); $offset = 0; // for context extraction on error reporting @@ -359,7 +380,7 @@ class TextPassDumper extends BackupDumper { 'XML import parse failure', xml_get_current_line_number( $parser ), xml_get_current_column_number( $parser ), - $byte . ( is_null( $chunk ) ? null : ( '; "' . substr( $chunk, $byte -$offset, 16 ) . '"' ) ), + $byte . ( is_null( $chunk ) ? null : ( '; "' . substr( $chunk, $byte - $offset, 16 ) . '"' ) ), xml_error_string( xml_get_error_code( $parser ) ) )->escaped(); xml_parser_free( $parser ); @@ -378,15 +399,16 @@ class TextPassDumper extends BackupDumper { # 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 ) { + if ( !$this->firstPageWritten ) { $firstPageID = str_pad( 0, 9, "0", STR_PAD_LEFT ); $lastPageID = str_pad( 0, 9, "0", STR_PAD_LEFT ); - } - else { + } 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++ ) { + + $filenameCount = count( $filenameList ); + for ( $i = 0; $i < $filenameCount; $i++ ) { $checkpointNameFilledIn = sprintf( $this->checkpointFiles[$i], $firstPageID, $lastPageID ); $fileinfo = pathinfo( $filenameList[$i] ); $newFilenames[] = $fileinfo['dirname'] . '/' . $checkpointNameFilledIn; @@ -408,7 +430,7 @@ class TextPassDumper extends BackupDumper { * $this->maxConsecutiveFailedTextRetrievals consecutive calls to getText, MWException * is thrown. * - * @param $id string The revision id to get the text for + * @param string $id The revision id to get the text for * * @return string The revision text for $id, or "" * @throws MWException @@ -420,8 +442,8 @@ class TextPassDumper extends BackupDumper { $text = false; // The candidate for a good text. false if no proper value. $failures = 0; // The number of times, this invocation of getText already failed. - static $consecutiveFailedTextRetrievals = 0; // The number of times getText failed without - // yielding a good text in between. + // The number of times getText failed without yielding a good text in between. + static $consecutiveFailedTextRetrievals = 0; $this->fetchCount++; @@ -478,7 +500,7 @@ class TextPassDumper extends BackupDumper { // Step 2: Checking for plausibility and return the text if it is // plausible $revID = intval( $this->thisRev ); - if ( ! isset( $this->db ) ) { + if ( !isset( $this->db ) ) { throw new MWException( "No database available" ); } @@ -497,9 +519,7 @@ class TextPassDumper extends BackupDumper { $revLength = $row->rev_len; } } - - } - else { + } else { $revLength = $this->db->selectField( 'revision', 'rev_len', array( 'rev_id' => $revID ) ); } @@ -507,12 +527,12 @@ class TextPassDumper extends BackupDumper { if ( $tryIsPrefetch ) { $this->prefetchCount++; } + return $text; } $text = false; throw new MWException( "Received text is unplausible for id " . $id ); - } catch ( Exception $e ) { $msg = "getting/checking text " . $id . " failed (" . $e->getMessage() . ")"; if ( $failures + 1 < $this->maxFailures ) { @@ -525,7 +545,7 @@ class TextPassDumper extends BackupDumper { $failures++; // A failure in a prefetch hit does not warrant resetting db connection etc. - if ( ! $tryIsPrefetch ) { + if ( !$tryIsPrefetch ) { // After backing off for some time, we try to reboot the whole process as // much as possible to not carry over failures from one part to the other // parts @@ -556,16 +576,15 @@ class TextPassDumper extends BackupDumper { return ""; } - /** * May throw a database error if, say, the server dies during query. - * @param $id + * @param int $id * @return bool|string * @throws MWException */ private function getTextDb( $id ) { global $wgContLang; - if ( ! isset( $this->db ) ) { + if ( !isset( $this->db ) ) { throw new MWException( __METHOD__ . "No database available" ); } $row = $this->db->selectRow( 'text', @@ -578,6 +597,7 @@ class TextPassDumper extends BackupDumper { } $stripped = str_replace( "\r", "", $text ); $normalized = $wgContLang->normalize( $stripped ); + return $normalized; } @@ -589,6 +609,7 @@ class TextPassDumper extends BackupDumper { } $text = $this->getTextSpawnedOnce( $id ); wfRestoreWarnings(); + return $text; } @@ -603,8 +624,7 @@ class TextPassDumper extends BackupDumper { "$IP/../multiversion/MWScript.php", "fetchText.php", '--wiki', wfWikiID() ) ) ); - } - else { + } else { $cmd = implode( " ", array_map( 'wfEscapeShellArg', array( @@ -623,11 +643,12 @@ class TextPassDumper extends BackupDumper { if ( !$this->spawnProc ) { // shit $this->progress( "Subprocess spawn failed." ); + return false; } list( $this->spawnWrite, // -> stdin - $this->spawnRead, // <- stdout + $this->spawnRead, // <- stdout ) = $pipes; return true; @@ -705,12 +726,14 @@ class TextPassDumper extends BackupDumper { $gotbytes = strlen( $text ); if ( $gotbytes != $nbytes ) { $this->progress( "Expected $nbytes bytes from database subprocess, got $gotbytes " ); + return false; } // Do normalization in the dump thread... $stripped = str_replace( "\r", "", $text ); $normalized = $wgContLang->normalize( $stripped ); + return $normalized; } @@ -758,7 +781,7 @@ class TextPassDumper extends BackupDumper { $this->buffer = ""; $this->thisRev = ""; } elseif ( $name == 'page' ) { - if ( ! $this->firstPageWritten ) { + if ( !$this->firstPageWritten ) { $this->firstPageWritten = trim( $this->thisPage ); } $this->lastPageWritten = trim( $this->thisPage ); @@ -779,7 +802,8 @@ class TextPassDumper extends BackupDumper { $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++ ) { + $filenamesCount = count( $filenameList ); + for ( $i = 0; $i < $filenamesCount; $i++ ) { $checkpointNameFilledIn = sprintf( $this->checkpointFiles[$i], $firstPageID, $lastPageID ); $fileinfo = pathinfo( $filenameList[$i] ); $newFilenames[] = $fileinfo['dirname'] . '/' . $checkpointNameFilledIn; @@ -790,13 +814,11 @@ class TextPassDumper extends BackupDumper { $this->timeOfCheckpoint = $this->lastTime; $this->firstPageWritten = false; $this->checkpointJustWritten = true; - } - else { + } else { $this->egress->writeClosePage( $this->buffer ); $this->buffer = ""; $this->thisPage = ""; } - } elseif ( $name == 'mediawiki' ) { $this->egress->writeCloseStream( $this->buffer ); $this->buffer = ""; diff --git a/maintenance/benchmarks/Benchmarker.php b/maintenance/benchmarks/Benchmarker.php index dd558f32..3f8a8990 100644 --- a/maintenance/benchmarks/Benchmarker.php +++ b/maintenance/benchmarks/Benchmarker.php @@ -46,38 +46,38 @@ abstract class Benchmarker extends Maintenance { $bench_number = 0; $count = $this->getOption( 'count', 100 ); - foreach( $benchs as $bench ) { + foreach ( $benchs as $bench ) { // handle empty args - if( !array_key_exists( 'args', $bench ) ) { + if ( !array_key_exists( 'args', $bench ) ) { $bench['args'] = array(); } $bench_number++; $start = microtime( true ); - for( $i = 0; $i < $count; $i++ ) { + for ( $i = 0; $i < $count; $i++ ) { call_user_func_array( $bench['function'], $bench['args'] ); } $delta = microtime( true ) - $start; // function passed as a callback - if( is_array( $bench['function'] ) ) { + if ( is_array( $bench['function'] ) ) { $ret = get_class( $bench['function'][0] ) . '->' . $bench['function'][1]; $bench['function'] = $ret; } $this->results[$bench_number] = array( - 'function' => $bench['function'], + 'function' => $bench['function'], 'arguments' => $bench['args'], - 'count' => $count, - 'delta' => $delta, - 'average' => $delta / $count, - ); + 'count' => $count, + 'delta' => $delta, + 'average' => $delta / $count, + ); } } public function getFormattedResults() { $ret = ''; - foreach( $this->results as $res ) { + foreach ( $this->results as $res ) { // show function with args $ret .= sprintf( "%s times: function %s(%s) :\n", $res['count'], @@ -89,6 +89,7 @@ abstract class Benchmarker extends Maintenance { $res['average'] * 1000 ); } + return $ret; } } diff --git a/maintenance/benchmarks/bench_HTTP_HTTPS.php b/maintenance/benchmarks/bench_HTTP_HTTPS.php index 6f800fb3..bb7499b7 100644 --- a/maintenance/benchmarks/bench_HTTP_HTTPS.php +++ b/maintenance/benchmarks/bench_HTTP_HTTPS.php @@ -31,8 +31,7 @@ require_once __DIR__ . '/Benchmarker.php'; * * @ingroup Benchmark */ -class bench_HTTP_HTTPS extends Benchmarker { - +class BenchHttpHttps extends Benchmarker { public function __construct() { parent::__construct(); $this->mDescription = "Benchmark HTTP request vs HTTPS request."; @@ -42,7 +41,7 @@ class bench_HTTP_HTTPS extends Benchmarker { $this->bench( array( array( 'function' => array( $this, 'getHTTP' ) ), array( 'function' => array( $this, 'getHTTPS' ) ), - )); + ) ); print $this->getFormattedResults(); } @@ -61,5 +60,5 @@ class bench_HTTP_HTTPS extends Benchmarker { } } -$maintClass = 'bench_HTTP_HTTPS'; +$maintClass = 'BenchHttpHttps'; require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/benchmarks/bench_delete_truncate.php b/maintenance/benchmarks/bench_delete_truncate.php index 3eff534b..8ae4f030 100644 --- a/maintenance/benchmarks/bench_delete_truncate.php +++ b/maintenance/benchmarks/bench_delete_truncate.php @@ -29,7 +29,6 @@ require_once __DIR__ . '/Benchmarker.php'; * @ingroup Benchmark */ class BenchmarkDeleteTruncate extends Benchmarker { - public function __construct() { parent::__construct(); $this->mDescription = "Benchmarks SQL DELETE vs SQL TRUNCATE."; @@ -70,20 +69,20 @@ class BenchmarkDeleteTruncate extends Benchmarker { } /** - * @param $dbw DatabaseBase + * @param DatabaseBase $dbw * @return void */ private function insertData( $dbw ) { $range = range( 0, 1024 ); $data = array(); - foreach( $range as $r ) { + foreach ( $range as $r ) { $data[] = array( 'text' => $r ); } $dbw->insert( 'test', $data, __METHOD__ ); } /** - * @param $dbw DatabaseBase + * @param DatabaseBase $dbw * @return void */ private function delete( $dbw ) { @@ -91,7 +90,7 @@ class BenchmarkDeleteTruncate extends Benchmarker { } /** - * @param $dbw DatabaseBase + * @param DatabaseBase $dbw * @return void */ private function truncate( $dbw ) { diff --git a/maintenance/benchmarks/bench_if_switch.php b/maintenance/benchmarks/bench_if_switch.php index 80fd9623..698a0f0a 100644 --- a/maintenance/benchmarks/bench_if_switch.php +++ b/maintenance/benchmarks/bench_if_switch.php @@ -31,8 +31,7 @@ require_once __DIR__ . '/Benchmarker.php'; * * @ingroup Maintenance */ -class bench_if_switch extends Benchmarker { - +class BenchIfSwitch extends Benchmarker { public function __construct() { parent::__construct(); $this->mDescription = "Benchmark if elseif... versus switch case."; @@ -42,55 +41,71 @@ class bench_if_switch extends Benchmarker { $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 {} + 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; + 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'; +$maintClass = 'BenchIfSwitch'; require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/benchmarks/bench_strtr_str_replace.php b/maintenance/benchmarks/bench_strtr_str_replace.php index bd21b186..44c8e032 100644 --- a/maintenance/benchmarks/bench_strtr_str_replace.php +++ b/maintenance/benchmarks/bench_strtr_str_replace.php @@ -38,8 +38,7 @@ function bfNormalizeTitleStrReplace( $str ) { * * @ingroup Benchmark */ -class bench_strtr_str_replace extends Benchmarker { - +class BenchStrtrStrReplace extends Benchmarker { public function __construct() { parent::__construct(); $this->mDescription = "Benchmark for strtr() vs str_replace()."; @@ -51,7 +50,7 @@ class bench_strtr_str_replace extends Benchmarker { array( 'function' => array( $this, 'benchstr_replace' ) ), array( 'function' => array( $this, 'benchstrtr_indirect' ) ), array( 'function' => array( $this, 'benchstr_replace_indirect' ) ), - )); + ) ); print $this->getFormattedResults(); } @@ -60,10 +59,9 @@ class bench_strtr_str_replace extends Benchmarker { } function benchstr_replace() { - str_replace( "_", " ", "[[MediaWiki:Some_random_test_page]]"); + str_replace( "_", " ", "[[MediaWiki:Some_random_test_page]]" ); } - function benchstrtr_indirect() { bfNormalizeTitleStrTr( "[[MediaWiki:Some_random_test_page]]" ); } @@ -71,8 +69,7 @@ class bench_strtr_str_replace extends Benchmarker { function benchstr_replace_indirect() { bfNormalizeTitleStrReplace( "[[MediaWiki:Some_random_test_page]]" ); } - } -$maintClass = 'bench_strtr_str_replace'; +$maintClass = 'BenchStrtrStrReplace'; require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/benchmarks/bench_utf8_title_check.php b/maintenance/benchmarks/bench_utf8_title_check.php index 078293eb..b742f666 100644 --- a/maintenance/benchmarks/bench_utf8_title_check.php +++ b/maintenance/benchmarks/bench_utf8_title_check.php @@ -29,8 +29,7 @@ require_once __DIR__ . '/Benchmarker.php'; * * @ingroup Benchmark */ -class bench_utf8_title_check extends Benchmarker { - +class BenchUtf8TitleCheck extends Benchmarker { private $canRun; private $data; @@ -38,6 +37,7 @@ class bench_utf8_title_check extends Benchmarker { public function __construct() { parent::__construct(); + // @codingStandardsIgnoreStart Ignore long line warnings. $this->data = array( "", "United States of America", // 7bit ASCII @@ -59,11 +59,13 @@ class bench_utf8_title_check extends Benchmarker { . "Sara%20Sidle%7CSofia%20Curtis%7CS%C3%A9rie%20t%C3%A9l%C3%A9vis%C3%A9e%7CWallace%20Langham%7C" . "Warrick%20Brown%7CWendy%20Simms%7C%C3%89tats-Unis" ); + // @codingStandardsIgnoreEnd - $this->canRun = function_exists ( 'mb_check_encoding' ); + $this->canRun = function_exists( 'mb_check_encoding' ); if ( $this->canRun ) { - $this->mDescription = "Benchmark for using a regexp vs. mb_check_encoding to check for UTF-8 encoding."; + $this->mDescription = "Benchmark for using a regexp vs. mb_check_encoding " . + "to check for UTF-8 encoding."; mb_internal_encoding( 'UTF-8' ); } else { $this->mDescription = "CANNOT RUN benchmark using mb_check_encoding: function not available."; @@ -75,22 +77,22 @@ class bench_utf8_title_check extends Benchmarker { return; } $benchmarks = array(); - foreach ($this->data as $val) { + foreach ( $this->data as $val ) { $benchmarks[] = array( 'function' => array( $this, 'use_regexp' ), - 'args' => array( rawurldecode ( $val ) ) + 'args' => array( rawurldecode( $val ) ) ); $benchmarks[] = array( 'function' => array( $this, 'use_regexp_non_capturing' ), - 'args' => array( rawurldecode ( $val ) ) + 'args' => array( rawurldecode( $val ) ) ); $benchmarks[] = array( 'function' => array( $this, 'use_regexp_once_only' ), - 'args' => array( rawurldecode ( $val ) ) + 'args' => array( rawurldecode( $val ) ) ); $benchmarks[] = array( 'function' => array( $this, 'use_mb_check_encoding' ), - 'args' => array( rawurldecode ( $val ) ) + 'args' => array( rawurldecode( $val ) ) ); } $this->bench( $benchmarks ); @@ -101,26 +103,25 @@ class bench_utf8_title_check extends Benchmarker { function use_regexp( $s ) { $this->isutf8 = preg_match( '/^([\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' . - '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})+$/', $s ); + '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})+$/', $s ); } function use_regexp_non_capturing( $s ) { // Same as above with a non-capturing subgroup. $this->isutf8 = preg_match( '/^(?:[\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' . - '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})+$/', $s ); + '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})+$/', $s ); } function use_regexp_once_only( $s ) { // Same as above with a once-only subgroup. $this->isutf8 = preg_match( '/^(?>[\x00-\x7f]|[\xc0-\xdf][\x80-\xbf]|' . - '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})+$/', $s ); + '[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xf7][\x80-\xbf]{3})+$/', $s ); } function use_mb_check_encoding( $s ) { $this->isutf8 = mb_check_encoding( $s, 'UTF-8' ); } - } -$maintClass = 'bench_utf8_title_check'; +$maintClass = 'BenchUtf8TitleCheck'; require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/benchmarks/bench_wfBaseConvert.php b/maintenance/benchmarks/bench_wfBaseConvert.php index f8a21562..b4be12bc 100644 --- a/maintenance/benchmarks/bench_wfBaseConvert.php +++ b/maintenance/benchmarks/bench_wfBaseConvert.php @@ -29,8 +29,7 @@ require_once __DIR__ . '/Benchmarker.php'; * * @ingroup Benchmark */ -class bench_wfBaseConvert extends Benchmarker { - +class BenchWfBaseConvert extends Benchmarker { public function __construct() { parent::__construct(); $this->mDescription = "Benchmark for wfBaseConvert."; @@ -58,7 +57,7 @@ class bench_wfBaseConvert extends Benchmarker { 'function' => 'wfBaseConvert', 'args' => array( $number, $inbase, $outbase, 0, true, 'gmp' ) ), - )); + ) ); $this->output( $this->getFormattedResults() ); } @@ -66,12 +65,13 @@ class bench_wfBaseConvert extends Benchmarker { protected static function makeRandomNumber( $base, $length ) { $baseChars = "0123456789abcdefghijklmnopqrstuvwxyz"; $res = ""; - for( $i = 0; $i < $length; $i++ ) { - $res .= $baseChars[mt_rand(0, $base - 1)]; + for ( $i = 0; $i < $length; $i++ ) { + $res .= $baseChars[mt_rand( 0, $base - 1 )]; } + return $res; } } -$maintClass = 'bench_wfBaseConvert'; +$maintClass = 'BenchWfBaseConvert'; require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/benchmarks/bench_wfIsWindows.php b/maintenance/benchmarks/bench_wfIsWindows.php index 1cd2016b..8446694b 100644 --- a/maintenance/benchmarks/bench_wfIsWindows.php +++ b/maintenance/benchmarks/bench_wfIsWindows.php @@ -31,8 +31,7 @@ require_once __DIR__ . '/Benchmarker.php'; * * @ingroup Benchmark */ -class bench_wfIsWindows extends Benchmarker { - +class BenchWfIsWindows extends Benchmarker { public function __construct() { parent::__construct(); $this->mDescription = "Benchmark for wfIsWindows."; @@ -42,12 +41,12 @@ class bench_wfIsWindows extends Benchmarker { $this->bench( array( array( 'function' => array( $this, 'wfIsWindows' ) ), array( 'function' => array( $this, 'wfIsWindowsCached' ) ), - )); + ) ); print $this->getFormattedResults(); } static function is_win() { - return substr( php_uname(), 0, 7 ) == 'Windows' ; + return substr( php_uname(), 0, 7 ) == 'Windows'; } // bench function 1 @@ -58,12 +57,13 @@ class bench_wfIsWindows extends Benchmarker { // bench function 2 function wfIsWindowsCached() { static $isWindows = null; - if( $isWindows == null ) { + if ( $isWindows == null ) { $isWindows = self::is_win(); } + return $isWindows; } } -$maintClass = 'bench_wfIsWindows'; +$maintClass = 'BenchWfIsWindows'; require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/benchmarks/benchmarkHooks.php b/maintenance/benchmarks/benchmarkHooks.php index 3f5d6db0..fb25b9d9 100644 --- a/maintenance/benchmarks/benchmarkHooks.php +++ b/maintenance/benchmarks/benchmarkHooks.php @@ -29,7 +29,6 @@ require_once __DIR__ . '/Benchmarker.php'; * @ingroup Benchmark */ class BenchmarkHooks extends Benchmarker { - public function __construct() { parent::__construct(); $this->mDescription = 'Benchmark MediaWiki Hooks.'; @@ -46,13 +45,13 @@ class BenchmarkHooks extends Benchmarker { $time = $this->benchHooks(); $this->output( 'Loaded (one) hook: ' . $time . "\n" ); - for( $i = 0; $i < 9; $i++ ) { + for ( $i = 0; $i < 9; $i++ ) { $wgHooks['Test'][] = array( $this, 'test' ); } $time = $this->benchHooks(); $this->output( 'Loaded (ten) hook: ' . $time . "\n" ); - for( $i = 0; $i < 90; $i++ ) { + for ( $i = 0; $i < 90; $i++ ) { $wgHooks['Test'][] = array( $this, 'test' ); } $time = $this->benchHooks(); @@ -61,7 +60,7 @@ class BenchmarkHooks extends Benchmarker { } /** - * @param $trials int + * @param int $trials * @return string */ private function benchHooks( $trials = 10 ) { @@ -71,6 +70,7 @@ class BenchmarkHooks extends Benchmarker { } $delta = microtime( true ) - $start; $pertrial = $delta / $trials; + return sprintf( "Took %6.3fms", $pertrial * 1000 ); } diff --git a/maintenance/benchmarks/benchmarkParse.php b/maintenance/benchmarks/benchmarkParse.php new file mode 100644 index 00000000..ce38dad6 --- /dev/null +++ b/maintenance/benchmarks/benchmarkParse.php @@ -0,0 +1,174 @@ + + * @ingroup Benchmark + */ + +require __DIR__ . '/../Maintenance.php'; + +/** + * Maintenance script to benchmark how long it takes to parse a given title at an optionally + * specified timestamp + * + * @since 1.23 + */ +class BenchmarkParse extends Maintenance { + /** @var string MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS) */ + private $templateTimestamp = null; + + /** @var array Cache that maps a Title DB key to revision ID for the requested timestamp */ + private $idCache = array(); + + function __construct() { + parent::__construct(); + $this->addDescription( 'Benchmark parse operation' ); + $this->addArg( 'title', 'The name of the page to parse' ); + $this->addOption( 'warmup', 'Repeat the parse operation this number of times to warm the cache', + false, true ); + $this->addOption( 'loops', 'Number of times to repeat parse operation post-warmup', + false, true ); + $this->addOption( 'page-time', + 'Use the version of the page which was current at the given time', + false, true ); + $this->addOption( 'tpl-time', + 'Use templates which were current at the given time (except that moves and ' . + 'deletes are not handled properly)', + false, true ); + } + + function execute() { + if ( $this->hasOption( 'tpl-time' ) ) { + $this->templateTimestamp = wfTimestamp( TS_MW, strtotime( $this->getOption( 'tpl-time' ) ) ); + Hooks::register( 'BeforeParserFetchTemplateAndtitle', array( $this, 'onFetchTemplate' ) ); + } + + $title = Title::newFromText( $this->getArg() ); + if ( !$title ) { + $this->error( "Invalid title" ); + exit( 1 ); + } + + if ( $this->hasOption( 'page-time' ) ) { + $pageTimestamp = wfTimestamp( TS_MW, strtotime( $this->getOption( 'page-time' ) ) ); + $id = $this->getRevIdForTime( $title, $pageTimestamp ); + if ( !$id ) { + $this->error( "The page did not exist at that time" ); + exit( 1 ); + } + + $revision = Revision::newFromId( $id ); + } else { + $revision = Revision::newFromTitle( $title ); + } + + if ( !$revision ) { + $this->error( "Unable to load revision, incorrect title?" ); + exit( 1 ); + } + + $warmup = $this->getOption( 'warmup', 1 ); + for ( $i = 0; $i < $warmup; $i++ ) { + $this->runParser( $revision ); + } + + $loops = $this->getOption( 'loops', 1 ); + if ( $loops < 1 ) { + $this->error( 'Invalid number of loops specified', true ); + } + $startUsage = getrusage(); + $startTime = microtime( true ); + for ( $i = 0; $i < $loops; $i++ ) { + $this->runParser( $revision ); + } + $endUsage = getrusage(); + $endTime = microtime( true ); + + printf( "CPU time = %.3f s, wall clock time = %.3f s\n", + // CPU time + ( $endUsage['ru_utime.tv_sec'] + $endUsage['ru_utime.tv_usec'] * 1e-6 + - $startUsage['ru_utime.tv_sec'] - $startUsage['ru_utime.tv_usec'] * 1e-6 ) / $loops, + // Wall clock time + ( $endTime - $startTime ) / $loops + ); + } + + /** + * Fetch the ID of the revision of a Title that occurred + * + * @param Title $title + * @param string $timestamp + * @return bool|string Revision ID, or false if not found or error + */ + function getRevIdForTime( Title $title, $timestamp ) { + $dbr = wfGetDB( DB_SLAVE ); + + $id = $dbr->selectField( + array( 'revision', 'page' ), + 'rev_id', + array( + 'page_namespace' => $title->getNamespace(), + 'page_title' => $title->getDBkey(), + 'rev_timestamp <= ' . $dbr->addQuotes( $timestamp ) + ), + __METHOD__, + array( 'ORDER BY' => 'rev_timestamp DESC', 'LIMIT' => 1 ), + array( 'revision' => array( 'INNER JOIN', 'rev_page=page_id' ) ) + ); + + return $id; + } + + /** + * Parse the text from a given Revision + * + * @param Revision $revision + */ + function runParser( Revision $revision ) { + $content = $revision->getContent(); + $content->getParserOutput( $revision->getTitle(), $revision->getId() ); + } + + /** + * Hook into the parser's revision ID fetcher. Make sure that the parser only + * uses revisions around the specified timestamp. + * + * @param Parser $parser + * @param Title $title + * @param bool &$skip + * @param string|bool &$id + * @return bool + */ + function onFetchTemplate( Parser $parser, Title $title, &$skip, &$id ) { + $pdbk = $title->getPrefixedDBkey(); + if ( !isset( $this->idCache[$pdbk] ) ) { + $proposedId = $this->getRevIdForTime( $title, $this->templateTimestamp ); + $this->idCache[$pdbk] = $proposedId; + } + if ( $this->idCache[$pdbk] !== false ) { + $id = $this->idCache[$pdbk]; + } + + return true; + } +} + +$maintClass = 'BenchmarkParse'; +require RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/benchmarks/benchmarkPurge.php b/maintenance/benchmarks/benchmarkPurge.php index fd863d52..42c1eb78 100644 --- a/maintenance/benchmarks/benchmarkPurge.php +++ b/maintenance/benchmarks/benchmarkPurge.php @@ -29,7 +29,6 @@ require_once __DIR__ . '/Benchmarker.php'; * @ingroup Benchmark */ class BenchmarkPurge extends Benchmarker { - public function __construct() { parent::__construct(); $this->mDescription = "Benchmark the Squid purge functions."; @@ -57,8 +56,8 @@ class BenchmarkPurge extends Benchmarker { /** * Run a bunch of URLs through SquidUpdate::purge() * to benchmark Squid response times. - * @param $urls array A bunch of URLs to purge - * @param $trials int How many times to run the test? + * @param array $urls A bunch of URLs to purge + * @param int $trials How many times to run the test? * @return string */ private function benchSquid( $urls, $trials = 1 ) { @@ -69,13 +68,14 @@ class BenchmarkPurge extends Benchmarker { $delta = microtime( true ) - $start; $pertrial = $delta / $trials; $pertitle = $pertrial / count( $urls ); + return sprintf( "%4d titles in %6.2fms (%6.2fms each)", count( $urls ), $pertrial * 1000.0, $pertitle * 1000.0 ); } /** * Get an array of randomUrl()'s. - * @param $length int How many urls to add to the array + * @param int $length How many urls to add to the array * @return array */ private function randomUrlList( $length ) { @@ -83,6 +83,7 @@ class BenchmarkPurge extends Benchmarker { for ( $i = 0; $i < $length; $i++ ) { $list[] = $this->randomUrl(); } + return $list; } @@ -93,6 +94,7 @@ class BenchmarkPurge extends Benchmarker { */ private function randomUrl() { global $wgServer, $wgArticlePath; + return $wgServer . str_replace( '$1', $this->randomTitle(), $wgArticlePath ); } @@ -107,6 +109,7 @@ class BenchmarkPurge extends Benchmarker { for ( $i = 0; $i < $length; $i++ ) { $str .= chr( mt_rand( ord( 'a' ), ord( 'z' ) ) ); } + return ucfirst( $str ); } } diff --git a/maintenance/cdb.php b/maintenance/cdb.php index d42f9f7a..86c686b4 100644 --- a/maintenance/cdb.php +++ b/maintenance/cdb.php @@ -52,7 +52,7 @@ do { $bad = false; $showhelp = false; $quit = false; - static $fileHandle; + static $fileHandle = false; $line = Maintenance::readconsole(); if ( $line === false ) { @@ -75,7 +75,11 @@ do { } $file = $args[0]; print "Loading cdb file $file..."; - $fileHandle = CdbReader::open( $file ); + try { + $fileHandle = CdbReader::open( $file ); + } catch ( CdbException $e ) { + } + if ( !$fileHandle ) { print "not a cdb file or unable to read it\n"; } else { @@ -91,7 +95,12 @@ do { print "Need to specify a key, Luke\n"; break; } - $res = $fileHandle->get( $args[0] ); + try { + $res = $fileHandle->get( $args[0] ); + } catch ( CdbException $e ) { + print "Unable to read key from file\n"; + break; + } if ( $res === false ) { print "No such key/value pair\n"; } elseif ( is_string( $res ) ) { diff --git a/maintenance/checkBadRedirects.php b/maintenance/checkBadRedirects.php index a96e9b80..fec92910 100644 --- a/maintenance/checkBadRedirects.php +++ b/maintenance/checkBadRedirects.php @@ -44,7 +44,7 @@ class CheckBadRedirects extends Maintenance { $count = $result->numRows(); $this->output( "Found $count redirects.\n" . - "Checking for bad redirects:\n\n" ); + "Checking for bad redirects:\n\n" ); foreach ( $result as $row ) { $title = Title::makeTitle( $row->page_namespace, $row->page_title ); diff --git a/maintenance/checkImages.php b/maintenance/checkImages.php index e6aea537..3921c079 100644 --- a/maintenance/checkImages.php +++ b/maintenance/checkImages.php @@ -73,13 +73,13 @@ class CheckImages extends Maintenance { } if ( $stat['size'] != $row->img_size ) { - $this->output( "{$row->img_name}: size mismatch DB={$row->img_size}, actual={$stat['size']}\n" ); + $this->output( "{$row->img_name}: size mismatch DB={$row->img_size}, " + . "actual={$stat['size']}\n" ); continue; } $numGood++; } - } while ( $res->numRows() ); $this->output( "Good images: $numGood/$numImages\n" ); diff --git a/maintenance/checkLess.php b/maintenance/checkLess.php index d02d8a7b..b97e1b0b 100644 --- a/maintenance/checkLess.php +++ b/maintenance/checkLess.php @@ -22,49 +22,33 @@ */ require_once __DIR__ . '/Maintenance.php'; +require_once 'PHPUnit/Autoload.php'; /** * @ingroup Maintenance */ class CheckLess extends Maintenance { + public function __construct() { parent::__construct(); - $this->mDescription = 'Checks LESS files for errors'; + $this->mDescription = + 'Checks LESS files for errors by running the LessTestSuite PHPUnit test suite'; } public function execute() { - $result = false; - $resourceLoader = new ResourceLoader(); - foreach ( $resourceLoader->getModuleNames() as $name ) { - /** @var ResourceLoaderFileModule $module */ - $module = $resourceLoader->getModule( $name ); - if ( !$module || !$module instanceof ResourceLoaderFileModule ) { - continue; - } + global $IP; + + // NOTE (phuedx, 2014-03-26) wgAutoloadClasses isn't set up + // by either of the dependencies at the top of the file, so + // require it here. + require_once __DIR__ . '/../tests/TestsAutoLoader.php'; - $hadErrors = false; - foreach ( $module->getAllStyleFiles() as $file ) { - if ( $module->getStyleSheetLang( $file ) !== 'less' ) { - continue; - } - try { - $compiler = ResourceLoader::getLessCompiler(); - $compiler->compileFile( $file ); - } catch ( Exception $e ) { - if ( !$hadErrors ) { - $this->error( "Errors checking module $name:\n" ); - $hadErrors = true; - } - $this->error( $e->getMessage() . "\n" ); - $result = true; - } - } - } - if ( !$result ) { - $this->output( "No errors found\n" ); - } else { - die( 1 ); - } + $textUICommand = new PHPUnit_TextUI_Command(); + $argv = array( + "$IP/tests/phpunit/phpunit.php", + "$IP/tests/phpunit/suites/LessTestSuite.php" + ); + $textUICommand->run( $argv ); } } diff --git a/maintenance/checkSyntax.php b/maintenance/checkSyntax.php index dc8626df..d1c2edd0 100644 --- a/maintenance/checkSyntax.php +++ b/maintenance/checkSyntax.php @@ -38,10 +38,23 @@ class CheckSyntax extends Maintenance { parent::__construct(); $this->mDescription = "Check syntax for all PHP files in MediaWiki"; $this->addOption( 'with-extensions', 'Also recurse the extensions folder' ); - $this->addOption( 'path', 'Specific path (file or directory) to check, either with absolute path or relative to the root of this MediaWiki installation', - false, true ); - $this->addOption( 'list-file', 'Text file containing list of files or directories to check', false, true ); - $this->addOption( 'modified', 'Check only files that were modified (requires Git command-line client)' ); + $this->addOption( + 'path', + 'Specific path (file or directory) to check, either with absolute path or ' + . 'relative to the root of this MediaWiki installation', + false, + true + ); + $this->addOption( + 'list-file', + 'Text file containing list of files or directories to check', + false, + true + ); + $this->addOption( + 'modified', + 'Check only files that were modified (requires Git command-line client)' + ); $this->addOption( 'syntax-only', 'Check for syntax validity only, skip code style warnings' ); } @@ -53,7 +66,8 @@ class CheckSyntax extends Maintenance { $this->buildFileList(); // ParseKit is broken on PHP 5.3+, disabled until this is fixed - $useParseKit = function_exists( 'parsekit_compile_file' ) && version_compare( PHP_VERSION, '5.3', '<' ); + $useParseKit = function_exists( 'parsekit_compile_file' ) + && version_compare( PHP_VERSION, '5.3', '<' ); $str = 'Checking syntax (using ' . ( $useParseKit ? 'parsekit' : ' php -l, this can take a long time' ) . ")\n"; @@ -82,7 +96,7 @@ class CheckSyntax extends Maintenance { $this->mIgnorePaths = array( // Compat stuff, explodes on PHP 5.3 "includes/NamespaceCompat.php$", - ); + ); $this->mNoStyleCheckPaths = array( // Third-party code we don't care about @@ -96,13 +110,14 @@ class CheckSyntax extends Maintenance { "QPoll/Excel/", "/geshi/", "/smarty/", - ); + ); if ( $this->hasOption( 'path' ) ) { $path = $this->getOption( 'path' ); if ( !$this->addPath( $path ) ) { $this->error( "Error: can't find file or directory $path\n", true ); } + return; // process only this path } elseif ( $this->hasOption( 'list-file' ) ) { $file = $this->getOption( 'list-file' ); @@ -117,6 +132,7 @@ class CheckSyntax extends Maintenance { $this->addPath( $path ); } fclose( $f ); + return; } elseif ( $this->hasOption( 'modified' ) ) { $this->output( "Retrieving list from Git... " ); @@ -127,6 +143,7 @@ class CheckSyntax extends Maintenance { $this->mFiles[] = $file; } } + return; } @@ -153,17 +170,14 @@ class CheckSyntax extends Maintenance { if ( file_exists( "$IP/LocalSettings.php" ) ) { $this->mFiles[] = "$IP/LocalSettings.php"; } - if ( file_exists( "$IP/AdminSettings.php" ) ) { - $this->mFiles[] = "$IP/AdminSettings.php"; - } $this->output( 'done.', 'listfiles' ); } /** * Returns a list of tracked files in a Git work tree differing from the master branch. - * @param $path string: Path to the repository - * @return array: Resulting list of changed files + * @param string $path Path to the repository + * @return array Resulting list of changed files */ private function getGitModifiedFiles( $path ) { @@ -215,7 +229,7 @@ class CheckSyntax extends Maintenance { /** * Returns true if $file is of a type we can check - * @param $file string + * @param string $file * @return bool */ private function isSuitableFile( $file ) { @@ -230,22 +244,24 @@ class CheckSyntax extends Maintenance { return false; } } + return true; } /** * Add given path to file list, searching it in include path if needed - * @param $path string + * @param string $path * @return bool */ private function addPath( $path ) { global $IP; + return $this->addFileOrDir( $path ) || $this->addFileOrDir( "$IP/$path" ); } /** * Add given file to file list, or, if it's a directory, add its content - * @param $path string + * @param string $path * @return bool */ private function addFileOrDir( $path ) { @@ -256,13 +272,14 @@ class CheckSyntax extends Maintenance { } else { return false; } + return true; } /** * Add all suitable files in given directory or its subdirectories to the file list * - * @param $dir String: directory to process + * @param string $dir Directory to process */ private function addDirectoryContent( $dir ) { $iterator = new RecursiveIteratorIterator( @@ -279,8 +296,8 @@ class CheckSyntax extends Maintenance { /** * Check a file for syntax errors using Parsekit. Shamelessly stolen * from tools/lint.php by TimStarling - * @param $file String Path to a file to check for syntax errors - * @return boolean + * @param string $file Path to a file to check for syntax errors + * @return bool */ private function checkFileWithParsekit( $file ) { static $okErrors = array( @@ -302,21 +319,24 @@ class CheckSyntax extends Maintenance { $this->mFailures[$file] = $errors; } } + return $ret; } /** * Check a file for syntax errors using php -l - * @param $file String Path to a file to check for syntax errors - * @return boolean + * @param string $file Path to a file to check for syntax errors + * @return bool */ private function checkFileWithCli( $file ) { $res = exec( 'php -l ' . wfEscapeShellArg( $file ) ); if ( strpos( $res, 'No syntax errors detected' ) === false ) { $this->mFailures[$file] = $res; $this->output( $res . "\n" ); + return false; } + return true; } @@ -324,8 +344,7 @@ class CheckSyntax extends Maintenance { * Check a file for non-fatal coding errors, such as byte-order marks in the beginning * or pointless ?> closing tags at the end. * - * @param $file String String Path to a file to check for errors - * @return boolean + * @param string $file String Path to a file to check for errors */ private function checkForMistakes( $file ) { foreach ( $this->mNoStyleCheckPaths as $regex ) { diff --git a/maintenance/checkUsernames.php b/maintenance/checkUsernames.php index 6df189fc..777c8334 100644 --- a/maintenance/checkUsernames.php +++ b/maintenance/checkUsernames.php @@ -55,7 +55,7 @@ class CheckUsernames extends Maintenance { ); foreach ( $res as $row ) { - if ( ! User::isValidUserName( $row->user_name ) ) { + if ( !User::isValidUserName( $row->user_name ) ) { $this->error( sprintf( "%s: %6d: '%s'\n", wfWikiID(), $row->user_id, $row->user_name ) ); wfDebugLog( 'checkUsernames', $row->user_name ); } diff --git a/maintenance/cleanupAncientTables.php b/maintenance/cleanupAncientTables.php index 694efaa6..2dbf8bc1 100644 --- a/maintenance/cleanupAncientTables.php +++ b/maintenance/cleanupAncientTables.php @@ -40,8 +40,9 @@ class CleanupAncientTables extends Maintenance { public function execute() { if ( !$this->hasOption( 'force' ) ) { $this->error( "This maintenance script will remove old columns and indexes.\n" - . "It is recommended to backup your database first, and ensure all your data has been migrated to newer tables\n" - . "If you want to continue, run this script again with the --force \n" + . "It is recommended to backup your database first, and ensure all your data has\n" + . "been migrated to newer tables. If you want to continue, run this script again\n" + . "with --force.\n" ); } @@ -82,7 +83,7 @@ class CleanupAncientTables extends Maintenance { if ( $db->indexExists( 'text', $index, __METHOD__ ) ) { $this->output( "Dropping index $index from the text table..." ); $db->query( "DROP INDEX " . $db->addIdentifierQuotes( $index ) - . " ON " . $db->tableName( 'text' ) ); + . " ON " . $db->tableName( 'text' ) ); $this->output( "done.\n" ); } } @@ -101,7 +102,7 @@ class CleanupAncientTables extends Maintenance { if ( $db->fieldExists( 'text', $field, __METHOD__ ) ) { $this->output( "Dropping the $field field from the text table..." ); $db->query( "ALTER TABLE " . $db->tableName( 'text' ) - . " DROP COLUMN " . $db->addIdentifierQuotes( $field ) ); + . " DROP COLUMN " . $db->addIdentifierQuotes( $field ) ); $this->output( "done.\n" ); } } diff --git a/maintenance/cleanupCaps.php b/maintenance/cleanupCaps.php index 1a47ac4e..9e88c135 100644 --- a/maintenance/cleanupCaps.php +++ b/maintenance/cleanupCaps.php @@ -7,7 +7,7 @@ * --dry-run don't actually try moving them * * Copyright © 2005 Brion Vibber - * http://www.mediawiki.org/ + * https://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 @@ -71,6 +71,7 @@ class CapsCleanup extends TableCleanup { $lower = $wgContLang->lcfirst( $row->page_title ); if ( $upper == $lower ) { $this->output( "\"$display\" already lowercase.\n" ); + return $this->progress( 0 ); } @@ -78,6 +79,7 @@ class CapsCleanup extends TableCleanup { $targetDisplay = $target->getPrefixedText(); if ( $target->exists() ) { $this->output( "\"$display\" skipped; \"$targetDisplay\" already exists\n" ); + return $this->progress( 0 ); } @@ -98,6 +100,7 @@ class CapsCleanup extends TableCleanup { } } } + return $this->progress( 0 ); } } diff --git a/maintenance/cleanupImages.php b/maintenance/cleanupImages.php index 0e0b6194..915a2c08 100644 --- a/maintenance/cleanupImages.php +++ b/maintenance/cleanupImages.php @@ -7,7 +7,7 @@ * --fix Actually clean up titles; otherwise just checks for them * * Copyright © 2005-2006 Brion Vibber - * http://www.mediawiki.org/ + * https://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 @@ -56,6 +56,7 @@ class ImageCleanup extends TableCleanup { if ( $source == '' ) { // Ye olde empty rows. Just kill them. $this->killRow( $source ); + return $this->progress( 1 ); } @@ -82,6 +83,7 @@ class ImageCleanup extends TableCleanup { return $this->progress( 0 ); } $this->pokeFile( $source, $safe ); + return $this->progress( 1 ); } @@ -89,6 +91,7 @@ class ImageCleanup extends TableCleanup { $munged = $title->getDBkey(); $this->output( "page $source ($munged) doesn't match self.\n" ); $this->pokeFile( $source, $munged ); + return $this->progress( 1 ); } @@ -96,7 +99,7 @@ class ImageCleanup extends TableCleanup { } /** - * @param $name string + * @param string $name */ private function killRow( $name ) { if ( $this->dryrun ) { @@ -114,6 +117,7 @@ class ImageCleanup extends TableCleanup { if ( !isset( $this->repo ) ) { $this->repo = RepoGroup::singleton()->getLocalRepo(); } + return $this->repo->getRootDirectory() . '/' . $this->repo->getHashPath( $name ) . $name; } @@ -122,7 +126,12 @@ class ImageCleanup extends TableCleanup { } private function pageExists( $name, $db ) { - return $db->selectField( 'page', '1', array( 'page_namespace' => NS_FILE, 'page_title' => $name ), __METHOD__ ); + return $db->selectField( + 'page', + '1', + array( 'page_namespace' => NS_FILE, 'page_title' => $name ), + __METHOD__ + ); } private function pokeFile( $orig, $new ) { @@ -130,6 +139,7 @@ class ImageCleanup extends TableCleanup { if ( !file_exists( $path ) ) { $this->output( "missing file: $path\n" ); $this->killRow( $orig ); + return; } @@ -145,7 +155,7 @@ class ImageCleanup extends TableCleanup { $version = 0; $final = $new; $conflict = ( $this->imageExists( $final, $db ) || - ( $this->pageExists( $orig, $db ) && $this->pageExists( $final, $db ) ) ); + ( $this->pageExists( $orig, $db ) && $this->pageExists( $final, $db ) ) ); while ( $conflict ) { $this->output( "Rename conflicts with '$final'...\n" ); @@ -179,6 +189,7 @@ class ImageCleanup extends TableCleanup { if ( !wfMkdirParents( $dir, null, __METHOD__ ) ) { $this->output( "RENAME FAILED, COULD NOT CREATE $dir" ); $db->rollback( __METHOD__ ); + return; } } @@ -205,6 +216,7 @@ class ImageCleanup extends TableCleanup { $test = Title::makeTitleSafe( NS_FILE, $x ); if ( is_null( $test ) || $test->getDBkey() !== $x ) { $this->error( "Unable to generate safe title from '$name', got '$x'" ); + return false; } diff --git a/maintenance/cleanupRemovedModules.php b/maintenance/cleanupRemovedModules.php index 84eec289..e1d0ed6e 100644 --- a/maintenance/cleanupRemovedModules.php +++ b/maintenance/cleanupRemovedModules.php @@ -36,12 +36,18 @@ class CleanupRemovedModules extends Maintenance { parent::__construct(); $this->mDescription = 'Remove cache entries for removed ResourceLoader modules from the database'; $this->addOption( 'batchsize', 'Delete rows in batches of this size. Default: 500', false, true ); - $this->addOption( 'max-slave-lag', 'If the slave lag exceeds this many seconds, wait until it drops below this value. Default: 5', false, true ); + $this->addOption( + 'max-slave-lag', + 'If the slave lag exceeds this many seconds, wait until it drops below this value. ' + . 'Default: 5', + false, + true + ); } public function execute() { $dbw = wfGetDB( DB_MASTER ); - $rl = new ResourceLoader(); + $rl = new ResourceLoader( ConfigFactory::getDefaultInstance()->makeConfig( 'main' ) ); $moduleNames = $rl->getModuleNames(); $moduleList = implode( ', ', array_map( array( $dbw, 'addQuotes' ), $moduleNames ) ); $limit = max( 1, intval( $this->getOption( 'batchsize', 500 ) ) ); diff --git a/maintenance/cleanupSpam.php b/maintenance/cleanupSpam.php index 4b8c9feb..f4a5147a 100644 --- a/maintenance/cleanupSpam.php +++ b/maintenance/cleanupSpam.php @@ -35,11 +35,14 @@ class CleanupSpam extends Maintenance { $this->mDescription = "Cleanup all spam from a given hostname"; $this->addOption( 'all', 'Check all wikis in $wgLocalDatabases' ); $this->addOption( 'delete', 'Delete pages containing only spam instead of blanking them' ); - $this->addArg( 'hostname', 'Hostname that was spamming, single * wildcard in the beginning allowed' ); + $this->addArg( + 'hostname', + 'Hostname that was spamming, single * wildcard in the beginning allowed' + ); } public function execute() { - global $wgLocalDatabases, $wgUser; + global $IP, $wgLocalDatabases, $wgUser; $username = wfMessage( 'spambot_username' )->text(); $wgUser = User::newFromName( $username ); @@ -67,7 +70,9 @@ class CleanupSpam extends Maintenance { array( 'el_index' . $dbr->buildLike( $like ) ), __METHOD__ ); if ( $count ) { $found = true; - passthru( "php cleanupSpam.php --wiki='$wikiID' $spec | sed 's/^/$wikiID: /'" ); + $cmd = wfShellWikiCmd( "$IP/maintenance/cleanupSpam.php", + array( '--wiki', $wikiID, $spec ) ); + passthru( "$cmd | sed 's/^/$wikiID: /'" ); } } if ( $found ) { @@ -96,6 +101,7 @@ class CleanupSpam extends Maintenance { $title = Title::newFromID( $id ); if ( !$title ) { $this->error( "Internal error: no page for ID $id" ); + return; } @@ -104,7 +110,8 @@ class CleanupSpam extends Maintenance { $currentRevId = $rev->getId(); while ( $rev && ( $rev->isDeleted( Revision::DELETED_TEXT ) - || LinkFilter::matchEntry( $rev->getContent( Revision::RAW ), $domain ) ) ) { + || LinkFilter::matchEntry( $rev->getContent( Revision::RAW ), $domain ) ) + ) { $rev = $rev->getPrevious(); } @@ -121,19 +128,28 @@ class CleanupSpam extends Maintenance { $content = $rev->getContent( Revision::RAW ); $this->output( "reverting\n" ); - $page->doEditContent( $content, wfMessage( 'spam_reverting', $domain )->inContentLanguage()->text(), - EDIT_UPDATE, $rev->getId() ); + $page->doEditContent( + $content, + wfMessage( 'spam_reverting', $domain )->inContentLanguage()->text(), + EDIT_UPDATE, + $rev->getId() + ); } elseif ( $this->hasOption( 'delete' ) ) { // Didn't find a non-spammy revision, blank the page $this->output( "deleting\n" ); - $page->doDeleteArticle( wfMessage( 'spam_deleting', $domain )->inContentLanguage()->text() ); + $page->doDeleteArticle( + wfMessage( 'spam_deleting', $domain )->inContentLanguage()->text() + ); } else { // Didn't find a non-spammy revision, blank the page $handler = ContentHandler::getForTitle( $title ); $content = $handler->makeEmptyContent(); $this->output( "blanking\n" ); - $page->doEditContent( $content, wfMessage( 'spam_blanking', $domain )->inContentLanguage()->text() ); + $page->doEditContent( + $content, + wfMessage( 'spam_blanking', $domain )->inContentLanguage()->text() + ); } $dbw->commit( __METHOD__ ); } diff --git a/maintenance/cleanupTitles.php b/maintenance/cleanupTitles.php index 5b5ef184..0df9e7fe 100644 --- a/maintenance/cleanupTitles.php +++ b/maintenance/cleanupTitles.php @@ -7,7 +7,7 @@ * --fix Actually clean up titles; otherwise just checks for them * * Copyright © 2005 Brion Vibber - * http://www.mediawiki.org/ + * https://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 @@ -148,9 +148,15 @@ class TitleCleanup extends TableCleanup { $ns = 0; } + # Namespace which no longer exists. Put the page in the main namespace + # since we don't have any idea of the old namespace name. See bug 68501. + if ( !MWNamespace::exists( $ns ) ) { + $ns = 0; + } + $clean = 'Broken/' . $prior; $verified = Title::makeTitleSafe( $ns, $clean ); - if ( $verified->exists() ) { + if ( !$verified || $verified->exists() ) { $blah = "Broken/id:" . $row->page_id; $this->output( "Couldn't legalize; form '$clean' exists; using '$blah'\n" ); $verified = Title::makeTitleSafe( $ns, $blah ); diff --git a/maintenance/cleanupUploadStash.php b/maintenance/cleanupUploadStash.php index c2ba5558..24b63a8b 100644 --- a/maintenance/cleanupUploadStash.php +++ b/maintenance/cleanupUploadStash.php @@ -38,6 +38,7 @@ class UploadStashCleanup extends Maintenance { public function __construct() { parent::__construct(); $this->mDescription = "Clean up abandoned files in temporary uploaded file stash"; + $this->setBatchSize( 50 ); } public function execute() { @@ -81,10 +82,9 @@ class UploadStashCleanup extends Maintenance { try { $stash->getFile( $key, true ); $stash->removeFileNoAuth( $key ); - } catch ( UploadStashBadPathException $ex ) { - $this->output( "Failed removing stashed upload with key: $key\n" ); - } catch ( UploadStashZeroLengthFileException $ex ) { - $this->output( "Failed removing stashed upload with key: $key\n" ); + } catch ( UploadStashException $ex ) { + $type = get_class( $ex ); + $this->output( "Failed removing stashed upload with key: $key ($type)\n" ); } if ( $i % 100 == 0 ) { $this->output( "$i\n" ); @@ -95,20 +95,25 @@ class UploadStashCleanup extends Maintenance { // Delete all the corresponding thumbnails... $dir = $tempRepo->getZonePath( 'thumb' ); - $iterator = $tempRepo->getBackend()->getFileList( array( 'dir' => $dir ) ); + $iterator = $tempRepo->getBackend()->getFileList( array( 'dir' => $dir, 'adviseStat' => 1 ) ); $this->output( "Deleting old thumbnails...\n" ); $i = 0; + $batch = array(); // operation batch foreach ( $iterator as $file ) { if ( wfTimestamp( TS_UNIX, $tempRepo->getFileTimestamp( "$dir/$file" ) ) < $cutoff ) { - $status = $tempRepo->quickPurge( "$dir/$file" ); - if ( !$status->isOK() ) { - $this->error( print_r( $status->getErrorsArray(), true ) ); - } - if ( ( ++$i % 100 ) == 0 ) { + $batch[] = array( 'op' => 'delete', 'src' => "$dir/$file" ); + if ( count( $batch ) >= $this->mBatchSize ) { + $this->doOperations( $tempRepo, $batch ); + $i += count( $batch ); + $batch = array(); $this->output( "$i\n" ); } } } + if ( count( $batch ) ) { + $this->doOperations( $tempRepo, $batch ); + $i += count( $batch ); + } $this->output( "$i done\n" ); // Apparently lots of stash files are not registered in the DB... @@ -119,24 +124,31 @@ class UploadStashCleanup extends Maintenance { $this->error( "Temp repo is not using the temp container.", 1 ); // die } $i = 0; + $batch = array(); // operation batch foreach ( $iterator as $file ) { - // Absolute sanity check for stashed files and file segments - if ( !preg_match( '#(^\d{14}!|\.\d+\.\w+\.\d+$)#', basename( $file ) ) ) { - $this->output( "Skipped non-stash $file\n" ); - continue; - } if ( wfTimestamp( TS_UNIX, $tempRepo->getFileTimestamp( "$dir/$file" ) ) < $cutoff ) { - $status = $tempRepo->quickPurge( "$dir/$file" ); - if ( !$status->isOK() ) { - $this->error( print_r( $status->getErrorsArray(), true ) ); - } - if ( ( ++$i % 100 ) == 0 ) { + $batch[] = array( 'op' => 'delete', 'src' => "$dir/$file" ); + if ( count( $batch ) >= $this->mBatchSize ) { + $this->doOperations( $tempRepo, $batch ); + $i += count( $batch ); + $batch = array(); $this->output( "$i\n" ); } } } + if ( count( $batch ) ) { + $this->doOperations( $tempRepo, $batch ); + $i += count( $batch ); + } $this->output( "$i done\n" ); } + + protected function doOperations( FileRepo $tempRepo, array $ops ) { + $status = $tempRepo->getBackend()->doQuickOperations( $ops ); + if ( !$status->isOK() ) { + $this->error( print_r( $status->getErrorsArray(), true ) ); + } + } } $maintClass = "UploadStashCleanup"; diff --git a/maintenance/cleanupWatchlist.php b/maintenance/cleanupWatchlist.php index f1a7b481..eb7d7b18 100644 --- a/maintenance/cleanupWatchlist.php +++ b/maintenance/cleanupWatchlist.php @@ -7,7 +7,7 @@ * --fix Actually remove entries; without will only report. * * Copyright © 2005,2006 Brion Vibber - * http://www.mediawiki.org/ + * https://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 @@ -65,9 +65,11 @@ class WatchlistCleanup extends TableCleanup { $title = Title::newFromText( $verified ); if ( $row->wl_user == 0 || is_null( $title ) || !$title->equals( $current ) ) { - $this->output( "invalid watch by {$row->wl_user} for ({$row->wl_namespace}, \"{$row->wl_title}\")\n" ); + $this->output( "invalid watch by {$row->wl_user} for " + . "({$row->wl_namespace}, \"{$row->wl_title}\")\n" ); $updated = $this->removeWatch( $row ); $this->progress( $updated ); + return; } $this->progress( 0 ); @@ -76,12 +78,16 @@ class WatchlistCleanup extends TableCleanup { private function removeWatch( $row ) { if ( !$this->dryrun && $this->hasOption( 'fix' ) ) { $dbw = wfGetDB( DB_MASTER ); - $dbw->delete( 'watchlist', array( + $dbw->delete( + 'watchlist', array( 'wl_user' => $row->wl_user, 'wl_namespace' => $row->wl_namespace, 'wl_title' => $row->wl_title ), - __METHOD__ ); + __METHOD__ + ); + $this->output( "- removed\n" ); + return 1; } else { return 0; diff --git a/maintenance/commandLine.inc b/maintenance/commandLine.inc index be071422..88776f4f 100644 --- a/maintenance/commandLine.inc +++ b/maintenance/commandLine.inc @@ -23,14 +23,18 @@ require_once __DIR__ . '/Maintenance.php'; +// @codingStandardsIgnoreStart MediaWiki.NamingConventions.ValidGlobalName.wgPrefix global $optionsWithArgs; +// @codingStandardsIgnoreEnd if ( !isset( $optionsWithArgs ) ) { $optionsWithArgs = array(); } class CommandLineInc extends Maintenance { public function __construct() { + // @codingStandardsIgnoreStart MediaWiki.NamingConventions.ValidGlobalName.wgPrefix global $optionsWithArgs; + // @codingStandardsIgnoreEnd parent::__construct(); foreach ( $optionsWithArgs as $name ) { $this->addOption( $name, '', false, true ); @@ -39,6 +43,7 @@ class CommandLineInc extends Maintenance { /** * No help, it would just be misleading since it misses custom options + * @param bool $force */ protected function maybeHelp( $force = false ) { if ( !$force ) { @@ -48,7 +53,9 @@ class CommandLineInc extends Maintenance { } public function execute() { + // @codingStandardsIgnoreStart MediaWiki.NamingConventions.ValidGlobalName.wgPrefix global $args, $options; + // @codingStandardsIgnoreEnd $args = $this->mArgs; $options = $this->mOptions; } diff --git a/maintenance/compareParserCache.php b/maintenance/compareParserCache.php new file mode 100644 index 00000000..98441b60 --- /dev/null +++ b/maintenance/compareParserCache.php @@ -0,0 +1,104 @@ +mDescription = "Parse random pages and compare output to cache."; + $this->addOption( 'namespace', 'Page namespace number', true, true ); + $this->addOption( 'maxpages', 'Number of pages to try', true, true ); + } + + public function execute() { + $pages = $this->getOption( 'maxpages' ); + + $dbr = $this->getDB( DB_SLAVE ); + + $totalsec = 0.0; + $scanned = 0; + $withcache = 0; + $withdiff = 0; + while ( $pages-- > 0 ) { + $row = $dbr->selectRow( 'page', '*', + array( + 'page_namespace' => $this->getOption( 'namespace' ), + 'page_is_redirect' => 0, + 'page_random >= ' . wfRandom() + ), + __METHOD__, + array( + 'ORDER BY' => 'page_random', + ) + ); + + if ( !$row ) { + continue; + } + ++$scanned; + + $title = Title::newFromRow( $row ); + $page = WikiPage::factory( $title ); + $revision = $page->getRevision(); + $content = $revision->getContent( Revision::RAW ); + + $parserOptions = $page->makeParserOptions( 'canonical' ); + + $parserOutputOld = ParserCache::singleton()->get( $page, $parserOptions ); + + if ( $parserOutputOld ) { + $t1 = microtime( true ); + $parserOutputNew = $content->getParserOutput( + $title, $revision->getId(), $parserOptions, false ); + $sec = microtime( true ) - $t1; + $totalsec += $sec; + + $this->output( "Parsed '{$title->getPrefixedText()}' in $sec seconds.\n" ); + + $this->output( "Found cache entry found for '{$title->getPrefixedText()}'..." ); + $oldHtml = trim( preg_replace( '##Us', '', $parserOutputOld->getText() ) ); + $newHtml = trim( preg_replace( '##Us', '', $parserOutputNew->getText() ) ); + $diff = wfDiff( $oldHtml, $newHtml ); + if ( strlen( $diff ) ) { + $this->output( "differences found:\n\n$diff\n\n" ); + ++$withdiff; + } else { + $this->output( "No differences found.\n" ); + } + ++$withcache; + } else { + $this->output( "No parser cache entry found for '{$title->getPrefixedText()}'.\n" ); + } + } + + $ave = $totalsec ? $totalsec / $scanned : 0; + $this->output( "Checked $scanned pages; $withcache had prior cache entries.\n" ); + $this->output( "Pages with differences found: $withdiff\n" ); + $this->output( "Average parse time: $ave sec\n" ); + } +} + +$maintClass = "CompareParserCache"; +require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/compareParsers.php b/maintenance/compareParsers.php index fabc2571..e67c439b 100644 --- a/maintenance/compareParsers.php +++ b/maintenance/compareParsers.php @@ -7,7 +7,7 @@ * Templates etc are pulled from the local wiki database, not from the dump. * * Copyright © 2011 Platonides - * http://www.mediawiki.org/ + * https://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 @@ -47,11 +47,31 @@ class CompareParsers extends DumpIterator { $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( + '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 ); + $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() { @@ -96,12 +116,13 @@ class CompareParsers extends DumpIterator { if ( !$this->stripParametersEnabled ) { return $text; } + return preg_replace( '/(]+>/', '$1>', $text ); } /** * Callback function for each revision, parse with both parsers and compare - * @param $rev Revision + * @param Revision $rev */ public function processRevision( $rev ) { $title = $rev->getTitle(); @@ -118,7 +139,9 @@ class CompareParsers extends DumpIterator { $content = $rev->getContent(); if ( $content->getModel() !== CONTENT_MODEL_WIKITEXT ) { - $this->error( "Page {$title->getPrefixedText()} does not contain wikitext but {$content->getModel()}\n" ); + $this->error( "Page {$title->getPrefixedText()} does not contain wikitext " + . "but {$content->getModel()}\n" ); + return; } @@ -132,13 +155,21 @@ class CompareParsers extends DumpIterator { $this->error( "Parsing for {$title->getPrefixedText()} differs\n" ); if ( $this->saveFailed ) { - file_put_contents( $this->saveFailed . '/' . rawurlencode( $title->getPrefixedText() ) . ".txt", $text ); + file_put_contents( + $this->saveFailed . '/' . rawurlencode( $title->getPrefixedText() ) . ".txt", + $text + ); } if ( $this->showDiff ) { - $this->output( wfDiff( $this->stripParameters( $output1->getText() ), $this->stripParameters( $output2->getText() ), '' ) ); + $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() ) ); } @@ -149,10 +180,9 @@ class CompareParsers extends DumpIterator { /* 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"; + $wgAutoloadClasses[$parserName] = realpath( '.' ) . "/$parserName.php"; } } - } $maintClass = "CompareParsers"; diff --git a/maintenance/convertLinks.php b/maintenance/convertLinks.php index 17b91110..221ebe37 100644 --- a/maintenance/convertLinks.php +++ b/maintenance/convertLinks.php @@ -36,14 +36,29 @@ class ConvertLinks extends Maintenance { public function __construct() { parent::__construct(); - $this->mDescription = "Convert from the old links schema (string->ID) to the new schema (ID->ID). -The wiki should be put into read-only mode while this script executes"; + $this->mDescription = + "Convert from the old links schema (string->ID) to the new schema (ID->ID)." + . "The wiki should be put into read-only mode while this script executes"; $this->addArg( 'logperformance', "Log performance to perfLogFilename.", false ); - $this->addArg( 'perfLogFilename', "Filename where performance is logged if --logperformance was set (defaults to 'convLinksPerf.txt').", false ); - $this->addArg( 'keep-links-table', "Don't overwrite the old links table with the new one, leave the new table at links_temp.", false ); - $this->addArg( 'nokeys', "Don't create keys, and so allow duplicates in the new links table.\n -This gives a huge speed improvement for very large links tables which are MyISAM." /* (What about InnoDB?) */, false ); + $this->addArg( + 'perfLogFilename', + "Filename where performance is logged if --logperformance was set " + . "(defaults to 'convLinksPerf.txt').", + false + ); + $this->addArg( + 'keep-links-table', + "Don't overwrite the old links table with the new one, leave the new table at links_temp.", + false + ); + $this->addArg( + 'nokeys', + /* (What about InnoDB?) */ + "Don't create keys, and so allow duplicates in the new links table.\n" + . "This gives a huge speed improvement for very large links tables which are MyISAM.", + false + ); } public function getDbType() { @@ -56,22 +71,34 @@ This gives a huge speed improvement for very large links tables which are MyISAM $type = $dbw->getType(); if ( $type != 'mysql' ) { $this->output( "Link table conversion not necessary for $type\n" ); + return; } global $wgContLang; - $numBadLinks = $curRowsRead = 0; # counters etc - $totalTuplesInserted = 0; # total tuples INSERTed into links_temp + # counters etc + $numBadLinks = $curRowsRead = 0; + + # total tuples INSERTed into links_temp + $totalTuplesInserted = 0; + + # whether or not to give progress reports while reading IDs from cur table + $reportCurReadProgress = true; - $reportCurReadProgress = true; # whether or not to give progress reports while reading IDs from cur table - $curReadReportInterval = 1000; # number of rows between progress reports + # number of rows between progress reports + $curReadReportInterval = 1000; - $reportLinksConvProgress = true; # whether or not to give progress reports during conversion - $linksConvInsertInterval = 1000; # number of rows per INSERT + # whether or not to give progress reports during conversion + $reportLinksConvProgress = true; + + # number of rows per INSERT + $linksConvInsertInterval = 1000; $initialRowOffset = 0; - # $finalRowOffset = 0; # not used yet; highest row number from links table to process + + # not used yet; highest row number from links table to process + # $finalRowOffset = 0; $overwriteLinksTable = !$this->hasOption( 'keep-links-table' ); $noKeys = $this->hasOption( 'noKeys' ); @@ -80,16 +107,19 @@ This gives a huge speed improvement for very large links tables which are MyISAM # -------------------------------------------------------------------- - list( $cur, $links, $links_temp, $links_backup ) = $dbw->tableNamesN( 'cur', 'links', 'links_temp', 'links_backup' ); + list( $cur, $links, $links_temp, $links_backup ) = + $dbw->tableNamesN( 'cur', 'links', 'links_temp', 'links_backup' ); if ( $dbw->tableExists( 'pagelinks' ) ) { $this->output( "...have pagelinks; skipping old links table updates\n" ); + return; } $res = $dbw->query( "SELECT l_from FROM $links LIMIT 1" ); if ( $dbw->fieldType( $res, 0 ) == "int" ) { $this->output( "Schema already converted\n" ); + return; } @@ -104,17 +134,17 @@ This gives a huge speed improvement for very large links tables which are MyISAM } else { $fh = false; if ( $this->logPerformance ) { - $fh = fopen ( $perfLogFilename, "w" ); + $fh = fopen( $perfLogFilename, "w" ); if ( !$fh ) { $this->error( "Couldn't open $perfLogFilename" ); $this->logPerformance = false; } } - $baseTime = $startTime = $this->getMicroTime(); + $baseTime = $startTime = microtime( true ); # Create a title -> cur_id map $this->output( "Loading IDs from $cur table...\n" ); - $this->performanceLog ( $fh, "Reading $numRows rows from cur table...\n" ); - $this->performanceLog ( $fh, "rows read vs seconds elapsed:\n" ); + $this->performanceLog( $fh, "Reading $numRows rows from cur table...\n" ); + $this->performanceLog( $fh, "rows read vs seconds elapsed:\n" ); $dbw->bufferResults( false ); $res = $dbw->query( "SELECT cur_namespace,cur_title,cur_id FROM $cur" ); @@ -129,7 +159,10 @@ This gives a huge speed improvement for very large links tables which are MyISAM $curRowsRead++; if ( $reportCurReadProgress ) { if ( ( $curRowsRead % $curReadReportInterval ) == 0 ) { - $this->performanceLog( $fh, $curRowsRead . " " . ( $this->getMicroTime() - $baseTime ) . "\n" ); + $this->performanceLog( + $fh, + $curRowsRead . " " . ( microtime( true ) - $baseTime ) . "\n" + ); $this->output( "\t$curRowsRead rows of $cur table read.\n" ); } } @@ -137,7 +170,10 @@ This gives a huge speed improvement for very large links tables which are MyISAM $dbw->freeResult( $res ); $dbw->bufferResults( true ); $this->output( "Finished loading IDs.\n\n" ); - $this->performanceLog( $fh, "Took " . ( $this->getMicroTime() - $baseTime ) . " seconds to load IDs.\n\n" ); + $this->performanceLog( + $fh, + "Took " . ( microtime( true ) - $baseTime ) . " seconds to load IDs.\n\n" + ); # -------------------------------------------------------------------- @@ -145,12 +181,14 @@ This gives a huge speed improvement for very large links tables which are MyISAM # convert, and write to the new table. $this->createTempTable(); $this->performanceLog( $fh, "Resetting timer.\n\n" ); - $baseTime = $this->getMicroTime(); + $baseTime = microtime( true ); $this->output( "Processing $numRows rows from $links table...\n" ); $this->performanceLog( $fh, "Processing $numRows rows from $links table...\n" ); $this->performanceLog( $fh, "rows inserted vs seconds elapsed:\n" ); - for ( $rowOffset = $initialRowOffset; $rowOffset < $numRows; $rowOffset += $linksConvInsertInterval ) { + for ( $rowOffset = $initialRowOffset; $rowOffset < $numRows; + $rowOffset += $linksConvInsertInterval + ) { $sqlRead = "SELECT * FROM $links "; $sqlRead = $dbw->limitResult( $sqlRead, $linksConvInsertInterval, $rowOffset ); $res = $dbw->query( $sqlRead ); @@ -176,7 +214,8 @@ This gives a huge speed improvement for very large links tables which are MyISAM } } $dbw->freeResult( $res ); - # $this->output( "rowOffset: $rowOffset\ttuplesAdded: $tuplesAdded\tnumBadLinks: $numBadLinks\n" ); + # $this->output( "rowOffset: $rowOffset\ttuplesAdded: " + # . "$tuplesAdded\tnumBadLinks: $numBadLinks\n" ); if ( $tuplesAdded != 0 ) { if ( $reportLinksConvProgress ) { $this->output( "Inserting $tuplesAdded tuples into $links_temp..." ); @@ -185,15 +224,25 @@ This gives a huge speed improvement for very large links tables which are MyISAM $totalTuplesInserted += $tuplesAdded; if ( $reportLinksConvProgress ) { $this->output( " done. Total $totalTuplesInserted tuples inserted.\n" ); - $this->performanceLog( $fh, $totalTuplesInserted . " " . ( $this->getMicroTime() - $baseTime ) . "\n" ); + $this->performanceLog( + $fh, + $totalTuplesInserted . " " . ( microtime( true ) - $baseTime ) . "\n" + ); } } } - $this->output( "$totalTuplesInserted valid titles and $numBadLinks invalid titles were processed.\n\n" ); - $this->performanceLog( $fh, "$totalTuplesInserted valid titles and $numBadLinks invalid titles were processed.\n" ); - $this->performanceLog( $fh, "Total execution time: " . ( $this->getMicroTime() - $startTime ) . " seconds.\n" ); + $this->output( "$totalTuplesInserted valid titles and " + . "$numBadLinks invalid titles were processed.\n\n" ); + $this->performanceLog( + $fh, + "$totalTuplesInserted valid titles and $numBadLinks invalid titles were processed.\n" + ); + $this->performanceLog( + $fh, + "Total execution time: " . ( microtime( true ) - $startTime ) . " seconds.\n" + ); if ( $this->logPerformance ) { - fclose ( $fh ); + fclose( $fh ); } } # -------------------------------------------------------------------- @@ -209,7 +258,6 @@ This gives a huge speed improvement for very large links tables which are MyISAM $dbw->query( "RENAME TABLE links TO $links_backup, $links_temp TO $links", __METHOD__ ); $this->output( " done.\n\n" ); - $dbw->close(); $this->output( "Conversion complete. The old table remains at $links_backup;\n" ); $this->output( "delete at your leisure.\n" ); } else { @@ -223,6 +271,7 @@ This gives a huge speed improvement for very large links tables which are MyISAM if ( !( $dbConn->isOpen() ) ) { $this->output( "Opening connection to database failed.\n" ); + return; } $links_temp = $dbConn->tableName( 'links_temp' ); @@ -234,14 +283,14 @@ This gives a huge speed improvement for very large links tables which are MyISAM $this->output( "Creating temporary links table..." ); if ( $this->hasOption( 'noKeys' ) ) { $dbConn->query( "CREATE TABLE $links_temp ( " . - "l_from int(8) unsigned NOT NULL default '0', " . - "l_to int(8) unsigned NOT NULL default '0')" ); + "l_from int(8) unsigned NOT NULL default '0', " . + "l_to int(8) unsigned NOT NULL default '0')" ); } else { $dbConn->query( "CREATE TABLE $links_temp ( " . - "l_from int(8) unsigned NOT NULL default '0', " . - "l_to int(8) unsigned NOT NULL default '0', " . - "UNIQUE KEY l_from(l_from,l_to), " . - "KEY (l_to))" ); + "l_from int(8) unsigned NOT NULL default '0', " . + "l_to int(8) unsigned NOT NULL default '0', " . + "UNIQUE KEY l_from(l_from,l_to), " . + "KEY (l_to))" ); } $this->output( " done.\n\n" ); } @@ -251,11 +300,6 @@ This gives a huge speed improvement for very large links tables which are MyISAM fwrite( $fh, $text ); } } - - private function getMicroTime() { # return time in seconds, with microsecond accuracy - list( $usec, $sec ) = explode( " ", microtime() ); - return ( (float)$usec + (float)$sec ); - } } $maintClass = "ConvertLinks"; diff --git a/maintenance/convertUserOptions.php b/maintenance/convertUserOptions.php index 34c643bb..1542a8c3 100644 --- a/maintenance/convertUserOptions.php +++ b/maintenance/convertUserOptions.php @@ -26,8 +26,6 @@ require_once __DIR__ . '/Maintenance.php'; /** * Maintenance script to convert user options to the new `user_properties` table. * - * Do each user sequentially, since accounts can't be deleted - * * @ingroup Maintenance */ class ConvertUserOptions extends Maintenance { @@ -37,6 +35,7 @@ class ConvertUserOptions extends Maintenance { public function __construct() { parent::__construct(); $this->mDescription = "Convert user options from old to new system"; + $this->setBatchSize( 50 ); } public function execute() { @@ -46,17 +45,23 @@ class ConvertUserOptions extends Maintenance { if ( !$dbw->fieldExists( 'user', 'user_options', __METHOD__ ) ) { $this->output( "nothing to migrate. " ); + return; } while ( $id !== null ) { - $idCond = 'user_id > ' . $dbw->addQuotes( $id ); - $optCond = "user_options != " . $dbw->addQuotes( '' ); // For compatibility - $res = $dbw->select( 'user', '*', - array( $optCond, $idCond ), __METHOD__, - array( 'LIMIT' => 50, 'FOR UPDATE' ) + $res = $dbw->select( 'user', + array( 'user_id', 'user_options' ), + array( + 'user_id > ' . $dbw->addQuotes( $id ), + "user_options != " . $dbw->addQuotes( '' ), + ), + __METHOD__, + array( + 'ORDER BY' => 'user_id', + 'LIMIT' => $this->mBatchSize, + ) ); $id = $this->convertOptionBatch( $res, $dbw ); - $dbw->commit( __METHOD__ ); wfWaitForSlaves(); @@ -68,20 +73,37 @@ class ConvertUserOptions extends Maintenance { } /** - * @param $res - * @param $dbw DatabaseBase + * @param ResultWrapper $res + * @param DatabaseBase $dbw * @return null|int */ function convertOptionBatch( $res, $dbw ) { $id = null; foreach ( $res as $row ) { $this->mConversionCount++; + $insertRows = array(); + foreach ( explode( "\n", $row->user_options ) as $s ) { + $m = array(); + if ( !preg_match( "/^(.[^=]*)=(.*)$/", $s, $m ) ) { + continue; + } - $u = User::newFromRow( $row ); + // MW < 1.16 would save even default values. Filter them out + // here (as in User) to avoid adding many unnecessary rows. + $defaultOption = User::getDefaultOption( $m[1] ); + if ( is_null( $defaultOption ) || $m[2] != $defaultOption ) { + $insertRows[] = array( + 'up_user' => $row->user_id, + 'up_property' => $m[1], + 'up_value' => $m[2], + ); + } + } - $u->saveSettings(); + if ( count( $insertRows ) ) { + $dbw->insert( 'user_properties', $insertRows, __METHOD__, array( 'IGNORE' ) ); + } - // Do this here as saveSettings() doesn't set user_options to '' anymore! $dbw->update( 'user', array( 'user_options' => '' ), diff --git a/maintenance/copyFileBackend.php b/maintenance/copyFileBackend.php index 21ef4ffa..9ed63c3c 100644 --- a/maintenance/copyFileBackend.php +++ b/maintenance/copyFileBackend.php @@ -35,7 +35,8 @@ require_once __DIR__ . '/Maintenance.php'; * @ingroup Maintenance */ class CopyFileBackend extends Maintenance { - protected $statCache = array(); + /** @var array|null (path sha1 => stat) Pre-computed dst stat entries from listings */ + protected $statCache = null; public function __construct() { parent::__construct(); @@ -98,7 +99,7 @@ class CopyFileBackend extends Maintenance { if ( $dstPathsRel === null ) { $this->error( "Could not list files in $container.", 1 ); // die } - $this->statCache = array(); // clear + $this->statCache = array(); foreach ( $dstPathsRel as $dstPathRel ) { $path = $dst->getRootStoragePath() . "/$backendRel/$dstPathRel"; $this->statCache[sha1( $path )] = $dst->getFileStat( array( 'src' => $path ) ); @@ -238,8 +239,8 @@ class CopyFileBackend extends Maintenance { $this->error( "$wikiId: Detected illegal (non-UTF8) path for $srcPath." ); continue; } elseif ( !$this->hasOption( 'missingonly' ) - && $this->filesAreSame( $src, $dst, $srcPath, $dstPath ) ) - { + && $this->filesAreSame( $src, $dst, $srcPath, $dstPath ) + ) { $this->output( "\tAlready have $srcPathRel.\n" ); continue; // assume already copied... } @@ -338,18 +339,42 @@ class CopyFileBackend extends Maintenance { $skipHash = $this->hasOption( 'skiphash' ); $srcStat = $src->getFileStat( array( 'src' => $sPath ) ); $dPathSha1 = sha1( $dPath ); - $dstStat = isset( $this->statCache[$dPathSha1] ) - ? $this->statCache[$dPathSha1] - : $dst->getFileStat( array( 'src' => $dPath ) ); - return ( + if ( $this->statCache !== null ) { + // All dst files are already in stat cache + $dstStat = isset( $this->statCache[$dPathSha1] ) + ? $this->statCache[$dPathSha1] + : false; + } else { + $dstStat = $dst->getFileStat( array( 'src' => $dPath ) ); + } + // Initial fast checks to see if files are obviously different + $sameFast = ( is_array( $srcStat ) // sanity check that source exists && is_array( $dstStat ) // dest exists && $srcStat['size'] === $dstStat['size'] - && ( !$skipHash || $srcStat['mtime'] <= $dstStat['mtime'] ) - && ( $skipHash || $src->getFileSha1Base36( array( 'src' => $sPath, 'latest' => 1 ) ) - === $dst->getFileSha1Base36( array( 'src' => $dPath, 'latest' => 1 ) ) - ) ); + // More thorough checks against files + if ( !$sameFast ) { + $same = false; // no need to look farther + } elseif ( isset( $srcStat['md5'] ) && isset( $dstStat['md5'] ) ) { + // If MD5 was already in the stat info, just use it. + // This is useful as many objects stores can return this in object listing, + // so we can use it to avoid slow per-file HEADs. + $same = ( $srcStat['md5'] === $dstStat['md5'] ); + } elseif ( $skipHash ) { + // This mode is good for copying to a backup location or resyncing clone + // backends in FileBackendMultiWrite (since they get writes second, they have + // higher timestamps). However, when copying the other way, this hits loads of + // false positives (possibly 100%) and wastes a bunch of time on GETs/PUTs. + $same = ( $srcStat['mtime'] <= $dstStat['mtime'] ); + } else { + // This is the slowest method which does many per-file HEADs (unless an object + // store tracks SHA-1 in listings). + $same = ( $src->getFileSha1Base36( array( 'src' => $sPath, 'latest' => 1 ) ) + === $dst->getFileSha1Base36( array( 'src' => $dPath, 'latest' => 1 ) ) ); + } + + return $same; } } diff --git a/maintenance/copyJobQueue.php b/maintenance/copyJobQueue.php index e833115b..a9c9547e 100644 --- a/maintenance/copyJobQueue.php +++ b/maintenance/copyJobQueue.php @@ -78,19 +78,18 @@ class CopyJobQueue extends Maintenance { ++$total; $batch[] = $job; if ( count( $batch ) >= $this->mBatchSize ) { - if ( $dst->push( $batch ) ) { - $totalOK += count( $batch ); - } + $dst->push( $batch ); + $totalOK += count( $batch ); $batch = array(); $dst->waitForBackups(); } } if ( count( $batch ) ) { - if ( $dst->push( $batch ) ) { - $totalOK += count( $batch ); - } + $dst->push( $batch ); + $totalOK += count( $batch ); $dst->waitForBackups(); } + return array( $total, $totalOK ); } } diff --git a/maintenance/createAndPromote.php b/maintenance/createAndPromote.php index aa25ee60..79f72542 100644 --- a/maintenance/createAndPromote.php +++ b/maintenance/createAndPromote.php @@ -31,13 +31,15 @@ require_once __DIR__ . '/Maintenance.php'; * @ingroup Maintenance */ class CreateAndPromote extends Maintenance { - - static $permitRoles = array( 'sysop', 'bureaucrat', 'bot' ); + private static $permitRoles = array( 'sysop', 'bureaucrat', 'bot' ); public function __construct() { parent::__construct(); $this->mDescription = "Create a new user account and/or grant it additional rights"; - $this->addOption( "force", "If acccount exists already, just grant it rights or change password." ); + $this->addOption( + 'force', + 'If acccount exists already, just grant it rights or change password.' + ); foreach ( self::$permitRoles as $role ) { $this->addOption( $role, "Add the account to the {$role} group" ); } @@ -67,10 +69,14 @@ class CreateAndPromote extends Maintenance { $inGroups = $user->getGroups(); } - $promotions = array_diff( array_filter( self::$permitRoles, array( $this, 'hasOption' ) ), $inGroups ); + $promotions = array_diff( + array_filter( self::$permitRoles, array( $this, 'hasOption' ) ), + $inGroups + ); if ( $exists && !$password && count( $promotions ) === 0 ) { $this->output( "Account exists and nothing to do.\n" ); + return; } elseif ( count( $promotions ) !== 0 ) { $promoText = "User:{$username} into " . implode( ', ', $promotions ) . "...\n"; diff --git a/maintenance/cssjanus/COPYING b/maintenance/cssjanus/COPYING deleted file mode 100644 index 3f2c8953..00000000 --- a/maintenance/cssjanus/COPYING +++ /dev/null @@ -1,13 +0,0 @@ - Copyright 2008 Google Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/maintenance/cssjanus/LICENSE b/maintenance/cssjanus/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/maintenance/cssjanus/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/maintenance/cssjanus/README b/maintenance/cssjanus/README deleted file mode 100644 index 1b96d1a2..00000000 --- a/maintenance/cssjanus/README +++ /dev/null @@ -1,91 +0,0 @@ -=CSSJanus= - -_Flips CSS from LTR to an RTL orienation and vice-versa_ - -Author: `Lindsey Simon ` - -==Introduction== - -CSSJanus is CSS parser utility designed to aid the conversion of a website's -layout from left-to-right(LTR) to right-to-left(RTL). The script was born out of -a need to convert CSS for RTL languages when tables are not being used for layout (since tables will automatically reorder TD's in RTL). -CSSJanus will change most of the obvious CSS property names and their values as -well as some not-so-obvious ones (cursor, background-position %, etc...). -The script is designed to offer flexibility to account for cases when you do -not want to change certain rules which exist to account for bidirectional text -display bugs, as well as situations where you may or may not want to flip annotations inside of the background url string. -Note that you can disable CSSJanus from running on an entire class or any -rule within a class by prepending a /* @noflip */ comment before the rule(s) -you want CSSJanus to ignore. - -CSSJanus itself is not always enough to make a website that works in a LTR -language context work in a RTL language all the way, but it is a start. - -==Getting the code== - -View the trunk at: - - http://cssjanus.googlecode.com/svn/trunk/ - -Check out the latest development version anonymously with: - -{{{ - $ svn checkout http://cssjanus.googlecode.com/svn/trunk/ cssjanus -}}} - -==Using== - -Usage: - ./cssjanus.py < file.css > file-rtl.css -Flags: - --swap_left_right_in_url: Fixes "left"/"right" string within urls. - Ex: ./cssjanus.py --swap_left_right_in_url < file.css > file_rtl.css - --swap_ltr_rtl_in_url: Fixes "ltr"/"rtl" string within urls. - Ex: ./cssjanus.py --swap_ltr_rtl_in_url < file.css > file_rtl.css - -If you'd like to make use of the webapp version of cssjanus, you'll need to -download the Google App Engine SDK - http://code.google.com/appengine/downloads.html -and also drop a "django" directory into this directory, with the latest svn -from django. You should be good to go with that setup. Please let me know -otherwise. - -==Bugs, Patches== - -Patches and bug reports are welcome, just please keep the style -consistent with the original source. If you find a bug, please include a diff -of cssjanus_test.py with the bug included as a new unit test which fails. It -will make understanding and fixing the bug easier. - -==Todo== - -* Include some helpers for some typical bidi text solutions? -* Aural CSS (azimuth) swapping? - -==Contributors== - -Additional thanks to Mike Samuel for his work on csslex.py, Andy Perelson for -his help coding and reviewing, Stephen Zabel for his help with i18n and my sanity, -and to Eric Meyer for his thoughtful input. -Thanks to Junyu Wang for the Chinese translation. -Thanks to Masashi Kawashima for the Japanese translation. -Thanks to Taaryk Taar and Tariq Al-Omaireeni for an updated Arabic translation. -Thanks to Jens Meiert for the German translation. - -==License== - -{{{ - Copyright 2008 Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the 'License'); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an 'AS IS' BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -}}} diff --git a/maintenance/cssjanus/cssjanus.py b/maintenance/cssjanus/cssjanus.py deleted file mode 100644 index dd14bd58..00000000 --- a/maintenance/cssjanus/cssjanus.py +++ /dev/null @@ -1,574 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2008 Google Inc. All Rights Reserved. - -"""Converts a LeftToRight Cascading Style Sheet into a RightToLeft one. - - This is a utility script for replacing "left" oriented things in a CSS file - like float, padding, margin with "right" oriented values. - It also does the opposite. - The goal is to be able to conditionally serve one large, cat'd, compiled CSS - file appropriate for LeftToRight oriented languages and RightToLeft ones. - This utility will hopefully help your structural layout done in CSS in - terms of its RTL compatibility. It will not help with some of the more - complicated bidirectional text issues. -""" - -__author__ = 'elsigh@google.com (Lindsey Simon)' -__version__ = '0.1' - -import logging -import re -import sys -import getopt -import os - -import csslex - -logging.getLogger().setLevel(logging.INFO) - -# Global for the command line flags. -SWAP_LTR_RTL_IN_URL_DEFAULT = False -SWAP_LEFT_RIGHT_IN_URL_DEFAULT = False -FLAGS = {'swap_ltr_rtl_in_url': SWAP_LTR_RTL_IN_URL_DEFAULT, - 'swap_left_right_in_url': SWAP_LEFT_RIGHT_IN_URL_DEFAULT} - -# Generic token delimiter character. -TOKEN_DELIMITER = '~' - -# This is a temporary match token we use when swapping strings. -TMP_TOKEN = '%sTMP%s' % (TOKEN_DELIMITER, TOKEN_DELIMITER) - -# Token to be used for joining lines. -TOKEN_LINES = '%sJ%s' % (TOKEN_DELIMITER, TOKEN_DELIMITER) - -# Global constant text strings for CSS value matches. -LTR = 'ltr' -RTL = 'rtl' -LEFT = 'left' -RIGHT = 'right' - -# This is a lookbehind match to ensure that we don't replace instances -# of our string token (left, rtl, etc...) if there's a letter in front of it. -# Specifically, this prevents replacements like 'background: url(bright.png)'. -LOOKBEHIND_NOT_LETTER = r'(?)*?{)' % - (csslex.NMCHAR, TOKEN_LINES, csslex.SPACE)) - - -# These two lookaheads are to test whether or not we are within a -# background: url(HERE) situation. -# Ref: http://www.w3.org/TR/CSS21/syndata.html#uri -VALID_AFTER_URI_CHARS = r'[\'\"]?%s' % csslex.WHITESPACE -LOOKAHEAD_NOT_CLOSING_PAREN = r'(?!%s?%s\))' % (csslex.URL_CHARS, - VALID_AFTER_URI_CHARS) -LOOKAHEAD_FOR_CLOSING_PAREN = r'(?=%s?%s\))' % (csslex.URL_CHARS, - VALID_AFTER_URI_CHARS) - -# Compile a regex to swap left and right values in 4 part notations. -# We need to match negatives and decimal numeric values. -# ex. 'margin: .25em -2px 3px 0' becomes 'margin: .25em 0 3px -2px'. -POSSIBLY_NEGATIVE_QUANTITY = r'((?:-?%s)|(?:inherit|auto))' % csslex.QUANTITY -POSSIBLY_NEGATIVE_QUANTITY_SPACE = r'%s%s%s' % (POSSIBLY_NEGATIVE_QUANTITY, - csslex.SPACE, - csslex.WHITESPACE) -FOUR_NOTATION_QUANTITY_RE = re.compile(r'%s%s%s%s' % - (POSSIBLY_NEGATIVE_QUANTITY_SPACE, - POSSIBLY_NEGATIVE_QUANTITY_SPACE, - POSSIBLY_NEGATIVE_QUANTITY_SPACE, - POSSIBLY_NEGATIVE_QUANTITY), - re.I) -COLOR = r'(%s|%s)' % (csslex.NAME, csslex.HASH) -COLOR_SPACE = r'%s%s' % (COLOR, csslex.SPACE) -FOUR_NOTATION_COLOR_RE = re.compile(r'(-color%s:%s)%s%s%s(%s)' % - (csslex.WHITESPACE, - csslex.WHITESPACE, - COLOR_SPACE, - COLOR_SPACE, - COLOR_SPACE, - COLOR), - re.I) - -# Compile the cursor resize regexes -CURSOR_EAST_RE = re.compile(LOOKBEHIND_NOT_LETTER + '([ns]?)e-resize') -CURSOR_WEST_RE = re.compile(LOOKBEHIND_NOT_LETTER + '([ns]?)w-resize') - -# Matches the condition where we need to replace the horizontal component -# of a background-position value when expressed in horizontal percentage. -# Had to make two regexes because in the case of position-x there is only -# one quantity, and otherwise we don't want to match and change cases with only -# one quantity. -BG_HORIZONTAL_PERCENTAGE_RE = re.compile(r'background(-position)?(%s:%s)' - '([^%%]*?)(%s)%%' - '(%s(?:%s|%s))' % (csslex.WHITESPACE, - csslex.WHITESPACE, - csslex.NUM, - csslex.WHITESPACE, - csslex.QUANTITY, - csslex.IDENT)) - -BG_HORIZONTAL_PERCENTAGE_X_RE = re.compile(r'background-position-x(%s:%s)' - '(%s)%%' % (csslex.WHITESPACE, - csslex.WHITESPACE, - csslex.NUM)) - -# Matches the opening of a body selector. -BODY_SELECTOR = r'body%s{%s' % (csslex.WHITESPACE, csslex.WHITESPACE) - -# Matches anything up until the closing of a selector. -CHARS_WITHIN_SELECTOR = r'[^\}]*?' - -# Matches the direction property in a selector. -DIRECTION_RE = r'direction%s:%s' % (csslex.WHITESPACE, csslex.WHITESPACE) - -# These allow us to swap "ltr" with "rtl" and vice versa ONLY within the -# body selector and on the same line. -BODY_DIRECTION_LTR_RE = re.compile(r'(%s)(%s)(%s)(ltr)' % - (BODY_SELECTOR, CHARS_WITHIN_SELECTOR, - DIRECTION_RE), - re.I) -BODY_DIRECTION_RTL_RE = re.compile(r'(%s)(%s)(%s)(rtl)' % - (BODY_SELECTOR, CHARS_WITHIN_SELECTOR, - DIRECTION_RE), - re.I) - - -# Allows us to swap "direction:ltr" with "direction:rtl" and -# vice versa anywhere in a line. -DIRECTION_LTR_RE = re.compile(r'%s(ltr)' % DIRECTION_RE) -DIRECTION_RTL_RE = re.compile(r'%s(rtl)' % DIRECTION_RE) - -# We want to be able to switch left with right and vice versa anywhere -# we encounter left/right strings, EXCEPT inside the background:url(). The next -# two regexes are for that purpose. We have alternate IN_URL versions of the -# regexes compiled in case the user passes the flag that they do -# actually want to have left and right swapped inside of background:urls. -LEFT_RE = re.compile('%s(%s)%s%s' % (LOOKBEHIND_NOT_LETTER, - LEFT, - LOOKAHEAD_NOT_CLOSING_PAREN, - LOOKAHEAD_NOT_OPEN_BRACE), - re.I) -RIGHT_RE = re.compile('%s(%s)%s%s' % (LOOKBEHIND_NOT_LETTER, - RIGHT, - LOOKAHEAD_NOT_CLOSING_PAREN, - LOOKAHEAD_NOT_OPEN_BRACE), - re.I) -LEFT_IN_URL_RE = re.compile('%s(%s)%s' % (LOOKBEHIND_NOT_LETTER, - LEFT, - LOOKAHEAD_FOR_CLOSING_PAREN), - re.I) -RIGHT_IN_URL_RE = re.compile('%s(%s)%s' % (LOOKBEHIND_NOT_LETTER, - RIGHT, - LOOKAHEAD_FOR_CLOSING_PAREN), - re.I) -LTR_IN_URL_RE = re.compile('%s(%s)%s' % (LOOKBEHIND_NOT_LETTER, - LTR, - LOOKAHEAD_FOR_CLOSING_PAREN), - re.I) -RTL_IN_URL_RE = re.compile('%s(%s)%s' % (LOOKBEHIND_NOT_LETTER, - RTL, - LOOKAHEAD_FOR_CLOSING_PAREN), - re.I) - -COMMENT_RE = re.compile('(%s)' % csslex.COMMENT, re.I) - -NOFLIP_TOKEN = r'\@noflip' -# The NOFLIP_TOKEN inside of a comment. For now, this requires that comments -# be in the input, which means users of a css compiler would have to run -# this script first if they want this functionality. -NOFLIP_ANNOTATION = r'/\*%s%s%s\*/' % (csslex.WHITESPACE, - NOFLIP_TOKEN, - csslex. WHITESPACE) - -# After a NOFLIP_ANNOTATION, and within a class selector, we want to be able -# to set aside a single rule not to be flipped. We can do this by matching -# our NOFLIP annotation and then using a lookahead to make sure there is not -# an opening brace before the match. -NOFLIP_SINGLE_RE = re.compile(r'(%s%s[^;}]+;?)' % (NOFLIP_ANNOTATION, - LOOKAHEAD_NOT_OPEN_BRACE), - re.I) - -# After a NOFLIP_ANNOTATION, we want to grab anything up until the next } which -# means the entire following class block. This will prevent all of its -# declarations from being flipped. -NOFLIP_CLASS_RE = re.compile(r'(%s%s})' % (NOFLIP_ANNOTATION, - CHARS_WITHIN_SELECTOR), - re.I) - - -class Tokenizer: - """Replaces any CSS comments with string tokens and vice versa.""" - - def __init__(self, token_re, token_string): - """Constructor for the Tokenizer. - - Args: - token_re: A regex for the string to be replace by a token. - token_string: The string to put between token delimiters when tokenizing. - """ - logging.debug('Tokenizer::init token_string=%s' % token_string) - self.token_re = token_re - self.token_string = token_string - self.originals = [] - - def Tokenize(self, line): - """Replaces any string matching token_re in line with string tokens. - - By passing a function as an argument to the re.sub line below, we bypass - the usual rule where re.sub will only replace the left-most occurrence of - a match by calling the passed in function for each occurrence. - - Args: - line: A line to replace token_re matches in. - - Returns: - line: A line with token_re matches tokenized. - """ - line = self.token_re.sub(self.TokenizeMatches, line) - logging.debug('Tokenizer::Tokenize returns: %s' % line) - return line - - def DeTokenize(self, line): - """Replaces tokens with the original string. - - Args: - line: A line with tokens. - - Returns: - line with any tokens replaced by the original string. - """ - - # Put all of the comments back in by their comment token. - for i, original in enumerate(self.originals): - token = '%s%s_%s%s' % (TOKEN_DELIMITER, self.token_string, i + 1, - TOKEN_DELIMITER) - line = line.replace(token, original) - logging.debug('Tokenizer::DeTokenize i:%s w/%s' % (i, token)) - logging.debug('Tokenizer::DeTokenize returns: %s' % line) - return line - - def TokenizeMatches(self, m): - """Replaces matches with tokens and stores the originals. - - Args: - m: A match object. - - Returns: - A string token which replaces the CSS comment. - """ - logging.debug('Tokenizer::TokenizeMatches %s' % m.group(1)) - self.originals.append(m.group(1)) - return '%s%s_%s%s' % (TOKEN_DELIMITER, - self.token_string, - len(self.originals), - TOKEN_DELIMITER) - - -def FixBodyDirectionLtrAndRtl(line): - """Replaces ltr with rtl and vice versa ONLY in the body direction. - - Args: - line: A string to replace instances of ltr with rtl. - Returns: - line with direction: ltr and direction: rtl swapped only in body selector. - line = FixBodyDirectionLtrAndRtl('body { direction:ltr }') - line will now be 'body { direction:rtl }'. - """ - - line = BODY_DIRECTION_LTR_RE.sub('\\1\\2\\3%s' % TMP_TOKEN, line) - line = BODY_DIRECTION_RTL_RE.sub('\\1\\2\\3%s' % LTR, line) - line = line.replace(TMP_TOKEN, RTL) - logging.debug('FixBodyDirectionLtrAndRtl returns: %s' % line) - return line - - -def FixLeftAndRight(line): - """Replaces left with right and vice versa in line. - - Args: - line: A string in which to perform the replacement. - - Returns: - line with left and right swapped. For example: - line = FixLeftAndRight('padding-left: 2px; margin-right: 1px;') - line will now be 'padding-right: 2px; margin-left: 1px;'. - """ - - line = LEFT_RE.sub(TMP_TOKEN, line) - line = RIGHT_RE.sub(LEFT, line) - line = line.replace(TMP_TOKEN, RIGHT) - logging.debug('FixLeftAndRight returns: %s' % line) - return line - - -def FixLeftAndRightInUrl(line): - """Replaces left with right and vice versa ONLY within background urls. - - Args: - line: A string in which to replace left with right and vice versa. - - Returns: - line with left and right swapped in the url string. For example: - line = FixLeftAndRightInUrl('background:url(right.png)') - line will now be 'background:url(left.png)'. - """ - - line = LEFT_IN_URL_RE.sub(TMP_TOKEN, line) - line = RIGHT_IN_URL_RE.sub(LEFT, line) - line = line.replace(TMP_TOKEN, RIGHT) - logging.debug('FixLeftAndRightInUrl returns: %s' % line) - return line - - -def FixLtrAndRtlInUrl(line): - """Replaces ltr with rtl and vice versa ONLY within background urls. - - Args: - line: A string in which to replace ltr with rtl and vice versa. - - Returns: - line with left and right swapped. For example: - line = FixLtrAndRtlInUrl('background:url(rtl.png)') - line will now be 'background:url(ltr.png)'. - """ - - line = LTR_IN_URL_RE.sub(TMP_TOKEN, line) - line = RTL_IN_URL_RE.sub(LTR, line) - line = line.replace(TMP_TOKEN, RTL) - logging.debug('FixLtrAndRtlInUrl returns: %s' % line) - return line - - -def FixCursorProperties(line): - """Fixes directional CSS cursor properties. - - Args: - line: A string to fix CSS cursor properties in. - - Returns: - line reformatted with the cursor properties substituted. For example: - line = FixCursorProperties('cursor: ne-resize') - line will now be 'cursor: nw-resize'. - """ - - line = CURSOR_EAST_RE.sub('\\1' + TMP_TOKEN, line) - line = CURSOR_WEST_RE.sub('\\1e-resize', line) - line = line.replace(TMP_TOKEN, 'w-resize') - logging.debug('FixCursorProperties returns: %s' % line) - return line - - -def FixFourPartNotation(line): - """Fixes the second and fourth positions in 4 part CSS notation. - - Args: - line: A string to fix 4 part CSS notation in. - - Returns: - line reformatted with the 4 part notations swapped. For example: - line = FixFourPartNotation('padding: 1px 2px 3px 4px') - line will now be 'padding: 1px 4px 3px 2px'. - """ - line = FOUR_NOTATION_QUANTITY_RE.sub('\\1 \\4 \\3 \\2', line) - line = FOUR_NOTATION_COLOR_RE.sub('\\1\\2 \\5 \\4 \\3', line) - logging.debug('FixFourPartNotation returns: %s' % line) - return line - - -def FixBackgroundPosition(line): - """Fixes horizontal background percentage values in line. - - Args: - line: A string to fix horizontal background position values in. - - Returns: - line reformatted with the 4 part notations swapped. - """ - line = BG_HORIZONTAL_PERCENTAGE_RE.sub(CalculateNewBackgroundPosition, line) - line = BG_HORIZONTAL_PERCENTAGE_X_RE.sub(CalculateNewBackgroundPositionX, - line) - logging.debug('FixBackgroundPosition returns: %s' % line) - return line - - -def CalculateNewBackgroundPosition(m): - """Fixes horizontal background-position percentages. - - This function should be used as an argument to re.sub since it needs to - perform replacement specific calculations. - - Args: - m: A match object. - - Returns: - A string with the horizontal background position percentage fixed. - BG_HORIZONTAL_PERCENTAGE_RE.sub(FixBackgroundPosition, - 'background-position: 75% 50%') - will return 'background-position: 25% 50%'. - """ - - # The flipped value is the offset from 100% - new_x = str(100-int(m.group(4))) - - # Since m.group(1) may very well be None type and we need a string.. - if m.group(1): - position_string = m.group(1) - else: - position_string = '' - - return 'background%s%s%s%s%%%s' % (position_string, m.group(2), m.group(3), - new_x, m.group(5)) - - -def CalculateNewBackgroundPositionX(m): - """Fixes percent based background-position-x. - - This function should be used as an argument to re.sub since it needs to - perform replacement specific calculations. - - Args: - m: A match object. - - Returns: - A string with the background-position-x percentage fixed. - BG_HORIZONTAL_PERCENTAGE_X_RE.sub(CalculateNewBackgroundPosition, - 'background-position-x: 75%') - will return 'background-position-x: 25%'. - """ - - # The flipped value is the offset from 100% - new_x = str(100-int(m.group(2))) - - return 'background-position-x%s%s%%' % (m.group(1), new_x) - - -def ChangeLeftToRightToLeft(lines, - swap_ltr_rtl_in_url=None, - swap_left_right_in_url=None): - """Turns lines into a stream and runs the fixing functions against it. - - Args: - lines: An list of CSS lines. - swap_ltr_rtl_in_url: Overrides this flag if param is set. - swap_left_right_in_url: Overrides this flag if param is set. - - Returns: - The same lines, but with left and right fixes. - """ - - global FLAGS - - # Possibly override flags with params. - logging.debug('ChangeLeftToRightToLeft swap_ltr_rtl_in_url=%s, ' - 'swap_left_right_in_url=%s' % (swap_ltr_rtl_in_url, - swap_left_right_in_url)) - if swap_ltr_rtl_in_url is None: - swap_ltr_rtl_in_url = FLAGS['swap_ltr_rtl_in_url'] - if swap_left_right_in_url is None: - swap_left_right_in_url = FLAGS['swap_left_right_in_url'] - - # Turns the array of lines into a single line stream. - logging.debug('LINES COUNT: %s' % len(lines)) - line = TOKEN_LINES.join(lines) - - # Tokenize any single line rules with the /* noflip */ annotation. - noflip_single_tokenizer = Tokenizer(NOFLIP_SINGLE_RE, 'NOFLIP_SINGLE') - line = noflip_single_tokenizer.Tokenize(line) - - # Tokenize any class rules with the /* noflip */ annotation. - noflip_class_tokenizer = Tokenizer(NOFLIP_CLASS_RE, 'NOFLIP_CLASS') - line = noflip_class_tokenizer.Tokenize(line) - - # Tokenize the comments so we can preserve them through the changes. - comment_tokenizer = Tokenizer(COMMENT_RE, 'C') - line = comment_tokenizer.Tokenize(line) - - # Here starteth the various left/right orientation fixes. - line = FixBodyDirectionLtrAndRtl(line) - - if swap_left_right_in_url: - line = FixLeftAndRightInUrl(line) - - if swap_ltr_rtl_in_url: - line = FixLtrAndRtlInUrl(line) - - line = FixLeftAndRight(line) - line = FixCursorProperties(line) - line = FixFourPartNotation(line) - line = FixBackgroundPosition(line) - - # DeTokenize the single line noflips. - line = noflip_single_tokenizer.DeTokenize(line) - - # DeTokenize the class-level noflips. - line = noflip_class_tokenizer.DeTokenize(line) - - # DeTokenize the comments. - line = comment_tokenizer.DeTokenize(line) - - # Rejoin the lines back together. - lines = line.split(TOKEN_LINES) - - return lines - -def usage(): - """Prints out usage information.""" - - print 'Usage:' - print ' ./cssjanus.py < file.css > file-rtl.css' - print 'Flags:' - print ' --swap_left_right_in_url: Fixes "left"/"right" string within urls.' - print ' Ex: ./cssjanus.py --swap_left_right_in_url < file.css > file_rtl.css' - print ' --swap_ltr_rtl_in_url: Fixes "ltr"/"rtl" string within urls.' - print ' Ex: ./cssjanus --swap_ltr_rtl_in_url < file.css > file_rtl.css' - -def setflags(opts): - """Parse the passed in command line arguments and set the FLAGS global. - - Args: - opts: getopt iterable intercepted from argv. - """ - - global FLAGS - - # Parse the arguments. - for opt, arg in opts: - logging.debug('opt: %s, arg: %s' % (opt, arg)) - if opt in ("-h", "--help"): - usage() - sys.exit() - elif opt in ("-d", "--debug"): - logging.getLogger().setLevel(logging.DEBUG) - elif opt == '--swap_ltr_rtl_in_url': - FLAGS['swap_ltr_rtl_in_url'] = True - elif opt == '--swap_left_right_in_url': - FLAGS['swap_left_right_in_url'] = True - - -def main(argv): - """Sends stdin lines to ChangeLeftToRightToLeft and writes to stdout.""" - - # Define the flags. - try: - opts, args = getopt.getopt(argv, 'hd', ['help', 'debug', - 'swap_left_right_in_url', - 'swap_ltr_rtl_in_url']) - except getopt.GetoptError: - usage() - sys.exit(2) - - # Parse and set the flags. - setflags(opts) - - # Call the main routine with all our functionality. - fixed_lines = ChangeLeftToRightToLeft(sys.stdin.readlines()) - sys.stdout.write(''.join(fixed_lines)) - -if __name__ == '__main__': - main(sys.argv[1:]) diff --git a/maintenance/cssjanus/csslex.py b/maintenance/cssjanus/csslex.py deleted file mode 100644 index 1fc7304e..00000000 --- a/maintenance/cssjanus/csslex.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python -# -# Copyright 2007 Google Inc. All Rights Reserved. - -"""CSS Lexical Grammar rules. - -CSS lexical grammar from http://www.w3.org/TR/CSS21/grammar.html -""" - -__author__ = ['elsigh@google.com (Lindsey Simon)', - 'msamuel@google.com (Mike Samuel)'] - -# public symbols -__all__ = [ "NEWLINE", "HEX", "NON_ASCII", "UNICODE", "ESCAPE", "NMSTART", "NMCHAR", "STRING1", "STRING2", "IDENT", "NAME", "HASH", "NUM", "STRING", "URL", "SPACE", "WHITESPACE", "COMMENT", "QUANTITY", "PUNC" ] - -# The comments below are mostly copied verbatim from the grammar. - -# "@import" {return IMPORT_SYM;} -# "@page" {return PAGE_SYM;} -# "@media" {return MEDIA_SYM;} -# "@charset" {return CHARSET_SYM;} -KEYWORD = r'(?:\@(?:import|page|media|charset))' - -# nl \n|\r\n|\r|\f ; a newline -NEWLINE = r'\n|\r\n|\r|\f' - -# h [0-9a-f] ; a hexadecimal digit -HEX = r'[0-9a-f]' - -# nonascii [\200-\377] -NON_ASCII = r'[\200-\377]' - -# unicode \\{h}{1,6}(\r\n|[ \t\r\n\f])? -UNICODE = r'(?:(?:\\' + HEX + r'{1,6})(?:\r\n|[ \t\r\n\f])?)' - -# escape {unicode}|\\[^\r\n\f0-9a-f] -ESCAPE = r'(?:' + UNICODE + r'|\\[^\r\n\f0-9a-f])' - -# nmstart [_a-z]|{nonascii}|{escape} -NMSTART = r'(?:[_a-z]|' + NON_ASCII + r'|' + ESCAPE + r')' - -# nmchar [_a-z0-9-]|{nonascii}|{escape} -NMCHAR = r'(?:[_a-z0-9-]|' + NON_ASCII + r'|' + ESCAPE + r')' - -# ident -?{nmstart}{nmchar}* -IDENT = r'-?' + NMSTART + NMCHAR + '*' - -# name {nmchar}+ -NAME = NMCHAR + r'+' - -# hash -HASH = r'#' + NAME - -# string1 \"([^\n\r\f\\"]|\\{nl}|{escape})*\" ; "string" -STRING1 = r'"(?:[^\"\\]|\\.)*"' - -# string2 \'([^\n\r\f\\']|\\{nl}|{escape})*\' ; 'string' -STRING2 = r"'(?:[^\'\\]|\\.)*'" - -# string {string1}|{string2} -STRING = '(?:' + STRING1 + r'|' + STRING2 + ')' - -# num [0-9]+|[0-9]*"."[0-9]+ -NUM = r'(?:[0-9]*\.[0-9]+|[0-9]+)' - -# s [ \t\r\n\f] -SPACE = r'[ \t\r\n\f]' - -# w {s}* -WHITESPACE = '(?:' + SPACE + r'*)' - -# url special chars -URL_SPECIAL_CHARS = r'[!#$%&*-~]' - -# url chars ({url_special_chars}|{nonascii}|{escape})* -URL_CHARS = r'(?:%s|%s|%s)*' % (URL_SPECIAL_CHARS, NON_ASCII, ESCAPE) - -# url -URL = r'url\(%s(%s|%s)%s\)' % (WHITESPACE, STRING, URL_CHARS, WHITESPACE) - -# comments -# see http://www.w3.org/TR/CSS21/grammar.html -COMMENT = r'/\*[^*]*\*+([^/*][^*]*\*+)*/' - -# {E}{M} {return EMS;} -# {E}{X} {return EXS;} -# {P}{X} {return LENGTH;} -# {C}{M} {return LENGTH;} -# {M}{M} {return LENGTH;} -# {I}{N} {return LENGTH;} -# {P}{T} {return LENGTH;} -# {P}{C} {return LENGTH;} -# {D}{E}{G} {return ANGLE;} -# {R}{A}{D} {return ANGLE;} -# {G}{R}{A}{D} {return ANGLE;} -# {M}{S} {return TIME;} -# {S} {return TIME;} -# {H}{Z} {return FREQ;} -# {K}{H}{Z} {return FREQ;} -# % {return PERCENTAGE;} -UNIT = r'(?:em|ex|px|cm|mm|in|pt|pc|deg|rad|grad|ms|s|hz|khz|%)' - -# {num}{UNIT|IDENT} {return NUMBER;} -QUANTITY = '%s(?:%s%s|%s)?' % (NUM, WHITESPACE, UNIT, IDENT) - -# "" {return CDC;} -# "~=" {return INCLUDES;} -# "|=" {return DASHMATCH;} -# {w}"{" {return LBRACE;} -# {w}"+" {return PLUS;} -# {w}">" {return GREATER;} -# {w}"," {return COMMA;} -PUNC = r'|~=|\|=|[\{\+>,:;]' diff --git a/maintenance/deleteArchivedFiles.inc b/maintenance/deleteArchivedFiles.inc index d58e9a40..0c0b34a3 100644 --- a/maintenance/deleteArchivedFiles.inc +++ b/maintenance/deleteArchivedFiles.inc @@ -39,6 +39,10 @@ class DeleteArchivedFilesImplementation { $count = 0; foreach ( $res as $row ) { $key = $row->fa_storage_key; + if ( !strlen( $key ) ) { + $output->handleOutput( "Entry with ID {$row->fa_id} has empty key, skipping\n" ); + continue; + } $group = $row->fa_storage_group; $id = $row->fa_id; $path = $repo->getZonePath( 'deleted' ) . '/' . $repo->getDeletedHashPath( $key ) . $key; @@ -49,9 +53,13 @@ class DeleteArchivedFilesImplementation { $sha1 = LocalRepo::getHashFromKey( $key ); } // Check if the file is used anywhere... - $inuse = $dbw->selectField( 'oldimage', '1', - array( 'oi_sha1' => $sha1, - 'oi_deleted & ' . File::DELETED_FILE => File::DELETED_FILE ), + $inuse = $dbw->selectField( + 'oldimage', + '1', + array( + 'oi_sha1' => $sha1, + 'oi_deleted & ' . File::DELETED_FILE => File::DELETED_FILE + ), __METHOD__, array( 'FOR UPDATE' ) ); diff --git a/maintenance/deleteArchivedFiles.php b/maintenance/deleteArchivedFiles.php index ad7b54d0..286b1f24 100644 --- a/maintenance/deleteArchivedFiles.php +++ b/maintenance/deleteArchivedFiles.php @@ -47,6 +47,7 @@ class DeleteArchivedFiles extends Maintenance { public function execute() { if ( !$this->hasOption( 'delete' ) ) { $this->output( "Use --delete to actually confirm this script\n" ); + return; } $force = $this->hasOption( 'force' ); diff --git a/maintenance/deleteArchivedRevisions.inc b/maintenance/deleteArchivedRevisions.inc index dd8e3dd4..ed620ee3 100644 --- a/maintenance/deleteArchivedRevisions.inc +++ b/maintenance/deleteArchivedRevisions.inc @@ -30,8 +30,7 @@ class DeleteArchivedRevisionsImplementation { /** * Perform the delete on archived revisions. - - * @param $maint Object An object (typically of class Maintenance) + * @param object $maint An object (typically of class Maintenance) * that implements two methods: handleOutput() and * purgeRedundantText(). See Maintenance for a description of * those methods. diff --git a/maintenance/deleteArchivedRevisions.php b/maintenance/deleteArchivedRevisions.php index ffd581c1..30883ba4 100644 --- a/maintenance/deleteArchivedRevisions.php +++ b/maintenance/deleteArchivedRevisions.php @@ -36,7 +36,8 @@ require_once __DIR__ . '/deleteArchivedRevisions.inc'; class DeleteArchivedRevisions extends Maintenance { public function __construct() { parent::__construct(); - $this->mDescription = "Deletes all archived revisions\nThese revisions will no longer be restorable"; + $this->mDescription = + "Deletes all archived revisions\nThese revisions will no longer be restorable"; $this->addOption( 'delete', 'Performs the deletion' ); } @@ -53,7 +54,8 @@ class DeleteArchivedRevisions extends Maintenance { $dbw = wfGetDB( DB_MASTER ); $res = $dbw->selectRow( 'archive', 'COUNT(*) as count', array(), __FUNCTION__ ); $this->output( "Found {$res->count} revisions to delete.\n" ); - $this->output( "Please run the script again with the --delete option to really delete the revisions.\n" ); + $this->output( "Please run the script again with the --delete option " + . "to really delete the revisions.\n" ); } } } diff --git a/maintenance/deleteBatch.php b/maintenance/deleteBatch.php index c1cc03cd..93507b34 100644 --- a/maintenance/deleteBatch.php +++ b/maintenance/deleteBatch.php @@ -3,11 +3,11 @@ * Deletes a batch of pages. * Usage: php deleteBatch.php [-u ] [-r ] [-i ] [listfile] * where - * [listfile] is a file where each line contains the title of a page to be - * deleted, standard input is used if listfile is not given. - * is the username - * is the delete reason - * is the number of seconds to sleep for after each delete + * [listfile] is a file where each line contains the title of a page to be + * deleted, standard input is used if listfile is not given. + * is the username + * is the delete reason + * is the number of seconds to sleep for after each delete * * 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 @@ -79,7 +79,9 @@ class DeleteBatch extends Maintenance { $dbw = wfGetDB( DB_MASTER ); # Handle each entry + // @codingStandardsIgnoreStart Ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed for ( $linenum = 1; !feof( $file ); $linenum++ ) { + // @codingStandardsIgnoreEnd $line = trim( fgets( $file ) ); if ( $line == '' ) { continue; @@ -97,7 +99,7 @@ class DeleteBatch extends Maintenance { $this->output( $title->getPrefixedText() ); $dbw->begin( __METHOD__ ); if ( $title->getNamespace() == NS_FILE ) { - $img = wfFindFile( $title ); + $img = wfFindFile( $title, array( 'ignoreRedirect' => true ) ); if ( $img && $img->isLocal() && !$img->delete( $reason ) ) { $this->output( " FAILED to delete associated file... " ); } diff --git a/maintenance/deleteDefaultMessages.php b/maintenance/deleteDefaultMessages.php index 7d8c80e4..5aeeb8e1 100644 --- a/maintenance/deleteDefaultMessages.php +++ b/maintenance/deleteDefaultMessages.php @@ -34,7 +34,7 @@ class DeleteDefaultMessages extends Maintenance { public function __construct() { parent::__construct(); $this->mDescription = "Deletes all pages in the MediaWiki namespace" . - " which were last edited by \"MediaWiki default\""; + " which were last edited by \"MediaWiki default\""; } public function execute() { @@ -54,6 +54,7 @@ class DeleteDefaultMessages extends Maintenance { if ( $dbr->numRows( $res ) == 0 ) { # No more messages left $this->output( "done.\n" ); + return; } diff --git a/maintenance/deleteEqualMessages.php b/maintenance/deleteEqualMessages.php index 81758913..dbe96982 100644 --- a/maintenance/deleteEqualMessages.php +++ b/maintenance/deleteEqualMessages.php @@ -30,15 +30,18 @@ require_once __DIR__ . '/Maintenance.php'; class DeleteEqualMessages extends Maintenance { public function __construct() { parent::__construct(); - $this->mDescription = "Deletes all pages in the MediaWiki namespace that are equal to the default message"; + $this->mDescription = 'Deletes all pages in the MediaWiki namespace that are equal to ' + . 'the default message'; $this->addOption( 'delete', 'Actually delete the pages (default: dry run)' ); $this->addOption( 'delete-talk', 'Don\'t leave orphaned talk pages behind during deletion' ); - $this->addOption( 'lang-code', 'Check for subpages of this language code (default: root page against content language). ' . - 'Use value "*" to run for all mwfile language code subpages (including the base pages that override content language).', false, true ); + $this->addOption( 'lang-code', 'Check for subpages of this language code (default: root ' + . 'page against content language). Use value "*" to run for all mwfile language code ' + . 'subpages (including the base pages that override content language).', false, true ); } /** * @param string|bool $langCode See --lang-code option. + * @param array &$messageInfo */ protected function fetchMessageInfo( $langCode, array &$messageInfo ) { global $wgContLang; @@ -59,7 +62,8 @@ class DeleteEqualMessages extends Maintenance { // Normalise message names for NS_MEDIAWIKI page_title $messageNames = array_map( array( $wgContLang, 'ucfirst' ), $messageNames ); - $statuses = AllmessagesTablePager::getCustomisedStatuses( $messageNames, $langCode, $nonContLang ); + $statuses = AllMessagesTablePager::getCustomisedStatuses( + $messageNames, $langCode, $nonContLang ); // getCustomisedStatuses is stripping the sub page from the page titles, add it back $titleSuffix = $nonContLang ? "/$langCode" : ''; @@ -130,11 +134,14 @@ class DeleteEqualMessages extends Maintenance { if ( $messageInfo['equalPages'] === 0 ) { // No more equal messages left $this->output( "\ndone.\n" ); + return; } - $this->output( "\n{$messageInfo['relevantPages']} pages in the MediaWiki namespace override messages." ); - $this->output( "\n{$messageInfo['equalPages']} pages are equal to the default message (+ {$messageInfo['equalPagesTalks']} talk pages).\n" ); + $this->output( "\n{$messageInfo['relevantPages']} pages in the MediaWiki namespace " + . "override messages." ); + $this->output( "\n{$messageInfo['equalPages']} pages are equal to the default message " + . "(+ {$messageInfo['equalPagesTalks']} talk pages).\n" ); if ( !$doDelete ) { $list = ''; @@ -151,6 +158,7 @@ class DeleteEqualMessages extends Maintenance { $this->output( " (include --delete-talk to also delete the talk pages)" ); } $this->output( "\n" ); + return; } @@ -170,7 +178,6 @@ class DeleteEqualMessages extends Maintenance { foreach ( $messageInfo['results'] as $result ) { wfWaitForSlaves(); $dbw->ping(); - $dbw->begin( __METHOD__ ); $title = Title::makeTitle( NS_MEDIAWIKI, $result['title'] ); $this->output( "\n* [[$title]]" ); $page = WikiPage::factory( $title ); @@ -181,9 +188,9 @@ class DeleteEqualMessages extends Maintenance { $this->output( "\n* [[$title]]" ); $page = WikiPage::factory( $title ); $error = ''; // Passed by ref - $page->doDeleteArticle( 'Orphaned talk page of no longer required message', false, 0, false, $error, $user ); + $page->doDeleteArticle( 'Orphaned talk page of no longer required message', + false, 0, false, $error, $user ); } - $dbw->commit( __METHOD__ ); } $this->output( "\n\ndone!\n" ); } diff --git a/maintenance/deleteImageMemcached.php b/maintenance/deleteImageMemcached.php index 835de352..4799e5e0 100644 --- a/maintenance/deleteImageMemcached.php +++ b/maintenance/deleteImageMemcached.php @@ -60,7 +60,12 @@ class DeleteImageCache extends Maintenance { foreach ( $res as $row ) { if ( $i % $this->report == 0 ) { - $this->output( sprintf( "%s: %13s done (%s)\n", wfWikiID(), "$i/$total", wfPercent( $i / $total * 100 ) ) ); + $this->output( sprintf( + "%s: %13s done (%s)\n", + wfWikiID(), + "$i/$total", + wfPercent( $i / $total * 100 ) + ) ); } $md5 = md5( $row->img_name ); $wgMemc->delete( wfMemcKey( 'Image', $md5 ) ); @@ -75,6 +80,7 @@ class DeleteImageCache extends Maintenance { private function getImageCount() { $dbr = wfGetDB( DB_SLAVE ); + return $dbr->selectField( 'image', 'COUNT(*)', array(), __METHOD__ ); } } diff --git a/maintenance/deleteOrphanedRevisions.php b/maintenance/deleteOrphanedRevisions.php index f0a96928..7f1ffe41 100644 --- a/maintenance/deleteOrphanedRevisions.php +++ b/maintenance/deleteOrphanedRevisions.php @@ -49,7 +49,8 @@ class DeleteOrphanedRevisions extends Maintenance { # Find all the orphaned revisions $this->output( "Checking for orphaned revisions..." ); - $sql = "SELECT rev_id FROM {$revision} LEFT JOIN {$page} ON rev_page = page_id WHERE page_namespace IS NULL"; + $sql = "SELECT rev_id FROM {$revision} LEFT JOIN {$page} ON rev_page = page_id " + . "WHERE page_namespace IS NULL"; $res = $dbw->query( $sql, 'deleteOrphanedRevisions' ); # Stash 'em all up for deletion (if needed) @@ -80,8 +81,8 @@ class DeleteOrphanedRevisions extends Maintenance { * Delete one or more revisions from the database * Do this inside a transaction * - * @param $id Array of revision id values - * @param $dbw DatabaseBase class (needs to be a master) + * @param array $id Array of revision id values + * @param DatabaseBase $dbw DatabaseBase class (needs to be a master) */ private function deleteRevs( $id, &$dbw ) { if ( !is_array( $id ) ) { diff --git a/maintenance/deleteRevision.php b/maintenance/deleteRevision.php index 6bc0f7cd..818ee360 100644 --- a/maintenance/deleteRevision.php +++ b/maintenance/deleteRevision.php @@ -42,41 +42,64 @@ class DeleteRevision extends Maintenance { } $this->output( "Deleting revision(s) " . implode( ',', $this->mArgs ) . - " from " . wfWikiID() . "...\n" ); + " from " . wfWikiID() . "...\n" ); $dbw = wfGetDB( DB_MASTER ); $affected = 0; foreach ( $this->mArgs as $revID ) { $dbw->insertSelect( 'archive', array( 'page', 'revision' ), array( - 'ar_namespace' => 'page_namespace', - 'ar_title' => 'page_title', - 'ar_page_id' => 'page_id', - 'ar_comment' => 'rev_comment', - 'ar_user' => 'rev_user', - 'ar_user_text' => 'rev_user_text', - 'ar_timestamp' => 'rev_timestamp', + 'ar_namespace' => 'page_namespace', + 'ar_title' => 'page_title', + 'ar_page_id' => 'page_id', + 'ar_comment' => 'rev_comment', + 'ar_user' => 'rev_user', + 'ar_user_text' => 'rev_user_text', + 'ar_timestamp' => 'rev_timestamp', 'ar_minor_edit' => 'rev_minor_edit', - 'ar_rev_id' => 'rev_id', - 'ar_text_id' => 'rev_text_id', - 'ar_deleted' => 'rev_deleted', - 'ar_len' => 'rev_len', - ), array( + 'ar_rev_id' => 'rev_id', + 'ar_text_id' => 'rev_text_id', + 'ar_deleted' => 'rev_deleted', + 'ar_len' => 'rev_len', + ), + array( 'rev_id' => $revID, 'page_id = rev_page' - ), __METHOD__ + ), + __METHOD__ ); if ( !$dbw->affectedRows() ) { $this->output( "Revision $revID not found\n" ); } else { $affected += $dbw->affectedRows(); - $pageID = $dbw->selectField( 'revision', 'rev_page', array( 'rev_id' => $revID ), __METHOD__ ); - $pageLatest = $dbw->selectField( 'page', 'page_latest', array( 'page_id' => $pageID ), __METHOD__ ); + $pageID = $dbw->selectField( + 'revision', + 'rev_page', + array( 'rev_id' => $revID ), + __METHOD__ + ); + $pageLatest = $dbw->selectField( + 'page', + 'page_latest', + array( 'page_id' => $pageID ), + __METHOD__ + ); $dbw->delete( 'revision', array( 'rev_id' => $revID ) ); if ( $pageLatest == $revID ) { // Database integrity - $newLatest = $dbw->selectField( 'revision', 'rev_id', array( 'rev_page' => $pageID ), __METHOD__, array( 'ORDER BY' => 'rev_timestamp DESC' ) ); - $dbw->update( 'page', array( 'page_latest' => $newLatest ), array( 'page_id' => $pageID ), __METHOD__ ); + $newLatest = $dbw->selectField( + 'revision', + 'rev_id', + array( 'rev_page' => $pageID ), + __METHOD__, + array( 'ORDER BY' => 'rev_timestamp DESC' ) + ); + $dbw->update( + 'page', + array( 'page_latest' => $newLatest ), + array( 'page_id' => $pageID ), + __METHOD__ + ); } } } diff --git a/maintenance/dev/includes/php.sh b/maintenance/dev/includes/php.sh index 7ce87944..3c5bef0d 100644 --- a/maintenance/dev/includes/php.sh +++ b/maintenance/dev/includes/php.sh @@ -4,7 +4,7 @@ # and previous home directory location # The binary path is returned in $PHP if any -for binary in $PHP `which php || true` "$DEV/php/bin/php" "$HOME/.mediawiki/php/bin/php" "$HOME/.mwphp/bin/php" ]; do +for binary in $PHP $(which php || true) "$DEV/php/bin/php" "$HOME/.mediawiki/php/bin/php" "$HOME/.mwphp/bin/php" ]; do if [ -x "$binary" ]; then if "$binary" -r 'exit((int)!version_compare(PHP_VERSION, "5.4", ">="));'; then PHP="$binary" diff --git a/maintenance/dev/includes/router.php b/maintenance/dev/includes/router.php index a3cc0ba3..0a65e31e 100644 --- a/maintenance/dev/includes/router.php +++ b/maintenance/dev/includes/router.php @@ -59,6 +59,7 @@ if ( $ext == 'php' || $ext == 'php5' ) { # the php webserver will discard post data and things like login # will not function in the dev environment. require $file; + return true; } $mime = false; @@ -79,7 +80,7 @@ if ( !$mime ) { } } if ( $mime ) { - # Use custom handling to serve files with a known mime type + # Use custom handling to serve files with a known MIME type # This way we can serve things like .svg files that the built-in # PHP webserver doesn't understand. # ;) Nicely enough we just happen to bundle a mime.types file @@ -93,6 +94,7 @@ if ( $mime ) { header( "Content-Length: " . filesize( $file ) ); // Stream that out to the browser fpassthru( $f ); + return true; } diff --git a/maintenance/dictionary/mediawiki.dic b/maintenance/dictionary/mediawiki.dic index 164b5b05..df8a34ca 100644 --- a/maintenance/dictionary/mediawiki.dic +++ b/maintenance/dictionary/mediawiki.dic @@ -1,10 +1,10 @@ -ænglisc -ævar &add & &bar +&img &sim &url +&wap ABNF API Aacute @@ -90,7 +90,6 @@ Edit Editor Education Egrave -Ehcache Elig Email Empty @@ -367,6 +366,7 @@ abusive ac acad accel +acceptbilling acceptlang accessdenied accesskey @@ -431,6 +431,8 @@ ai aifc aiff aiprop +airtel +aisort ajaxwatch al alefsym @@ -442,6 +444,7 @@ allcategories alldata alle allexamples +allfileusages allhidden allimages allimit @@ -459,6 +462,8 @@ allpagesbadtitle allpagesprefix allpagesredirect allpagessubmit +allpartners +allredirects allrev alltitles alltransclusions @@ -471,15 +476,16 @@ alreadyexists alreadyrolled alunique am +analyticsconfig anchor anchorclose anchorencode and andconvert +andreescu andtitle anon anoneditwarning -anonlogin anonnotice anononly anonpreviewwarning @@ -656,6 +662,7 @@ badversion balancer balancers banjar +barebone barstein base basefont @@ -685,6 +692,7 @@ bgcolor bgzip bidi bigdelete +bingbot binhex bitdepth bitfield @@ -742,11 +750,13 @@ bmwschema bmysql bname bodycontent +bogo boldening bolding booksources bool boolean +bordercolor borderhack bot botedit @@ -831,12 +841,14 @@ capitalizeallnouns captchaid captchas captchaword +carriersnoips cascade cascadeable cascadeon cascadeprotected cascadeprotectedwarning cascading +cascadinglevels cascadingness categories categories's @@ -850,6 +862,7 @@ categorypage categoryviewer catids catlinks +catmsg catpage catrope cattitles @@ -863,6 +876,7 @@ cedil ceebc cellpadding cellspacing +cellulant central centralauth centralnotice @@ -941,7 +955,6 @@ collapsable collectionsaveascommunitypage collectionsaveasuserpage colname -cologneblue colonseparator colorer colspan @@ -1070,6 +1083,7 @@ danga danielc darr datalen +datapath dataset datasets datasize @@ -1110,6 +1124,7 @@ defaultcontentmodel defaultmessagetext defaultmissing defaultns +defaultoptions defaultsort defaultval deferr @@ -1170,6 +1185,7 @@ devangari devel df dflt +dflts dhtml diams didn @@ -1197,7 +1213,6 @@ disabled disabledtranscode disablemail disablepp -disablesuggest disclaimerpage diskussion displayname @@ -1237,6 +1252,7 @@ domainpart domainparts domas doms +dont dotdotcount dotm dotsc @@ -1254,6 +1270,7 @@ dropdown dump dumpfm dupfunc +dupl duplicatefiles duplicatesoffile dvips @@ -1314,7 +1331,6 @@ editusercssjs edituserjs edoe egrave -ehcache ei eich eiinvalidparammix @@ -1322,7 +1338,9 @@ eimissingparam eititle el elapsedreal +elastica elemname +elems elink eltitle email @@ -1385,6 +1403,7 @@ epcampus epcoordinator epinstructor eponline +eqiad erevoke errno error @@ -1441,6 +1460,7 @@ externaldberror externaldiff externaledit externaleditor +externalimages externallinks externalstore extet @@ -1450,6 +1470,7 @@ extlinks extracts extradata extrafields +extralanglink extraq extratags exturlusage @@ -1520,6 +1541,7 @@ filepage filepath filerenameerror filerepo +filerepoinfo filerevert filerevisions files @@ -1558,6 +1580,7 @@ flagtype flatlist flds float +flrevs fmttime fname fnof @@ -1576,6 +1599,7 @@ forcebot forceditsummary forceeditsummary forcelinkupdate +forcerecursivelinkupdate forcetoc forcontent formaction @@ -1593,6 +1617,7 @@ found founder fr frac +frameborder frameless framesets frasl @@ -1629,12 +1654,15 @@ gadgetcategories gadgets gaid gaifilterredir +gaifrom gallerybox gallerycaption gallerytext gapdir gapfilterredir +gapfrom gaplimit +gapnamespace gapprefix garber gblblock @@ -1648,8 +1676,10 @@ general generatexml generator geocoordinate +geodata geosearch gerrit +geshi getcookie getenv getheader @@ -1686,6 +1716,7 @@ globe gmail gmdate goodtitle +googlebot gopher graymap grayscale @@ -1782,8 +1813,8 @@ hit hitcount hitcounter hits +hlist hmac -hmtl hobby homelink hookaborted @@ -1847,9 +1878,11 @@ ilfrom ilto im image +imagecolorallocate imagegetsize imageinfo imageinvalidfilename +imagelimits imagelinks imagemagick imagemaxsize @@ -1862,6 +1895,7 @@ imagesize imagetype imagetypemismatch imageusage +imagewhitelistenabled imagick imgmultigo imgmultigoto @@ -1933,6 +1967,7 @@ interwiki interwikimap interwikipage interwikis +interwikisearchinfo interwikisource intnull intoken @@ -1980,6 +2015,8 @@ ipchain ipedits iphash ipinrange +ipset +ipsets ipusers iquest irc @@ -1991,12 +2028,14 @@ isconnected iscur isin isip +islocal ismap isminor ismodsince ismulti isnew ispermalink +isroot isself isset istainted @@ -2024,6 +2063,7 @@ iwltitle iwprefix iwtitle iwurl +ized javascript javascripttest jbartsh @@ -2042,6 +2082,7 @@ jslint jsmimetype jsminplus json +jsonconfig jsonfm jsparse jstext @@ -2060,6 +2101,7 @@ keyname keynames keytype khash +kikongo kludgy knownnamespace konqueror @@ -2075,11 +2117,13 @@ langcode langcodes langconversion langlinks +langname langprop langs language languagelinks languages +languageselection languageshtml laquo large @@ -2090,6 +2134,7 @@ lastdot lastedit lasteditor lastedittime +lastfile lastlink lastmod lastmodifiedat @@ -2140,6 +2185,8 @@ link linkarr linkcolour linkprefix +linkprefixcharset +linkpurge links linkstoimage linktbl @@ -2175,6 +2222,7 @@ localdayname localdow locale localhour +localinterwiki localmonth localmonthabbrev localmonthname @@ -2205,6 +2253,7 @@ loginerror loginfo loginlanguagelinks loginlink +loginout loginprompt loginreqlink loginreqpagetext @@ -2241,7 +2290,6 @@ ltitle ltrimmed lurl lysator -möller macr magicarr magicfile @@ -2260,6 +2308,7 @@ mailtext mailto mainmodule mainpage +maint maintainership makesafe male @@ -2295,16 +2344,19 @@ maxwidth mazeland mbresponse mbstring +mccmnc mckey mcklmqw mcrypt mcvalue md mdash +mdot medialink mediaqueries mediatype mediawarning +mediawiki mediawiki's mediawikipage megapixels @@ -2385,6 +2437,7 @@ mkdir mms mobile mobileformat +mobilelanding mobileview modified modifiedarticleprotection @@ -2405,6 +2458,7 @@ moodbar moredotdotdot morelinkstoimage morethan +mouseup move movedarticleprotection moveddeleted @@ -2442,6 +2496,7 @@ msgsmall msgtext msie msmetafile +msnbot mssql msvideo msword @@ -2449,6 +2504,7 @@ mtime mtype mullane multi +multiactions multibyte multicast multipage @@ -2486,10 +2542,13 @@ mysqldump mytalk mytext mywatchlist +möller nabla name namehidden nameinlowercase +namelookup +namemsg names namespace namespacealiases @@ -2530,9 +2589,7 @@ newheader newid newimages newlen -newmessagesdifflink newmessagesdifflinkplural -newmessageslink newmessageslinkplural newname newnames @@ -2548,6 +2605,7 @@ newpos newquery newrevid news +newsectionheaderdefaultlevel newsectionlink newsectionsummary newset @@ -2574,6 +2632,7 @@ nextredirect nextrevision nextval nfkc +nfkd nginx nheight niklas @@ -2787,6 +2846,7 @@ noto notoc notoggle notoken +notpatrollable notransform notreviewable notrustworthy @@ -2888,6 +2948,7 @@ oldtitle oldtitlemsg oline oname +onerror onkeyup online onload @@ -2898,6 +2959,7 @@ onlyquery onsubmit onthisday ontop +onuser openbasedir opendoc opendocument @@ -2906,6 +2968,7 @@ opensearchdescription openssl's openxml openxmlformats +operamini oplus oppositedm optgroup @@ -2920,6 +2983,7 @@ ordertype ordf ordm org +orghttp origcategory ortime oslash @@ -2991,6 +3055,7 @@ pagetriagetagging pagetriagetemplate pageurl pageview +pango param parameters paraminfo @@ -3091,8 +3156,10 @@ pltitles plusminus plusmn pname +png'd pnmtojpeg pnmtopng +pointsize poolcounter popts popularpages @@ -3117,17 +3184,18 @@ pptm pptx precaching precompiled +preemptively preferences preferencestoken prefill prefilled prefix prefixindex +prefixsearch prefixsearchdisabled prefs prefsection -prefsnologin -prefsnologintext +prefsnologintext2 prefsubmit preload preloads @@ -3139,6 +3207,7 @@ preprocessing preprocessors presentationml presep +pretransfer prevchar prevdiff previd @@ -3202,6 +3271,7 @@ protecttoken proto protocol protocols +protorel protos proxied proxyblocker @@ -3224,6 +3294,7 @@ purged qabardjajəbza qbar qbsettings +qlow qmoicj qp quasit @@ -3242,6 +3313,7 @@ querytype question queuefull quickbar +quicksorts quicktemplate quicktime qunit @@ -3337,6 +3409,7 @@ redirectable redirectcreated redirectedfrom redirections +redirector redirectpagesub redirectparams redirects @@ -3351,6 +3424,7 @@ redis redlink redlinks redocument +redux reedyboy reenables reencode @@ -3377,7 +3451,6 @@ relname relnamespace remarticle remembermypassword -rememberpassword removablegroups removal remove @@ -3389,6 +3462,8 @@ remstudent renameuser renaming renderable +renderesibanner +renderwarning renormalized repeating repl @@ -3574,6 +3649,7 @@ selflink selfmove semiglobal semiprotected +semiprotectedlevels semiprotectedpagewarning sendemail sendmail @@ -3692,9 +3768,14 @@ slideshow sm smaxage smil +smpp +sms's +smscontent +smslogs smtp snippet sodipodi +softredirect softtabstop solaris somecontent @@ -3748,6 +3829,7 @@ stabilize stable stablesettings stansvik +starcode start startid startime @@ -3755,6 +3837,7 @@ startsortkey startsortkeyprefix starttime starttimestamp +starttransfer stash stashfailed stashimageinfo @@ -3771,6 +3854,7 @@ stopwords storedversion strcasecmp strcmp +strftime string stripos stripslashes @@ -3887,6 +3971,7 @@ taglist tags tagset tagstack +tahoma tailorings talk talkable @@ -3904,8 +3989,8 @@ talkpagetext talkspace talkspacee talkto -taraškievica tarask +taraškievica target tb tbase @@ -3932,6 +4017,7 @@ test testclean testdata testmailuser +teston testpass testrunner testswarm @@ -3972,6 +4058,7 @@ thumbheight thumbhtml thumbimage thumbinner +thumblimits thumbmime thumbnail thumbnailing @@ -4045,6 +4132,7 @@ toparse topbar toplevel toplinks +topojson toponly torev torevid @@ -4076,7 +4164,9 @@ transwiki troff true truespeed +truncatedtext trustworthy +truteq truthy tsearch tsquery @@ -4099,6 +4189,7 @@ udpprofile ufffd ugrave ui +uids uint ulimit ulink @@ -4124,6 +4215,8 @@ undel undelete undeleted undeletion +undismissable +undismissible undo undoafter undofailure @@ -4138,6 +4231,7 @@ unhelpful unhidden unhide unidata +unidecode unindent unindexed uniq @@ -4165,6 +4259,7 @@ unprotect unprotectedarticle unprotection unprotectthispage +unreadcount unredacted unrequest unrequested @@ -4177,6 +4272,7 @@ unseed unserialization unserialize unserialized +unserializes unserializing unsetting unstub @@ -4220,6 +4316,7 @@ uploadscripted uploadsource uploadstash uploadvirus +uploadwarning uppercased upsih urandom @@ -4303,6 +4400,9 @@ usertoollinks useskin useto usort +usrmonth +ussd +ussdcontent ustar ustoken utfnormal @@ -4347,6 +4447,7 @@ viewdeleted viewhelppage viewmyprivateinfo viewmywatchlist +viewport viewprevnext viewsource viewsourcelink @@ -4363,6 +4464,7 @@ vorbis vpad vrml vslow +vumi vvcv vxml wais @@ -4400,6 +4502,7 @@ wbxml wddx wddxfm weblog +weblogs webm webp webrequest @@ -4442,17 +4545,23 @@ wikilove wikiloveimagelog wikimedia wikimediacommons +wikimediafoundation +wikinews wikipage wikipedia wikipedian wikipedias +wikiquote wikis +wikisource wikisyntax wikitable wikitables wikitech wikitext wikiuser +wikiversity +wikivoyage wiktionary wincache wininet @@ -4477,6 +4586,7 @@ wmls wmlsc wmlscript wmlscriptc +wmnet wordcount wordprocessingml wordwg @@ -4491,6 +4601,7 @@ writerequired writerights wrongpassword x +xanalytics xbitmap xcache xcancel @@ -4512,8 +4623,8 @@ xml xmldoublequote xmlfm xmlimport +xmlmeta xmlns -xmlsafe xmlselect xor xpinstall @@ -4526,6 +4637,7 @@ xxxxx yacute yaml yamlfm +yandex year yes youhavenewmessages @@ -4547,10 +4659,26 @@ yourvariant yourwiki yuml yyyymmddhhiiss +zcmd +zerobanner +zerobar +zerobutton +zeroconfig +zerodontask +zerodot +zeroinfo +zeronet +zeroportal +zfile zhdaemon zhengzhu zhtable zijdel +zlang zlib zoffset +zrma zwnj +ænglisc +ævar +świerkosz diff --git a/maintenance/doMaintenance.php b/maintenance/doMaintenance.php index 3bd508cb..46844c9d 100644 --- a/maintenance/doMaintenance.php +++ b/maintenance/doMaintenance.php @@ -44,6 +44,7 @@ if ( !$maintClass || !class_exists( $maintClass ) ) { } // Get an object to start us off +/** @var Maintenance $maintenance */ $maintenance = new $maintClass(); // Basic sanity checks and such @@ -77,37 +78,24 @@ if ( defined( 'MW_CONFIG_CALLBACK' ) ) { # Use a callback function to configure MediaWiki call_user_func( MW_CONFIG_CALLBACK ); } else { - if ( file_exists( "$IP/../wmf-config/wikimedia-mode" ) ) { - // Load settings, using wikimedia-mode if needed - // @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; - $cluster = 'pmtpa'; - require "$IP/../wmf-config/wgConf.php"; - } // Require the configuration (probably LocalSettings.php) require $maintenance->loadSettings(); } -if ( $maintenance->getDbType() === Maintenance::DB_ADMIN && - is_readable( "$IP/AdminSettings.php" ) ) -{ - require "$IP/AdminSettings.php"; -} - if ( $maintenance->getDbType() === Maintenance::DB_NONE ) { - if ( $wgLocalisationCacheConf['storeClass'] === false && ( $wgLocalisationCacheConf['store'] == 'db' || ( $wgLocalisationCacheConf['store'] == 'detect' && !$wgCacheDirectory ) ) ) { - $wgLocalisationCacheConf['storeClass'] = 'LCStore_Null'; + if ( $wgLocalisationCacheConf['storeClass'] === false + && ( $wgLocalisationCacheConf['store'] == 'db' + || ( $wgLocalisationCacheConf['store'] == 'detect' && !$wgCacheDirectory ) ) + ) { + $wgLocalisationCacheConf['storeClass'] = 'LCStoreNull'; } } + +$maintenance->setConfig( ConfigFactory::getDefaultInstance()->makeConfig( 'main' ) ); $maintenance->finalSetup(); // Some last includes require_once "$IP/includes/Setup.php"; -// Much much faster startup than creating a title object -$wgTitle = null; - // Do the work try { $maintenance->execute(); diff --git a/maintenance/dumpBackup.php b/maintenance/dumpBackup.php index 25a777cd..18c78dcd 100644 --- a/maintenance/dumpBackup.php +++ b/maintenance/dumpBackup.php @@ -4,7 +4,7 @@ * wrapper format for export or backup * * Copyright © 2005 Brion Vibber - * http://www.mediawiki.org/ + * https://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 @@ -127,5 +127,5 @@ Fancy stuff: (Works? Add examples please.) --filter=[:] Add a filter on an output branch ENDS -); + ); } diff --git a/maintenance/dumpIterator.php b/maintenance/dumpIterator.php index dd468a9f..4b2ff717 100644 --- a/maintenance/dumpIterator.php +++ b/maintenance/dumpIterator.php @@ -5,7 +5,7 @@ * We implement below the simple task of searching inside a dump. * * Copyright © 2011 Platonides - * http://www.mediawiki.org/ + * https://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 @@ -57,8 +57,11 @@ abstract class DumpIterator extends Maintenance { $revision = new WikiRevision; $revision->setText( file_get_contents( $this->getOption( 'file' ) ) ); - $revision->setTitle( Title::newFromText( rawurldecode( basename( $this->getOption( 'file' ), '.txt' ) ) ) ); + $revision->setTitle( Title::newFromText( + rawurldecode( basename( $this->getOption( 'file' ), '.txt' ) ) + ) ); $this->handleRevision( $revision ); + return; } @@ -67,7 +70,8 @@ abstract class DumpIterator extends Maintenance { 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 ); + $this->error( "Sorry, I don't support dump filenames yet. " + . "Use - and provide it on stdin on the meantime.", true ); } $importer = new WikiImporter( $source ); @@ -86,8 +90,9 @@ abstract class DumpIterator extends Maintenance { $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) + # 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" ); } @@ -97,7 +102,7 @@ abstract class DumpIterator extends Maintenance { if ( $this->getDbType() == Maintenance::DB_NONE ) { global $wgUseDatabaseMessages, $wgLocalisationCacheConf, $wgHooks; $wgUseDatabaseMessages = false; - $wgLocalisationCacheConf['storeClass'] = 'LCStore_Null'; + $wgLocalisationCacheConf['storeClass'] = 'LCStoreNull'; $wgHooks['InterwikiLoadPrefix'][] = 'DumpIterator::disableInterwikis'; } } @@ -112,12 +117,13 @@ abstract class DumpIterator extends Maintenance { /** * Callback function for each revision, child classes should override * processRevision instead. - * @param $rev Revision + * @param DatabaseBase $rev */ public function handleRevision( $rev ) { $title = $rev->getTitle(); if ( !$title ) { $this->error( "Got bogus revision with null title!" ); + return; } @@ -167,7 +173,7 @@ class SearchDump extends DumpIterator { } /** - * @param $rev Revision + * @param Revision $rev */ public function processRevision( $rev ) { if ( preg_match( $this->getOption( 'regex' ), $rev->getContent()->getTextForSearchIndex() ) ) { diff --git a/maintenance/dumpLinks.php b/maintenance/dumpLinks.php index be0b4633..888c2dc1 100644 --- a/maintenance/dumpLinks.php +++ b/maintenance/dumpLinks.php @@ -9,7 +9,7 @@ * Dumps ASCII text to stdout; command-line. * * Copyright © 2005 Brion Vibber - * http://www.mediawiki.org/ + * https://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 @@ -59,7 +59,7 @@ class DumpLinks extends Maintenance { $lastPage = null; foreach ( $result as $row ) { if ( $lastPage != $row->page_id ) { - if ( isset( $lastPage ) ) { + if ( $lastPage !== null ) { $this->output( "\n" ); } $page = Title::makeTitle( $row->page_namespace, $row->page_title ); @@ -69,7 +69,7 @@ class DumpLinks extends Maintenance { $link = Title::makeTitle( $row->pl_namespace, $row->pl_title ); $this->output( " " . $link->getPrefixedURL() ); } - if ( isset( $lastPage ) ) { + if ( $lastPage !== null ) { $this->output( "\n" ); } } diff --git a/maintenance/dumpSisterSites.php b/maintenance/dumpSisterSites.php index 5f0c5b7c..784dc7a8 100644 --- a/maintenance/dumpSisterSites.php +++ b/maintenance/dumpSisterSites.php @@ -4,7 +4,7 @@ * http://www.eekim.com/cgi-bin/wiki.pl?SisterSites * * Copyright © 2006 Brion Vibber - * http://www.mediawiki.org/ + * https://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 diff --git a/maintenance/dumpTextPass.php b/maintenance/dumpTextPass.php index 5d783cb9..7c176071 100644 --- a/maintenance/dumpTextPass.php +++ b/maintenance/dumpTextPass.php @@ -3,7 +3,7 @@ * Script that postprocesses XML dumps from dumpBackup.php to add page text * * Copyright (C) 2005 Brion Vibber - * http://www.mediawiki.org/ + * https://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 @@ -29,7 +29,6 @@ $originalDir = getcwd(); require_once __DIR__ . '/commandLine.inc'; require_once __DIR__ . '/backupTextPass.inc'; - $dumper = new TextPassDumper( $argv ); if ( !isset( $options['help'] ) ) { @@ -62,5 +61,5 @@ Options: --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 1a9293cb..9d53f07c 100644 --- a/maintenance/dumpUploads.php +++ b/maintenance/dumpUploads.php @@ -64,7 +64,7 @@ By default, outputs relative paths against the parent directory of \$wgUploadDir $this->mSharedSupplement = true; } } - $this-> { $this->mAction } ( $this->mShared ); + $this->{$this->mAction} ( $this->mShared ); if ( $this->mSharedSupplement ) { $this->fetchUsed( true ); } @@ -73,7 +73,7 @@ By default, outputs relative paths against the parent directory of \$wgUploadDir /** * Fetch a list of used images from a particular image source. * - * @param $shared Boolean: true to pass shared-dir settings to hash func + * @param bool $shared True to pass shared-dir settings to hash func */ function fetchUsed( $shared ) { $dbr = wfGetDB( DB_SLAVE ); @@ -94,7 +94,7 @@ By default, outputs relative paths against the parent directory of \$wgUploadDir /** * Fetch a list of all images from a particular image source. * - * @param $shared Boolean: true to pass shared-dir settings to hash func + * @param bool $shared True to pass shared-dir settings to hash func */ function fetchLocal( $shared ) { $dbr = wfGetDB( DB_SLAVE ); diff --git a/maintenance/edit.php b/maintenance/edit.php index 7c24f0fa..75ec12bf 100644 --- a/maintenance/edit.php +++ b/maintenance/edit.php @@ -38,11 +38,13 @@ class EditCLI extends Maintenance { $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->addOption( 'nocreate', 'Don\'t create new pages', false, false ); + $this->addOption( 'createonly', 'Only create new pages', false, false ); $this->addArg( 'title', 'Title of article to edit' ); } public function execute() { - global $wgUser, $wgTitle; + global $wgUser; $userName = $this->getOption( 'user', 'Maintenance script' ); $summary = $this->getOption( 'summary', '' ); @@ -52,8 +54,6 @@ class EditCLI extends Maintenance { $noRC = $this->hasOption( 'no-rc' ); $wgUser = User::newFromName( $userName ); - $context = RequestContext::getMain(); - $context->setUser( $wgUser ); if ( !$wgUser ) { $this->error( "Invalid username", true ); } @@ -61,17 +61,22 @@ class EditCLI extends Maintenance { $wgUser->addToDatabase(); } - $wgTitle = Title::newFromText( $this->getArg() ); - if ( !$wgTitle ) { + $title = Title::newFromText( $this->getArg() ); + if ( !$title ) { $this->error( "Invalid title", true ); } - $context->setTitle( $wgTitle ); - $page = WikiPage::factory( $wgTitle ); + if ( $this->hasOption( 'nocreate' ) && !$title->exists() ) { + $this->error( "Page does not exist", true ); + } elseif ( $this->hasOption( 'createonly' ) && $title->exists() ) { + $this->error( "Page already exists", true ); + } + + $page = WikiPage::factory( $title ); # Read the text $text = $this->getStdin( Maintenance::STDIN_ALL ); - $content = ContentHandler::makeContent( $text, $wgTitle ); + $content = ContentHandler::makeContent( $text, $title ); # Do the edit $this->output( "Saving... " ); diff --git a/maintenance/eraseArchivedFile.php b/maintenance/eraseArchivedFile.php index 1c3f0376..94ca604d 100644 --- a/maintenance/eraseArchivedFile.php +++ b/maintenance/eraseArchivedFile.php @@ -27,7 +27,7 @@ require_once __DIR__ . '/Maintenance.php'; /** * Maintenance script to delete archived (non-current) files from storage. * - * @TODO: Maybe add some simple logging + * @todo Maybe add some simple logging * * @ingroup Maintenance * @since 1.22 diff --git a/maintenance/eval.php b/maintenance/eval.php index abedc61a..51f2cace 100644 --- a/maintenance/eval.php +++ b/maintenance/eval.php @@ -1,6 +1,5 @@ getMessage()}\n" . $e->getTraceAsString() . "\n"; + continue; + } + if ( wfIsHHVM() || 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 05470d30..fc676b89 100644 --- a/maintenance/fetchText.php +++ b/maintenance/fetchText.php @@ -37,11 +37,11 @@ class FetchText extends Maintenance { /** * returns a string containing the following in order: - * textid - * \n - * length of text (-1 on error = failure to retrieve/unserialize/gunzip/etc) - * \n - * text (may be empty) + * textid + * \n + * length of text (-1 on error = failure to retrieve/unserialize/gunzip/etc) + * \n + * text (may be empty) * * note that that the text string itself is *not* followed by newline */ @@ -59,8 +59,7 @@ class FetchText extends Maintenance { if ( $text === false ) { # actual error, not zero-length text $textLen = "-1"; - } - else { + } else { $textLen = strlen( $text ); } $this->output( $textId . "\n" . $textLen . "\n" . $text ); @@ -69,9 +68,9 @@ class FetchText extends Maintenance { /** * May throw a database error if, say, the server dies during query. - * @param $db DatabaseBase object - * @param $id int The old_id - * @return String + * @param DatabaseBase $db + * @param int $id The old_id + * @return string */ private function doGetText( $db, $id ) { $id = intval( $id ); @@ -83,6 +82,7 @@ class FetchText extends Maintenance { if ( $text === false ) { return false; } + return $text; } } diff --git a/maintenance/findHooks.php b/maintenance/findHooks.php index 373170ff..36760d7e 100644 --- a/maintenance/findHooks.php +++ b/maintenance/findHooks.php @@ -42,6 +42,11 @@ require_once __DIR__ . '/Maintenance.php'; * @ingroup Maintenance */ class FindHooks extends Maintenance { + /* + * Hooks that are ignored + */ + protected static $ignore = array( 'testRunLegacyHooks' ); + public function __construct() { parent::__construct(); $this->mDescription = 'Find hooks that are undocumented, missing, or just plain wrong'; @@ -58,34 +63,53 @@ class FindHooks extends Maintenance { $documented = $this->getHooksFromDoc( $IP . '/docs/hooks.txt' ); $potential = array(); $bad = array(); + + // TODO: Don't hardcode the list of directories $pathinc = array( $IP . '/', $IP . '/includes/', $IP . '/includes/actions/', $IP . '/includes/api/', $IP . '/includes/cache/', + $IP . '/includes/changes/', + $IP . '/includes/clientpool/', $IP . '/includes/content/', $IP . '/includes/context/', + $IP . '/includes/dao/', $IP . '/includes/db/', + $IP . '/includes/debug/', + $IP . '/includes/deferred/', $IP . '/includes/diff/', + $IP . '/includes/externalstore/', + $IP . '/includes/filebackend/', $IP . '/includes/filerepo/', $IP . '/includes/filerepo/file/', + $IP . '/includes/gallery/', + $IP . '/includes/htmlform/', $IP . '/includes/installer/', $IP . '/includes/interwiki/', + $IP . '/includes/jobqueue/', + $IP . '/includes/json/', $IP . '/includes/logging/', $IP . '/includes/media/', + $IP . '/includes/page/', $IP . '/includes/parser/', + $IP . '/includes/rcfeed/', $IP . '/includes/resourceloader/', $IP . '/includes/revisiondelete/', $IP . '/includes/search/', + $IP . '/includes/site/', + $IP . '/includes/skins/', + $IP . '/includes/specialpage/', $IP . '/includes/specials/', $IP . '/includes/upload/', + $IP . '/includes/utils/', $IP . '/languages/', $IP . '/maintenance/', + $IP . '/maintenance/language/', $IP . '/tests/', $IP . '/tests/parser/', $IP . '/tests/phpunit/suites/', - $IP . '/skins/', ); foreach ( $pathinc as $dir ) { @@ -103,15 +127,15 @@ class FindHooks extends Maintenance { $this->printArray( 'Documented and not found', $deprecated ); $this->printArray( 'Unclear hook calls', $bad ); - if ( count( $todo ) == 0 && count( $deprecated ) == 0 && count( $bad ) == 0 ) - { + if ( count( $todo ) == 0 && count( $deprecated ) == 0 && count( $bad ) == 0 ) { $this->output( "Looks good!\n" ); } } /** * Get the hook documentation, either locally or from MediaWiki.org - * @return array of documented hooks + * @param string $doc + * @return array Array of documented hooks */ private function getHooksFromDoc( $doc ) { if ( $this->hasOption( 'online' ) ) { @@ -123,62 +147,75 @@ class FindHooks extends Maintenance { /** * Get hooks from a local file (for example docs/hooks.txt) - * @param $doc string: filename to look in - * @return array of documented hooks + * @param string $doc Filename to look in + * @return array Array of documented hooks */ private function getHooksFromLocalDoc( $doc ) { - $m = array(); - $content = file_get_contents( $doc ); - preg_match_all( "/\n'(.*?)'/", $content, $m ); - return array_unique( $m[1] ); + $m = array(); + $content = file_get_contents( $doc ); + preg_match_all( "/\n'(.*?)':/", $content, $m ); + + return array_unique( $m[1] ); } /** * Get hooks from www.mediawiki.org using the API - * @return array of documented hooks + * @return array Array of documented hooks */ private function getHooksFromOnlineDoc() { - // All hooks - $allhookdata = Http::get( 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:MediaWiki_hooks&cmlimit=500&format=php' ); - $allhookdata = unserialize( $allhookdata ); - $allhooks = array(); - foreach ( $allhookdata['query']['categorymembers'] as $page ) { - $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches ); - if ( $found ) { - $hook = str_replace( ' ', '_', $matches[1] ); - $allhooks[] = $hook; - } + // All hooks + $allhookdata = Http::get( + 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&' + . 'cmtitle=Category:MediaWiki_hooks&cmlimit=500&format=php' + ); + $allhookdata = unserialize( $allhookdata ); + $allhooks = array(); + foreach ( $allhookdata['query']['categorymembers'] as $page ) { + $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches ); + if ( $found ) { + $hook = str_replace( ' ', '_', $matches[1] ); + $allhooks[] = $hook; } - // Removed hooks - $oldhookdata = Http::get( 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:Removed_hooks&cmlimit=500&format=php' ); - $oldhookdata = unserialize( $oldhookdata ); - $removed = array(); - foreach ( $oldhookdata['query']['categorymembers'] as $page ) { - $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches ); - if ( $found ) { - $hook = str_replace( ' ', '_', $matches[1] ); - $removed[] = $hook; - } + } + // Removed hooks + $oldhookdata = Http::get( + 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&' + . 'cmtitle=Category:Removed_hooks&cmlimit=500&format=php' + ); + $oldhookdata = unserialize( $oldhookdata ); + $removed = array(); + foreach ( $oldhookdata['query']['categorymembers'] as $page ) { + $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches ); + if ( $found ) { + $hook = str_replace( ' ', '_', $matches[1] ); + $removed[] = $hook; } - return array_diff( $allhooks, $removed ); + } + + return array_diff( $allhooks, $removed ); } /** * Get hooks from a PHP file - * @param $file string Full filename to the PHP file. - * @return array of hooks found. + * @param string $file Full filename to the PHP file. + * @return array Array of hooks found */ private function getHooksFromFile( $file ) { $content = file_get_contents( $file ); $m = array(); - preg_match_all( '/(?:wfRunHooks|Hooks\:\:run|ContentHandler\:\:runLegacyHooks)\(\s*([\'"])(.*?)\1/', $content, $m ); + preg_match_all( + '/(?:wfRunHooks|Hooks\:\:run|ContentHandler\:\:runLegacyHooks)\(\s*([\'"])(.*?)\1/', + $content, + $m + ); + return $m[2]; } /** * Get hooks from the source code. - * @param $path Directory where the include files can be found - * @return array of hooks found. + * @param string $path Directory where the include files can be found + * @return array Array of hooks found */ private function getHooksFromPath( $path ) { $hooks = array(); @@ -191,13 +228,14 @@ class FindHooks extends Maintenance { } closedir( $dh ); } + return $hooks; } /** * Get bad hooks (where the hook name could not be determined) from a PHP file - * @param $file string Full filename to the PHP file. - * @return array of bad wfRunHooks() lines + * @param string $file Full filename to the PHP file. + * @return array Array of bad wfRunHooks() lines */ private function getBadHooksFromFile( $file ) { $content = file_get_contents( $file ); @@ -208,13 +246,14 @@ class FindHooks extends Maintenance { foreach ( $m[0] as $match ) { $list[] = $match . "(" . $file . ")"; } + return $list; } /** * Get bad hooks from the source code. - * @param $path Directory where the include files can be found - * @return array of bad wfRunHooks() lines + * @param string $path Directory where the include files can be found + * @return array Array of bad wfRunHooks() lines */ private function getBadHooksFromPath( $path ) { $hooks = array(); @@ -228,21 +267,25 @@ class FindHooks extends Maintenance { } closedir( $dh ); } + return $hooks; } /** * Nicely output the array - * @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) + * @param string $msg A message to show before the value + * @param array $arr + * @param bool $sort 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 ( !in_array( $v, self::$ignore ) ) { + $this->output( "$msg: $v\n" ); + } } } } diff --git a/maintenance/findMissingFiles.php b/maintenance/findMissingFiles.php new file mode 100644 index 00000000..5f9f643a --- /dev/null +++ b/maintenance/findMissingFiles.php @@ -0,0 +1,115 @@ +mDescription = 'Find registered files with no corresponding file.'; + $this->addOption( 'start', 'Starting file name', false, true ); + $this->addOption( 'mtimeafter', 'Only include files changed since this time', false, true ); + $this->addOption( 'mtimebefore', 'Only includes files changed before this time', false, true ); + $this->setBatchSize( 300 ); + } + + function execute() { + $lastName = $this->getOption( 'start', '' ); + + $repo = RepoGroup::singleton()->getLocalRepo(); + $dbr = $repo->getSlaveDB(); + $be = $repo->getBackend(); + + $mtime1 = $dbr->timestampOrNull( $this->getOption( 'mtimeafter', null ) ); + $mtime2 = $dbr->timestampOrNull( $this->getOption( 'mtimebefore', null ) ); + + $joinTables = array( 'image' ); + $joinConds = array( 'image' => array( 'INNER JOIN', 'img_name = page_title' ) ); + if ( $mtime1 || $mtime2 ) { + $joinTables[] = 'logging'; + $on = array( 'log_page = page_id', 'log_type' => array( 'upload', 'move', 'delete' ) ); + if ( $mtime1 ) { + $on[] = "log_timestamp > {$dbr->addQuotes($mtime1)}"; + } + if ( $mtime2 ) { + $on[] = "log_timestamp < {$dbr->addQuotes($mtime2)}"; + } + $joinConds['logging'] = array( 'INNER JOIN', $on ); + } + + do { + $res = $dbr->select( + array_merge( array( 'page' ), $joinTables ), + array( 'img_name' => 'DISTINCT(page_title)' ), + array( 'page_namespace' => NS_FILE, + "page_title >= " . $dbr->addQuotes( $lastName ) ), + __METHOD__, + array( 'ORDER BY' => 'page_title', 'LIMIT' => $this->mBatchSize ), + $joinConds + ); + + // Check if any of these files are missing... + $pathsByName = array(); + foreach ( $res as $row ) { + $file = $repo->newFile( $row->img_name ); + $pathsByName[$row->img_name] = $file->getPath(); + $lastName = $row->img_name; + } + $be->preloadFileStat( array( 'srcs' => $pathsByName ) ); + foreach ( $pathsByName as $path ) { + if ( $be->fileExists( array( 'src' => $path ) ) === false ) { + $this->output( "$path\n" ); + } + } + + // Find all missing old versions of any of the files in this batch... + if ( count( $pathsByName ) ) { + $ores = $dbr->select( 'oldimage', + array( 'oi_name', 'oi_archive_name' ), + array( 'oi_name' => array_keys( $pathsByName ) ), + __METHOD__ + ); + + $checkPaths = array(); + foreach ( $ores as $row ) { + if ( !strlen( $row->oi_archive_name ) ) { + continue; // broken row + } + $file = $repo->newFromArchiveName( $row->oi_name, $row->oi_archive_name ); + $checkPaths[] = $file->getPath(); + } + + foreach ( array_chunk( $checkPaths, $this->mBatchSize ) as $paths ) { + $be->preloadFileStat( array( 'srcs' => $paths ) ); + foreach ( $paths as $path ) { + if ( $be->fileExists( array( 'src' => $path ) ) === false ) { + $this->output( "$path\n" ); + } + } + } + } + } while ( $res->numRows() >= $this->mBatchSize ); + } +} + +$maintClass = 'FindMissingFiles'; +require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/fixDoubleRedirects.php b/maintenance/fixDoubleRedirects.php index 523be7ef..95682847 100644 --- a/maintenance/fixDoubleRedirects.php +++ b/maintenance/fixDoubleRedirects.php @@ -3,7 +3,7 @@ * Fix double redirects. * * Copyright © 2011 Ilmari Karonen - * http://www.mediawiki.org/ + * https://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 @@ -44,13 +44,14 @@ class FixDoubleRedirects extends Maintenance { 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 ( $this->hasOption( 'title' ) ) { + $title = Title::newFromText( $this->getOption( 'title' ) ); if ( !$title || !$title->isRedirect() ) { $this->error( $title->getPrefixedText() . " is not a redirect!\n", true ); } + } else { + $title = null; } $dbr = wfGetDB( DB_SLAVE ); @@ -71,11 +72,11 @@ class FixDoubleRedirects extends Maintenance { 'rd_from = pa.page_id', 'rd_namespace = pb.page_namespace', 'rd_title = pb.page_title', - '(rd_interwiki IS NULL OR rd_interwiki = "")', // bug 40352 + 'rd_interwiki IS NULL OR rd_interwiki = ' . $dbr->addQuotes( '' ), // bug 40352 'pb.page_is_redirect' => 1, ); - if ( isset( $title ) ) { + if ( $title != null ) { $conds['pb.page_namespace'] = $title->getNamespace(); $conds['pb.page_title'] = $title->getDBkey(); } @@ -85,6 +86,7 @@ class FixDoubleRedirects extends Maintenance { if ( !$res->numRows() ) { $this->output( "No double redirects found.\n" ); + return; } @@ -105,7 +107,8 @@ class FixDoubleRedirects extends Maintenance { if ( !$async ) { $success = ( $dryrun ? true : $job->run() ); if ( !$success ) { - $this->error( "Error fixing " . $titleA->getPrefixedText() . ": " . $job->getLastError() . "\n" ); + $this->error( "Error fixing " . $titleA->getPrefixedText() + . ": " . $job->getLastError() . "\n" ); } } else { $jobs[] = $job; diff --git a/maintenance/fixExtLinksProtocolRelative.php b/maintenance/fixExtLinksProtocolRelative.php index 02d65ed1..0c60e62c 100644 --- a/maintenance/fixExtLinksProtocolRelative.php +++ b/maintenance/fixExtLinksProtocolRelative.php @@ -34,7 +34,8 @@ require_once __DIR__ . '/Maintenance.php'; class FixExtLinksProtocolRelative extends LoggedUpdateMaintenance { public function __construct() { parent::__construct(); - $this->mDescription = "Fixes any entries in the externallinks table containing protocol-relative URLs"; + $this->mDescription = + "Fixes any entries in the externallinks table containing protocol-relative URLs"; } protected function getUpdateKey() { @@ -49,6 +50,7 @@ class FixExtLinksProtocolRelative extends LoggedUpdateMaintenance { $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" ); @@ -79,9 +81,18 @@ class FixExtLinksProtocolRelative extends LoggedUpdateMaintenance { ) ), __METHOD__, array( 'IGNORE' ) ); - $db->delete( 'externallinks', array( 'el_index' => $row->el_index, 'el_from' => $row->el_from, 'el_to' => $row->el_to ), __METHOD__ ); + $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; } } diff --git a/maintenance/fixSlaveDesync.php b/maintenance/fixSlaveDesync.php index e4e557fe..a5418ced 100644 --- a/maintenance/fixSlaveDesync.php +++ b/maintenance/fixSlaveDesync.php @@ -30,6 +30,9 @@ require_once __DIR__ . '/Maintenance.php'; * @ingroup Maintenance */ class FixSlaveDesync extends Maintenance { + /** @var array */ + private $slaveIndexes; + public function __construct() { parent::__construct(); $this->mDescription = ""; @@ -41,7 +44,8 @@ class FixSlaveDesync extends Maintenance { public function execute() { $this->slaveIndexes = array(); - for ( $i = 1; $i < wfGetLB()->getServerCount(); $i++ ) { + $serverCount = wfGetLB()->getServerCount(); + for ( $i = 1; $i < $serverCount; $i++ ) { if ( wfGetLB()->isNonZeroLoad( $i ) ) { $this->slaveIndexes[] = $i; } @@ -66,7 +70,12 @@ class FixSlaveDesync extends Maintenance { $n = 0; $dbw = wfGetDB( DB_MASTER ); $masterIDs = array(); - $res = $dbw->select( 'page', array( 'page_id', 'page_latest' ), array( 'page_id<6054123' ), __METHOD__ ); + $res = $dbw->select( + 'page', + array( 'page_id', 'page_latest' ), + array( 'page_id<6054123' ), + __METHOD__ + ); $this->output( "Number of pages: " . $res->numRows() . "\n" ); foreach ( $res as $row ) { $masterIDs[$row->page_id] = $row->page_latest; @@ -78,7 +87,12 @@ class FixSlaveDesync extends Maintenance { foreach ( $this->slaveIndexes as $i ) { $db = wfGetDB( $i ); - $res = $db->select( 'page', array( 'page_id', 'page_latest' ), array( 'page_id<6054123' ), __METHOD__ ); + $res = $db->select( + 'page', + array( 'page_id', 'page_latest' ), + array( 'page_id<6054123' ), + __METHOD__ + ); foreach ( $res as $row ) { if ( isset( $masterIDs[$row->page_id] ) && $masterIDs[$row->page_id] != $row->page_latest ) { $desync[$row->page_id] = true; @@ -87,12 +101,13 @@ class FixSlaveDesync extends Maintenance { } } $this->output( "\n" ); + return $desync; } /** * Fix a broken page entry - * @param $pageID int The page_id to fix + * @param int $pageID The page_id to fix */ private function desyncFixPage( $pageID ) { # Check for a corrupted page_latest @@ -122,6 +137,7 @@ class FixSlaveDesync extends Maintenance { if ( !$found ) { $this->output( "page_id $pageID seems fine\n" ); $dbw->commit( __METHOD__ ); + return; } @@ -141,7 +157,8 @@ class FixSlaveDesync extends Maintenance { if ( count( $masterIDs ) < count( $slaveIDs ) ) { $missingIDs = array_diff( $slaveIDs, $masterIDs ); if ( count( $missingIDs ) ) { - $this->output( "Found " . count( $missingIDs ) . " lost in master, copying from slave... " ); + $this->output( "Found " . count( $missingIDs ) + . " lost in master, copying from slave... " ); $dbFrom = $dbw; $found = true; $toMaster = true; @@ -151,7 +168,8 @@ class FixSlaveDesync extends Maintenance { } else { $missingIDs = array_diff( $masterIDs, $slaveIDs ); if ( count( $missingIDs ) ) { - $this->output( "Found " . count( $missingIDs ) . " missing revision(s), copying from master... " ); + $this->output( "Found " . count( $missingIDs ) + . " missing revision(s), copying from master... " ); $dbFrom = $dbw; $found = true; $toMaster = false; @@ -199,11 +217,23 @@ class FixSlaveDesync extends Maintenance { if ( $found ) { $this->output( "Fixing page_latest... " ); if ( $toMaster ) { - # $dbw->update( 'page', array( 'page_latest' => $realLatest ), array( 'page_id' => $pageID ), __METHOD__ ); + /* + $dbw->update( + 'page', + array( 'page_latest' => $realLatest ), + array( 'page_id' => $pageID ), + __METHOD__ + ); + */ } else { foreach ( $this->slaveIndexes as $i ) { $db = wfGetDB( $i ); - $db->update( 'page', array( 'page_latest' => $realLatest ), array( 'page_id' => $pageID ), __METHOD__ ); + $db->update( + 'page', + array( 'page_latest' => $realLatest ), + array( 'page_id' => $pageID ), + __METHOD__ + ); } } $this->output( "done\n" ); diff --git a/maintenance/fixTimestamps.php b/maintenance/fixTimestamps.php index b0609d17..5431cf2c 100644 --- a/maintenance/fixTimestamps.php +++ b/maintenance/fixTimestamps.php @@ -85,11 +85,11 @@ class FixTimestamps extends Maintenance { if ( $sign == 0 || $sign == $expectedSign ) { // Monotonic change $lastNormal = $timestamp; - ++ $numGoodRevs; + ++$numGoodRevs; continue; } elseif ( abs( $delta ) <= $grace ) { // Non-monotonic change within grace interval - ++ $numGoodRevs; + ++$numGoodRevs; continue; } else { // Non-monotonic change larger than grace interval @@ -100,7 +100,7 @@ class FixTimestamps extends Maintenance { $numBadRevs = count( $badRevs ); if ( $numBadRevs > $numGoodRevs ) { $this->error( - "The majority of revisions in the search interval are marked as bad. + "The majority of revisions in the search interval are marked as bad. Are you sure the offset ($offset) has the right sign? Positive means the clock was incorrectly set forward, negative means the clock was incorrectly set back. @@ -117,7 +117,8 @@ class FixTimestamps extends Maintenance { $fixup = -$offset; $sql = "UPDATE $revisionTable " . - "SET rev_timestamp=DATE_FORMAT(DATE_ADD(rev_timestamp, INTERVAL $fixup SECOND), '%Y%m%d%H%i%s') " . + "SET rev_timestamp=" + . "DATE_FORMAT(DATE_ADD(rev_timestamp, INTERVAL $fixup SECOND), '%Y%m%d%H%i%s') " . "WHERE rev_id IN (" . $dbw->makeList( $badRevs ) . ')'; $dbw->query( $sql, __METHOD__ ); $this->output( "Done\n" ); diff --git a/maintenance/fixUserRegistration.php b/maintenance/fixUserRegistration.php index 097936c9..878593c7 100644 --- a/maintenance/fixUserRegistration.php +++ b/maintenance/fixUserRegistration.php @@ -44,10 +44,20 @@ class FixUserRegistration extends Maintenance { foreach ( $res as $row ) { $id = $row->user_id; // Get first edit time - $timestamp = $dbr->selectField( 'revision', 'MIN(rev_timestamp)', array( 'rev_user' => $id ), __METHOD__ ); + $timestamp = $dbr->selectField( + 'revision', + 'MIN(rev_timestamp)', + array( 'rev_user' => $id ), + __METHOD__ + ); // Update if ( !empty( $timestamp ) ) { - $dbw->update( 'user', array( 'user_registration' => $timestamp ), array( 'user_id' => $id ), __METHOD__ ); + $dbw->update( + 'user', + array( 'user_registration' => $timestamp ), + array( 'user_id' => $id ), + __METHOD__ + ); $this->output( "$id $timestamp\n" ); } else { $this->output( "$id NULL\n" ); diff --git a/maintenance/fuzz-tester.php b/maintenance/fuzz-tester.php deleted file mode 100644 index 548bb2f2..00000000 --- a/maintenance/fuzz-tester.php +++ /dev/null @@ -1,2692 +0,0 @@ -\nStrict Standards: Type: $type: $message in $file on line $line
\n"; - } - // --------- End --------- - - Also add/change this in LocalSettings.php: - // --------- Start --------- - $wgEnableProfileInfo = true; - $wgDBserver = "localhost"; // replace with DB server hostname - // --------- End --------- - -Usage: - Run with "php fuzz-tester.php". - To see the various command-line options, run "php fuzz-tester.php --help". - To stop the script, press Ctrl-C. - -Console output: - - If requested, first any previously failed tests will be rerun. - - Then new tests will be generated and run. Any tests that fail will be saved, - and a brief message about why they failed will be printed on the console. - - The console will show the number of tests run, time run, number of tests - failed, number of tests being done per minute, and the name of the current test. - -TODO: - Some known things that could improve this script: - - Logging in with cookie jar storage needed for some tests (as there are some - pages that cannot be tested without being logged in, and which are currently - untested - e.g. Special:Emailuser, Special:Preferences, adding to Watchist). - - Testing of Timeline extension (I cannot test as ploticus has/had issues on - my architecture). - -*/ - -// ///////////////////////// COMMAND LINE HELP //////////////////////////////////// - -// This is a command line script, load MediaWiki env (gives command line options); -require_once __DIR__ . '/commandLine.inc'; - -// if the user asked for an explanation of command line options. -if ( isset( $options["help"] ) ) { - print <<] - [--directory=] [--include-binary] - [--w3c-validate] [--delete-passed-retests] [--help] - [--user=] [--password=] - [--rerun-failed-tests] [--max-errors=] - [--max-runtime=] - [--specific-test=] - -Options: - --quiet : Hides passed tests, shows only failed tests. - --base-url : URL to a wiki on which to run the tests. - The "http://" is optional and can be omitted. - --directory : Full path to directory for storing failed tests. - Will be created if it does not exist. - --include-binary : Includes non-alphanumeric characters in the tests. - --w3c-validate : Validates pages using the W3C's web validator. - Slow. Currently many pages fail validation. - --user : Login name of a valid user on your test wiki. - --password : Password for the valid user on your test wiki. - --delete-passed-retests : Will delete retests that now pass. - Requires --rerun-failed-tests to be meaningful. - --rerun-failed-tests : Whether to rerun any previously failed tests. - --max-errors : Maximum number of errors to report before exiting. - Does not include errors from --rerun-failed-tests - --max-runtime : Maximum runtime, in minutes, to run before exiting. - Only applies to new tests, not --rerun-failed-tests - --specific-test : Runs only the specified fuzz test. - Only applies to new tests, not --rerun-failed-tests - --keep-passed-tests : Saves all test files, even those that pass. - --help : Show this help message. - -Example: - If you wanted to fuzz test a nightly MediaWiki checkout using cron for 1 hour, - and only wanted to be informed of errors, and did not want to redo previously - failed tests, and wanted a maximum of 100 errors, then you could do: - php {$_SERVER["SCRIPT_NAME"]} --quiet --max-errors=100 --max-runtime=60 - - -ENDS; - - exit( 0 ); -} - - -// if we got command line options, check they look valid. -$validOptions = array ( "quiet", "base-url", "directory", "include-binary", - "w3c-validate", "user", "password", "delete-passed-retests", - "rerun-failed-tests", "max-errors", - "max-runtime", "specific-test", "keep-passed-tests", "help" ); -if ( !empty( $options ) ) { - $unknownArgs = array_diff ( array_keys( $options ), $validOptions ); - foreach ( $unknownArgs as $invalidArg ) { - print "Ignoring invalid command-line option: --$invalidArg\n"; - } -} - - -// /////////////////////////// CONFIGURATION //////////////////////////////////// - -// URL to some wiki on which we can run our tests. -if ( !empty( $options["base-url"] ) ) { - define( "WIKI_BASE_URL", $options["base-url"] ); -} else { - define( "WIKI_BASE_URL", $wgServer . $wgScriptPath . '/' ); -} - -// The directory name where we store the output. -// Example for Windows: "c:\\temp\\wiki-fuzz" -if ( !empty( $options["directory"] ) ) { - define( "DIRECTORY", $options["directory"] ); -} else { - define( "DIRECTORY", "{$wgUploadDirectory}/fuzz-tests" ); -} - -// Should our test fuzz data include binary strings? -define( "INCLUDE_BINARY", isset( $options["include-binary"] ) ); - -// Whether we want to validate HTML output on the web. -// At the moment very few generated pages will validate, so not recommended. -define( "VALIDATE_ON_WEB", isset( $options["w3c-validate"] ) ); -// URL to use to validate our output: -define( "VALIDATOR_URL", "http://validator.w3.org/check" ); - -// Location of Tidy standalone executable. -define( "PATH_TO_TIDY", "/usr/bin/tidy" ); - -// The name of a user who has edited on your wiki. Used -// when testing the Special:Contributions and Special:Userlogin page. -if ( !empty( $options["user"] ) ) { - define( "USER_ON_WIKI", $options["user"] ); -} else { - define( "USER_ON_WIKI", "nickj" ); -} - -// The password of the above user. Used when testing the login page, -// and to do this we sometimes need to login successfully. -if ( !empty( $options["password"] ) ) { - define( "USER_PASSWORD", $options["password"] ); -} else { - // And no, this is not a valid password on any public wiki. - define( "USER_PASSWORD", "nickj" ); -} - -// If we have a test that failed, and then we run it again, and it passes, -// do you want to delete it or keep it? -define( "DELETE_PASSED_RETESTS", isset( $options["delete-passed-retests"] ) ); - -// Do we want to rerun old saved tests at script startup? -// Set to true to help catch regressions, or false if you only want new stuff. -define( "RERUN_OLD_TESTS", isset( $options["rerun-failed-tests"] ) ); - -// File where the database errors are logged. Should be defined in LocalSettings.php. -define( "DB_ERROR_LOG_FILE", $wgDBerrorLog ); - -// Run in chatty mode (all output, default), or run in quiet mode (only prints out details of failed tests)? -define( "QUIET", isset( $options["quiet"] ) ); - -// Keep all test files, even those that pass. Potentially useful to tracking input that causes something -// unusual to happen, if you don't know what "unusual" is until later. -define( "KEEP_PASSED_TESTS", isset( $options["keep-passed-tests"] ) ); - -// The maximum runtime, if specified. -if ( !empty( $options["max-runtime"] ) && intval( $options["max-runtime"] ) > 0 ) { - define( "MAX_RUNTIME", intval( $options["max-runtime"] ) ); -} - -// The maximum number of problems to find, if specified. Excludes retest errors. -if ( !empty( $options["max-errors"] ) && intval( $options["max-errors"] ) > 0 ) { - define( "MAX_ERRORS", intval( $options["max-errors"] ) ); -} - -// if the user has requested a specific test (instead of all tests), and the test they asked for looks valid. -if ( !empty( $options["specific-test"] ) ) { - if ( class_exists( $options["specific-test"] ) && get_parent_class( $options["specific-test"] ) == "pageTest" ) { - define( "SPECIFIC_TEST", $options["specific-test"] ); - } - else { - print "Ignoring invalid --specific-test\n"; - } -} - -// Define the file extensions we'll use: -define( "PHP_TEST" , ".test.php" ); -define( "CURL_TEST", ".curl.sh" ); -define( "DATA_FILE", ".data.bin" ); -define( "INFO_FILE", ".info.txt" ); -define( "HTML_FILE", ".wiki_preview.html" ); - -// If it goes wrong, we want to know about it. -error_reporting( E_ALL | E_STRICT ); - -// ////////////// A CLASS THAT GENERATES RANDOM NASTY WIKI & HTML STRINGS ////////////////////// - -class wikiFuzz { - - // Only some HTML tags are understood with params by MediaWiki, the rest are ignored. - // List the tags that accept params below, as well as what those params are. - public static $data = array( - "B" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "CAPTION" => array( "CLASS", "ID", "STYLE", "align", "lang", "dir", "title" ), - "CENTER" => array( "CLASS", "STYLE", "ID", "lang", "dir", "title" ), - "DIV" => array( "CLASS", "STYLE", "ID", "align", "lang", "dir", "title" ), - "FONT" => array( "CLASS", "STYLE", "ID", "lang", "dir", "title", "face", "size", "color" ), - "H1" => array( "STYLE", "CLASS", "ID", "align", "lang", "dir", "title" ), - "H2" => array( "STYLE", "CLASS", "ID", "align", "lang", "dir", "title" ), - "HR" => array( "STYLE", "CLASS", "ID", "WIDTH", "lang", "dir", "title", "size", "noshade" ), - "LI" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "value" ), - "TABLE" => array( "STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "BORDER", "CELLPADDING", - "CELLSPACING", "lang", "dir", "title", "summary", "frame", "rules" ), - "TD" => array( "STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN", - "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang", - "dir", "title", "char", "charoff" ), - "TH" => array( "STYLE", "CLASS", "ID", "BGCOLOR", "WIDTH", "ALIGN", "COLSPAN", "ROWSPAN", - "VALIGN", "abbr", "axis", "headers", "scope", "nowrap", "height", "lang", - "dir", "title", "char", "charoff" ), - "TR" => array( "CLASS", "STYLE", "ID", "BGCOLOR", "ALIGN", "VALIGN", "lang", "dir", "title", "char", "charoff" ), - "UL" => array( "CLASS", "STYLE", "ID", "lang", "dir", "title", "type" ), - "P" => array( "style", "class", "id", "align", "lang", "dir", "title" ), - "blockquote" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "cite" ), - "span" => array( "CLASS", "ID", "STYLE", "align", "lang", "dir", "title" ), - "code" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "tt" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "small" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "big" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "s" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "u" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "del" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite" ), - "ins" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "datetime", "cite" ), - "sub" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "sup" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "ol" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "type", "start" ), - "br" => array( "CLASS", "ID", "STYLE", "title", "clear" ), - "cite" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "var" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "ruby" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "rt" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "rp" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "dt" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "dl" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "em" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "strong" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "i" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title" ), - "thead" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign' ), - "tfoot" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign' ), - "tbody" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign' ), - "colgroup" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign', 'span', 'width' ), - "col" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", 'align', 'char', 'charoff', 'valign', 'span', 'width' ), - "pre" => array( "CLASS", "ID", "STYLE", "lang", "dir", "title", "width" ), - - // extension tags that accept parameters: - "sort" => array( "order", "class" ), - "ref" => array( "name" ), - "categorytree" => array( "hideroot", "mode", "style" ), - "chemform" => array( "link", "wikilink", "query" ), - "section" => array( "begin", "new" ), - - // older MW transclusion. - "transclude" => array( "page" ), - ); - - // The types of the HTML that we will be testing were defined above - // Note: this needs to be initialized later to be equal to: array_keys(wikiFuzz::$data); - // as such, it also needs to also be publicly modifiable. - public static $types; - - - // Some attribute values. - static private $other = array( "&", "=", ":", "?", "\"", "\n", "%n%n%n%n%n%n%n%n%n%n%n%n", "\\" ); - static private $ints = array( - // various numbers - "0", "-1", "127", "-7897", "89000", "808080", "90928345", - "0xfffffff", "ffff", - - // Different ways of saying: ' - "'", // Long UTF-8 Unicode encoding - "'", // dec version. - "'", // hex version. - "§", // malformed hex variant, MSB not zero. - - // Different ways of saying: " - """, // Long UTF-8 Unicode encoding - """, - """, // hex version. - "¢", // malformed hex variant, MSB not zero. - - // Different ways of saying: < - "<", - "<", // Long UTF-8 Unicode encoding without semicolon (Mediawiki wants the colon) - "<", // Long UTF-8 Unicode encoding with semicolon - "<", - "<", // hex version. - "¼", // malformed hex variant, MSB not zero. - "<", // mid-length hex version - "<", // slightly longer hex version, with capital "X" - - // Different ways of saying: > - ">", - ">", // Long UTF-8 Unicode encoding - ">", - ">", // hex version. - "¾", // malformed variant, MSB not zero. - - // Different ways of saying: [ - "[", // Long UTF-8 Unicode encoding - "[", - "[", // hex version. - - // Different ways of saying: {{ - "{{", // Long UTF-8 Unicode encoding - "{{", - "{{", // hex version. - - // Different ways of saying: | - "|", // Long UTF-8 Unicode encoding - "|", - "|", // hex version. - "ü", // malformed hex variant, MSB not zero. - - // a "lignature" - http://www.robinlionheart.com/stds/html4/spchars#ligature - // ‌ == ‌ - "‌" - ); - - // Defines various wiki-related bits of syntax, that can potentially cause - // MediaWiki to do something other than just print that literal text. - static private $ext = array( - // links, templates, parameters. - "[[", "]]", "{{", "}}", "|", "[", "]", "{{{", "}}}", "|]]", - - // wiki tables. - "\n{|", "\n|}", - "!", - "\n!", - "!!", - "||", - "\n|-", "| ", "\n|", - - // section headings. - "=", "==", "===", "====", "=====", "======", - - // lists (ordered and unordered) and indentation. - "\n*", "*", "\n:", ":", - "\n#", "#", - - // definition lists (dl, dt, dd), newline, and newline with pre, and a tab. - "\n;", ";", "\n ", - - // Whitespace: newline, tab, space. - "\n", "\t", " ", - - // Some XSS attack vectors from http://ha.ckers.org/xss.html - " ", // tab - " ", // newline - " ", // carriage return - "\0", // null character - "  ", // spaces and meta characters - "'';!--\"=&{()}", // compact injection of XSS & SQL tester - - // various NULL fields - "%00", - "�", - "\0", - - // horizontal rule. - "-----", "\n-----", - - // signature, redirect, bold, italics. - "~~~~", "#REDIRECT [[", "'''", "''", - - // comments. - "", - - // quotes. - "\"", "'", - - // tag start and tag end. - "<", ">", - - // implicit link creation on URIs. - "http://", - "https://", - "ftp://", - "irc://", - "news:", - 'gopher://', - 'telnet://', - 'nntp://', - 'worldwind://', - 'mailto:', - - // images. - "[[image:", - ".gif", - ".png", - ".jpg", - ".jpeg", - 'thumbnail=', - 'thumbnail', - 'thumb=', - 'thumb', - 'right', - 'none', - 'left', - 'framed', - 'frame', - 'enframed', - 'centre', - 'center', - "Image:", - "[[:Image", - 'px', - 'upright=', - 'border', - - // misc stuff to throw at the Parser. - '%08X', - '/', - ":x{|", - "\n|+", - "", - "", - " \302\273", - " :", - " !", - " ;", - "\302\253", - "[[category:", - "?=", - "(", - ")", - "]]]", - "../", - "{{{{", - "}}}}", - "[[Special:", - "", - "", - "', - - // implicit link creation on booknum, RFC, and PubMed ID usage (both with and without IDs) - "ISBN 2", - "RFC 000", - "PMID 000", - "ISBN ", - "RFC ", - "PMID ", - - // magic words: - '__NOTOC__', - '__FORCETOC__', - '__NOEDITSECTION__', - '__START__', - '__NOTITLECONVERT__', - '__NOCONTENTCONVERT__', - '__END__', - '__TOC__', - '__NOTC__', - '__NOCC__', - "__FORCETOC__", - "__NEWSECTIONLINK__", - "__NOGALLERY__", - - // more magic words / internal templates. - '{{PAGENAME}}', - '{{PAGENAMEE}}', - '{{NAMESPACE}}', - "{{MSG:", - "}}", - "{{MSGNW:", - "}}", - "{{INT:", - "}}", - '{{SITENAME}}', - "{{NS:", - "}}", - "{{LOCALURL:", - "}}", - "{{LOCALURLE:", - "}}", - "{{SCRIPTPATH}}", - "{{GRAMMAR:gentiv|", - "}}", - "{{REVISIONID}}", - "{{SUBPAGENAME}}", - "{{SUBPAGENAMEE}}", - "{{ns:0}}", - "{{fullurle:", - "}}", - "{{subst::", - "}}", - "{{UCFIRST:", - "}}", - "{{UC:", - '{{SERVERNAME}}', - '{{SERVER}}', - "{{RAW:", - "}}", - "{{PLURAL:", - "}}", - "{{LCFIRST:", - "}}", - "{{LC:", - "}}", - '{{CURRENTWEEK}}', - '{{CURRENTDOW}}', - "{{INT:{{LC:contribs-showhideminor}}|", - "}}", - "{{INT:googlesearch|", - "}}", - "{{ROOTPAGENAME}}", - "{{BASEPAGENAME}}", - "{{CONTENTLANGUAGE}}", - "{{PAGESINNAMESPACE:}}", - "{{#language:", - "}}", - "{{#special:", - "}}", - "{{#special:emailuser", - "}}", - - // Some raw link for magic words. - "{{NUMBEROFPAGES:R", - "}}", - "{{NUMBEROFUSERS:R", - "}}", - "{{NUMBEROFARTICLES:R", - "}}", - "{{NUMBEROFFILES:R", - "}}", - "{{NUMBEROFADMINS:R", - "}}", - "{{padleft:", - "}}", - "{{padright:", - "}}", - "{{DEFAULTSORT:", - "}}", - - // internal Math "extension": - "", - "", - - // Parser extension functions: - "{{#expr:", - "{{#if:", - "{{#ifeq:", - "{{#ifexist:", - "{{#ifexpr:", - "{{#switch:", - "{{#time:", - "}}", - - // references table for the Cite extension. - "", - - // Internal Parser tokens - try inserting some of these. - "UNIQ25f46b0524f13e67NOPARSE", - "UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002", - "\x07UNIQ17197916557e7cd6-HTMLCommentStrip46238afc3bb0cf5f00000002-QINU", - - // Inputbox extension: - "\ntype=search\nsearchbuttonlabel=\n", - "", - - // charInsert extension: - "", - "", - - // wikiHiero extension: - "", - "", - - // Image gallery: - "", - "", - - // FixedImage extension. - "", - - // Timeline extension: currently untested. - - // Nowiki: - "", - "", - - // an external image to test the external image displaying code - "http://debian.org/Pics/debian.png", - - // LabeledSectionTransclusion extension. - "{{#lstx:", - "}}", - "{{#lst:", - "}}", - "{{#lst:Main Page|", - "}}" - ); - - /** - ** Randomly returns one element of the input array. - */ - public static function chooseInput( array $input ) { - $randindex = wikiFuzz::randnum( count( $input ) - 1 ); - return $input[$randindex]; - } - - // Max number of parameters for HTML attributes. - static private $maxparams = 10; - - /** - * Returns random number between finish and start. - * @param $finish - * @param $start int - * @return int - */ - public static function randnum( $finish, $start = 0 ) { - return mt_rand( $start, $finish ); - } - - /** - * Returns a mix of random text and random wiki syntax. - * @return string - */ - private static function randstring() { - $thestring = ""; - - for ( $i = 0; $i < 40; $i++ ) { - $what = wikiFuzz::randnum( 1 ); - - if ( $what == 0 ) { // include some random wiki syntax - $which = wikiFuzz::randnum( count( wikiFuzz::$ext ) - 1 ); - $thestring .= wikiFuzz::$ext[$which]; - } - else { // include some random text - $char = INCLUDE_BINARY - // Decimal version: - // "&#" . wikiFuzz::randnum(255) . ";" - // Hex version: - ? "&#x" . str_pad( dechex( wikiFuzz::randnum( 255 ) ), wikiFuzz::randnum( 2, 7 ), "0", STR_PAD_LEFT ) . ";" - // A truly binary version: - // ? chr(wikiFuzz::randnum(0,255)) - : chr( wikiFuzz::randnum( 126, 32 ) ); - - $length = wikiFuzz::randnum( 8 ); - $thestring .= str_repeat ( $char, $length ); - } - } - return $thestring; - } - - /** - * Returns either random text, or random wiki syntax, or random data from "ints", - * or random data from "other". - * @return string - */ - private static function makestring() { - $what = wikiFuzz::randnum( 2 ); - if ( $what == 0 ) { - return wikiFuzz::randstring(); - } elseif ( $what == 1 ) { - return wikiFuzz::$ints[wikiFuzz::randnum( count( wikiFuzz::$ints ) - 1 )]; - } else { - return wikiFuzz::$other[wikiFuzz::randnum( count( wikiFuzz::$other ) - 1 )]; - } - } - - /** - * Returns the matched character slash-escaped as in a C string - * Helper for makeTitleSafe callback - * @param $matches - * @return string - */ - private static function stringEscape( $matches ) { - return sprintf( "\\x%02x", ord( $matches[1] ) ); - } - - /** - ** Strips out the stuff that Mediawiki balks at in a page's title. - ** Implementation copied/pasted from cleanupTable.inc & cleanupImages.php - * @param $str string - * @return string - */ - public static function makeTitleSafe( $str ) { - $legalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF"; - return preg_replace_callback( - "/([^$legalTitleChars])/", 'wikiFuzz::stringEscape', - $str ); - } - - /** - ** Returns a string of fuzz text. - * @return string - */ - private static function loop() { - switch ( wikiFuzz::randnum( 3 ) ) { - case 1: // an opening tag, with parameters. - $string = ""; - $i = wikiFuzz::randnum( count( wikiFuzz::$types ) - 1 ); - $t = wikiFuzz::$types[$i]; - $arr = wikiFuzz::$data[$t]; - $string .= "<" . $t . " "; - $num_params = min( wikiFuzz::$maxparams, count( $arr ) ); - for ( $z = 0; $z < $num_params; $z++ ) { - $badparam = $arr[wikiFuzz::randnum( count( $arr ) - 1 )]; - $badstring = wikiFuzz::makestring(); - $string .= $badparam . "=" . wikiFuzz::getRandQuote() . $badstring . wikiFuzz::getRandQuote() . " "; - } - $string .= ">\n"; - return $string; - case 2: // a closing tag. - $i = wikiFuzz::randnum( count( wikiFuzz::$types ) - 1 ); - return ""; - case 3: // a random string, between tags. - return wikiFuzz::makeString(); - } - return ""; // catch-all, should never be called. - } - - /** - * Returns one of the three styles of random quote: ', ", and nothing. - * @return string - */ - private static function getRandQuote() { - switch ( wikiFuzz::randnum( 3 ) ) { - case 1 : return "'"; - case 2 : return "\""; - default: return ""; - } - } - - /** - ** Returns fuzz text, with the parameter indicating approximately how many lines of text you want. - * @param $maxtypes int - * @return string - */ - public static function makeFuzz( $maxtypes = 2 ) { - $page = ""; - for ( $k = 0; $k < $maxtypes; $k++ ) { - $page .= wikiFuzz::loop(); - } - return $page; - } -} - - -// ////// MEDIAWIKI PAGES TO TEST, AND HOW TO TEST THEM /////// - -/** - ** A page test has just these things: - ** 1) Form parameters. - ** 2) the URL we are going to test those parameters on. - ** 3) Any cookies required for the test. - ** 4) Whether Tidy should validate the page. Defaults to true, but can be turned off. - ** Declared abstract because it should be extended by a class - ** that supplies these parameters. - */ -abstract class pageTest { - protected $params; - protected $pagePath; - protected $cookie = ""; - protected $tidyValidate = true; - - public function getParams() { - return $this->params; - } - - public function getPagePath() { - return $this->pagePath; - } - - public function getCookie() { - return $this->cookie; - } - - public function tidyValidate() { - return $this->tidyValidate; - } -} - - -/** - ** a page test for the "Edit" page. Tests Parser.php and Sanitizer.php. - */ -class editPageTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=WIKIFUZZ"; - - $this->params = array ( - "action" => "submit", - "wpMinoredit" => wikiFuzz::makeFuzz( 2 ), - "wpPreview" => wikiFuzz::makeFuzz( 2 ), - "wpSection" => wikiFuzz::makeFuzz( 2 ), - "wpEdittime" => wikiFuzz::makeFuzz( 2 ), - "wpSummary" => wikiFuzz::makeFuzz( 2 ), - "wpScrolltop" => wikiFuzz::makeFuzz( 2 ), - "wpStarttime" => wikiFuzz::makeFuzz( 2 ), - "wpAutoSummary" => wikiFuzz::makeFuzz( 2 ), - "wpTextbox1" => wikiFuzz::makeFuzz( 40 ) // the main wiki text, need lots of this. - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpSection"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpEdittime"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpSummary"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpScrolltop"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpStarttime"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpAutoSummary"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpTextbox1"] ); - } -} - - -/** - ** a page test for "Special:Listusers". - */ -class listusersTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Listusers"; - - $this->params = array ( - "title" => wikiFuzz::makeFuzz( 2 ), - "group" => wikiFuzz::makeFuzz( 2 ), - "username" => wikiFuzz::makeFuzz( 2 ), - "Go" => wikiFuzz::makeFuzz( 2 ), - "limit" => wikiFuzz::chooseInput( array( "0", "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - "offset" => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz( 2 ) ) ) - ); - } -} - - -/** - ** a page test for "Special:Search". - */ -class searchTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Search"; - - $this->params = array ( - "action" => "index.php?title=Special:Search", - "ns0" => wikiFuzz::makeFuzz( 2 ), - "ns1" => wikiFuzz::makeFuzz( 2 ), - "ns2" => wikiFuzz::makeFuzz( 2 ), - "ns3" => wikiFuzz::makeFuzz( 2 ), - "ns4" => wikiFuzz::makeFuzz( 2 ), - "ns5" => wikiFuzz::makeFuzz( 2 ), - "ns6" => wikiFuzz::makeFuzz( 2 ), - "ns7" => wikiFuzz::makeFuzz( 2 ), - "ns8" => wikiFuzz::makeFuzz( 2 ), - "ns9" => wikiFuzz::makeFuzz( 2 ), - "ns10" => wikiFuzz::makeFuzz( 2 ), - "ns11" => wikiFuzz::makeFuzz( 2 ), - "ns12" => wikiFuzz::makeFuzz( 2 ), - "ns13" => wikiFuzz::makeFuzz( 2 ), - "ns14" => wikiFuzz::makeFuzz( 2 ), - "ns15" => wikiFuzz::makeFuzz( 2 ), - "redirs" => wikiFuzz::makeFuzz( 2 ), - "search" => wikiFuzz::makeFuzz( 2 ), - "offset" => wikiFuzz::chooseInput( array( "", "0", "-1", "--------'-----0", "+1", "81343242346234234", wikiFuzz::makeFuzz( 2 ) ) ), - "fulltext" => wikiFuzz::chooseInput( array( "", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz( 2 ) ) ), - "searchx" => wikiFuzz::chooseInput( array( "", "0", "1", "--------'-----0", "+1", wikiFuzz::makeFuzz( 2 ) ) ) - ); - } -} - - -/** - ** a page test for "Special:Recentchanges". - */ -class recentchangesTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Recentchanges"; - - $this->params = array ( - "action" => wikiFuzz::makeFuzz( 2 ), - "title" => wikiFuzz::makeFuzz( 2 ), - "namespace" => wikiFuzz::chooseInput( range( -1, 15 ) ), - "Go" => wikiFuzz::makeFuzz( 2 ), - "invert" => wikiFuzz::chooseInput( array( "-1", "---'----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - "hideanons" => wikiFuzz::chooseInput( array( "-1", "------'-------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "---------'----0", "+1", "81340909772349234", wikiFuzz::makeFuzz( 2 ) ) ), - "days" => wikiFuzz::chooseInput( array( "-1", "----------'---0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - "hideminor" => wikiFuzz::chooseInput( array( "-1", "-----------'--0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - "hidebots" => wikiFuzz::chooseInput( array( "-1", "---------'----0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - "hideliu" => wikiFuzz::chooseInput( array( "-1", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - "hidepatrolled" => wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - "hidemyself" => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - 'categories_any' => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - 'categories' => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - 'feed' => wikiFuzz::chooseInput( array( "-1", "--'-----------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ) - ); - } -} - - -/** - ** a page test for "Special:Prefixindex". - */ -class prefixindexTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Prefixindex"; - - $this->params = array ( - "title" => "Special:Prefixindex", - "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( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) ); - } - if ( wikiFuzz::randnum( 3 ) == 0 ) { - $this->params["from"] = wikiFuzz::chooseInput( array( "-1", "-----'--------0", "+++--+1", - wikiFuzz::randnum( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) ); - } - } -} - - -/** - ** a page test for "Special:MIMEsearch". - */ -class mimeSearchTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:MIMEsearch"; - - $this->params = array ( - "action" => "index.php?title=Special:MIMEsearch", - "mime" => wikiFuzz::makeFuzz( 3 ), - 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "-------'------0", "+1", "81342321351235325", wikiFuzz::makeFuzz( 2 ) ) ), - 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "-----'--------0", "+1", "81341231235365252234324", wikiFuzz::makeFuzz( 2 ) ) ) - ); - } -} - - -/** - ** a page test for "Special:Log". - */ -class specialLogTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Log"; - - $this->params = array ( - "type" => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ), - "par" => wikiFuzz::makeFuzz( 2 ), - "user" => wikiFuzz::makeFuzz( 2 ), - "page" => wikiFuzz::makeFuzz( 2 ), - "from" => wikiFuzz::makeFuzz( 2 ), - "until" => wikiFuzz::makeFuzz( 2 ), - "title" => wikiFuzz::makeFuzz( 2 ) - ); - } -} - - -/** - ** a page test for "Special:Userlogin", with a successful login. - */ -class successfulUserLoginTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Userlogin&action=submitlogin&type=login&returnto=" . wikiFuzz::makeFuzz( 2 ); - - $this->params = array ( - "wpName" => USER_ON_WIKI, - // sometimes real password, sometimes not: - 'wpPassword' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), USER_PASSWORD ) ), - 'wpRemember' => wikiFuzz::makeFuzz( 2 ) - ); - - $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array( "1" , wikiFuzz::makeFuzz( 2 ) ) ); - } -} - - -/** - ** a page test for "Special:Userlogin". - */ -class userLoginTest extends pageTest { - function __construct() { - - $this->pagePath = "index.php?title=Special:Userlogin"; - - $this->params = array ( - 'wpRetype' => wikiFuzz::makeFuzz( 2 ), - 'wpRemember' => wikiFuzz::makeFuzz( 2 ), - 'wpRealName' => wikiFuzz::makeFuzz( 2 ), - 'wpPassword' => wikiFuzz::makeFuzz( 2 ), - 'wpName' => wikiFuzz::makeFuzz( 2 ), - 'wpMailmypassword' => wikiFuzz::makeFuzz( 2 ), - 'wpLoginattempt' => wikiFuzz::makeFuzz( 2 ), - 'wpEmail' => wikiFuzz::makeFuzz( 2 ), - 'wpDomain' => wikiFuzz::chooseInput( array( "", "local", wikiFuzz::makeFuzz( 2 ) ) ), - 'wpCreateaccountMail' => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ), - 'wpCreateaccount' => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ), - 'wpCookieCheck' => wikiFuzz::chooseInput( array( "", wikiFuzz::makeFuzz( 2 ) ) ), - 'type' => wikiFuzz::chooseInput( array( "signup", "login", "", wikiFuzz::makeFuzz( 2 ) ) ), - 'returnto' => wikiFuzz::makeFuzz( 2 ), - 'action' => wikiFuzz::chooseInput( array( "", "submitlogin", wikiFuzz::makeFuzz( 2 ) ) ) - ); - - $this->cookie = "wikidb_session=" . wikiFuzz::chooseInput( array( "1" , wikiFuzz::makeFuzz( 2 ) ) ); - } -} - - -/** - ** a page test for "Special:Ipblocklist" (also includes unblocking) - */ -class ipblocklistTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Ipblocklist"; - - $this->params = array ( - 'wpUnblockAddress' => wikiFuzz::makeFuzz( 2 ), - 'ip' => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ), - // something like an IP address, sometimes invalid: - ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "." - . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ), - 'id' => wikiFuzz::makeFuzz( 2 ), - 'wpUnblockReason' => wikiFuzz::makeFuzz( 2 ), - 'action' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "success", "submit", "unblock" ) ), - 'wpEditToken' => wikiFuzz::makeFuzz( 2 ), - 'wpBlock' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "" ) ), - 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1", - "09700982312351132098234", wikiFuzz::makeFuzz( 2 ) ) ), - 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", - "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) ) - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] ); - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["ip"] ); - if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["id"] ); - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpUnblockAddress"] ); - } -} - - -/** - ** a page test for "Special:Newimages". - */ -class newImagesTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Newimages"; - - $this->params = array ( - 'hidebots' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "1", "", "-1" ) ), - 'wpIlMatch' => wikiFuzz::makeFuzz( 2 ), - 'until' => wikiFuzz::makeFuzz( 2 ), - 'from' => wikiFuzz::makeFuzz( 2 ) - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["until"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["from"] ); - } -} - - -/** - ** a page test for the "Special:Imagelist" page. - */ -class imagelistTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Imagelist"; - - $this->params = array ( - 'sort' => wikiFuzz::chooseInput( array( "bysize", "byname" , "bydate", wikiFuzz::makeFuzz( 2 ) ) ), - 'limit' => wikiFuzz::chooseInput( array( "0", "-1", "--------'-----0", "+1", "09700982312351132098234", wikiFuzz::makeFuzz( 2 ) ) ), - 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) ), - 'wpIlMatch' => wikiFuzz::makeFuzz( 2 ) - ); - } -} - - -/** - ** a page test for "Special:Export". - */ -class specialExportTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Export"; - - $this->params = array ( - 'action' => wikiFuzz::chooseInput( array( "submit", "", wikiFuzz::makeFuzz( 2 ) ) ), - 'pages' => wikiFuzz::makeFuzz( 2 ), - 'curonly' => wikiFuzz::chooseInput( array( "", "0", "-1", wikiFuzz::makeFuzz( 2 ) ) ), - 'listauthors' => wikiFuzz::chooseInput( array( "", "0", "-1", wikiFuzz::makeFuzz( 2 ) ) ), - 'history' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "09700980982341535324234234", wikiFuzz::makeFuzz( 2 ) ) ), - - ); - - // For the time being, need to disable "submit" action as Tidy barfs on MediaWiki's XML export. - if ( $this->params['action'] == 'submit' ) $this->params['action'] = ''; - - // Sometimes remove the history field. - if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["history"] ); - - // page does not produce HTML. - $this->tidyValidate = false; - } -} - - -/** - ** a page test for "Special:Booksources". - */ -class specialBooksourcesTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Booksources"; - - $this->params = array ( - 'go' => wikiFuzz::makeFuzz( 2 ), - // ISBN codes have to contain some semi-numeric stuff or will be ignored: - 'isbn' => "0X0" . wikiFuzz::makeFuzz( 2 ) - ); - } -} - - -/** - ** a page test for "Special:Allpages". - */ -class specialAllpagesTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special%3AAllpages"; - - $this->params = array ( - 'from' => wikiFuzz::makeFuzz( 2 ), - 'namespace' => wikiFuzz::chooseInput( range( -1, 15 ) ), - 'go' => wikiFuzz::makeFuzz( 2 ) - ); - } -} - - -/** - ** a page test for the page History. - */ -class pageHistoryTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Main_Page&action=history"; - - $this->params = array ( - 'limit' => wikiFuzz::chooseInput( array( "-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - 'offset' => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) ), - "go" => wikiFuzz::chooseInput( array( "first", "last", wikiFuzz::makeFuzz( 2 ) ) ), - "dir" => wikiFuzz::chooseInput( array( "prev", "next", wikiFuzz::makeFuzz( 2 ) ) ), - "diff" => wikiFuzz::chooseInput( array( "-1", "--------'-----0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - "oldid" => wikiFuzz::chooseInput( array( "prev", "-1", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - "feed" => wikiFuzz::makeFuzz( 2 ) - ); - } -} - - -/** - ** a page test for the Special:Contributions". - */ -class contributionsTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Contributions/" . USER_ON_WIKI; - - $this->params = array ( - 'target' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), "newbies", USER_ON_WIKI ) ), - 'namespace' => wikiFuzz::chooseInput( array( -1, 15, 1, wikiFuzz::makeFuzz( 2 ) ) ), - 'offset' => wikiFuzz::chooseInput( array( "0", "-1", "------'-------0", "+1", "982342131232131231241", wikiFuzz::makeFuzz( 2 ) ) ), - 'bot' => wikiFuzz::chooseInput( array( "", "-1", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ), - 'go' => wikiFuzz::chooseInput( array( "-1", 'prev', 'next', wikiFuzz::makeFuzz( 2 ) ) ) - ); - } -} - - -/** - ** a page test for viewing a normal page, whilst posting various params. - */ -class viewPageTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Main_Page"; - - $this->params = array ( - "useskin" => wikiFuzz::chooseInput( array( "chick", "cologneblue", "myskin", - "nostalgia", "simple", "standard", wikiFuzz::makeFuzz( 2 ) ) ), - "uselang" => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ), - "ab", "af", "an", "ar", "arc", "as", "ast", "av", "ay", "az", "ba", - "bat-smg", "be", "bg", "bm", "bn", "bo", "bpy", "br", "bs", "ca", - "ce", "cs", "csb", "cv", "cy", "da", "de", "dv", "dz", "el", "en", - "eo", "es", "et", "eu", "fa", "fi", "fo", "fr", "fur", "fy", "ga", - "gn", "gsw", "gu", "he", "hi", "hr", "hu", "ia", "id", "ii", "is", - "it", "ja", "jv", "ka", "km", "kn", "ko", "ks", "ku", "kv", "la", - "li", "lo", "lt", "lv", "mk", "ml", "ms", "nah", "nap", "nds", - "nds-nl", "nl", "nn", "no", "non", "nv", "oc", "or", "os", "pa", - "pl", "pms", "ps", "pt", "pt-br", "qu", "rmy", "ro", "ru", "sc", - "sd", "sk", "sl", "sq", "sr", "sr-ec", "sr-el", - "su", "sv", "ta", "te", "th", "tr", "tt", "ty", "tyv", "udm", - "ug", "uk", "ur", "utf8", "vec", "vi", "wa", "xal", "yi", "za", - "zh", "zh-cn", "zh-hk", "zh-sg", "zh-tw", "zh-tw" ) ), - "returnto" => wikiFuzz::makeFuzz( 2 ), - "feed" => wikiFuzz::chooseInput( array( "atom", "rss", wikiFuzz::makeFuzz( 2 ) ) ), - "rcid" => wikiFuzz::makeFuzz( 2 ), - "action" => wikiFuzz::chooseInput( array( "view", "raw", "render", wikiFuzz::makeFuzz( 2 ), "markpatrolled" ) ), - "printable" => wikiFuzz::makeFuzz( 2 ), - "oldid" => wikiFuzz::makeFuzz( 2 ), - "redirect" => wikiFuzz::makeFuzz( 2 ), - "diff" => wikiFuzz::makeFuzz( 2 ), - "search" => wikiFuzz::makeFuzz( 2 ), - "rdfrom" => wikiFuzz::makeFuzz( 2 ), // things from Article.php from here on: - "token" => wikiFuzz::makeFuzz( 2 ), - "tbid" => wikiFuzz::makeFuzz( 2 ), - // @todo FIXME: Duplicate array key. - "action" => wikiFuzz::chooseInput( array( "purge", wikiFuzz::makeFuzz( 2 ) ) ), - "wpReason" => wikiFuzz::makeFuzz( 2 ), - "wpEditToken" => wikiFuzz::makeFuzz( 2 ), - "from" => wikiFuzz::makeFuzz( 2 ), - "bot" => wikiFuzz::makeFuzz( 2 ), - "summary" => wikiFuzz::makeFuzz( 2 ), - "direction" => wikiFuzz::chooseInput( array( "next", "prev", wikiFuzz::makeFuzz( 2 ) ) ), - "section" => wikiFuzz::makeFuzz( 2 ), - "preload" => wikiFuzz::makeFuzz( 2 ), - - ); - - // 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"] ); } - elseif ( $this->params["feed"] == "rss" ) { unset( $this->params["feed"] ); } - - // Raw pages cannot really be validated - if ( $this->params["action"] == "raw" ) unset( $this->params["action"] ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["rcid"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["diff"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["rdfrom"] ); - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["oldid"] ); - - // usually don't want action == purge. - if ( wikiFuzz::randnum( 6 ) > 1 ) unset( $this->params["action"] ); - } -} - - -/** - ** a page test for "Special:Allmessages". - */ -class specialAllmessagesTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Allmessages"; - - // only really has one parameter - $this->params = array ( - "ot" => wikiFuzz::chooseInput( array( "php", "html", wikiFuzz::makeFuzz( 2 ) ) ) - ); - } -} - -/** - ** a page test for "Special:Newpages". - */ -class specialNewpagesPageTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Newpages"; - - $this->params = array ( - "namespace" => wikiFuzz::chooseInput( range( -1, 15 ) ), - "feed" => wikiFuzz::chooseInput( array( "atom", "rss", wikiFuzz::makeFuzz( 2 ) ) ), - 'limit' => wikiFuzz::chooseInput( array( "-1", "0", "-------'------0", "+1", "8134", wikiFuzz::makeFuzz( 2 ) ) ), - 'offset' => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) ) - ); - - // 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"] ); } - elseif ( $this->params["feed"] == "rss" ) { unset( $this->params["feed"] ); } - } -} - -/** - ** a page test for "redirect.php" - */ -class redirectTest extends pageTest { - function __construct() { - $this->pagePath = "redirect.php"; - - $this->params = array ( - "wpDropdown" => wikiFuzz::makeFuzz( 2 ) - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpDropdown"] ); - } -} - - -/** - ** a page test for "Special:Confirmemail" - */ -class confirmEmail extends pageTest { - function __construct() { - // sometimes we send a bogus confirmation code, and sometimes we don't. - $this->pagePath = "index.php?title=Special:Confirmemail" . wikiFuzz::chooseInput( array( "", "/" . wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 1 ) ) ) ); - - $this->params = array ( - "token" => wikiFuzz::makeFuzz( 2 ) - ); - } -} - - -/** - ** a page test for "Special:Watchlist" - ** Note: this test would be better if we were logged in. - */ -class watchlistTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Watchlist"; - - $this->params = array ( - "remove" => wikiFuzz::chooseInput( array( "Remove checked items from watchlist", wikiFuzz::makeFuzz( 2 ) ) ), - 'days' => wikiFuzz::chooseInput( array( 0, -1, -230, "--", 3, 9, wikiFuzz::makeFuzz( 2 ) ) ), - 'hideOwn' => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ), - 'hideBots' => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ), - 'namespace' => wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ), - 'action' => wikiFuzz::chooseInput( array( "submit", "clear", wikiFuzz::makeFuzz( 2 ) ) ), - 'id[]' => wikiFuzz::makeFuzz( 2 ), - 'edit' => wikiFuzz::makeFuzz( 2 ), - 'token' => wikiFuzz::chooseInput( array( "", "1243213", wikiFuzz::makeFuzz( 2 ) ) ) - ); - - // sometimes we specifiy "reset", and sometimes we don't. - if ( wikiFuzz::randnum( 3 ) == 0 ) $this->params["reset"] = wikiFuzz::chooseInput( array( "", "0", "1", wikiFuzz::makeFuzz( 2 ) ) ); - } -} - -/** - ** a page test for "Special:Movepage" - */ -class specialMovePage extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Movepage"; - - $this->params = array ( - "action" => wikiFuzz::chooseInput( array( "success", "submit", "", wikiFuzz::makeFuzz( 2 ) ) ), - 'wpEditToken' => wikiFuzz::chooseInput( array( '', 0, 34987987, wikiFuzz::makeFuzz( 2 ) ) ), - 'target' => wikiFuzz::chooseInput( array( "x", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ) ) ), - 'wpOldTitle' => wikiFuzz::chooseInput( array( "z", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ), wikiFuzz::makeFuzz( 2 ) ) ), - 'wpNewTitle' => wikiFuzz::chooseInput( array( "y", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ), wikiFuzz::makeFuzz( 2 ) ) ), - 'wpReason' => wikiFuzz::chooseInput( array( wikiFuzz::makeFuzz( 2 ) ) ), - 'wpMovetalk' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - 'wpDeleteAndMove' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - 'wpConfirm' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - 'talkmoved' => wikiFuzz::chooseInput( array( "1", wikiFuzz::makeFuzz( 2 ), "articleexists", 'notalkpage' ) ), - 'oldtitle' => wikiFuzz::makeFuzz( 2 ), - 'newtitle' => wikiFuzz::makeFuzz( 2 ), - 'wpMovetalk' => wikiFuzz::chooseInput( array( "1", "0", wikiFuzz::makeFuzz( 2 ) ) ) - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["wpEditToken"] ); - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["target"] ); - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpNewTitle"] ); - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpReason"] ); - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpOldTitle"] ); - } -} - - -/** - ** a page test for "Special:Undelete" - */ -class specialUndeletePageTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Undelete"; - - $this->params = array ( - "action" => wikiFuzz::chooseInput( array( "submit", "", wikiFuzz::makeFuzz( 2 ) ) ), - 'wpEditToken' => wikiFuzz::chooseInput( array( '', 0, 34987987, wikiFuzz::makeFuzz( 2 ) ) ), - 'target' => wikiFuzz::chooseInput( array( "x", wikiFuzz::makeTitleSafe( wikiFuzz::makeFuzz( 2 ) ) ) ), - 'timestamp' => wikiFuzz::chooseInput( array( "125223", wikiFuzz::makeFuzz( 2 ) ) ), - 'file' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - 'restore' => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) ), - 'preview' => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) ), - 'wpComment' => wikiFuzz::makeFuzz( 2 ) - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["wpEditToken"] ); - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["target"] ); - if ( wikiFuzz::randnum( 1 ) == 0 ) unset( $this->params["restore"] ); - if ( wikiFuzz::randnum( 1 ) == 0 ) unset( $this->params["preview"] ); - } -} - - -/** - ** a page test for "Special:Unlockdb" - */ -class specialUnlockdbPageTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Unlockdb"; - - $this->params = array ( - "action" => wikiFuzz::chooseInput( array( "submit", "success", "", wikiFuzz::makeFuzz( 2 ) ) ), - 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ), - 'wpLockConfirm' => wikiFuzz::chooseInput( array( "0", "1", wikiFuzz::makeFuzz( 2 ) ) ) - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpEditToken"] ); - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] ); - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpLockConfirm"] ); - } -} - - -/** - ** a page test for "Special:Lockdb" - */ -class specialLockdbPageTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Lockdb"; - - $this->params = array ( - "action" => wikiFuzz::chooseInput( array( "submit", "success", "", wikiFuzz::makeFuzz( 2 ) ) ), - 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ), - 'wpLockReason' => wikiFuzz::makeFuzz( 2 ), - 'wpLockConfirm' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ) - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpEditToken"] ); - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["action"] ); - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpLockConfirm"] ); - } -} - - -/** - ** a page test for "Special:Userrights" - */ -class specialUserrights extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Userrights"; - - $this->params = array ( - 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ), - 'user-editname' => wikiFuzz::chooseInput( array( "Nickj2", "Nickj2\n", wikiFuzz::makeFuzz( 2 ) ) ), - 'ssearchuser' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - 'saveusergroups' => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ), "Save User Groups" ), - 'member[]' => wikiFuzz::chooseInput( array( "0", "bot", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - "available[]" => wikiFuzz::chooseInput( array( "0", "sysop", "bureaucrat", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ) - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['ssearchuser'] ); - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['saveusergroups'] ); - } -} - - -/** - ** a test for page protection and unprotection. - */ -class pageProtectionForm extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Main_Page"; - - $this->params = array ( - "action" => "protect", - 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ), - "mwProtect-level-edit" => wikiFuzz::chooseInput( array( '', 'autoconfirmed', 'sysop', wikiFuzz::makeFuzz( 2 ) ) ), - "mwProtect-level-move" => wikiFuzz::chooseInput( array( '', 'autoconfirmed', 'sysop', wikiFuzz::makeFuzz( 2 ) ) ), - "mwProtectUnchained" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - 'mwProtect-reason' => wikiFuzz::chooseInput( array( "because it was there", wikiFuzz::makeFuzz( 2 ) ) ) - ); - - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["mwProtectUnchained"] ); - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params['mwProtect-reason'] ); - } -} - - -/** - ** a page test for "Special:Blockip". - */ -class specialBlockip extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Blockip"; - - $this->params = array ( - "action" => wikiFuzz::chooseInput( array( "submit", "", wikiFuzz::makeFuzz( 2 ) ) ), - 'wpEditToken' => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ), - "wpBlockAddress" => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ), - // something like an IP address, sometimes invalid: - ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "." - . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ), - "ip" => wikiFuzz::chooseInput( array( "20398702394", "", "Nickj2", wikiFuzz::makeFuzz( 2 ), - // something like an IP address, sometimes invalid: - ( wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) . "." - . wikiFuzz::randnum( 300, -20 ) . "." . wikiFuzz::randnum( 300, -20 ) ) ) ), - "wpBlockOther" => wikiFuzz::chooseInput( array( '', 'Nickj2', wikiFuzz::makeFuzz( 2 ) ) ), - "wpBlockExpiry" => wikiFuzz::chooseInput( array( "other", "2 hours", "1 day", "3 days", "1 week", "2 weeks", - "1 month", "3 months", "6 months", "1 year", "infinite", wikiFuzz::makeFuzz( 2 ) ) ), - "wpBlockReason" => wikiFuzz::chooseInput( array( "because it was there", wikiFuzz::makeFuzz( 2 ) ) ), - "wpAnonOnly" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - "wpCreateAccount" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - "wpBlock" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ) - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockOther"] ); - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockExpiry"] ); - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockReason"] ); - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpAnonOnly"] ); - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpCreateAccount"] ); - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["wpBlockAddress"] ); - if ( wikiFuzz::randnum( 4 ) == 0 ) unset( $this->params["ip"] ); - } -} - - -/** - ** a test for the imagepage. - */ -class imagepageTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Image:Small-email.png"; - - $this->params = array ( - "image" => wikiFuzz::chooseInput( array( "Small-email.png", wikiFuzz::makeFuzz( 2 ) ) ), - "wpReason" => wikiFuzz::makeFuzz( 2 ), - "oldimage" => wikiFuzz::chooseInput( array( "Small-email.png", wikiFuzz::makeFuzz( 2 ) ) ), - "wpEditToken" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ), - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["image"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpReason"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldimage"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpEditToken"] ); - } -} - - -/** - ** a test for page deletion form. - */ -class pageDeletion extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Main_Page&action=delete"; - - $this->params = array ( - "wpEditToken" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ), - "wpReason" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - "wpConfirm" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpReason"] ); - if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpEditToken"] ); - if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["wpConfirm"] ); - } -} - - - -/** - ** a test for Revision Deletion. - */ -class specialRevisionDeletePageTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Revisiondelete"; - - $this->params = array ( - "target" => wikiFuzz::chooseInput( array( "Main Page", wikiFuzz::makeFuzz( 2 ) ) ), - "oldid" => wikiFuzz::makeFuzz( 2 ), - "oldid[]" => wikiFuzz::makeFuzz( 2 ), - "wpReason" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - "revdelete-hide-text" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - "revdelete-hide-comment" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - "revdelete-hide-user" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - "revdelete-hide-restricted" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["target"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldid"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["oldid[]"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["wpReason"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-text"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-comment"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-user"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["revdelete-hide-restricted"] ); - } -} - - -/** - ** a test for Special:Import. - */ -class specialImportPageTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Import"; - - $this->params = array ( - "action" => "submit", - "source" => wikiFuzz::chooseInput( array( "upload", "interwiki", wikiFuzz::makeFuzz( 2 ) ) ), - "MAX_FILE_SIZE" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - "xmlimport" => wikiFuzz::chooseInput( array( "/var/www/hosts/mediawiki/wiki/AdminSettings.php", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - "namespace" => wikiFuzz::chooseInput( array( wikiFuzz::randnum( 30, -6 ), wikiFuzz::makeFuzz( 2 ) ) ), - "interwiki" => wikiFuzz::makeFuzz( 2 ), - "interwikiHistory" => wikiFuzz::makeFuzz( 2 ), - "frompage" => wikiFuzz::makeFuzz( 2 ), - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["action"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["source"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["MAX_FILE_SIZE"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["xmlimport"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["interwiki"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["interwikiHistory"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["frompage"] ); - - // Note: Need to do a file upload to fully test this Special page. - } -} - - -/** - ** a test for thumb.php - */ -class thumbTest extends pageTest { - function __construct() { - $this->pagePath = "thumb.php"; - - $this->params = array ( - "f" => wikiFuzz::chooseInput( array( "..", "\\", "small-email.png", wikiFuzz::makeFuzz( 2 ) ) ), - "w" => wikiFuzz::chooseInput( array( "80", wikiFuzz::randnum( 6000, -200 ), wikiFuzz::makeFuzz( 2 ) ) ), - "r" => wikiFuzz::chooseInput( array( "0", wikiFuzz::makeFuzz( 2 ) ) ), - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["f"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["w"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["r"] ); - } -} - -/** - ** a test for profileinfo.php - */ -class profileInfo extends pageTest { - function __construct() { - $this->pagePath = "profileinfo.php"; - - $this->params = array ( - "expand" => wikiFuzz::makeFuzz( 2 ), - "sort" => wikiFuzz::chooseInput( array( "time", "count", "name", wikiFuzz::makeFuzz( 2 ) ) ), - "filter" => wikiFuzz::chooseInput( array( "Main Page", wikiFuzz::makeFuzz( 2 ) ) ), - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["sort"] ); - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["filter"] ); - } -} - - -/** - ** a test for Special:Cite (extension Special page). - */ -class specialCitePageTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Cite"; - - $this->params = array ( - "page" => wikiFuzz::chooseInput( array( "\" onmouseover=\"alert(1);\"", "Main Page", wikiFuzz::makeFuzz( 2 ) ) ), - "id" => wikiFuzz::chooseInput( array( "-1", "0", "------'-------0", "+1", "-9823412312312412435", wikiFuzz::makeFuzz( 2 ) ) ), - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["page"] ); - if ( wikiFuzz::randnum( 6 ) == 0 ) unset( $this->params["id"] ); - } -} - - -/** - ** a test for Special:Filepath (extension Special page). - */ -class specialFilepathPageTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Filepath"; - - $this->params = array ( - "file" => wikiFuzz::chooseInput( array( "Small-email.png", "Small-email.png" . wikiFuzz::makeFuzz( 1 ), wikiFuzz::makeFuzz( 2 ) ) ), - ); - } -} - - -/** - ** a test for Special:Renameuser (extension Special page). - */ -class specialRenameuserPageTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Renameuser"; - - $this->params = array ( - "oldusername" => wikiFuzz::chooseInput( array( "Nickj2", "192.168.0.2", wikiFuzz::makeFuzz( 1 ) ) ), - "newusername" => wikiFuzz::chooseInput( array( "Nickj2", "192.168.0.2", wikiFuzz::makeFuzz( 1 ) ) ), - "token" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ), - ); - } -} - - -/** - ** a test for Special:Linksearch (extension Special page). - */ -class specialLinksearch extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special%3ALinksearch"; - - $this->params = array ( - "target" => wikiFuzz::makeFuzz( 2 ), - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 10 ) == 0 ) unset( $this->params["target"] ); - } -} - - -/** - ** a test for Special:CategoryTree (extension Special page). - */ -class specialCategoryTree extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:CategoryTree"; - - $this->params = array ( - "target" => wikiFuzz::makeFuzz( 2 ), - "from" => wikiFuzz::makeFuzz( 2 ), - "until" => wikiFuzz::makeFuzz( 2 ), - "showas" => wikiFuzz::makeFuzz( 2 ), - "mode" => wikiFuzz::chooseInput( array( "pages", "categories", "all", wikiFuzz::makeFuzz( 2 ) ) ), - ); - - // sometimes we do want to specify certain parameters. - if ( wikiFuzz::randnum( 5 ) == 0 ) $this->params["notree"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) ); - } -} - - -/** - ** a test for "Special:Chemicalsources" (extension Special page). - */ -class specialChemicalsourcesTest extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Chemicalsources"; - - // choose an input format to use. - $format = wikiFuzz::chooseInput( - array( 'go', - 'CAS', - 'EINECS', - 'CHEBI', - 'PubChem', - 'SMILES', - 'InChI', - 'ATCCode', - 'KEGG', - 'RTECS', - 'ECNumber', - 'DrugBank', - 'Formula', - 'Name' - ) - ); - - // values for different formats usually start with either letters or numbers. - switch ( $format ) { - case 'Name' : $value = "A"; break; - case 'InChI' : - case 'SMILES' : - case 'Formula': $value = "C"; break; - default : $value = "0"; break; - } - - // and then we append the fuzz input. - $this->params = array ( $format => $value . wikiFuzz::makeFuzz( 2 ) ); - } -} - - -/** - ** A test for api.php (programmatic interface to MediaWiki in XML/JSON/RSS/etc formats). - ** Quite involved to test because there are lots of options/parameters, and because - ** for a lot of the functionality if all the parameters don't make sense then it just - ** returns the help screen - so currently a lot of the tests aren't actually doing much - ** because something wasn't right in the query. - ** - ** @todo Incomplete / unfinished; Runs too fast (suggests not much testing going on). - */ -class api extends pageTest { - - // API login mode. - private static function loginMode() { - $arr = array ( "lgname" => wikiFuzz::makeFuzz( 2 ), - "lgpassword" => wikiFuzz::makeFuzz( 2 ), - ); - // sometimes we want to specify the extra "lgdomain" parameter. - if ( wikiFuzz::randnum( 3 ) == 0 ) { - $arr["lgdomain"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) ); - } - - return $arr; - } - - // API OpenSearch mode. - private static function opensearchMode() { - return array ( "search" => wikiFuzz::makeFuzz( 2 ) ); - } - - // API watchlist feed mode. - private static function feedwatchlistMode() { - // @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() { - // @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 ( - // @todo FIXME: More titles. - "titles" => wikiFuzz::chooseInput( array( "Main Page" ) ), - // @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" ) ), - "meta" => wikiFuzz::chooseInput( array( "siteinfo" ) ), - "generator" => wikiFuzz::chooseInput( array( "allpages", "logevents", "watchlist", "info", "revisions" ) ), - "siprop" => wikiFuzz::chooseInput( array( "general", "namespaces", "general|namespaces" ) ), - ); - - // Add extra parameters based on what list choice we got. - switch ( $params["list"] ) { - case "usercontribs" : self::addListParams ( $params, "uc", array( "limit", "start", "end", "user", "dir" ) ); break; - case "allpages" : self::addListParams ( $params, "ap", array( "from", "prefix", "namespace", "filterredir", "limit" ) ); break; - case "watchlist" : self::addListParams ( $params, "wl", array( "allrev", "start", "end", "namespace", "dir", "limit", "prop" ) ); break; - case "logevents" : self::addListParams ( $params, "le", array( "limit", "type", "start", "end", "user", "dir" ) ); break; - case "recentchanges": self::addListParams ( $params, "rc", array( "limit", "prop", "show", "namespace", "start", "end", "dir" ) ); break; - case "backlinks" : self::addListParams ( $params, "bl", array( "continue", "namespace", "redirect", "limit" ) ); break; - case "embeddedin" : self::addListParams ( $params, "ei", array( "continue", "namespace", "redirect", "limit" ) ); break; - case "imagelinks" : self::addListParams ( $params, "il", array( "continue", "namespace", "redirect", "limit" ) ); break; - } - - if ( $params["prop"] == "revisions" ) { - self::addListParams ( $params, "rv", array( "prop", "limit", "startid", "endid", "end", "dir" ) ); - } - - // Sometimes we want redirects, sometimes we don't. - if ( wikiFuzz::randnum( 3 ) == 0 ) { - $params["redirects"] = wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) ); - } - - return $params; - } - - // Adds all the elements to the array, using the specified prefix. - private static function addListParams( &$array, $prefix, $elements ) { - foreach ( $elements as $element ) { - $array[$prefix . $element] = self::getParamDetails( $element ); - } - } - - // For a given element name, returns the data for that element. - private static function getParamDetails( $element ) { - switch ( $element ) { - case 'startid' : - case 'endid' : - case 'start' : - case 'end' : - case 'limit' : return wikiFuzz::chooseInput( array( "0", "-1", "---'----------0", "+1", "8134", "320742734234235", "20060230121212", wikiFuzz::randnum( 9000, -100 ), wikiFuzz::makeFuzz( 2 ) ) ); - case 'dir' : return wikiFuzz::chooseInput( array( "newer", "older", wikiFuzz::makeFuzz( 2 ) ) ); - case 'user' : return wikiFuzz::chooseInput( array( USER_ON_WIKI, wikiFuzz::makeFuzz( 2 ) ) ); - case 'namespace' : return wikiFuzz::chooseInput( array( -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 200000, wikiFuzz::makeFuzz( 2 ) ) ); - case 'filterredir': return wikiFuzz::chooseInput( array( "all", "redirects", "nonredirectsallpages", wikiFuzz::makeFuzz( 2 ) ) ); - case 'allrev' : return wikiFuzz::chooseInput( array( "1", 0, "", wikiFuzz::makeFuzz( 2 ) ) ); - case 'prop' : return wikiFuzz::chooseInput( array( "user", "comment", "timestamp", "patrol", "flags", "user|user|comment|flags", wikiFuzz::makeFuzz( 2 ) ) ); - case 'type' : return wikiFuzz::chooseInput( array( "block", "protect", "rights", "delete", "upload", "move", "import", "renameuser", "newusers", "makebot", wikiFuzz::makeFuzz( 2 ) ) ); - case 'hide' : return wikiFuzz::chooseInput( array( "minor", "bots", "anons", "liu", "liu|bots|", wikiFuzz::makeFuzz( 2 ) ) ); - case 'show' : return wikiFuzz::chooseInput( array( 'minor', '!minor', 'bot', '!bot', 'anon', '!anon', wikiFuzz::makeFuzz( 2 ) ) ); - default : return wikiFuzz::makeFuzz( 2 ); - } - } - - // Entry point. - function __construct() { - $this->pagePath = "api.php"; - - $modes = array ( "help", - "login", - "opensearch", - "feedwatchlist", - "query" ); - $action = wikiFuzz::chooseInput( array_merge ( $modes, array( wikiFuzz::makeFuzz( 2 ) ) ) ); - - switch ( $action ) { - case "login" : $this->params = self::loginMode(); - break; - case "opensearch" : $this->params = self::opensearchMode(); - break; - case "feedwatchlist" : $this->params = self::feedwatchlistMode(); - break; - case "query" : $this->params = self::queryMode(); - break; - case "help" : - default : // Do something random - "Crazy Ivan" mode. - $random_mode = wikiFuzz::chooseInput( $modes ) . "Mode"; - // There is no "helpMode". - if ( $random_mode == "helpMode" ) $random_mode = "queryMode"; - $this->params = self::$random_mode(); - break; - } - - // Save the selected action. - $this->params["action"] = $action; - - // Set the cookie: - // @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 - $this->params["format"] = wikiFuzz::chooseInput( array( "json", "jsonfm", "php", "phpfm", - "wddx", "wddxfm", "xml", "xmlfm", - "yaml", "yamlfm", "raw", "rawfm", - wikiFuzz::makeFuzz( 2 ) ) ); - - // Page does not produce HTML (sometimes). - $this->tidyValidate = false; - } -} - - -/** - ** a page test for the GeSHi extension. - */ -class GeSHi_Test extends pageTest { - - private function getGeSHiContent() { - return "getLang() . "\" " - . ( wikiFuzz::randnum( 2 ) == 0 ? "line " : "" ) - . ( wikiFuzz::randnum( 2 ) == 0 ? "strict " : "" ) - . "start=" . wikiFuzz::chooseInput( array( wikiFuzz::randnum( 6000, -6000 ), wikiFuzz::makeFuzz( 2 ) ) ) - . ">" - . wikiFuzz::makeFuzz( 2 ) - . ""; - } - - private function getLang() { - return wikiFuzz::chooseInput( array( "actionscript", "ada", "apache", "applescript", "asm", "asp", "autoit", "bash", "blitzbasic", "bnf", "c", "c_mac", "caddcl", "cadlisp", - "cfdg", "cfm", "cpp", "cpp-qt", "csharp", "css", "d", "delphi", "diff", "div", "dos", "eiffel", "fortran", "freebasic", "gml", "groovy", "html4strict", "idl", - "ini", "inno", "io", "java", "java5", "javascript", "latex", "lisp", "lua", "matlab", "mirc", "mpasm", "mysql", "nsis", "objc", "ocaml", "ocaml-brief", "oobas", - "oracle8", "pascal", "perl", "php", "php-brief", "plsql", "python", "qbasic", "rails", "reg", "robots", "ruby", "sas", "scheme", "sdlbasic", "smalltalk", "smarty", - "sql", "tcl", "text", "thinbasic", "tsql", "vb", "vbnet", "vhdl", "visualfoxpro", "winbatch", "xml", "xpp", "z80", wikiFuzz::makeFuzz( 1 ) ) ); - } - - function __construct() { - $this->pagePath = "index.php?title=WIKIFUZZ"; - - $this->params = array ( - "action" => "submit", - "wpMinoredit" => "test", - "wpPreview" => "test", - "wpSection" => "test", - "wpEdittime" => "test", - "wpSummary" => "test", - "wpScrolltop" => "test", - "wpStarttime" => "test", - "wpAutoSummary" => "test", - "wpTextbox1" => $this->getGeSHiContent() // the main wiki text, contains fake GeSHi content. - ); - } -} - -/** - ** selects a page test to run. - * @param $count - * @return \api|\confirmEmail|\contributionsTest|\editPageTest|\imagelistTest|\imagepageTest|\ipblocklistTest|\listusersTest|\mimeSearchTest|\newImagesTest|\pageDeletion|\pageHistoryTest|\pageProtectionForm|\prefixindexTest|\profileInfo|\recentchangesTest|\redirectTest|\searchTest|\specialAllmessagesTest|\specialAllpagesTest|\specialBlockip|\specialBooksourcesTest|\specialCategoryTree|\specialChemicalsourcesTest|\specialCitePageTest|\specialExportTest|\specialFilepathPageTest|\specialImportPageTest|\specialLinksearch|\specialLockdbPageTest|\specialLogTest|\specialMovePage|\specialNewpagesPageTest|\specialRenameuserPageTest|\specialRevisionDeletePageTest|\specialUndeletePageTest|\specialUnlockdbPageTest|\specialUserrights|\successfulUserLoginTest|\thumbTest|\userLoginTest|\viewPageTest|\watchlistTest - */ -function selectPageTest( $count ) { - - // if the user only wants a specific test, then only ever give them that. - if ( defined( "SPECIFIC_TEST" ) ) { - $testType = SPECIFIC_TEST; - return new $testType (); - } - - // Some of the time we test Special pages, the remaining - // time we test using the standard edit page. - switch ( $count % 100 ) { - case 0 : return new successfulUserLoginTest(); - case 1 : return new listusersTest(); - case 2 : return new searchTest(); - case 3 : return new recentchangesTest(); - case 4 : return new prefixindexTest(); - case 5 : return new mimeSearchTest(); - case 6 : return new specialLogTest(); - case 7 : return new userLoginTest(); - case 8 : return new ipblocklistTest(); - case 9 : return new newImagesTest(); - case 10: return new imagelistTest(); - case 11: return new specialExportTest(); - case 12: return new specialBooksourcesTest(); - case 13: return new specialAllpagesTest(); - case 14: return new pageHistoryTest(); - case 15: return new contributionsTest(); - case 16: return new viewPageTest(); - case 17: return new specialAllmessagesTest(); - 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 24: return new specialUndeletePageTest(); - case 25: return new specialMovePage(); - 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 specialRevisionDeletePageTest(); - case 34: return new specialImportPageTest(); - case 35: return new thumbTest(); - case 37: return new profileInfo(); - case 38: return new specialCitePageTest(); - case 39: return new specialFilepathPageTest(); - case 40: return new specialRenameuserPageTest(); - case 41: return new specialLinksearch(); - case 42: return new specialCategoryTree(); - case 43: return new api(); - case 44: return new specialChemicalsourcesTest(); - default: return new editPageTest(); - } -} - - -// ///////////////////// SAVING OUTPUT ///////////////////////// - -/** - ** Utility function for saving a file. Currently has no error checking. - */ -function saveFile( $data, $name ) { - file_put_contents( $name, $data ); -} - -/** - ** Returns a test as an experimental GET-to-POST URL. - ** This doesn't seem to always work though, and sometimes the output is too long - ** to be a valid GET URL, so we also save in other formats. - * @param $test pageTest - * @return string - */ -function getAsURL( pageTest $test ) { - $used_question_mark = ( strpos( $test->getPagePath(), "?" ) !== false ); - $retval = "http://get-to-post.nickj.org/?" . WIKI_BASE_URL . $test->getPagePath(); - foreach ( $test->getParams() as $param => $value ) { - if ( !$used_question_mark ) { - $retval .= "?"; - $used_question_mark = true; - } - else { - $retval .= "&"; - } - $retval .= $param . "=" . urlencode( $value ); - } - return $retval; -} - - -/** - ** Saves a plain-text human-readable version of a test. - */ -function saveTestAsText( pageTest $test, $filename ) { - $str = "Test: " . $test->getPagePath(); - foreach ( $test->getParams() as $param => $value ) { - $str .= "\n$param: $value"; - } - $str .= "\nGet-to-post URL: " . getAsURL( $test ) . "\n"; - saveFile( $str, $filename ); -} - - -/** - ** Saves a test as a standalone basic PHP script that shows this one problem. - ** Resulting script requires PHP-Curl be installed in order to work. - */ -function saveTestAsPHP( pageTest $test, $filename ) { - $str = "getParams() ), true ) . ";\n" - . "\$ch = curl_init();\n" - . "curl_setopt(\$ch, CURLOPT_POST, 1);\n" - . "curl_setopt(\$ch, CURLOPT_POSTFIELDS, \$params );\n" - . "curl_setopt(\$ch, CURLOPT_URL, " . var_export( WIKI_BASE_URL . $test->getPagePath(), true ) . ");\n" - . "curl_setopt(\$ch, CURLOPT_RETURNTRANSFER,1);\n" - . ( $test->getCookie() ? "curl_setopt(\$ch, CURLOPT_COOKIE, " . var_export( $test->getCookie(), true ) . ");\n" : "" ) - . "\$result=curl_exec(\$ch);\n" - . "curl_close (\$ch);\n" - . "print \$result;\n" - . "\n"; - saveFile( $str, $filename ); -} - -/** - * Escapes a value so that it can be used on the command line by Curl. - * Specifically, "<" and "@" need to be escaped if they are the first character, - * otherwise curl interprets these as meaning that we want to insert a file. - * @param $input_params array - * @return array - */ -function escapeForCurl( array $input_params ) { - $output_params = array(); - foreach ( $input_params as $param => $value ) { - if ( strlen( $value ) > 0 && ( $value[0] == "@" || $value[0] == "<" ) ) { - $value = "\\" . $value; - } - $output_params[$param] = $value; - } - return $output_params; -} - - -/** - ** Saves a test as a standalone CURL shell script that shows this one problem. - ** Resulting script requires standalone Curl be installed in order to work. - */ -function saveTestAsCurl( pageTest $test, $filename ) { - $str = "#!/bin/bash\n" - . "curl --silent --include --globoff \\\n" - . ( $test->getCookie() ? " --cookie " . escapeshellarg( $test->getCookie() ) . " \\\n" : "" ); - foreach ( escapeForCurl( $test->getParams() ) as $param => $value ) { - $str .= " -F " . escapeshellarg( $param ) . "=" . escapeshellarg( $value ) . " \\\n"; - } - $str .= " " . escapeshellarg( WIKI_BASE_URL . $test->getPagePath() ); // beginning space matters. - $str .= "\n"; - saveFile( $str, $filename ); - chmod( $filename, 0755 ); // make executable -} - - -/** - ** Saves the internal data structure to file. - */ -function saveTestData ( pageTest $test, $filename ) { - saveFile( serialize( $test ), $filename ); -} - - -/** - ** saves a test in the various formats. - */ -function saveTest( pageTest $test, $testname ) { - $base_name = DIRECTORY . "/" . $testname; - saveTestAsText( $test, $base_name . INFO_FILE ); - saveTestAsPHP ( $test, $base_name . PHP_TEST ); - saveTestAsCurl( $test, $base_name . CURL_TEST ); - saveTestData ( $test, $base_name . DATA_FILE ); -} - -// ////////////////// MEDIAWIKI OUTPUT ///////////////////////// - -/** - * Asks MediaWiki for the HTML output of a test. - * @param $test pageTest - * @return string - */ -function wikiTestOutput( pageTest $test ) { - - $ch = curl_init(); - - // specify the cookie, if required. - if ( $test->getCookie() ) { - curl_setopt( $ch, CURLOPT_COOKIE, $test->getCookie() ); - } - curl_setopt( $ch, CURLOPT_POST, 1 ); // save form using a POST - - $params = escapeForCurl( $test->getParams() ); - curl_setopt( $ch, CURLOPT_POSTFIELDS, $params ); // load the POST variables - - curl_setopt( $ch, CURLOPT_URL, WIKI_BASE_URL . $test->getPagePath() ); // set url to post to - curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); // return into a variable - - $result = curl_exec ( $ch ); - - // if we encountered an error, then say so, and return an empty string. - if ( curl_error( $ch ) ) { - print "\nCurl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch ); - $result = ""; - } - - curl_close ( $ch ); - - return $result; -} - - -// ////////////////// HTML VALIDATION ///////////////////////// - -/** - * Asks the validator whether this is valid HTML, or not. - * @param $text string - * @return array - */ -function validateHTML( $text ) { - - $params = array ( "fragment" => $text ); - - $ch = curl_init(); - - curl_setopt( $ch, CURLOPT_POST, 1 ); // save form using a POST - curl_setopt( $ch, CURLOPT_POSTFIELDS, $params ); // load the POST variables - curl_setopt( $ch, CURLOPT_URL, VALIDATOR_URL ); // set url to post to - curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); // return into a variable - - $result = curl_exec ( $ch ); - - // if we encountered an error, then log it, and exit. - if ( curl_error( $ch ) ) { - trigger_error( "Curl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch ) ); - print "Curl error #: " . curl_errno( $ch ) . " - " . curl_error ( $ch ) . " - exiting.\n"; - exit( 1 ); - } - - curl_close ( $ch ); - - $valid = ( strpos( $result, "Failed validation" ) === false ); - - return array( $valid, $result ); -} - -/** - * Get tidy to check for no HTML errors in the output file (e.g. unescaped strings). - * @param $name - * @return bool - */ -function tidyCheckFile( $name ) { - $file = DIRECTORY . "/" . $name; - $command = PATH_TO_TIDY . " -output /tmp/out.html -quiet $file 2>&1"; - $x = `$command`; - - // Look for the most interesting Tidy errors and warnings. - if ( strpos( $x, "end of file while parsing attributes" ) !== false - || strpos( $x, "attribute with missing trailing quote mark" ) !== false - || strpos( $x, "missing '>' for end of tag" ) !== false - || strpos( $x, "Error:" ) !== false ) { - print "\nTidy found something - view details with: $command"; - return false; - } else { - return true; - } -} - -/** - ** Returns whether or not an database error log file has changed in size since - ** the last time this was run. This is used to tell if a test caused a DB error. - * @return bool - */ -function dbErrorLogged() { - static $filesize; - - // first time running this function - if ( !isset( $filesize ) ) { - // create log if it does not exist - if ( DB_ERROR_LOG_FILE && !file_exists( DB_ERROR_LOG_FILE ) ) { - saveFile( '', DB_ERROR_LOG_FILE ); - } - $filesize = filesize( DB_ERROR_LOG_FILE ); - return false; - } - - $newsize = filesize( DB_ERROR_LOG_FILE ); - // if the log has grown, then assume the current test caused it. - if ( $newsize != $filesize ) { - $filesize = $newsize; - return true; - } - - return false; -} - -// //////////////// TOP-LEVEL PROBLEM-FINDING FUNCTION //////////////////////// - -/** - * takes a page test, and runs it and tests it for problems in the output. - * Returns: False on finding a problem, or True on no problems being found. - * @param $test pageTest - * @param $testname - * @param $can_overwrite bool - * @return bool - */ -function runWikiTest( pageTest $test, &$testname, $can_overwrite = false ) { - - // by default don't overwrite a previous test of the same name. - while ( ! $can_overwrite && file_exists( DIRECTORY . "/" . $testname . DATA_FILE ) ) { - $testname .= "-" . mt_rand( 0, 9 ); - } - - $filename = DIRECTORY . "/" . $testname . DATA_FILE; - - // Store the time before and after, to find slow pages. - $before = microtime( true ); - - // Get MediaWiki to give us the output of this test. - $wiki_preview = wikiTestOutput( $test ); - - $after = microtime( true ); - - // if we received no response, then that's interesting. - if ( $wiki_preview == "" ) { - print "\nNo response received for: $filename"; - return false; - } - - // save output HTML to file. - $html_file = DIRECTORY . "/" . $testname . HTML_FILE; - saveFile( $wiki_preview, $html_file ); - - // if there were PHP errors in the output, then that's interesting too. - if ( strpos( $wiki_preview, "Warning: " ) !== false - || strpos( $wiki_preview, "Fatal error: " ) !== false - || strpos( $wiki_preview, "Notice: " ) !== false - || strpos( $wiki_preview, "Error: " ) !== false - || strpos( $wiki_preview, "Strict Standards:" ) !== false - ) { - $error = substr( $wiki_preview, strpos( $wiki_preview, ":" ) + 7, 50 ); - // Avoid probable PHP bug with bad session ids; http://bugs.php.net/bug.php?id=38224 - if ( $error != "Unknown: The session id contains illegal character" ) { - print "\nPHP error/warning/notice in HTML output: $html_file ; $error"; - return false; - } - } - - // if there was a MediaWiki Backtrace message in the output, then that's also interesting. - if ( strpos( $wiki_preview, "Backtrace:" ) !== false ) { - print "\nInternal MediaWiki error in HTML output: $html_file"; - return false; - } - - // if there was a Parser error comment in the output, then that's potentially interesting. - if ( strpos( $wiki_preview, "!-- ERR" ) !== false ) { - print "\nParser Error comment in HTML output: $html_file"; - return false; - } - - // if a database error was logged, then that's definitely interesting. - if ( dbErrorLogged() ) { - print "\nDatabase Error logged for: $filename"; - return false; - } - - // validate result - $valid = true; - if ( VALIDATE_ON_WEB ) { - list ( $valid, $validator_output ) = validateHTML( $wiki_preview ); - if ( !$valid ) print "\nW3C web validation failed - view details with: html2text " . DIRECTORY . "/" . $testname . ".validator_output.html"; - } - - // Get tidy to check the page, unless we already know it produces non-(X)HTML output. - if ( $test->tidyValidate() ) { - $valid = tidyCheckFile( $testname . HTML_FILE ) && $valid; - } - - // if it took more than 2 seconds to render, then it may be interesting too. (Possible DoS attack?) - if ( ( $after - $before ) >= 2 ) { - print "\nParticularly slow to render (" . round( $after - $before, 2 ) . " seconds): $filename"; - return false; - } - - if ( $valid ) { - // Remove temp HTML file if test was valid: - unlink( $html_file ); - } elseif ( VALIDATE_ON_WEB ) { - saveFile( $validator_output, DIRECTORY . "/" . $testname . ".validator_output.html" ); - } - - return $valid; -} - - -// ///////////////// RERUNNING OLD TESTS /////////////////// - -/** - ** We keep our failed tests so that they can be rerun. - ** This function does that retesting. - */ -function rerunPreviousTests() { - print "Retesting previously found problems.\n"; - - $dir_contents = scandir ( DIRECTORY ); - - // sort file into the order a normal person would use. - natsort ( $dir_contents ); - - foreach ( $dir_contents as $file ) { - - // if file is not a test, then skip it. - // Note we need to escape any periods or will be treated as "any character". - $matches = array(); - if ( !preg_match( "/(.*)" . str_replace( ".", "\.", DATA_FILE ) . "$/", $file, $matches ) ) continue; - - // reload the test. - $full_path = DIRECTORY . "/" . $file; - $test = unserialize( file_get_contents( $full_path ) ); - - // if this is not a valid test, then skip it. - if ( ! $test instanceof pageTest ) { - print "\nSkipping invalid test - $full_path"; - continue; - } - - // The date format is in Apache log format, which makes it easier to locate - // which retest caused which error in the Apache logs (only happens usually if - // apache segfaults). - if ( !QUIET ) print "[" . date ( "D M d H:i:s Y" ) . "] Retesting $file (" . get_class( $test ) . ")"; - - // run test - $testname = $matches[1]; - $valid = runWikiTest( $test, $testname, true ); - - if ( !$valid ) { - saveTest( $test, $testname ); - if ( QUIET ) { - print "\nTest: " . get_class( $test ) . " ; Testname: $testname\n------"; - } else { - print "\n"; - } - } - else { - if ( !QUIET ) print "\r"; - if ( DELETE_PASSED_RETESTS ) { - $prefix = DIRECTORY . "/" . $testname; - if ( is_file( $prefix . DATA_FILE ) ) unlink( $prefix . DATA_FILE ); - if ( is_file( $prefix . PHP_TEST ) ) unlink( $prefix . PHP_TEST ); - if ( is_file( $prefix . CURL_TEST ) ) unlink( $prefix . CURL_TEST ); - if ( is_file( $prefix . INFO_FILE ) ) unlink( $prefix . INFO_FILE ); - } - } - } - - print "\nDone retesting.\n"; -} - - -// //////////////////// MAIN LOOP //////////////////////// - - -// first check whether CURL is installed, because sometimes it's not. -if ( ! function_exists( 'curl_init' ) ) { - die( "Could not find 'curl_init' function. Is the curl extension compiled into PHP?\n" ); -} - -// Initialization of types. wikiFuzz doesn't have a constructor because we want to -// access it staticly and not have any globals. -wikiFuzz::$types = array_keys( wikiFuzz::$data ); - -// Make directory if doesn't exist -if ( !is_dir( DIRECTORY ) ) { - mkdir ( DIRECTORY, 0700 ); -} -// otherwise, we first retest the things that we have found in previous runs -elseif ( RERUN_OLD_TESTS ) { - rerunPreviousTests(); -} - -// main loop. -$start_time = date( "U" ); -$num_errors = 0; -if ( !QUIET ) { - print "Beginning main loop. Results are stored in the " . DIRECTORY . " directory.\n"; - print "Press CTRL+C to stop testing.\n"; -} - -for ( $count = 0; true; $count++ ) { - if ( !QUIET ) { - // spinning progress indicator. - switch( $count % 4 ) { - case '0': print "\r/"; break; - case '1': print "\r-"; break; - case '2': print "\r\\"; break; - case '3': print "\r|"; break; - } - print " $count"; - } - - // generate a page test to run. - $test = selectPageTest( $count ); - - $mins = ( date( "U" ) - $start_time ) / 60; - if ( !QUIET && $mins > 0 ) { - print ". $num_errors poss errors. " - . floor( $mins ) . " mins. " - . round ( $count / $mins, 0 ) . " tests/min. " - . get_class( $test ); // includes the current test name. - } - - // run this test against MediaWiki, and see if the output was valid. - $testname = $count; - $valid = runWikiTest( $test, $testname, false ); - - // save the failed test - if ( ! $valid ) { - if ( QUIET ) { - print "\nTest: " . get_class( $test ) . " ; Testname: $testname\n------"; - } else { - print "\n"; - } - saveTest( $test, $testname ); - $num_errors += 1; - } 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 ); - } - - // stop if we have reached max number of errors. - if ( defined( "MAX_ERRORS" ) && $num_errors >= MAX_ERRORS ) { - break; - } - - // stop if we have reached max number of mins runtime. - if ( defined( "MAX_RUNTIME" ) && $mins >= MAX_RUNTIME ) { - break; - } -} diff --git a/maintenance/generateJsonI18n.php b/maintenance/generateJsonI18n.php new file mode 100644 index 00000000..22d99405 --- /dev/null +++ b/maintenance/generateJsonI18n.php @@ -0,0 +1,287 @@ +mDescription = "Build JSON messages files from a PHP messages file"; + + $this->addArg( 'phpfile', 'PHP file defining a $messages array', false ); + $this->addArg( 'jsondir', 'Directory to write JSON files to', false ); + $this->addOption( 'langcode', 'Language code; only needed for converting core i18n files', + false, true ); + $this->addOption( 'extension', 'Perform default conversion on an extension', + false, true ); + $this->addOption( 'shim-only', 'Only create or update the backward-compatibility shim' ); + $this->addOption( 'supplementary', 'Find supplementary i18n files in subdirs and convert those', + false, false ); + } + + public function execute() { + global $IP; + + $phpfile = $this->getArg( 0 ); + $jsondir = $this->getArg( 1 ); + $extension = $this->getOption( 'extension' ); + $convertSupplementaryI18nFiles = $this->hasOption( 'supplementary' ); + + if ( $extension ) { + if ( $phpfile ) { + $this->error( "The phpfile is already specified, conflicts with --extension.\n", 1 ); + } + $phpfile = "$IP/extensions/$extension/$extension.i18n.php"; + } + + if ( !$phpfile ) { + $this->error( "I'm here for an argument!\n" ); + $this->maybeHelp( true ); + // dies. + } + + if ( $convertSupplementaryI18nFiles ) { + if ( is_readable( $phpfile ) ) { + $this->transformI18nFile( $phpfile, $jsondir ); + } else { + // This is non-fatal because we might want to continue searching for + // i18n files in subdirs even if the extension does not include a + // primary i18n.php. + $this->error( "Warning: no primary i18n file was found." ); + } + $this->output( "Searching for supplementary i18n files...\n" ); + $dir_iterator = new RecursiveDirectoryIterator( dirname( $phpfile ) ); + $iterator = new RecursiveIteratorIterator( + $dir_iterator, RecursiveIteratorIterator::LEAVES_ONLY ); + foreach ( $iterator as $path => $fileObject ) { + if ( fnmatch( "*.i18n.php", $fileObject->getFilename() ) ) { + $this->output( "Converting $path.\n" ); + $this->transformI18nFile( $path ); + } + } + } else { + // Just convert the primary i18n file. + $this->transformI18nFile( $phpfile, $jsondir ); + } + } + + public function transformI18nFile( $phpfile, $jsondir = null ) { + if ( !$jsondir ) { + // Assume the json directory should be in the same directory as the + // .i18n.php file. + $jsondir = dirname( $phpfile ) . "/i18n"; + } + if ( !is_dir( $jsondir ) ) { + $this->output( "Creating directory $jsondir.\n" ); + $success = mkdir( $jsondir ); + if ( !$success ) { + $this->error( "Could not create directory $jsondir\n", 1 ); + } + } + + if ( $this->hasOption( 'shim-only' ) ) { + $this->shimOnly( $phpfile, $jsondir ); + + return; + } + + if ( $jsondir === null ) { + $this->error( 'Argument [jsondir] is required unless --shim-only is specified.' ); + $this->maybeHelp( true ); + } + + if ( !is_readable( $phpfile ) ) { + $this->error( "Error reading $phpfile\n", 1 ); + } + include $phpfile; + $phpfileContents = file_get_contents( $phpfile ); + + if ( !isset( $messages ) ) { + $this->error( "PHP file $phpfile does not define \$messages array\n", 1 ); + } + + $extensionStyle = true; + if ( !isset( $messages['en'] ) || !is_array( $messages['en'] ) ) { + if ( !$this->hasOption( 'langcode' ) ) { + $this->error( "PHP file $phpfile does not set language codes, --langcode " . + "is required.\n", 1 ); + } + $extensionStyle = false; + $langcode = $this->getOption( 'langcode' ); + $messages = array( $langcode => $messages ); + } elseif ( $this->hasOption( 'langcode' ) ) { + $this->output( "Warning: --langcode option set but will not be used.\n" ); + } + + foreach ( $messages as $langcode => $langmsgs ) { + $authors = $this->getAuthorsFromComment( $this->findCommentBefore( + $extensionStyle ? "\$messages['$langcode'] =" : '$messages =', + $phpfileContents + ) ); + // Make sure the @metadata key is the first key in the output + $langmsgs = array_merge( + array( '@metadata' => array( 'authors' => $authors ) ), + $langmsgs + ); + + $jsonfile = "$jsondir/$langcode.json"; + $success = file_put_contents( + $jsonfile, + FormatJson::encode( $langmsgs, "\t", FormatJson::ALL_OK ) . "\n" + ); + if ( $success === false ) { + $this->error( "FAILED to write $jsonfile", 1 ); + } + $this->output( "$jsonfile\n" ); + } + + if ( !$this->hasOption( 'langcode' ) ) { + $shim = $this->doShim( $jsondir ); + file_put_contents( $phpfile, $shim ); + } + + $this->output( "All done.\n" ); + $this->output( "Also add \$wgMessagesDirs['YourExtension'] = __DIR__ . '/i18n';\n" ); + } + + protected function shimOnly( $phpfile, $jsondir ) { + if ( file_exists( $phpfile ) ) { + if ( !is_readable( $phpfile ) ) { + $this->error( "Error reading $phpfile\n", 1 ); + } + + $phpfileContents = file_get_contents( $phpfile ); + $m = array(); + if ( !preg_match( '!"/([^"$]+)/\$csCode.json";!', $phpfileContents, $m ) ) { + $this->error( "Cannot recognize $phpfile as a shim.\n", 1 ); + } + + if ( $jsondir === null ) { + $jsondir = $m[1]; + } + + $this->output( "Updating existing shim $phpfile\n" ); + } elseif ( $jsondir === null ) { + $this->error( "$phpfile does not exist.\n" . + "Argument [jsondir] is required in order to create a new shim.\n", 1 ); + } else { + $this->output( "Creating new shim $phpfile\n" ); + } + + $shim = $this->doShim( $jsondir ); + file_put_contents( $phpfile, $shim ); + $this->output( "All done.\n" ); + } + + protected function doShim( $jsondir ) { + $shim = <<<'PHP' +mDescription = "Creates a sitemap for the site"; - $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( + '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( 'skip-redirects', 'Do not include redirecting articles in the sitemap' ); - $this->addOption( 'identifier', 'What site identifier to use for the wiki, defaults to $wgDBname', false, true ); + $this->addOption( + 'identifier', + 'What site identifier to use for the wiki, defaults to $wgDBname', + false, + true + ); } /** @@ -219,13 +240,10 @@ class GenerateSitemap extends Maintenance { /** * Create directory if it does not exist and return pathname with a trailing slash - * @param $fspath string + * @param string $fspath * @return null|string */ private static function init_path( $fspath ) { - if ( !isset( $fspath ) ) { - return null; - } # Create directory if needed if ( $fspath && !is_dir( $fspath ) ) { wfMkdirParents( $fspath, null, __METHOD__ ) or die( "Can not create directory $fspath.\n" ); @@ -242,6 +260,7 @@ class GenerateSitemap extends Maintenance { global $wgSitemapNamespaces; if ( is_array( $wgSitemapNamespaces ) ) { $this->namespaces = $wgSitemapNamespaces; + return; } @@ -263,11 +282,13 @@ class GenerateSitemap extends Maintenance { /** * Get the priority of a given namespace * - * @param $namespace Integer: the namespace to get the priority for - * @return String + * @param int $namespace The namespace to get the priority for + * @return string */ function priority( $namespace ) { - return isset( $this->priorities[$namespace] ) ? $this->priorities[$namespace] : $this->guessPriority( $namespace ); + return isset( $this->priorities[$namespace] ) + ? $this->priorities[$namespace] + : $this->guessPriority( $namespace ); } /** @@ -275,17 +296,19 @@ class GenerateSitemap extends Maintenance { * default priority for the namespace, varies depending on whether it's * a talkpage or not. * - * @param $namespace Integer: the namespace to get the priority for - * @return String + * @param int $namespace The namespace to get the priority for + * @return string */ function guessPriority( $namespace ) { - return MWNamespace::isSubject( $namespace ) ? $this->priorities[self::GS_MAIN] : $this->priorities[self::GS_TALK]; + return MWNamespace::isSubject( $namespace ) + ? $this->priorities[self::GS_MAIN] + : $this->priorities[self::GS_TALK]; } /** * Return a database resolution of all the pages in a given namespace * - * @param $namespace Integer: limit the query to this namespace + * @param int $namespace Limit the query to this namespace * @return Resource */ function getPageRes( $namespace ) { @@ -318,14 +341,17 @@ class GenerateSitemap extends Maintenance { $fns = $wgContLang->getFormattedNsText( $namespace ); $this->output( "$namespace ($fns)\n" ); - $skippedRedirects = 0; // Number of redirects skipped for that namespace + $skippedRedirects = 0; // Number of redirects skipped for that namespace foreach ( $res as $row ) { if ( $this->skipRedirects && $row->page_is_redirect ) { $skippedRedirects++; continue; } - if ( $i++ === 0 || $i === $this->url_limit + 1 || $length + $this->limit[1] + $this->limit[2] > $this->size_limit ) { + if ( $i++ === 0 + || $i === $this->url_limit + 1 + || $length + $this->limit[1] + $this->limit[2] > $this->size_limit + ) { if ( $this->file !== false ) { $this->write( $this->file, $this->closeFile() ); $this->close( $this->file ); @@ -350,7 +376,11 @@ class GenerateSitemap extends Maintenance { if ( $vCode == $wgContLang->getCode() ) { continue; // we don't want default variant } - $entry = $this->fileEntry( $title->getCanonicalURL( '', $vCode ), $date, $this->priority( $namespace ) ); + $entry = $this->fileEntry( + $title->getCanonicalURL( '', $vCode ), + $date, + $this->priority( $namespace ) + ); $length += strlen( $entry ); $this->write( $this->file, $entry ); } @@ -373,18 +403,25 @@ class GenerateSitemap extends Maintenance { /** * gzopen() / fopen() wrapper * - * @return Resource + * @param string $file + * @param string $flags + * @return resource */ function open( $file, $flags ) { $resource = $this->compress ? gzopen( $file, $flags ) : fopen( $file, $flags ); if ( $resource === false ) { - throw new MWException( __METHOD__ . " error opening file $file with flags $flags. Check permissions?" ); + throw new MWException( __METHOD__ + . " error opening file $file with flags $flags. Check permissions?" ); } + return $resource; } /** * gzwrite() / fwrite() wrapper + * + * @param resource $handle + * @param string $str */ function write( &$handle, $str ) { if ( $handle === true || $handle === false ) { @@ -399,6 +436,8 @@ class GenerateSitemap extends Maintenance { /** * gzclose() / fclose() wrapper + * + * @param resource $handle */ function close( &$handle ) { if ( $this->compress ) { @@ -411,12 +450,13 @@ class GenerateSitemap extends Maintenance { /** * Get a sitemap filename * - * @param $namespace Integer: the namespace - * @param $count Integer: the count - * @return String + * @param int $namespace The namespace + * @param int $count The count + * @return string */ function sitemapFilename( $namespace, $count ) { $ext = $this->compress ? '.gz' : ''; + return "sitemap-{$this->identifier}-NS_$namespace-$count.xml$ext"; } @@ -432,7 +472,7 @@ class GenerateSitemap extends Maintenance { /** * Return the XML schema being used * - * @return String + * @return string */ function xmlSchema() { return 'http://www.sitemaps.org/schemas/sitemap/0.9'; @@ -441,7 +481,7 @@ class GenerateSitemap extends Maintenance { /** * Return the XML required to open a sitemap index file * - * @return String + * @return string */ function openIndex() { return $this->xmlHead() . '' . "\n"; @@ -450,8 +490,8 @@ class GenerateSitemap extends Maintenance { /** * Return the XML for a single sitemap indexfile entry * - * @param $filename String: the filename of the sitemap file - * @return String + * @param string $filename The filename of the sitemap file + * @return string */ function indexEntry( $filename ) { return @@ -464,7 +504,7 @@ class GenerateSitemap extends Maintenance { /** * Return the XML required to close a sitemap index file * - * @return String + * @return string */ function closeIndex() { return "\n"; @@ -473,7 +513,7 @@ class GenerateSitemap extends Maintenance { /** * Return the XML required to open a sitemap file * - * @return String + * @return string */ function openFile() { return $this->xmlHead() . '' . "\n"; @@ -482,10 +522,10 @@ class GenerateSitemap extends Maintenance { /** * Return the XML for a single sitemap entry * - * @param $url String: an RFC 2396 compliant URL - * @param $date String: a ISO 8601 date - * @param $priority String: a priority indicator, 0.0 - 1.0 inclusive with a 0.1 stepsize - * @return String + * @param string $url An RFC 2396 compliant URL + * @param string $date A ISO 8601 date + * @param string $priority A priority indicator, 0.0 - 1.0 inclusive with a 0.1 stepsize + * @return string */ function fileEntry( $url, $date, $priority ) { return @@ -500,7 +540,7 @@ class GenerateSitemap extends Maintenance { /** * Return the XML required to close sitemap file * - * @return String + * @return string */ function closeFile() { return "\n"; @@ -508,6 +548,8 @@ class GenerateSitemap extends Maintenance { /** * Populate $this->limit + * + * @param int $namespace */ function generateLimit( $namespace ) { // bug 17961: make a title with the longest possible URL in this namespace @@ -515,7 +557,11 @@ class GenerateSitemap extends Maintenance { $this->limit = array( strlen( $this->openFile() ), - strlen( $this->fileEntry( $title->getCanonicalURL(), 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/getConfiguration.php b/maintenance/getConfiguration.php index 5a5eb587..d5f68346 100644 --- a/maintenance/getConfiguration.php +++ b/maintenance/getConfiguration.php @@ -63,7 +63,7 @@ class GetConfiguration extends Maintenance { $format = strtolower( $this->getOption( 'format', 'PHP' ) ); $validFormat = in_array( $format, self::$outFormats ); - if ( ! $validFormat ) { + if ( !$validFormat ) { $this->error( "--format set to an unrecognized format", 0 ); $error_out = true; } @@ -87,11 +87,11 @@ class GetConfiguration extends Maintenance { public function finalSetup() { parent::finalSetup(); - $this->regex = $this->getOption( 'regex' ) ? : $this->getOption( 'iregex' ); + $this->regex = $this->getOption( 'regex' ) ?: $this->getOption( 'iregex' ); if ( $this->regex ) { $this->regex = '/' . $this->regex . '/'; if ( $this->hasOption( 'iregex' ) ) { - $this->regex .= 'i'; # case insensitive regex + $this->regex .= 'i'; # case insensitive regex } } @@ -115,7 +115,7 @@ class GetConfiguration extends Maintenance { $res = array(); # Sane default: dump any wg / wmg variable - if ( ! $this->regex && ! $this->getOption( 'settings' ) ) { + if ( !$this->regex && !$this->getOption( 'settings' ) ) { $this->regex = '/^wm?g/'; } @@ -165,7 +165,7 @@ class GetConfiguration extends Maintenance { protected function formatVarDump( $res ) { $ret = ''; foreach ( $res as $key => $value ) { - ob_start(); # intercept var_dump() output + ob_start(); # intercept var_dump() output print "\${$key} = "; var_dump( $value ); # grab var_dump() output and discard it from the output buffer @@ -182,10 +182,12 @@ class GetConfiguration extends Maintenance { return false; } } + return true; } elseif ( is_scalar( $value ) ) { return true; } + return false; } } diff --git a/maintenance/getSlaveServer.php b/maintenance/getSlaveServer.php index d618825f..68c19439 100644 --- a/maintenance/getSlaveServer.php +++ b/maintenance/getSlaveServer.php @@ -34,6 +34,7 @@ class GetSlaveServer extends Maintenance { $this->addOption( "group", "Query group to check specifically" ); $this->mDescription = "Report the hostname of a slave server"; } + public function execute() { global $wgAllDBsAreLocalhost; if ( $wgAllDBsAreLocalhost ) { diff --git a/maintenance/getText.php b/maintenance/getText.php index 9c4bdfb8..7d7c1cc4 100644 --- a/maintenance/getText.php +++ b/maintenance/getText.php @@ -52,7 +52,10 @@ class GetTextMaint extends Maintenance { $titleText = $title->getPrefixedText(); $this->error( "Page $titleText does not exist.\n", true ); } - $content = $rev->getContent( $this->hasOption( 'show-private' ) ? Revision::RAW : Revision::FOR_PUBLIC ); + $content = $rev->getContent( $this->hasOption( 'show-private' ) + ? Revision::RAW + : Revision::FOR_PUBLIC ); + if ( $content === false ) { $titleText = $title->getPrefixedText(); $this->error( "Couldn't extract the text from $titleText.\n", true ); diff --git a/maintenance/importDump.php b/maintenance/importDump.php index 1f47cf12..1f75bccf 100644 --- a/maintenance/importDump.php +++ b/maintenance/importDump.php @@ -3,7 +3,7 @@ * Import XML dump files into the current wiki. * * Copyright © 2005 Brion Vibber - * http://www.mediawiki.org/ + * https://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 @@ -42,8 +42,12 @@ class BackupReader extends Maintenance { 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)'; + $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->stderr = fopen( "php://stderr", "wt" ); $this->addOption( 'report', @@ -67,7 +71,10 @@ TEXT; $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( 'no-updates', 'Disable link table updates. Is faster but leaves the wiki in an inconsistent state' ); + $this->addOption( + 'no-updates', + 'Disable link table updates. Is faster but leaves the wiki in an inconsistent state' + ); $this->addOption( 'image-base-path', 'Import files from a specified path', false, true ); $this->addArg( 'file', 'Dump file to import [else use stdin]', false ); } @@ -104,6 +111,7 @@ TEXT; function setNsfilter( array $namespaces ) { if ( count( $namespaces ) == 0 ) { $this->nsFilter = false; + return; } $this->nsFilter = array_unique( array_map( array( $this, 'getNsIndex' ), $namespaces ) ); @@ -122,7 +130,7 @@ TEXT; } /** - * @param $obj Title|Revision + * @param Title|Revision $obj * @return bool */ private function skippedNamespace( $obj ) { @@ -133,9 +141,9 @@ TEXT; } elseif ( $obj instanceof WikiRevision ) { $ns = $obj->title->getNamespace(); } else { - echo wfBacktrace(); - $this->error( "Cannot get namespace of object in " . __METHOD__, true ); + throw new MWException( "Cannot get namespace of object in " . __METHOD__ ); } + return is_array( $this->nsFilter ) && !in_array( $ns, $this->nsFilter ); } @@ -144,13 +152,13 @@ TEXT; } /** - * @param $rev Revision - * @return mixed + * @param Revision $rev */ function handleRevision( $rev ) { $title = $rev->getTitle(); if ( !$title ) { $this->progress( "Got bogus revision with null title!" ); + return; } @@ -167,13 +175,13 @@ TEXT; } /** - * @param $revision Revision + * @param Revision $revision * @return bool */ function handleUpload( $revision ) { if ( $this->uploads ) { if ( $this->skippedNamespace( $revision ) ) { - return; + return false; } $this->uploadCount++; // $this->report(); @@ -183,9 +191,12 @@ TEXT; // bluuuh hack // call_user_func( $this->uploadCallback, $revision ); $dbw = wfGetDB( DB_MASTER ); + return $dbw->deadlockLoop( array( $revision, 'importUpload' ) ); } } + + return false; } function handleLogItem( $rev ) { @@ -242,6 +253,7 @@ TEXT; } $file = fopen( $filename, 'rt' ); + return $this->importFromHandle( $file ); } @@ -250,6 +262,7 @@ TEXT; if ( self::posix_isatty( $file ) ) { $this->maybeHelp( true ); } + return $this->importFromHandle( $file ); } diff --git a/maintenance/importImages.inc b/maintenance/importImages.inc index 5ae6d6be..b803e3da 100644 --- a/maintenance/importImages.inc +++ b/maintenance/importImages.inc @@ -26,10 +26,10 @@ /** * Search a directory for files with one of a set of extensions * - * @param $dir string Path to directory to search - * @param $exts Array of extensions to search for - * @param $recurse Bool Search subdirectories recursively - * @return mixed Array of filenames on success, or false on failure + * @param string $dir Path to directory to search + * @param array $exts Array of extensions to search for + * @param bool $recurse Search subdirectories recursively + * @return array|bool Array of filenames on success, or false on failure */ function findFiles( $dir, $exts, $recurse = false ) { if ( is_dir( $dir ) ) { @@ -46,6 +46,7 @@ function findFiles( $dir, $exts, $recurse = false ) { $files = array_merge( $files, findFiles( $dir . '/' . $file, $exts, true ) ); } } + return $files; } else { return array(); @@ -58,14 +59,15 @@ function findFiles( $dir, $exts, $recurse = false ) { /** * Split a filename into filename and extension * - * @param $filename string Filename + * @param string $filename Filename * @return array */ function splitFilename( $filename ) { $parts = explode( '.', $filename ); - $ext = $parts[ count( $parts ) - 1 ]; - unset( $parts[ count( $parts ) - 1 ] ); + $ext = $parts[count( $parts ) - 1]; + unset( $parts[count( $parts ) - 1] ); $fname = implode( '.', $parts ); + return array( $fname, $ext ); } @@ -78,10 +80,10 @@ function splitFilename( $filename ) { * files for acme.foo.bar and the extension ".txt". With $maxStrip = 2, * acme.txt would also be acceptable. * - * @param $file string base path - * @param $auxExtension string the extension to be appended to the base path - * @param $maxStrip int the maximum number of extensions to strip from the base path (default: 1) - * @return string or false + * @param string $file Base path + * @param string $auxExtension The extension to be appended to the base path + * @param int $maxStrip The maximum number of extensions to strip from the base path (default: 1) + * @return string|bool */ function findAuxFile( $file, $auxExtension, $maxStrip = 1 ) { if ( strpos( $auxExtension, '.' ) !== 0 ) { @@ -110,9 +112,11 @@ function findAuxFile( $file, $auxExtension, $maxStrip = 1 ) { return false; } -# FIXME: Access the api in a saner way and performing just one query (preferably batching files too). +# @todo FIXME: Access the api in a saner way and performing just one query +# (preferably batching files too). function getFileCommentFromSourceWiki( $wiki_host, $file ) { - $url = $wiki_host . '/api.php?action=query&format=xml&titles=File:' . rawurlencode( $file ) . '&prop=imageinfo&&iiprop=comment'; + $url = $wiki_host . '/api.php?action=query&format=xml&titles=File:' + . rawurlencode( $file ) . '&prop=imageinfo&&iiprop=comment'; $body = Http::get( $url ); if ( preg_match( '##', $body, $matches ) == 0 ) { return false; @@ -122,7 +126,8 @@ function getFileCommentFromSourceWiki( $wiki_host, $file ) { } function getFileUserFromSourceWiki( $wiki_host, $file ) { - $url = $wiki_host . '/api.php?action=query&format=xml&titles=File:' . rawurlencode( $file ) . '&prop=imageinfo&&iiprop=user'; + $url = $wiki_host . '/api.php?action=query&format=xml&titles=File:' + . rawurlencode( $file ) . '&prop=imageinfo&&iiprop=user'; $body = Http::get( $url ); if ( preg_match( '##', $body, $matches ) == 0 ) { return false; diff --git a/maintenance/importImages.php b/maintenance/importImages.php index 54fd4e2d..ae70441f 100644 --- a/maintenance/importImages.php +++ b/maintenance/importImages.php @@ -4,7 +4,8 @@ * using the web-based interface. * * "Smart import" additions: - * - aim: preserve the essential metadata (user, description) when importing medias from an existing wiki + * - aim: preserve the essential metadata (user, description) when importing media + * files from an existing wiki. * - process: * - interface with the source wiki, don't use bare files only (see --source-wiki-url). * - fetch metadata from source wiki for each file to import. @@ -87,16 +88,24 @@ if ( isset( $options['check-userblock'] ) ) { } # Get --from -$from = @$options['from']; +wfSuppressWarnings(); +$from = $options['from']; +wfRestoreWarnings(); # Get sleep time. -$sleep = @$options['sleep']; +wfSuppressWarnings(); +$sleep = $options['sleep']; +wfRestoreWarnings(); + if ( $sleep ) { $sleep = (int)$sleep; } # Get limit number -$limit = @$options['limit']; +wfSuppressWarnings(); +$limit = $options['limit']; +wfRestoreWarnings(); + if ( $limit ) { $limit = (int)$limit; } @@ -167,7 +176,8 @@ if ( $count > 0 ) { } else { if ( isset( $options['skip-dupes'] ) ) { $repo = $image->getRepo(); - $sha1 = File::sha1Base36( $file ); # XXX: we end up calculating this again when actually uploading. that sucks. + # XXX: we end up calculating this again when actually uploading. that sucks. + $sha1 = FSFile::getSha1Base36FromPath( $file ); $dupes = $repo->findBySha1( $sha1 ); @@ -210,7 +220,8 @@ if ( $count > 0 ) { if ( $commentExt ) { $f = findAuxFile( $file, $commentExt ); if ( !$f ) { - echo " No comment file with extension {$commentExt} found for {$file}, using default comment. "; + echo " No comment file with extension {$commentExt} found " + . "for {$file}, using default comment. "; } else { $commentText = file_get_contents( $f ); if ( !$commentText ) { @@ -254,7 +265,13 @@ if ( $count > 0 ) { if ( isset( $options['dry'] ) ) { echo "done.\n"; - } elseif ( $image->recordUpload2( $archive->value, $summary, $commentText, $props, $timestamp ) ) { + } elseif ( $image->recordUpload2( + $archive->value, + $summary, + $commentText, + $props, + $timestamp + ) ) { # We're done! echo "done.\n"; @@ -273,25 +290,24 @@ if ( $count > 0 ) { } if ( $doProtect ) { - # Protect the file - echo "\nWaiting for slaves...\n"; - // Wait for slaves. - sleep( 2.0 ); # Why this sleep? - wfWaitForSlaves(); - - echo "\nSetting image restrictions ... "; - - $cascade = false; - $restrictions = array(); - foreach ( $title->getRestrictionTypes() as $type ) { - $restrictions[$type] = $protectLevel; - } + # Protect the file + echo "\nWaiting for slaves...\n"; + // Wait for slaves. + sleep( 2.0 ); # Why this sleep? + wfWaitForSlaves(); + + echo "\nSetting image restrictions ... "; + + $cascade = false; + $restrictions = array(); + foreach ( $title->getRestrictionTypes() as $type ) { + $restrictions[$type] = $protectLevel; + } - $page = WikiPage::factory( $title ); - $status = $page->doUpdateRestrictions( $restrictions, array(), $cascade, '', $user ); - echo ( $status->isOK() ? 'done' : 'failed' ) . "\n"; + $page = WikiPage::factory( $title ); + $status = $page->doUpdateRestrictions( $restrictions, array(), $cascade, '', $user ); + echo ( $status->isOK() ? 'done' : 'failed' ) . "\n"; } - } else { echo "failed. (at recordUpload stage)\n"; $svar = 'failed'; @@ -311,14 +327,21 @@ if ( $count > 0 ) { # Print out some statistics echo "\n"; - foreach ( array( 'count' => 'Found', 'limit' => 'Limit', 'ignored' => 'Ignored', - 'added' => 'Added', 'skipped' => 'Skipped', 'overwritten' => 'Overwritten', - 'failed' => 'Failed' ) as $var => $desc ) { + foreach ( + array( + 'count' => 'Found', + 'limit' => 'Limit', + 'ignored' => 'Ignored', + 'added' => 'Added', + 'skipped' => 'Skipped', + 'overwritten' => 'Overwritten', + 'failed' => 'Failed' + ) as $var => $desc + ) { if ( $$var > 0 ) { echo "{$desc}: {$$var}\n"; } } - } else { echo "No suitable files could be found for import.\n"; } @@ -337,28 +360,37 @@ USAGE: php importImages.php [options] : Path to the directory containing images to be imported Options: ---extensions= Comma-separated list of allowable extensions, defaults to \$wgFileExtensions ---overwrite Overwrite existing images with the same name (default is to skip them) ---limit= Limit the number of images to process. Ignored or skipped images are not counted. ---from= Ignore all files until the one with the given name. Useful for resuming - aborted imports. should be the file's canonical database form. ---skip-dupes Skip images that were already uploaded under a different name (check SHA1) ---search-recursively Search recursively for files in subdirectories +--extensions= Comma-separated list of allowable extensions, defaults + to \$wgFileExtensions. +--overwrite Overwrite existing images with the same name (default + is to skip them). +--limit= Limit the number of images to process. Ignored or + skipped images are not counted. +--from= Ignore all files until the one with the given name. + Useful for resuming aborted imports. should be + the file's canonical database form. +--skip-dupes Skip images that were already uploaded under a different + name (check SHA1). +--search-recursively Search recursively for files in subdirectories. --sleep= Sleep between files. Useful mostly for debugging. ---user= Set username of uploader, default 'Maintenance script' +--user= Set username of uploader, default 'Maintenance script'. --check-userblock Check if the user got blocked during import. --comment= Set file description, default 'Importing file'. --comment-file= Set description to the content of . ---comment-ext= Causes the description for each file to be loaded from a file with the same name - but the extension . If a global description is also given, it is appended. ---license= Use an optional license template ---dry Dry run, don't import anything ---protect= Specify the protect value (autoconfirmed,sysop) ---summary= Upload summary, description will be used if not provided ---timestamp= Override upload time/date, all MediaWiki timestamp formats are accepted ---unprotect Unprotects all uploaded images ---source-wiki-url If specified, take User and Comment data for each imported file from this URL. - For example, --source-wiki-url="http://en.wikipedia.org/" +--comment-ext= Causes the description for each file to be loaded from a + file with the same name, but the extension . If a + global description is also given, it is appended. +--license= Use an optional license template. +--dry Dry run, don't import anything. +--protect= Specify the protect value (autoconfirmed,sysop). +--summary= Upload summary, description will be used if not + provided. +--timestamp= Override upload time/date, all MediaWiki timestamp + formats are accepted. +--unprotect Unprotects all uploaded images. +--source-wiki-url If specified, take User and Comment data for each + imported file from this URL. For example, + --source-wiki-url="http://en.wikipedia.org/." TEXT; exit( 1 ); diff --git a/maintenance/importSiteScripts.php b/maintenance/importSiteScripts.php index fd768b34..7705ec9c 100644 --- a/maintenance/importSiteScripts.php +++ b/maintenance/importSiteScripts.php @@ -65,13 +65,12 @@ class ImportSiteScripts extends Maintenance { $content = ContentHandler::makeContent( $text, $wikiPage->getTitle() ); $wikiPage->doEditContent( $content, "Importing from $url", 0, false, $user ); } - } protected function fetchScriptList() { $data = array( 'action' => 'query', - 'format' => 'php',//'json', + 'format' => 'php', //'json', 'list' => 'allpages', 'apnamespace' => '8', 'aplimit' => '500', @@ -100,7 +99,6 @@ class ImportSiteScripts extends Maintenance { } while ( isset( $result['query-continue'] ) ); return $pages; - } } diff --git a/maintenance/importTextFile.php b/maintenance/importTextFile.php deleted file mode 100644 index 4a07f2c7..00000000 --- a/maintenance/importTextFile.php +++ /dev/null @@ -1,110 +0,0 @@ - - */ - -$options = array( 'help', 'nooverwrite', 'norc' ); -$optionsWithArgs = array( 'title', 'user', 'comment' ); -require_once __DIR__ . '/commandLine.inc'; -echo "Import Text File\n\n"; - -if ( count( $args ) < 1 || isset( $options['help'] ) ) { - showHelp(); -} else { - - $filename = $args[0]; - echo "Using {$filename}..."; - if ( is_file( $filename ) ) { - - $title = isset( $options['title'] ) ? $options['title'] : titleFromFilename( $filename ); - $title = Title::newFromURL( $title ); - - if ( is_object( $title ) ) { - - echo "\nUsing title '" . $title->getPrefixedText() . "'..."; - if ( !$title->exists() || !isset( $options['nooverwrite'] ) ) { - RequestContext::getMain()->setTitle( $title ); - - $text = file_get_contents( $filename ); - $user = isset( $options['user'] ) ? $options['user'] : 'Maintenance script'; - $user = User::newFromName( $user ); - - if ( is_object( $user ) ) { - - echo "\nUsing username '" . $user->getName() . "'..."; - $wgUser =& $user; - $comment = isset( $options['comment'] ) ? $options['comment'] : 'Importing text file'; - $flags = 0 | ( isset( $options['norc'] ) ? EDIT_SUPPRESS_RC : 0 ); - - echo "\nPerforming edit..."; - $page = WikiPage::factory( $title ); - $content = ContentHandler::makeContent( $text, $title ); - $page->doEditContent( $content, $comment, $flags, false, $user ); - echo "done.\n"; - - } else { - echo "invalid username.\n"; - } - - } else { - echo "page exists.\n"; - } - - } else { - echo "invalid title.\n"; - } - - } else { - echo "does not exist.\n"; - } - -} - -function titleFromFilename( $filename ) { - $parts = explode( '/', $filename ); - $parts = explode( '.', $parts[ count( $parts ) - 1 ] ); - return $parts[0]; -} - -function showHelp() { -print << - - : Path to the file containing page content to import - -Options: - ---title - Title for the new page; default is to use the filename as a base ---user <user> - User to be associated with the edit ---comment <comment> - Edit summary ---nooverwrite - Don't overwrite existing content ---norc - Don't update recent changes ---help - Show this information - -EOF; -} diff --git a/maintenance/initEditCount.php b/maintenance/initEditCount.php index 4b046835..7c6e7d4f 100644 --- a/maintenance/initEditCount.php +++ b/maintenance/initEditCount.php @@ -47,7 +47,7 @@ in the load balancer, usually indicating a replication environment.' ); // Autodetect mode... $backgroundMode = wfGetLB()->getServerCount() > 1 || - ( $dbw instanceof DatabaseMysql && version_compare( $dbver, '4.1' ) < 0 ); + ( $dbw instanceof DatabaseMysql ); if ( $this->hasOption( 'background' ) ) { $backgroundMode = true; diff --git a/maintenance/initSiteStats.php b/maintenance/initSiteStats.php index 92268b3e..49e0e9d7 100644 --- a/maintenance/initSiteStats.php +++ b/maintenance/initSiteStats.php @@ -34,7 +34,10 @@ class InitSiteStats extends Maintenance { public function __construct() { parent::__construct(); $this->mDescription = "Re-initialise the site statistics tables"; - $this->addOption( 'update', 'Update the existing statistics (preserves the ss_total_views field)' ); + $this->addOption( + 'update', + 'Update the existing statistics (preserves the ss_total_views field)' + ); $this->addOption( 'noviews', "Don't update the page view counter" ); $this->addOption( 'active', 'Also update active users count' ); $this->addOption( 'use-master', 'Count using the master database' ); @@ -66,21 +69,19 @@ class InitSiteStats extends Maintenance { $this->output( "{$views}\n" ); } + if ( $this->hasOption( 'update' ) ) { + $this->output( "\nUpdating site statistics..." ); + $counter->refresh(); + $this->output( "done.\n" ); + } + if ( $this->hasOption( 'active' ) ) { - $this->output( "Counting active users..." ); + $this->output( "\nCounting and updating active users..." ); $active = SiteStatsUpdate::cacheUpdate( wfGetDB( DB_MASTER ) ); $this->output( "{$active}\n" ); } - $this->output( "\nUpdating site statistics..." ); - - if ( $this->hasOption( 'update' ) ) { - $counter->update(); - } else { - $counter->refresh(); - } - - $this->output( "done.\n" ); + $this->output( "\nDone.\n" ); } } diff --git a/maintenance/install.php b/maintenance/install.php index d118747a..b948b674 100644 --- a/maintenance/install.php +++ b/maintenance/install.php @@ -21,19 +21,20 @@ * @ingroup Maintenance */ -if ( !function_exists( 'version_compare' ) || ( version_compare( phpversion(), '5.3.2' ) < 0 ) ) { - require_once dirname( __FILE__ ) . '/../includes/PHPVersionError.php'; - wfPHPVersionError( 'cli' ); -} +// Checking for old versions of PHP is done in Maintenance.php +// We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+ +require_once dirname( __FILE__ ) . '/Maintenance.php'; define( 'MW_CONFIG_CALLBACK', 'Installer::overrideConfig' ); define( 'MEDIAWIKI_INSTALL', true ); -require_once dirname( __DIR__ ) . "/maintenance/Maintenance.php"; - /** * Maintenance script to install and configure MediaWiki * + * Default values for the options are defined in DefaultSettings.php + * (see the mapping in CliInstaller.php) + * Default for --dbpath (SQLite-specific) is defined in SqliteInstaller::getGlobalDefaults + * * @ingroup Maintenance */ class CommandLineInstaller extends Maintenance { @@ -41,13 +42,26 @@ class CommandLineInstaller extends Maintenance { parent::__construct(); global $IP; - $this->addArg( 'name', 'The name of the wiki', true ); + $this->addDescription( "CLI-based MediaWiki installation and configuration.\n" . + "Defaut options are indicated in parenthesis." ); + + $this->addArg( 'name', 'The name of the wiki (MediaWiki)', false ); - $this->addArg( 'admin', 'The username of the wiki administrator (WikiSysop)', true ); + $this->addArg( 'admin', 'The username of the wiki administrator.' ); $this->addOption( 'pass', 'The password for the wiki administrator.', false, true ); - $this->addOption( 'passfile', 'An alternative way to provide pass option, as the contents of this file', false, true ); + $this->addOption( + 'passfile', + 'An alternative way to provide pass option, as the contents of this file', + 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( + 'scriptpath', + 'The relative path of the wiki in the web server (/wiki)', + false, + true + ); $this->addOption( 'lang', 'The language to use (en)', false, true ); /* $this->addOption( 'cont-lang', 'The content language (en)', false, true ); */ @@ -56,32 +70,42 @@ class CommandLineInstaller extends Maintenance { $this->addOption( 'dbserver', 'The database host (localhost)', false, true ); $this->addOption( 'dbport', 'The database port; only for PostgreSQL (5432)', false, true ); $this->addOption( 'dbname', 'The database name (my_wiki)', false, true ); - $this->addOption( 'dbpath', 'The path for the SQLite DB (/var/data)', false, true ); + $this->addOption( 'dbpath', 'The path for the SQLite DB ($IP/data)', false, true ); $this->addOption( 'dbprefix', 'Optional database table name prefix', false, true ); $this->addOption( 'installdbuser', 'The user to use for installing (root)', false, true ); - $this->addOption( 'installdbpass', 'The pasword for the DB user to install as.', false, true ); + $this->addOption( 'installdbpass', 'The password 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 ); */ + $this->addOption( 'dbpass', 'The password 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 ($IP)", false, true ); + $this->addOption( 'dbschema', 'The schema for the MediaWiki DB in ' + . 'PostgreSQL/Microsoft SQL Server (mediawiki)', false, true ); + /* + $this->addOption( 'namespace', 'The project namespace (same as the "name" argument)', + false, true ); + */ $this->addOption( 'env-checks', "Run environment checks only, don't change anything" ); } function execute() { - global $IP, $wgTitle; - $siteName = isset( $this->mArgs[0] ) ? $this->mArgs[0] : "Don't care"; // Will not be set if used with --env-checks - $adminName = isset( $this->mArgs[1] ) ? $this->mArgs[1] : null; - $wgTitle = Title::newFromText( 'Installer script' ); - - $dbpassfile = $this->getOption( 'dbpassfile', false ); - if ( $dbpassfile !== false ) { - if ( $this->getOption( 'dbpass', false ) !== false ) { - $this->error( 'WARNING: You provide the options "dbpass" and "dbpassfile". The content of "dbpassfile" overwrites "dbpass".' ); + global $IP; + + $siteName = $this->getArg( 0, 'MediaWiki' ); // Will not be set if used with --env-checks + $adminName = $this->getArg( 1 ); + + $dbpassfile = $this->getOption( 'dbpassfile' ); + if ( $dbpassfile !== null ) { + if ( $this->getOption( 'dbpass' ) !== null ) { + $this->error( 'WARNING: You have provided the options "dbpass" and "dbpassfile". ' + . 'The content of "dbpassfile" overrides "dbpass".' ); } wfSuppressWarnings(); - $dbpass = file_get_contents( $dbpassfile ); + $dbpass = file_get_contents( $dbpassfile ); // returns false on failure wfRestoreWarnings(); if ( $dbpass === false ) { $this->error( "Couldn't open $dbpassfile", true ); @@ -89,30 +113,31 @@ class CommandLineInstaller extends Maintenance { $this->mOptions['dbpass'] = trim( $dbpass, "\r\n" ); } - $passfile = $this->getOption( 'passfile', false ); - if ( $passfile !== false ) { - if ( $this->getOption( 'pass', false ) !== false ) { - $this->error( 'WARNING: You provide the options "pass" and "passfile". The content of "passfile" overwrites "pass".' ); + $passfile = $this->getOption( 'passfile' ); + if ( $passfile !== null ) { + if ( $this->getOption( 'pass' ) !== null ) { + $this->error( 'WARNING: You have provided the options "pass" and "passfile". ' + . 'The content of "passfile" overrides "pass".' ); } wfSuppressWarnings(); - $pass = file_get_contents( $passfile ); + $pass = file_get_contents( $passfile ); // returns false on failure wfRestoreWarnings(); if ( $pass === false ) { $this->error( "Couldn't open $passfile", true ); } - $this->mOptions['pass'] = str_replace( array( "\n", "\r" ), "", $pass ); - } elseif ( $this->getOption( 'pass', false ) === false ) { + $this->mOptions['pass'] = trim( $pass, "\r\n" ); + } elseif ( $this->getOption( 'pass' ) === null ) { $this->error( 'You need to provide the option "pass" or "passfile"', true ); } - $installer = - InstallerOverrides::getCliInstaller( $siteName, $adminName, $this->mOptions ); + $installer = InstallerOverrides::getCliInstaller( $siteName, $adminName, $this->mOptions ); $status = $installer->doEnvironmentChecks(); if ( $status->isGood() ) { $installer->showMessage( 'config-env-good' ); } else { $installer->showStatusMessage( $status ); + return; } if ( !$this->hasOption( 'env-checks' ) ) { @@ -128,6 +153,6 @@ class CommandLineInstaller extends Maintenance { } } -$maintClass = "CommandLineInstaller"; +$maintClass = 'CommandLineInstaller'; require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/interwiki.list b/maintenance/interwiki.list index 179fa5c6..0660e55f 100644 --- a/maintenance/interwiki.list +++ b/maintenance/interwiki.list @@ -1,98 +1,77 @@ # Based more or less on the public interwiki map from MeatballWiki # Default interwiki prefixes... -acronym|http://www.acronymfinder.com/af-query.asp?String=exact&Acronym=$1|0 +acronym|http://www.acronymfinder.com/~/search/af.aspx?string=exact&Acronym=$1|0 advogato|http://www.advogato.org/$1|0 -annotationwiki|http://www.seedwiki.com/page.cfm?wikiid=368&doc=$1|0 arxiv|http://www.arxiv.org/abs/$1|0 c2find|http://c2.com/cgi/wiki?FindPage&value=$1|0 cache|http://www.google.com/search?q=cache:$1|0 -commons|http://commons.wikimedia.org/wiki/$1|0 -corpknowpedia|http://corpknowpedia.org/wiki/index.php/$1|0 +commons|https://commons.wikimedia.org/wiki/$1|0 dictionary|http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query=$1|0 -disinfopedia|http://www.disinfopedia.org/wiki.phtml?title=$1|0 -docbook|http://wiki.docbook.org/topic/$1|0 +docbook|http://wiki.docbook.org/$1|0 doi|http://dx.doi.org/$1|0 -drumcorpswiki|http://www.drumcorpswiki.com/index.php/$1|0 +drumcorpswiki|http://www.drumcorpswiki.com/$1|0 dwjwiki|http://www.suberic.net/cgi-bin/dwj/wiki.cgi?$1|0 elibre|http://enciclopedia.us.es/index.php/$1|0 emacswiki|http://www.emacswiki.org/cgi-bin/wiki.pl?$1|0 foldoc|http://foldoc.org/?$1|0 foxwiki|http://fox.wikis.com/wc.dll?Wiki~$1|0 freebsdman|http://www.FreeBSD.org/cgi/man.cgi?apropos=1&query=$1|0 -gej|http://www.esperanto.de/cgi-bin/aktivikio/wiki.pl?$1|0 +gej|http://www.esperanto.de/dej.malnova/aktivikio.pl?$1|0 gentoo-wiki|http://gentoo-wiki.com/$1|0 google|http://www.google.com/search?q=$1|0 googlegroups|http://groups.google.com/groups?q=$1|0 hammondwiki|http://www.dairiki.org/HammondWiki/$1|0 -hewikisource|http://he.wikisource.org/wiki/$1|1 -hrwiki|http://www.hrwiki.org/index.php/$1|0 -imdb|http://us.imdb.com/Title?$1|0 +hrwiki|http://www.hrwiki.org/wiki/$1|0 +imdb|http://www.imdb.com/find?q=$1&tt=on|0 jargonfile|http://sunir.org/apps/meta.pl?wiki=JargonFile&redirect=$1|0 -jspwiki|http://www.jspwiki.org/wiki/$1|0 -keiki|http://kei.ki/en/$1|0 kmwiki|http://kmwiki.wikispaces.com/$1|0 linuxwiki|http://linuxwiki.de/$1|0 lojban|http://www.lojban.org/tiki/tiki-index.php?page=$1|0 lqwiki|http://wiki.linuxquestions.org/wiki/$1|0 -lugkr|http://lug-kr.sourceforge.net/cgi-bin/lugwiki.pl?$1|0 -mathsongswiki|http://SeedWiki.com/page.cfm?wikiid=237&doc=$1|0 +lugkr|http://www.lug-kr.de/wiki/$1|0 meatball|http://www.usemod.com/cgi-bin/mb.pl?$1|0 -mediawikiwiki|http://www.mediawiki.org/wiki/$1|0 -mediazilla|https://bugzilla.wikimedia.org/$1|1 -memoryalpha|http://www.memory-alpha.org/en/index.php/$1|0 +mediawikiwiki|https://www.mediawiki.org/wiki/$1|0 +mediazilla|https://bugzilla.wikimedia.org/$1|0 +memoryalpha|http://en.memory-alpha.org/wiki/$1|0 metawiki|http://sunir.org/apps/meta.pl?$1|0 -metawikimedia|http://meta.wikimedia.org/wiki/$1|0 -moinmoin|http://purl.net/wiki/moin/$1|0 -mozillawiki|http://wiki.mozilla.org/index.php/$1|0 +metawikimedia|https://meta.wikimedia.org/wiki/$1|0 +mozillawiki|http://wiki.mozilla.org/$1|0 mw|http://www.mediawiki.org/wiki/$1|0 -oeis|http://www.research.att.com/cgi-bin/access.cgi/as/njas/sequences/eisA.cgi?Anum=$1|0 -openfacts|http://openfacts.berlios.de/index.phtml?title=$1|0 -openwiki|http://openwiki.com/?$1|0 -# patwiki|http://gauss.ffii.org/$1|0 # 2008-02-27: lots of spambots -pmeg|http://www.bertilow.com/pmeg/$1.php|0 +oeis|http://oeis.org/$1|0 +openwiki|http://openwiki.com/ow.asp?$1|0 ppr|http://c2.com/cgi/wiki?$1|0 pythoninfo|http://wiki.python.org/moin/$1|0 rfc|http://www.rfc-editor.org/rfc/rfc$1.txt|0 -s23wiki|http://is-root.de/wiki/index.php/$1|0 -seattlewiki|http://seattle.wikia.com/wiki/$1|0 -seattlewireless|http://seattlewireless.net/?$1|0 +s23wiki|http://s23.org/wiki/$1|0 +seattlewireless|http://seattlewireless.net/$1|0 senseislibrary|http://senseis.xmp.net/?$1|0 -# slashdot|http://slashdot.org/article.pl?sid=$1|0 # 2008-02-27: update me +shoutwiki|http://www.shoutwiki.com/wiki/$1|0 sourceforge|http://sourceforge.net/$1|0 +sourcewatch|http://www.sourcewatch.org/index.php?title=$1|0 squeak|http://wiki.squeak.org/squeak/$1|0 -susning|http://www.susning.nu/$1|0 -svgwiki|http://wiki.svg.org/$1|0 -tavi|http://tavi.sourceforge.net/$1|0 tejo|http://www.tejo.org/vikio/$1|0 tmbw|http://www.tmbw.net/wiki/$1|0 tmnet|http://www.technomanifestos.net/?$1|0 -tmwiki|http://www.EasyTopicMaps.com/?page=$1|0 theopedia|http://www.theopedia.com/$1|0 twiki|http://twiki.org/cgi-bin/view/$1|0 -uea|http://www.tejo.org/uea/$1|0 -unreal|http://wiki.beyondunreal.com/wiki/$1|0 +uea|http://uea.org/vikio/index.php/$1|0 +uncyclopedia|http://en.uncyclopedia.co/wiki/$1|0 +unreal|http://wiki.beyondunreal.com/$1|0 usemod|http://www.usemod.com/cgi-bin/wiki.pl?$1|0 -vinismo|http://vinismo.com/en/$1|0 webseitzwiki|http://webseitz.fluxent.com/wiki/$1|0 -why|http://clublet.com/c/c/why?$1|0 wiki|http://c2.com/cgi/wiki?$1|0 wikia|http://www.wikia.com/wiki/$1|0 -wikibooks|http://en.wikibooks.org/wiki/$1|1 -wikicities|http://www.wikia.com/wiki/$1|0 +wikibooks|https://en.wikibooks.org/wiki/$1|0 wikif1|http://www.wikif1.org/$1|0 wikihow|http://www.wikihow.com/$1|0 -wikinfo|http://www.wikinfo.org/index.php/$1|0 -# The following wik[it]* interwikis but wikitravel belong to the Wikimedia Family: -wikimedia|http://wikimediafoundation.org/wiki/$1|0 -wikinews|http://en.wikinews.org/wiki/$1|1 -wikipedia|http://en.wikipedia.org/wiki/$1|1 -wikiquote|http://en.wikiquote.org/wiki/$1|1 -wikisource|http://wikisource.org/wiki/$1|1 -wikispecies|http://species.wikimedia.org/wiki/$1|1 -wikitravel|http://wikitravel.org/en/$1|0 -wikiversity|http://en.wikiversity.org/wiki/$1|1 -wikt|http://en.wiktionary.org/wiki/$1|1 -wiktionary|http://en.wiktionary.org/wiki/$1|1 -wlug|http://www.wlug.org.nz/$1|0 -zwiki|http://zwiki.org/$1|0 -zzz wiki|http://wiki.zzz.ee/index.php/$1|0 +wikinfo|http://wikinfo.co/English/index.php/$1|0 +wikimedia|https://wikimediafoundation.org/wiki/$1|0 +wikinews|https://en.wikinews.org/wiki/$1|0 +wikipedia|https://en.wikipedia.org/wiki/$1|0 +wikiquote|https://en.wikiquote.org/wiki/$1|0 +wikisource|https://wikisource.org/wiki/$1|0 +wikispecies|https://species.wikimedia.org/wiki/$1|0 +wikiversity|https://en.wikiversity.org/wiki/$1|0 +wikivoyage|https://en.wikivoyage.org/wiki/$1|0 +wikt|https://en.wiktionary.org/wiki/$1|0 +wiktionary|https://en.wiktionary.org/wiki/$1|0 diff --git a/maintenance/interwiki.sql b/maintenance/interwiki.sql index 370460af..aad0cc3b 100644 --- a/maintenance/interwiki.sql +++ b/maintenance/interwiki.sql @@ -2,99 +2,79 @@ -- Default interwiki prefixes... REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local) VALUES -('acronym','http://www.acronymfinder.com/af-query.asp?String=exact&Acronym=$1',0), +('acronym','http://www.acronymfinder.com/~/search/af.aspx?string=exact&Acronym=$1',0), ('advogato','http://www.advogato.org/$1',0), -('annotationwiki','http://www.seedwiki.com/page.cfm?wikiid=368&doc=$1',0), ('arxiv','http://www.arxiv.org/abs/$1',0), ('c2find','http://c2.com/cgi/wiki?FindPage&value=$1',0), ('cache','http://www.google.com/search?q=cache:$1',0), -('commons','http://commons.wikimedia.org/wiki/$1',0), -('corpknowpedia','http://corpknowpedia.org/wiki/index.php/$1',0), +('commons','https://commons.wikimedia.org/wiki/$1',0), ('dictionary','http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query=$1',0), -('disinfopedia','http://www.disinfopedia.org/wiki.phtml?title=$1',0), -('docbook','http://wiki.docbook.org/topic/$1',0), +('docbook','http://wiki.docbook.org/$1',0), ('doi','http://dx.doi.org/$1',0), -('drumcorpswiki','http://www.drumcorpswiki.com/index.php/$1',0), +('drumcorpswiki','http://www.drumcorpswiki.com/$1',0), ('dwjwiki','http://www.suberic.net/cgi-bin/dwj/wiki.cgi?$1',0), ('elibre','http://enciclopedia.us.es/index.php/$1',0), ('emacswiki','http://www.emacswiki.org/cgi-bin/wiki.pl?$1',0), ('foldoc','http://foldoc.org/?$1',0), ('foxwiki','http://fox.wikis.com/wc.dll?Wiki~$1',0), ('freebsdman','http://www.FreeBSD.org/cgi/man.cgi?apropos=1&query=$1',0), -('gej','http://www.esperanto.de/cgi-bin/aktivikio/wiki.pl?$1',0), +('gej','http://www.esperanto.de/dej.malnova/aktivikio.pl?$1',0), ('gentoo-wiki','http://gentoo-wiki.com/$1',0), ('google','http://www.google.com/search?q=$1',0), ('googlegroups','http://groups.google.com/groups?q=$1',0), ('hammondwiki','http://www.dairiki.org/HammondWiki/$1',0), -('hewikisource','http://he.wikisource.org/wiki/$1',1), -('hrwiki','http://www.hrwiki.org/index.php/$1',0), -('imdb','http://us.imdb.com/Title?$1',0), +('hrwiki','http://www.hrwiki.org/wiki/$1',0), +('imdb','http://www.imdb.com/find?q=$1&tt=on',0), ('jargonfile','http://sunir.org/apps/meta.pl?wiki=JargonFile&redirect=$1',0), -('jspwiki','http://www.jspwiki.org/wiki/$1',0), -('keiki','http://kei.ki/en/$1',0), ('kmwiki','http://kmwiki.wikispaces.com/$1',0), ('linuxwiki','http://linuxwiki.de/$1',0), ('lojban','http://www.lojban.org/tiki/tiki-index.php?page=$1',0), ('lqwiki','http://wiki.linuxquestions.org/wiki/$1',0), -('lugkr','http://lug-kr.sourceforge.net/cgi-bin/lugwiki.pl?$1',0), -('mathsongswiki','http://SeedWiki.com/page.cfm?wikiid=237&doc=$1',0), +('lugkr','http://www.lug-kr.de/wiki/$1',0), ('meatball','http://www.usemod.com/cgi-bin/mb.pl?$1',0), -('mediawikiwiki','http://www.mediawiki.org/wiki/$1',0), -('mediazilla','https://bugzilla.wikimedia.org/$1',1), -('memoryalpha','http://www.memory-alpha.org/en/index.php/$1',0), +('mediawikiwiki','https://www.mediawiki.org/wiki/$1',0), +('mediazilla','https://bugzilla.wikimedia.org/$1',0), +('memoryalpha','http://en.memory-alpha.org/wiki/$1',0), ('metawiki','http://sunir.org/apps/meta.pl?$1',0), -('metawikimedia','http://meta.wikimedia.org/wiki/$1',0), -('moinmoin','http://purl.net/wiki/moin/$1',0), -('mozillawiki','http://wiki.mozilla.org/index.php/$1',0), +('metawikimedia','https://meta.wikimedia.org/wiki/$1',0), +('mozillawiki','http://wiki.mozilla.org/$1',0), ('mw','http://www.mediawiki.org/wiki/$1',0), -('oeis','http://www.research.att.com/cgi-bin/access.cgi/as/njas/sequences/eisA.cgi?Anum=$1',0), -('openfacts','http://openfacts.berlios.de/index.phtml?title=$1',0), -('openwiki','http://openwiki.com/?$1',0), -('patwiki','http://gauss.ffii.org/$1',0), # 2008-02-27: lots of spambots -('pmeg','http://www.bertilow.com/pmeg/$1.php',0), +('oeis','http://oeis.org/$1',0), +('openwiki','http://openwiki.com/ow.asp?$1',0), ('ppr','http://c2.com/cgi/wiki?$1',0), ('pythoninfo','http://wiki.python.org/moin/$1',0), ('rfc','http://www.rfc-editor.org/rfc/rfc$1.txt',0), -('s23wiki','http://is-root.de/wiki/index.php/$1',0), -('seattlewiki','http://seattle.wikia.com/wiki/$1',0), -('seattlewireless','http://seattlewireless.net/?$1',0), +('s23wiki','http://s23.org/wiki/$1',0), +('seattlewireless','http://seattlewireless.net/$1',0), ('senseislibrary','http://senseis.xmp.net/?$1',0), -('slashdot','http://slashdot.org/article.pl?sid=$1',0), # 2008-02-27: update me +('shoutwiki','http://www.shoutwiki.com/wiki/$1',0), ('sourceforge','http://sourceforge.net/$1',0), +('sourcewatch','http://www.sourcewatch.org/index.php?title=$1',0), ('squeak','http://wiki.squeak.org/squeak/$1',0), -('susning','http://www.susning.nu/$1',0), -('svgwiki','http://wiki.svg.org/$1',0), -('tavi','http://tavi.sourceforge.net/$1',0), ('tejo','http://www.tejo.org/vikio/$1',0), ('tmbw','http://www.tmbw.net/wiki/$1',0), ('tmnet','http://www.technomanifestos.net/?$1',0), -('tmwiki','http://www.EasyTopicMaps.com/?page=$1',0), ('theopedia','http://www.theopedia.com/$1',0), ('twiki','http://twiki.org/cgi-bin/view/$1',0), -('uea','http://www.tejo.org/uea/$1',0), -('unreal','http://wiki.beyondunreal.com/wiki/$1',0), +('uea','http://uea.org/vikio/index.php/$1',0), +('uncyclopedia','http://en.uncyclopedia.co/wiki/$1',0), +('unreal','http://wiki.beyondunreal.com/$1',0), ('usemod','http://www.usemod.com/cgi-bin/wiki.pl?$1',0), -('vinismo','http://vinismo.com/en/$1',0), ('webseitzwiki','http://webseitz.fluxent.com/wiki/$1',0), -('why','http://clublet.com/c/c/why?$1',0), ('wiki','http://c2.com/cgi/wiki?$1',0), ('wikia','http://www.wikia.com/wiki/$1',0), -('wikibooks','http://en.wikibooks.org/wiki/$1',1), -('wikicities','http://www.wikia.com/wiki/$1',0), +('wikibooks','https://en.wikibooks.org/wiki/$1',0), ('wikif1','http://www.wikif1.org/$1',0), ('wikihow','http://www.wikihow.com/$1',0), -('wikinfo','http://www.wikinfo.org/index.php/$1',0), -# The following wik[it]* interwikis but wikitravel belong to the Wikimedia Family: -('wikimedia','http://wikimediafoundation.org/wiki/$1',0), -('wikinews','http://en.wikinews.org/wiki/$1',1), -('wikipedia','http://en.wikipedia.org/wiki/$1',1), -('wikiquote','http://en.wikiquote.org/wiki/$1',1), -('wikisource','http://wikisource.org/wiki/$1',1), -('wikispecies','http://species.wikimedia.org/wiki/$1',1), -('wikitravel','http://wikitravel.org/en/$1',0), -('wikiversity','http://en.wikiversity.org/wiki/$1',1), -('wikt','http://en.wiktionary.org/wiki/$1',1), -('wiktionary','http://en.wiktionary.org/wiki/$1',1), -('wlug','http://www.wlug.org.nz/$1',0), -('zwiki','http://zwiki.org/$1',0), -('zzz wiki','http://wiki.zzz.ee/index.php/$1',0); +('wikinfo','http://wikinfo.co/English/index.php/$1',0), +('wikimedia','https://wikimediafoundation.org/wiki/$1',0), +('wikinews','https://en.wikinews.org/wiki/$1',0), +('wikipedia','https://en.wikipedia.org/wiki/$1',0), +('wikiquote','https://en.wikiquote.org/wiki/$1',0), +('wikisource','https://wikisource.org/wiki/$1',0), +('wikispecies','https://species.wikimedia.org/wiki/$1',0), +('wikiversity','https://en.wikiversity.org/wiki/$1',0), +('wikivoyage','https://en.wikivoyage.org/wiki/$1',0), +('wikt','https://en.wiktionary.org/wiki/$1',0), +('wiktionary','https://en.wiktionary.org/wiki/$1',0) +; diff --git a/maintenance/jsduck/CustomTags.rb b/maintenance/jsduck/CustomTags.rb new file mode 100644 index 00000000..2aff9881 --- /dev/null +++ b/maintenance/jsduck/CustomTags.rb @@ -0,0 +1,116 @@ +# Custom tags for JSDuck 5.x +# See also: +# - https://github.com/senchalabs/jsduck/wiki/Tags +# - https://github.com/senchalabs/jsduck/wiki/Custom-tags +# - https://github.com/senchalabs/jsduck/wiki/Custom-tags/7f5c32e568eab9edc8e3365e935bcb836cb11f1d +require 'jsduck/tag/tag' + +class CommonTag < JsDuck::Tag::Tag + def initialize + @html_position = POS_DOC + 0.1 + @repeatable = true + end + + def parse_doc(scanner, position) + if @multiline + return { :tagname => @tagname, :doc => :multiline } + else + text = scanner.match(/.*$/) + return { :tagname => @tagname, :doc => text } + end + end + + def process_doc(context, tags, position) + context[@tagname] = tags + end + + def format(context, formatter) + context[@tagname].each do |tag| + tag[:doc] = formatter.format(tag[:doc]) + end + end +end + +class SourceTag < CommonTag + def initialize + @tagname = :source + @pattern = "source" + super + end + + def to_html(context) + context[@tagname].map do |source| + <<-EOHTML + <h3 class='pa'>Source</h3> + #{source[:doc]} + EOHTML + end.join + end +end + +class SeeTag < CommonTag + def initialize + @tagname = :see + @pattern = "see" + super + end + + def format(context, formatter) + position = context[:files][0] + context[@tagname].each do |tag| + tag[:doc] = '<li>' + render_long_see(tag[:doc], formatter, position) + '</li>' + end + end + + def to_html(context) + <<-EOHTML + <h3 class="pa">Related</h3> + <ul> + #{ context[@tagname].map {|tag| tag[:doc] }.join("\n") } + </ul> + EOHTML + end + + def render_long_see(tag, formatter, position) + if tag =~ /\A([^\s]+)( .*)?\Z/m + name = $1 + doc = $2 ? ': ' + $2 : '' + return formatter.format("{@link #{name}} #{doc}") + else + JsDuck::Logger.warn(nil, 'Unexpected @see argument: "'+tag+'"', position) + return tag + end + end +end + +class ContextTag < CommonTag + def initialize + @tagname = :context + @pattern = 'context' + super + end + + def format(context, formatter) + position = context[:files][0] + context[@tagname].each do |tag| + tag[:doc] = render_long_context(tag[:doc], formatter, position) + end + end + + def to_html(context) + <<-EOHTML + <h3 class="pa">Context</h3> + #{ context[@tagname].last[:doc] } + EOHTML + end + + def render_long_context(tag, formatter, position) + if tag =~ /\A([^\s]+)/m + name = $1 + return formatter.format("`context` : {@link #{name}}") + else + JsDuck::Logger.warn(nil, 'Unexpected @context argument: "'+tag+'"', position) + return tag + end + end +end diff --git a/maintenance/jsduck/MetaTags.rb b/maintenance/jsduck/MetaTags.rb deleted file mode 100644 index 83cc0884..00000000 --- a/maintenance/jsduck/MetaTags.rb +++ /dev/null @@ -1,69 +0,0 @@ -# See also: -# - https://github.com/senchalabs/jsduck/wiki/Tags -# - https://github.com/senchalabs/jsduck/wiki/Custom-tags -require 'jsduck/meta_tag' - -class SourceTag < JsDuck::MetaTag - def initialize - # This defines the name of the @tag - @name = 'source' - end - - # Generate HTML output for this tag. - # One can make use of the #format method to easily support - # Markdown and {@link} tags inside the contents of the tag. - # - # @param tags All matches of this tag on one class. - def to_html(tags) - '<h3 class="pa">Source</h3>' + tags.map {|tag| format(tag) }.join("\n") - end -end - -class ContextTag < JsDuck::MetaTag - def initialize - @name = 'context' - end - - # @param tags All matches of this tag on one class. - def to_html(tags) - return '<h3 class="pa">Context</h3>' + render_long_context(tags.last) - end - - def render_long_context(tag) - if tag =~ /\A([^\s]+)/m - name = $1 - return format("`this` : {@link #{name}}") - end - end -end - -class SeeTag < JsDuck::MetaTag - def initialize - @name = 'see' - @multiline = true - end - - # @param tags All matches of this tag on one class. - def to_html(tags) - doc = [] - doc << '<h3 class="pa">Related</h3>' - doc << [ - '<ul>', - tags.map {|tag| render_long_see(tag) }, - '</ul>', - ] - doc - end - - def render_long_see(tag) - if tag =~ /\A([^\s]+)( .*)?\Z/m - name = $1 - doc = $2 ? ': ' + $2 : '' - return [ - '<li>', - format("{@link #{name}} #{doc}"), - '</li>' - ] - end - end -end diff --git a/maintenance/jsduck/categories.json b/maintenance/jsduck/categories.json index f96902d8..d6163bde 100644 --- a/maintenance/jsduck/categories.json +++ b/maintenance/jsduck/categories.json @@ -9,7 +9,7 @@ "mw.Map", "mw.Message", "mw.loader", - "mw.log", + "mw.loader.store", "mw.html", "mw.html.Cdata", "mw.html.Raw", @@ -20,12 +20,13 @@ "name": "General", "classes": [ "mw.Title", - "mw.inspect", - "mw.inspect.reports", + "mw.Uri", "mw.notification", + "mw.Notification_", "mw.user", "mw.util", - "mw.plugin.*" + "mw.plugin.*", + "mw.cookie" ] }, { @@ -35,6 +36,42 @@ { "name": "API", "classes": ["mw.Api*"] + }, + { + "name": "Language", + "classes": [ + "mw.language*", + "mw.cldr", + "mw.jqueryMsg" + ] + }, + { + "name": "Page", + "classes": [ + "mw.page*" + ] + }, + { + "name": "Interfaces", + "classes": [ + "mw.Feedback" + ] + }, + { + "name": "Special", + "classes": [ + "mw.special*" + ] + }, + { + "name": "Development", + "classes": [ + "mw.log", + "mw.inspect", + "mw.inspect.reports", + "mw.Debug", + "mw.Debug.profile" + ] } ] }, @@ -43,13 +80,21 @@ "groups": [ { "name": "Plugins", - "classes": ["jQuery.plugin.*"] + "classes": [ + "jQuery.client", + "jQuery.colorUtil", + "jQuery.plugin.*" + ] } ] }, { "name": "Upstream", "groups": [ + { + "name": "OOJS", + "classes": ["OO", "OO.*"] + }, { "name": "jQuery", "classes": ["jQuery", "jQuery.Event", "jQuery.Callbacks", "jQuery.Promise", "jQuery.Deferred", "jQuery.jqXHR", "QUnit"] diff --git a/maintenance/jsduck/config.json b/maintenance/jsduck/config.json index e6e0f658..e97f2923 100644 --- a/maintenance/jsduck/config.json +++ b/maintenance/jsduck/config.json @@ -1,27 +1,40 @@ { "--title": "MediaWiki core - Documentation", - "--footer": "Documentation for MediaWiki core. Generated on {DATE} by {JSDUCK} {VERSION}.", "--categories": "./categories.json", - "--meta-tags": "./MetaTags.rb", "--eg-iframe": "./eg-iframe.html", - "--warnings": ["-no_doc"], + "--tags": "./CustomTags.rb", + "--warnings": ["-nodoc(class,public)"], "--builtin-classes": true, + "--warnings-exit-nonzero": true, + "--external": "HTMLElement,HTMLDocument,Window,File", + "--footer": "Documentation for MediaWiki core. Generated on {DATE} by {JSDUCK} {VERSION}.", "--output": "../../docs/js", "--": [ "./external.js", - "../../resources/mediawiki/mediawiki.js", - "../../resources/mediawiki/mediawiki.log.js", - "../../resources/mediawiki/mediawiki.util.js", - "../../resources/mediawiki/mediawiki.Title.js", - "../../resources/mediawiki/mediawiki.inspect.js", - "../../resources/mediawiki/mediawiki.notify.js", - "../../resources/mediawiki/mediawiki.notification.js", - "../../resources/mediawiki/mediawiki.user.js", - "../../resources/mediawiki.action/mediawiki.action.edit.js", - "../../resources/mediawiki.action/mediawiki.action.view.postEdit.js", - "../../resources/mediawiki.page/mediawiki.page.startup.js", - "../../resources/mediawiki.api", - "../../resources/jquery/jquery.localize.js", - "../../resources/jquery/jquery.spinner.js" + "../../resources/src/mediawiki", + "../../resources/src/mediawiki.action", + "../../resources/src/mediawiki.api", + "../../resources/src/mediawiki.language", + "../../resources/src/mediawiki.page", + "../../resources/src/mediawiki.special", + "../../resources/src/jquery/jquery.accessKeyLabel.js", + "../../resources/src/jquery/jquery.arrowSteps.js", + "../../resources/src/jquery/jquery.autoEllipsis.js", + "../../resources/src/jquery/jquery.badge.js", + "../../resources/src/jquery/jquery.byteLength.js", + "../../resources/src/jquery/jquery.byteLimit.js", + "../../resources/src/jquery/jquery.checkboxShiftClick.js", + "../../resources/src/jquery/jquery.client.js", + "../../resources/src/jquery/jquery.colorUtil.js", + "../../resources/src/jquery/jquery.confirmable.js", + "../../resources/src/jquery/jquery.footHovzer.js", + "../../resources/src/jquery/jquery.getAttrs.js", + "../../resources/src/jquery/jquery.hidpi.js", + "../../resources/src/jquery/jquery.localize.js", + "../../resources/src/jquery/jquery.makeCollapsible.js", + "../../resources/src/jquery/jquery.spinner.js", + "../../resources/src/jquery/jquery.tabIndex.js", + "../../resources/lib/oojs", + "../../resources/lib/oojs-ui" ] } diff --git a/maintenance/jsduck/eg-iframe.html b/maintenance/jsduck/eg-iframe.html index 86eae4b6..7dc4afa8 100644 --- a/maintenance/jsduck/eg-iframe.html +++ b/maintenance/jsduck/eg-iframe.html @@ -1,88 +1,88 @@ <!DOCTYPE html> <html> <head> - <meta charset="utf-8"> - <title>MediaWiki Code Example - - - - - + .mw-jsduck-log-line:nth-child(odd) { + background: #fff; + } + - + /** + * Method called by jsduck to execute the example code. + */ + function loadInlineExample( code, options, callback ) { + try { + eval( code ); + callback && callback( true ); + } catch ( e ) { + mw.log( 'Uncaught exception: ' + e ); + callback && callback( false, e ); + throw e; + } + } + diff --git a/maintenance/lag.php b/maintenance/lag.php index 410bf756..52f8201a 100644 --- a/maintenance/lag.php +++ b/maintenance/lag.php @@ -39,7 +39,9 @@ class DatabaseLag extends Maintenance { if ( $this->hasOption( 'r' ) ) { $lb = wfGetLB(); echo 'time '; - for ( $i = 1; $i < $lb->getServerCount(); $i++ ) { + + $serverCount = $lb->getServerCount(); + for ( $i = 1; $i < $serverCount; $i++ ) { $hostname = $lb->getServerName( $i ); printf( "%-12s ", $hostname ); } diff --git a/maintenance/language/StatOutputs.php b/maintenance/language/StatOutputs.php index e9d8c86d..31ce7024 100644 --- a/maintenance/language/StatOutputs.php +++ b/maintenance/language/StatOutputs.php @@ -1,7 +1,4 @@ " . $version . "\n\n"; - echo "'''Note:''' These statistics can be generated by running php maintenance/language/transstat.php.\n\n"; - echo "For additional information on specific languages (the message names, the actual problems, etc.), run php maintenance/language/checkLanguage.php --lang=foo.\n\n"; + echo "'''Note:''' These statistics can be generated by running " . + "php maintenance/language/transstat.php.\n\n"; + echo "For additional information on specific languages (the message names, the actual " . + "problems, etc.), run php maintenance/language/checkLanguage.php --lang=foo.\n\n"; echo 'English (en) is excluded because it is the default localization'; if ( is_array( $wgDummyLanguageCodes ) ) { $dummyCodes = array(); foreach ( $wgDummyLanguageCodes as $dummyCode => $correctCode ) { $dummyCodes[] = Language::fetchLanguageName( $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 ', 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" style="background-color: #F9F9F9; border: 1px #AAAAAA solid; border-collapse: collapse; clear:both; width:100%;"' . "\n"; + echo '{| class="sortable wikitable" border="2" style="background-color: #F9F9F9; ' . + 'border: 1px #AAAAAA solid; border-collapse: collapse; clear:both; width:100%;"' . "\n"; } + function footer() { echo "|}\n"; } + function blockstart() { echo "|-\n"; } + function blockend() { echo ''; } + function element( $in, $heading = false ) { echo ( $heading ? '!' : '|' ) . "$in\n"; } + function formatPercent( $subset, $total, $revert = false, $accuracy = 2 ) { - $v = @round( 255 * $subset / $total ); + wfSuppressWarnings(); + $v = round( 255 * $subset / $total ); + wfRestoreWarnings(); + if ( $revert ) { # Weigh reverse with factor 20 so coloring takes effect more quickly as # this option is used solely for reporting 'bad' percentages. @@ -100,25 +118,28 @@ class wikiStatsOutput extends statsOutput { $color = $red . $green . $blue; $percent = parent::formatPercent( $subset, $total, $revert, $accuracy ); + return 'style="background-color:#' . $color . ';"|' . $percent; } } /** Output text. To be used on a terminal for example. */ -class textStatsOutput extends statsOutput { +class TextStatsOutput extends StatsOutput { function element( $in, $heading = false ) { echo $in . "\t"; } + function blockend() { echo "\n"; } } /** csv output. Some people love excel */ -class csvStatsOutput extends statsOutput { +class CsvStatsOutput extends StatsOutput { function element( $in, $heading = false ) { echo $in . ";"; } + function blockend() { echo "\n"; } diff --git a/maintenance/language/checkDupeMessages.php b/maintenance/language/checkDupeMessages.php index 381ddae1..5d482442 100644 --- a/maintenance/language/checkDupeMessages.php +++ b/maintenance/language/checkDupeMessages.php @@ -67,8 +67,7 @@ if ( $runTest ) { $messagesFileC = $messagesDir . 'Messages' . $langCodeFC . '.php'; if ( file_exists( $messagesFile ) && file_exists( $messagesFileC ) ) { $run = true; - } - else { + } else { echo "Messages file(s) could not be found.\nMake sure both files are exists.\n"; } } diff --git a/maintenance/language/checkLanguage.inc b/maintenance/language/checkLanguage.inc index 4b49ada3..990f2585 100644 --- a/maintenance/language/checkLanguage.inc +++ b/maintenance/language/checkLanguage.inc @@ -41,7 +41,7 @@ class CheckLanguageCLI { /** * Constructor. - * @param $options array Options for script. + * @param array $options Options for script. */ public function __construct( array $options ) { if ( isset( $options['help'] ) ) { @@ -89,7 +89,7 @@ class CheckLanguageCLI { $this->output = $options['output']; } - $this->L = new languages( $this->includeExif ); + $this->L = new Languages( $this->includeExif ); } /** @@ -118,7 +118,7 @@ class CheckLanguageCLI { /** * Get the checks that can easily be treated by non-speakers of the language. - * @return Array A list of the easy checks. + * @return array A list of the easy checks. */ protected function easyChecks() { return array( @@ -182,21 +182,25 @@ class CheckLanguageCLI { return array( 'untranslated' => '$1 message(s) of $2 are not translated to $3, but exist in en:', 'duplicate' => '$1 message(s) of $2 are translated the same in en and $3:', - 'obsolete' => '$1 message(s) of $2 do not exist in en or are in the ignore list, but exist in $3:', + 'obsolete' => + '$1 message(s) of $2 do not exist in en or are in the ignore list, but exist in $3:', 'variables' => '$1 message(s) of $2 in $3 don\'t match the variables used in en:', 'plural' => '$1 message(s) of $2 in $3 don\'t use {{plural}} while en uses:', 'empty' => '$1 message(s) of $2 in $3 are empty or -:', 'whitespace' => '$1 message(s) of $2 in $3 have trailing whitespace:', 'xhtml' => '$1 message(s) of $2 in $3 contain illegal XHTML:', - 'chars' => '$1 message(s) of $2 in $3 include hidden chars which should not be used in the messages:', + 'chars' => + '$1 message(s) of $2 in $3 include hidden chars which should not be used in the messages:', 'links' => '$1 message(s) of $2 in $3 have problematic link(s):', 'unbalanced' => '$1 message(s) of $2 in $3 have unbalanced {[]}:', 'namespace' => '$1 namespace name(s) of $2 are not translated to $3, but exist in en:', - 'projecttalk' => '$1 namespace name(s) and alias(es) in $3 are project talk namespaces without the parameter:', + 'projecttalk' => + '$1 namespace name(s) and alias(es) in $3 are project talk namespaces without the parameter:', 'magic' => '$1 magic word(s) of $2 are not translated to $3, but exist in en:', 'magic-old' => '$1 magic word(s) of $2 do not exist in en, but exist in $3:', 'magic-over' => '$1 magic word(s) of $2 in $3 do not contain the original en word(s):', - 'magic-case' => '$1 magic word(s) of $2 in $3 change the case-sensitivity of the original en word:', + 'magic-case' => + '$1 magic word(s) of $2 in $3 change the case-sensitivity of the original en word:', 'special' => '$1 special page alias(es) of $2 are not translated to $3, but exist in en:', 'special-old' => '$1 special page alias(es) of $2 do not exist in en, but exist in $3:', ); @@ -216,35 +220,49 @@ Parameters: --all: Check all customized languages. --level: Show the following display level (default: 2): * 0: Skip the checks (useful for checking syntax). - * 1: Show only the stub headers and number of wrong messages, without list of messages. - * 2: Show only the headers and the message keys, without the message values. - * 3: Show both the headers and the complete messages, with both keys and values. + * 1: Show only the stub headers and number of wrong messages, without + list of messages. + * 2: Show only the headers and the message keys, without the message + values. + * 3: Show both the headers and the complete messages, with both keys and + values. --links: Link the message values (default off). --prefix: prefix to add to links. - --wikilang: For the links, what is the content language of the wiki to display the output in (default en). - --noexif: Do not check for Exif messages (a bit hard and boring to translate), if you know - that they are currently not translated and want to focus on other problems (default off). + --wikilang: For the links, what is the content language of the wiki to + display the output in (default en). + --noexif: Do not check for Exif messages (a bit hard and boring to + translate), if you know what they are currently not translated and want + to focus on other problems (default off). --whitelist: Do only the following checks (form: code,code). --blacklist: Do not do the following checks (form: code,code). - --easy: Do only the easy checks, which can be treated by non-speakers of the language. - -Check codes (ideally, all of them should result 0; all the checks are executed by default (except language-specific check blacklists in checkLanguage.inc): - * untranslated: Messages which are required to translate, but are not translated. - * duplicate: Messages which translation equal to fallback - * obsolete: Messages which are untranslatable or do not exist, but are translated. - * variables: Messages without variables which should be used, or with variables which should not be used. + --easy: Do only the easy checks, which can be treated by non-speakers of + the language. + +Check codes (ideally, all of them should result 0; all the checks are executed +by default (except language-specific check blacklists in checkLanguage.inc): + * untranslated: Messages which are required to translate, but are not + translated. + * duplicate: Messages which translation equal to fallback. + * obsolete: Messages which are untranslatable or do not exist, but are + translated. + * variables: Messages without variables which should be used, or with + variables which should not be used. * empty: Empty messages and messages that contain only -. * whitespace: Messages which have trailing whitespace. - * xhtml: Messages which are not well-formed XHTML (checks only few common errors). + * xhtml: Messages which are not well-formed XHTML (checks only few common + errors). * chars: Messages with hidden characters. * links: Messages which contains broken links to pages (does not find all). - * unbalanced: Messages which contains unequal numbers of opening {[ and closing ]}. + * unbalanced: Messages which contains unequal numbers of opening {[ and + closing ]}. * namespace: Namespace names that were not translated. - * projecttalk: Namespace names and aliases where the project talk does not contain $1. + * projecttalk: Namespace names and aliases where the project talk does not + contain $1. * magic: Magic words that were not translated. * magic-old: Magic words which do not exist. * magic-over: Magic words that override the original English word. - * magic-case: Magic words whose translation changes the case-sensitivity of the original English word. + * magic-case: Magic words whose translation changes the case-sensitivity of + the original English word. * special: Special page names that were not translated. * special-old: Special page names which do not exist. @@ -291,6 +309,17 @@ ENDS; $this->results[$this->code] = $this->checkLanguage( $this->code ); } } + + $results = $this->results; + foreach ( $results as $code => $checks ) { + foreach ( $checks as $check => $messages ) { + foreach ( $messages as $key => $details ) { + if ( $this->isCheckBlacklisted( $check, $code, $key ) ) { + unset( $this->results[$code][$check][$key] ); + } + } + } + } } /** @@ -298,14 +327,58 @@ ENDS; * @return array The list of checks which should not be executed. */ protected function getCheckBlacklist() { + static $blacklist = null; + + if ( $blacklist !== null ) { + return $blacklist; + } + + // @codingStandardsIgnoreStart Ignore that globals should have a "wg" prefix. global $checkBlacklist; + // @codingStandardsIgnoreEnd + + $blacklist = $checkBlacklist; + + wfRunHooks( 'LocalisationChecksBlacklist', array( &$blacklist ) ); + + return $blacklist; + } + + /** + * Verify whether a check is blacklisted. + * + * @param string $check Check name + * @param string $code Language code + * @param string|bool $message Message name, or False for a whole language + * @return bool Whether the check is blacklisted + */ + protected function isCheckBlacklisted( $check, $code, $message ) { + $blacklist = $this->getCheckBlacklist(); + + foreach ( $blacklist as $item ) { + if ( isset( $item['check'] ) && $check !== $item['check'] ) { + continue; + } - return $checkBlacklist; + if ( isset( $item['code'] ) && !in_array( $code, $item['code'] ) ) { + continue; + } + + if ( isset( $item['message'] ) && + ( $message === false || !in_array( $message, $item['message'] ) ) + ) { + continue; + } + + return true; + } + + return false; } /** * Check a language. - * @param $code string The language code. + * @param string $code The language code. * @throws MWException * @return array The results. */ @@ -319,11 +392,8 @@ ENDS; } $checkFunctions = $this->getChecks(); - $checkBlacklist = $this->getCheckBlacklist(); foreach ( $this->checks as $check ) { - if ( isset( $checkBlacklist[$code] ) && - in_array( $check, $checkBlacklist[$code] ) - ) { + if ( $this->isCheckBlacklisted( $check, $code, false ) ) { $results[$check] = array(); continue; } @@ -340,8 +410,8 @@ ENDS; /** * Format a message key. - * @param $key string The message key. - * @param $code string The language code. + * @param string $key The message key. + * @param string $code The language code. * @return string The formatted message key. */ protected function formatKey( $key, $code ) { @@ -407,7 +477,8 @@ ENDS; */ function outputWiki() { $detailText = ''; - $rows[] = '! Language !! Code !! Total !! ' . implode( ' !! ', array_diff( $this->checks, $this->nonMessageChecks() ) ); + $rows[] = '! Language !! Code !! Total !! ' . + implode( ' !! ', array_diff( $this->checks, $this->nonMessageChecks() ) ); foreach ( $this->results as $code => $results ) { $detailTextForLang = "==$code==\n"; $numbers = array(); @@ -447,6 +518,7 @@ ENDS; $tableRows = implode( "\n|-\n", $rows ); $version = SpecialVersion::getVersion( 'nodb' ); + // @codingStandardsIgnoreStart Long line. echo <<$version @@ -458,6 +530,7 @@ $tableRows $detailText EOL; + // @codingStandardsIgnoreEnd } /** @@ -485,8 +558,8 @@ class CheckExtensionsCLI extends CheckLanguageCLI { /** * Constructor. - * @param $options array Options for script. - * @param $extension string The extension name (or names). + * @param array $options Options for script. + * @param string $extension The extension name (or names). */ public function __construct( array $options, $extension ) { if ( isset( $options['help'] ) ) { @@ -539,19 +612,19 @@ class CheckExtensionsCLI extends CheckLanguageCLI { if ( $extension == 'all' ) { foreach ( MessageGroups::singleton()->getGroups() as $group ) { if ( strpos( $group->getId(), 'ext-' ) === 0 && !$group->isMeta() ) { - $this->extensions[] = new extensionLanguages( $group ); + $this->extensions[] = new ExtensionLanguages( $group ); } } } elseif ( $extension == 'wikimedia' ) { $wikimedia = MessageGroups::getGroup( 'ext-0-wikimedia' ); foreach ( $wikimedia->wmfextensions() as $extension ) { $group = MessageGroups::getGroup( $extension ); - $this->extensions[] = new extensionLanguages( $group ); + $this->extensions[] = new ExtensionLanguages( $group ); } } elseif ( $extension == 'flaggedrevs' ) { foreach ( MessageGroups::singleton()->getGroups() as $group ) { if ( strpos( $group->getId(), 'ext-flaggedrevs-' ) === 0 && !$group->isMeta() ) { - $this->extensions[] = new extensionLanguages( $group ); + $this->extensions[] = new ExtensionLanguages( $group ); } } } else { @@ -559,7 +632,7 @@ class CheckExtensionsCLI extends CheckLanguageCLI { foreach ( $extensions as $extension ) { $group = MessageGroups::getGroup( 'ext-' . $extension ); if ( $group ) { - $extension = new extensionLanguages( $group ); + $extension = new ExtensionLanguages( $group ); $this->extensions[] = $extension; } else { print "No such extension $extension.\n"; @@ -589,7 +662,7 @@ class CheckExtensionsCLI extends CheckLanguageCLI { /** * Get the checks that can easily be treated by non-speakers of the language. - * @return arrayA list of the easy checks. + * @return array A list of the easy checks. */ protected function easyChecks() { return array( @@ -603,34 +676,50 @@ class CheckExtensionsCLI extends CheckLanguageCLI { */ protected function help() { return <<