From d9022f63880ce039446fba8364f68e656b7bf4cb Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Thu, 3 May 2012 13:01:35 +0200 Subject: Update to MediaWiki 1.19.0 --- maintenance/Doxyfile | 180 ++-- maintenance/Maintenance.php | 102 +-- maintenance/Makefile | 7 + maintenance/Site.php | 19 - maintenance/addwiki.php | 479 ---------- maintenance/archives/patch-ar_sha1.sql | 3 + maintenance/archives/patch-drop-user_options.sql | 1 + maintenance/archives/patch-jobs-add-timestamp.sql | 2 + .../archives/patch-logging-type-action-index.sql | 1 + .../archives/patch-page_redirect_namespace_len.sql | 6 + maintenance/archives/patch-rev_sha1.sql | 3 + maintenance/archives/patch-trackbacks.sql | 10 - .../archives/patch-ufg_group-length-increase.sql | 2 + .../archives/patch-ug_group-length-increase.sql | 2 + maintenance/archives/patch-uploadstash_chunk.sql | 3 + maintenance/archives/patch-user_former_groups.sql | 2 +- maintenance/attachLatest.php | 4 +- maintenance/backup.inc | 14 +- maintenance/backupPrefetch.inc | 5 + maintenance/benchmarks/Benchmarker.php | 3 +- maintenance/benchmarks/bench_HTTP_HTTPS.php | 4 +- maintenance/benchmarks/bench_delete_truncate.php | 4 + maintenance/benchmarks/bench_if_switch.php | 4 +- maintenance/benchmarks/bench_strtr_str_replace.php | 4 + maintenance/benchmarks/bench_wfIsWindows.php | 4 +- maintenance/benchmarks/benchmarkHooks.php | 80 ++ maintenance/benchmarks/benchmarkPurge.php | 6 +- maintenance/changePassword.php | 2 +- maintenance/checkImages.php | 4 +- maintenance/checkSyntax.php | 10 +- maintenance/cleanupCaps.php | 13 +- maintenance/cleanupImages.php | 17 +- maintenance/cleanupSpam.php | 28 +- maintenance/cleanupTable.inc | 2 +- maintenance/cleanupTitles.php | 3 +- maintenance/cleanupUploadStash.php | 34 +- maintenance/clear_stats.php | 3 +- maintenance/commandLine.inc | 12 +- maintenance/convertUserOptions.php | 33 +- maintenance/deleteBatch.php | 39 +- maintenance/deleteDefaultMessages.php | 18 +- maintenance/dev/README | 7 + maintenance/dev/includes/php.sh | 12 + maintenance/dev/includes/require-php.sh | 8 + maintenance/dev/includes/router.php | 82 ++ maintenance/dev/install.sh | 8 + maintenance/dev/installmw.sh | 18 + maintenance/dev/installphp.sh | 57 ++ maintenance/dev/start.sh | 14 + maintenance/doMaintenance.php | 22 +- maintenance/dtrace/counts.d | 23 - maintenance/dtrace/tree.d | 26 - maintenance/dumpBackup.php | 2 +- maintenance/dumpHTML.php | 7 - maintenance/dumpInterwiki.php | 251 ------ maintenance/dumpLinks.php | 2 +- maintenance/dumpTextPass.php | 52 +- maintenance/edit.php | 4 +- maintenance/eval.php | 13 +- maintenance/fetchText.php | 2 +- maintenance/findHooks.php | 34 +- maintenance/fixExtLinksProtocolRelative.php | 4 +- maintenance/formatInstallDoc.php | 3 + maintenance/fuzz-tester.php | 165 ++-- maintenance/gearman/gearman.inc | 104 --- maintenance/gearman/gearmanRefreshLinks.php | 45 - maintenance/gearman/gearmanWorker.php | 43 - maintenance/generateSitemap.php | 31 +- maintenance/getSlaveServer.php | 14 +- maintenance/hiphop/extra-files | 1 - maintenance/hiphop/make | 3 + maintenance/ibm_db2/foreignkeys.sql | 5 - maintenance/ibm_db2/tables.sql | 984 +++++++++++++-------- maintenance/importDump.php | 32 +- maintenance/importImages.php | 390 ++++---- maintenance/importSiteScripts.php | 22 +- maintenance/importTextFile.php | 4 +- maintenance/importUseModWiki.php | 375 -------- maintenance/importUseModWikipedia.php | 892 ------------------- maintenance/install.php | 1 + maintenance/jsparse.php | 1 - maintenance/language/StatOutputs.php | 4 +- maintenance/language/checkLanguage.inc | 10 +- maintenance/language/function-list.php | 63 -- maintenance/language/generateCollationData.php | 23 +- maintenance/language/languages.inc | 112 +-- maintenance/language/messageTypes.inc | 33 +- maintenance/language/messages.inc | 280 ++++-- maintenance/language/transstat.php | 4 +- maintenance/language/writeMessagesArray.inc | 3 + maintenance/locking/LockServerDaemon.php | 617 +++++++++++++ maintenance/locking/file_locks.sql | 11 + maintenance/mctest.php | 5 +- maintenance/mergeMessageFileList.php | 79 +- maintenance/moveBatch.php | 3 + maintenance/mssql/tables.sql | 10 - maintenance/mwdocgen.php | 69 +- maintenance/namespaceDupes.php | 20 + maintenance/nextJobDB.php | 10 +- maintenance/nukeNS.php | 6 +- .../oracle/archives/patch-ar_sha1_field.sql | 3 + maintenance/oracle/archives/patch-config.sql | 8 - .../oracle/archives/patch-job_timestamp_field.sql | 4 + .../oracle/archives/patch-job_timestamp_index.sql | 4 + .../archives/patch-logging_type_action_index.sql | 4 + .../archives/patch-page_redirect_namespace_len.sql | 4 + .../oracle/archives/patch-rev_sha1_field.sql | 4 + .../archives/patch-ug_group-length-increase.sql | 3 + .../oracle/archives/patch-us_chunk_inx_field.sql | 4 + .../oracle/archives/patch_fk_rename_deferred.sql | 1 - .../oracle/archives/patch_rebuild_dupfunc.sql | 11 +- .../archives/patch_remove_not_null_empty_defs2.sql | 3 + maintenance/oracle/tables.sql | 75 +- maintenance/orphans.php | 11 +- maintenance/ourusers.php | 70 -- maintenance/parse.php | 110 +++ maintenance/populateCategory.php | 2 - maintenance/populateImageSha1.php | 121 +++ maintenance/populateLogSearch.php | 42 +- maintenance/populateLogUsertext.php | 29 +- maintenance/populateParentId.php | 39 +- maintenance/populateRevisionLength.php | 44 +- maintenance/populateRevisionSha1.php | 185 ++++ maintenance/populateSha1.php | 99 --- maintenance/postgres/mediawiki_mysql2postgres.pl | 2 - maintenance/postgres/tables.sql | 23 +- maintenance/protect.php | 25 +- maintenance/proxy_check.php | 1 + maintenance/pruneFileCache.php | 105 +++ maintenance/purgeDeletedFiles.php | 89 ++ maintenance/purgeList.php | 2 +- maintenance/purgeParserCache.php | 26 +- maintenance/purgeStaleMemcachedText.php | 4 + maintenance/reassignEdits.php | 7 +- maintenance/rebuildFileCache.php | 67 +- maintenance/rebuildImages.php | 26 +- maintenance/rebuildInterwiki.php | 275 ------ maintenance/rebuildrecentchanges.php | 4 +- maintenance/rebuildtextindex.php | 2 +- maintenance/refreshImageMetadata.php | 12 +- maintenance/refreshLinks.php | 15 +- maintenance/removeUnusedAccounts.php | 2 +- maintenance/renamewiki.php | 89 -- maintenance/rollbackEdits.php | 9 +- maintenance/runBatchedQuery.php | 1 - maintenance/runJobs.php | 6 +- maintenance/showJobs.php | 2 +- maintenance/showStats.php | 3 +- maintenance/sql.php | 74 +- maintenance/sqlite.php | 4 +- maintenance/sqlite/archives/initial-indexes.sql | 5 +- .../sqlite/archives/patch-drop-user_options.sql | 31 + .../sqlite/archives/patch-jobs-add-timestamp.sql | 2 + .../archives/patch-page_redirect_namespace_len.sql | 7 + .../archives/patch-ufg_group-length-increase.sql | 15 + .../archives/patch-ug_group-length-increase.sql | 15 + maintenance/stats.php | 4 +- maintenance/storage/checkStorage.php | 39 +- maintenance/storage/compressOld.php | 19 +- maintenance/storage/fixBug20757.php | 3 + maintenance/storage/recompressTracked.php | 7 + maintenance/tables.sql | 54 +- maintenance/term/MWTerm.php | 50 ++ maintenance/undelete.php | 3 + maintenance/update.php | 6 + maintenance/updateCollation.php | 8 +- maintenance/upgrade1_5.php | 14 +- maintenance/userOptions.inc | 34 +- maintenance/userOptions.php | 2 +- maintenance/wikipedia-interwiki.sql | 289 ------ maintenance/wiktionary-interwiki.sql | 184 ---- 171 files changed, 3966 insertions(+), 4877 deletions(-) delete mode 100644 maintenance/Site.php delete mode 100644 maintenance/addwiki.php create mode 100644 maintenance/archives/patch-ar_sha1.sql create mode 100644 maintenance/archives/patch-drop-user_options.sql create mode 100644 maintenance/archives/patch-jobs-add-timestamp.sql create mode 100644 maintenance/archives/patch-logging-type-action-index.sql create mode 100644 maintenance/archives/patch-page_redirect_namespace_len.sql create mode 100644 maintenance/archives/patch-rev_sha1.sql delete mode 100644 maintenance/archives/patch-trackbacks.sql create mode 100644 maintenance/archives/patch-ufg_group-length-increase.sql create mode 100644 maintenance/archives/patch-ug_group-length-increase.sql create mode 100644 maintenance/archives/patch-uploadstash_chunk.sql create mode 100644 maintenance/benchmarks/benchmarkHooks.php create mode 100644 maintenance/dev/README create mode 100644 maintenance/dev/includes/php.sh create mode 100644 maintenance/dev/includes/require-php.sh create mode 100644 maintenance/dev/includes/router.php create mode 100644 maintenance/dev/install.sh create mode 100644 maintenance/dev/installmw.sh create mode 100644 maintenance/dev/installphp.sh create mode 100644 maintenance/dev/start.sh delete mode 100644 maintenance/dtrace/counts.d delete mode 100644 maintenance/dtrace/tree.d delete mode 100644 maintenance/dumpHTML.php delete mode 100644 maintenance/dumpInterwiki.php delete mode 100644 maintenance/gearman/gearman.inc delete mode 100644 maintenance/gearman/gearmanRefreshLinks.php delete mode 100644 maintenance/gearman/gearmanWorker.php delete mode 100644 maintenance/importUseModWiki.php delete mode 100644 maintenance/importUseModWikipedia.php delete mode 100644 maintenance/language/function-list.php create mode 100644 maintenance/locking/LockServerDaemon.php create mode 100644 maintenance/locking/file_locks.sql create mode 100644 maintenance/oracle/archives/patch-ar_sha1_field.sql delete mode 100644 maintenance/oracle/archives/patch-config.sql create mode 100644 maintenance/oracle/archives/patch-job_timestamp_field.sql create mode 100644 maintenance/oracle/archives/patch-job_timestamp_index.sql create mode 100644 maintenance/oracle/archives/patch-logging_type_action_index.sql create mode 100644 maintenance/oracle/archives/patch-page_redirect_namespace_len.sql create mode 100644 maintenance/oracle/archives/patch-rev_sha1_field.sql create mode 100644 maintenance/oracle/archives/patch-ug_group-length-increase.sql create mode 100644 maintenance/oracle/archives/patch-us_chunk_inx_field.sql create mode 100644 maintenance/oracle/archives/patch_remove_not_null_empty_defs2.sql delete mode 100644 maintenance/ourusers.php create mode 100644 maintenance/parse.php create mode 100644 maintenance/populateImageSha1.php create mode 100644 maintenance/populateRevisionSha1.php delete mode 100644 maintenance/populateSha1.php create mode 100644 maintenance/pruneFileCache.php create mode 100644 maintenance/purgeDeletedFiles.php delete mode 100644 maintenance/rebuildInterwiki.php delete mode 100644 maintenance/renamewiki.php create mode 100644 maintenance/sqlite/archives/patch-drop-user_options.sql create mode 100644 maintenance/sqlite/archives/patch-jobs-add-timestamp.sql create mode 100644 maintenance/sqlite/archives/patch-page_redirect_namespace_len.sql create mode 100644 maintenance/sqlite/archives/patch-ufg_group-length-increase.sql create mode 100644 maintenance/sqlite/archives/patch-ug_group-length-increase.sql create mode 100644 maintenance/term/MWTerm.php delete mode 100644 maintenance/wikipedia-interwiki.sql delete mode 100644 maintenance/wiktionary-interwiki.sql (limited to 'maintenance') diff --git a/maintenance/Doxyfile b/maintenance/Doxyfile index 7d7849c8..b7c1e5e8 100644 --- a/maintenance/Doxyfile +++ b/maintenance/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.5.6 +# Doxyfile 1.7.5.1 # # Some placeholders have been added for MediaWiki usage: @@ -8,14 +8,16 @@ # {{SVNSTAT}} # {{INPUT}} # -# A number of MediaWiki-specific aliases are near the end of this file. # To generate documentation run: php mwdocgen.php --no-extensions #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = MediaWiki PROJECT_NUMBER = {{CURRENT_VERSION}} +PROJECT_BRIEF = +PROJECT_LOGO = OUTPUT_DIRECTORY = {{OUTPUT_DIRECTORY}} CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English @@ -36,18 +38,47 @@ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = {{STRIP_FROM_PATH}} -STRIP_FROM_INC_PATH = +STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 8 +ALIASES = "type{1}= \1 :" \ + "types{2}= \1 or \2 :" \ + "types{3}= \1 , \2 , or \3 :" \ + "arrayof{2}= Array of \2" \ + "null=\type{Null}" \ + "boolean=\type{Boolean}" \ + "bool=\type{Boolean}" \ + "integer=\type{Integer}" \ + "int=\type{Integer}" \ + "string=\type{String}" \ + "str=\type{String}" \ + "mixed=\type{Mixed}" \ + "access=\par Access:\n" \ + "private=\access private" \ + "protected=\access protected" \ + "public=\access public" \ + "copyright=\note" \ + "license=\note" OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = NO DISTRIBUTE_GROUP_DOC = YES SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -56,6 +87,7 @@ EXTRACT_PRIVATE = YES EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO @@ -64,20 +96,27 @@ INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES +FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES -SORT_BRIEF_DOCS = NO +SORT_BRIEF_DOCS = YES +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES -ENABLED_SECTIONS = +ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES -SHOW_DIRECTORIES = NO +SHOW_DIRECTORIES = YES +SHOW_FILES = YES SHOW_NAMESPACES = NO FILE_VERSION_FILTER = {{SVNSTAT}} +LAYOUT_FILE = +CITE_BIB_FILES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- @@ -87,11 +126,12 @@ WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" -WARN_LOGFILE = +WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = {{INPUT}} +INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.c \ *.cc \ *.cxx \ @@ -134,16 +174,18 @@ FILE_PATTERNS = *.c \ *.MM \ *.PY RECURSIVE = YES -EXCLUDE = {{EXCLUDE}} +EXCLUDE = {{EXCLUDE}} EXCLUDE_SYMLINKS = YES EXCLUDE_PATTERNS = LocalSettings.php AdminSettings.php StartProfiler.php .svn */.git/* {{EXCLUDE_PATTERNS}} -EXAMPLE_PATH = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO -IMAGE_PATH = -INPUT_FILTER = -FILTER_PATTERNS = +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- @@ -152,6 +194,7 @@ INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = YES USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- @@ -159,27 +202,58 @@ VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = NO COLS_IN_ALPHA_INDEX = 5 -IGNORE_PREFIX = +IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html -HTML_HEADER = -HTML_FOOTER = -HTML_STYLESHEET = +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = YES HTML_ALIGN_MEMBERS = YES +HTML_DYNAMIC_SECTIONS = NO +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +DOCSET_PUBLISHER_ID = org.doxygen.Publisher +DOCSET_PUBLISHER_NAME = Publisher GENERATE_HTMLHELP = NO -CHM_FILE = -HHC_LOCATION = +CHM_FILE = +HHC_LOCATION = GENERATE_CHI = NO +CHM_INDEX_ENCODING = BINARY_TOC = NO TOC_EXPAND = YES +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.doxygen.Project +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.doxygen.Project DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = YES +USE_INLINE_TREES = YES TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_RELPATH = http://www.mathjax.org/mathjax +MATHJAX_EXTENSIONS = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = YES #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- @@ -189,12 +263,15 @@ LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide -EXTRA_PACKAGES = -LATEX_HEADER = +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- @@ -202,12 +279,12 @@ GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO -RTF_STYLESHEET_FILE = -RTF_EXTENSIONS_FILE = +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- -GENERATE_MAN = NO +GENERATE_MAN = {{GENERATE_MAN}} MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO @@ -216,8 +293,8 @@ MAN_LINKS = NO #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml -XML_SCHEMA = -XML_DTD = +XML_SCHEMA = +XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output @@ -229,33 +306,38 @@ GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES -PERLMOD_MAKEVAR_PREFIX = +PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- -# Configuration options related to the preprocessor +# Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO SEARCH_INCLUDES = YES -INCLUDE_PATH = -INCLUDE_FILE_PATTERNS = -PREDEFINED = -EXPAND_AS_DEFINED = +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- -# Configuration::additions related to external references +# Configuration::additions related to external references #--------------------------------------------------------------------------- -TAGFILES = +TAGFILES = GENERATE_TAGFILE = {{OUTPUT_DIRECTORY}}/html/tagfile.xml ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- -# Configuration options related to the dot tool +# Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = NO +MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = {{HAVE_DOT}} +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES @@ -268,34 +350,14 @@ CALLER_GRAPH = YES GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png -DOT_PATH = -DOTFILE_DIRS = +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 1000 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES -#--------------------------------------------------------------------------- -# Configuration::additions related to the search engine -#--------------------------------------------------------------------------- -SEARCHENGINE = NO - -ALIASES = "type{1}= \1 :" \ - "types{2}= \1 or \2 :" \ - "types{3}= \1 , \2 , or \3 :" \ - "arrayof{2}= Array of \2" \ - "null=\type{Null}" \ - "boolean=\type{Boolean}" \ - "bool=\type{Boolean}" \ - "integer=\type{Integer}" \ - "int=\type{Integer}" \ - "string=\type{String}" \ - "str=\type{String}" \ - "mixed=\type{Mixed}" \ - "access=\par Access:\n" \ - "private=\access private" \ - "protected=\access protected" \ - "public=\access public" \ - "copyright=\note" \ - "license=\note" diff --git a/maintenance/Maintenance.php b/maintenance/Maintenance.php index 3618515a..082cf8be 100644 --- a/maintenance/Maintenance.php +++ b/maintenance/Maintenance.php @@ -20,6 +20,11 @@ * @defgroup Maintenance Maintenance */ +/** + * @defgroup MaintenanceArchive Maintenance archives + * @ingroup Maintenance + */ + // Define this so scripts can easily find doMaintenance.php define( 'RUN_MAINTENANCE_IF_MAIN', dirname( __FILE__ ) . '/doMaintenance.php' ); define( 'DO_MAINTENANCE', RUN_MAINTENANCE_IF_MAIN ); // original name, harmless @@ -130,14 +135,15 @@ abstract class Maintenance { */ public static function shouldExecute() { $bt = debug_backtrace(); - if ( count( $bt ) < 2 ) { + $count = count( $bt ); + if ( $count < 2 ) { return false; // sanity } if ( $bt[0]['class'] !== 'Maintenance' || $bt[0]['function'] !== 'shouldExecute' ) { return false; // last call should be to this function } - $includeFuncs = array( 'require_once', 'require', 'include' ); - for( $i=1; $i < count( $bt ); $i++ ) { + $includeFuncs = array( 'require_once', 'require', 'include', 'include_once' ); + for( $i=1; $i < $count; $i++ ) { if ( !in_array( $bt[$i]['function'], $includeFuncs ) ) { return false; // previous calls should all be "requires" } @@ -247,6 +253,20 @@ abstract class Maintenance { */ protected function setBatchSize( $s = 0 ) { $this->mBatchSize = $s; + + // If we support $mBatchSize, show the option. + // Used to be in addDefaultParams, but in order for that to + // work, subclasses would have to call this function in the constructor + // before they called parent::__construct which is just weird + // (and really wasn't done). + if ( $this->mBatchSize ) { + $this->addOption( 'batch-size', 'Run this many operations ' . + 'per batch, default: ' . $this->mBatchSize, false, true ); + if ( isset( $this->mParams['batch-size'] ) ) { + // This seems a little ugly... + $this->mDependantParameters['batch-size'] = $this->mParams['batch-size']; + } + } } /** @@ -299,8 +319,7 @@ abstract class Maintenance { } else { print( $out ); } - } - else { + } else { $out = preg_replace( '/\n\z/', '', $out ); $this->outputChanneled( $out, $channel ); } @@ -427,11 +446,7 @@ abstract class Maintenance { $this->addOption( 'dbuser', 'The DB user to use for this script', false, true ); $this->addOption( 'dbpass', 'The password to use for this script', false, true ); } - // If we support $mBatchSize, show the option - if ( $this->mBatchSize ) { - $this->addOption( 'batch-size', 'Run this many operations ' . - 'per batch, default: ' . $this->mBatchSize, false, true ); - } + # Save additional script dependant options to display # them separately in help $this->mDependantParameters = array_diff_key( $this->mParams, $this->mGenericParameters ); @@ -455,6 +470,9 @@ abstract class Maintenance { } } + /** + * @var $child Maintenance + */ $child = new $maintClass(); $child->loadParamsAndArgs( $this->mSelf, $this->mOptions, $this->mArgs ); if ( !is_null( $this->mDb ) ) { @@ -525,6 +543,7 @@ abstract class Maintenance { * to allow sysadmins to explicitly set one if they'd prefer to override * defaults (or for people using Suhosin which yells at you for trying * to disable the limits) + * @return string */ public function memoryLimit() { $limit = $this->getOption( 'memory-limit', 'max' ); @@ -851,6 +870,9 @@ abstract class Maintenance { $wgDBpassword = $wgDBadminpassword; if ( $wgDBservers ) { + /** + * @var $wgDBservers array + */ foreach ( $wgDBservers as $i => $server ) { $wgDBservers[$i]['user'] = $wgDBuser; $wgDBservers[$i]['password'] = $wgDBpassword; @@ -889,57 +911,6 @@ abstract class Maintenance { } } - /** - * Do setup specific to WMF - */ - public function loadWikimediaSettings() { - global $IP, $wgNoDBParam, $wgUseNormalUser, $wgConf, $site, $lang; - - if ( empty( $wgNoDBParam ) ) { - # Check if we were passed a db name - if ( isset( $this->mOptions['wiki'] ) ) { - $db = $this->mOptions['wiki']; - } else { - $db = array_shift( $this->mArgs ); - } - list( $site, $lang ) = $wgConf->siteFromDB( $db ); - - # If not, work out the language and site the old way - if ( is_null( $site ) || is_null( $lang ) ) { - if ( !$db ) { - $lang = 'aa'; - } else { - $lang = $db; - } - if ( isset( $this->mArgs[0] ) ) { - $site = array_shift( $this->mArgs ); - } else { - $site = 'wikipedia'; - } - } - } else { - $lang = 'aa'; - $site = 'wikipedia'; - } - - # This is for the IRC scripts, which now run as the apache user - # The apache user doesn't have access to the wikiadmin_pass command - if ( $_ENV['USER'] == 'apache' ) { - # if ( posix_geteuid() == 48 ) { - $wgUseNormalUser = true; - } - - putenv( 'wikilang=' . $lang ); - - ini_set( 'include_path', ".:$IP:$IP/includes:$IP/languages:$IP/maintenance" ); - - if ( $lang == 'test' && $site == 'wikipedia' ) { - if ( !defined( 'TESTWIKI' ) ) { - define( 'TESTWIKI', 1 ); - } - } - } - /** * Generic setup for most installs. Returns the location of LocalSettings * @return String @@ -1030,6 +1001,7 @@ abstract class Maintenance { /** * Get the maintenance directory. + * @return string */ protected function getDir() { return dirname( __FILE__ ); @@ -1177,6 +1149,7 @@ abstract class Maintenance { * Update the searchindex table for a given pageid * @param $dbw Database: a database write handle * @param $pageId Integer: the page ID to update. + * @return null|string */ public function updateSearchIndexForPage( $dbw, $pageId ) { // Get current revision @@ -1208,7 +1181,7 @@ abstract class Maintenance { } else { return posix_isatty( $fd ); } -} + } /** * Prompt the console for input @@ -1274,6 +1247,9 @@ abstract class Maintenance { } } +/** + * Fake maintenance wrapper, mostly used for the web installer/updater + */ class FakeMaintenance extends Maintenance { protected $mSelf = "FakeMaintenanceScript"; public function execute() { @@ -1347,4 +1323,4 @@ abstract class LoggedUpdateMaintenance extends Maintenance { * @return String */ abstract protected function getUpdateKey(); -} \ No newline at end of file +} diff --git a/maintenance/Makefile b/maintenance/Makefile index a92751c9..30b568dc 100644 --- a/maintenance/Makefile +++ b/maintenance/Makefile @@ -1,6 +1,7 @@ help: @echo "Run 'make test' to run the parser tests." @echo "Run 'make doc' to run the doxygen generation." + @echo "Run 'make man' to run the doxygen generation with man pages." test: php tests/parserTests.php --quiet @@ -8,3 +9,9 @@ test: doc: php mwdocgen.php --all @echo 'Doc generation done. Look at ./docs/html/' + +man: + php mwdocgen.php --all --generate-man + @echo 'Doc generation done. Look at ./docs/html/ and ./docs/man' + @echo 'You might want to update your MANPATH currently:' + @echo 'MANPATH: $(MANPATH)' diff --git a/maintenance/Site.php b/maintenance/Site.php deleted file mode 100644 index 87d637b4..00000000 --- a/maintenance/Site.php +++ /dev/null @@ -1,19 +0,0 @@ -suffix = $s; - $this->lateral = $l; - $this->url = $u; - } - - function getURL( $lang, $urlprotocol ) { - $xlang = str_replace( '_', '-', $lang ); - return "$urlprotocol//$xlang.{$this->url}/wiki/\$1"; - } -} diff --git a/maintenance/addwiki.php b/maintenance/addwiki.php deleted file mode 100644 index 43f42be5..00000000 --- a/maintenance/addwiki.php +++ /dev/null @@ -1,479 +0,0 @@ -mDescription = "Add a new wiki to the family. Wikimedia specific!"; - $this->addArg( 'language', 'Language code of new site, e.g. en' ); - $this->addArg( 'site', 'Type of site, e.g. wikipedia' ); - $this->addArg( 'dbname', 'Name of database to create, e.g. enwiki' ); - $this->addArg( 'domain', 'Domain name of the wiki, e.g. en.wikipedia.org' ); - - $wgNoDBParam = true; - } - - public function getDbType() { - return Maintenance::DB_ADMIN; - } - - public function execute() { - global $IP, $wgDefaultExternalStore, $wgVersionNumber; - if ( !$wgVersionNumber ) { // set in CommonSettings.php - $this->error( '$wgVersionNumber is not set, please use MWScript.php wrapper.', true ); - } - - $lang = $this->getArg( 0 ); - $site = $this->getArg( 1 ); - $dbName = $this->getArg( 2 ); - $domain = $this->getArg( 3 ); - $languageNames = Language::getLanguageNames(); - - if ( !isset( $languageNames[$lang] ) ) { - $this->error( "Language $lang not found in Names.php", true ); - } - $name = $languageNames[$lang]; - - $dbw = wfGetDB( DB_MASTER ); - $common = "/home/wikipedia/common"; - - $this->output( "Creating database $dbName for $lang.$site ($name)\n" ); - - # Set up the database - $dbw->query( "SET table_type=Innodb" ); - $dbw->query( "CREATE DATABASE $dbName" ); - $dbw->selectDB( $dbName ); - - $this->output( "Initialising tables\n" ); - $dbw->sourceFile( $this->getDir() . '/tables.sql' ); - $dbw->sourceFile( "$IP/extensions/OAI/update_table.sql" ); - $dbw->sourceFile( "$IP/extensions/AntiSpoof/sql/patch-antispoof.mysql.sql" ); - $dbw->sourceFile( "$IP/extensions/CheckUser/cu_changes.sql" ); - $dbw->sourceFile( "$IP/extensions/CheckUser/cu_log.sql" ); - $dbw->sourceFile( "$IP/extensions/TitleKey/titlekey.sql" ); - $dbw->sourceFile( "$IP/extensions/Oversight/hidden.sql" ); - $dbw->sourceFile( "$IP/extensions/GlobalBlocking/localdb_patches/setup-global_block_whitelist.sql" ); - $dbw->sourceFile( "$IP/extensions/AbuseFilter/abusefilter.tables.sql" ); - $dbw->sourceFile( "$IP/extensions/PrefStats/patches/PrefStats.sql" ); - $dbw->sourceFile( "$IP/extensions/ProofreadPage/ProofreadPage.sql" ); - $dbw->sourceFile( "$IP/extensions/ClickTracking/patches/ClickTrackingEvents.sql" ); - $dbw->sourceFile( "$IP/extensions/ClickTracking/patches/ClickTracking.sql" ); - $dbw->sourceFile( "$IP/extensions/UserDailyContribs/patches/UserDailyContribs.sql" ); - - $dbw->query( "INSERT INTO site_stats(ss_row_id) VALUES (1)" ); - - # Initialise external storage - if ( is_array( $wgDefaultExternalStore ) ) { - $stores = $wgDefaultExternalStore; - } elseif ( $wgDefaultExternalStore ) { - $stores = array( $wgDefaultExternalStore ); - } else { - $stores = array(); - } - if ( count( $stores ) ) { - global $wgDBuser, $wgDBpassword, $wgExternalServers; - foreach ( $stores as $storeURL ) { - $m = array(); - if ( !preg_match( '!^DB://(.*)$!', $storeURL, $m ) ) { - continue; - } - - $cluster = $m[1]; - $this->output( "Initialising external storage $cluster...\n" ); - - # Hack - $wgExternalServers[$cluster][0]['user'] = $wgDBuser; - $wgExternalServers[$cluster][0]['password'] = $wgDBpassword; - - $store = new ExternalStoreDB; - $extdb = $store->getMaster( $cluster ); - $extdb->query( "SET table_type=InnoDB" ); - $extdb->query( "CREATE DATABASE $dbName" ); - $extdb->selectDB( $dbName ); - - # Hack x2 - $blobsTable = $store->getTable( $extdb ); - $sedCmd = "sed s/blobs\\\\\\>/$blobsTable/ " . $this->getDir() . "/storage/blobs.sql"; - $blobsFile = popen( $sedCmd, 'r' ); - $extdb->sourceStream( $blobsFile ); - pclose( $blobsFile ); - $extdb->commit(); - } - } - - $title = Title::newFromText( wfMessage( 'mainpage' )->inLanguage( $lang )->useDatabase( false )->plain() ); - $this->output( "Writing main page to " . $title->getPrefixedDBkey() . "\n" ); - $article = new Article( $title ); - $ucsite = ucfirst( $site ); - - $article->doEdit( $this->getFirstArticle( $ucsite, $name ), '', EDIT_NEW | EDIT_AUTOSUMMARY ); - - $this->output( "Adding to dblists\n" ); - - # Add to dblist - $file = fopen( "$common/all.dblist", "a" ); - fwrite( $file, "$dbName\n" ); - fclose( $file ); - - # Update the sublists - shell_exec( "cd $common && ./refresh-dblist" ); - - # Add to wikiversions.dat - $file = fopen( "$common/wikiversions.dat", "a" ); - fwrite( $file, "$dbName php-$wgVersionNumber\n" ); - fclose( $file ); - # Rebuild wikiversions.cdb - shell_exec( "cd $common/multiversion && ./refreshWikiversionsCDB" ); - - # print "Constructing interwiki SQL\n"; - # Rebuild interwiki tables - # passthru( '/home/wikipedia/conf/interwiki/update' ); - - $time = wfTimestamp( TS_RFC2822 ); - // These arguments need to be escaped twice: once for echo and once for at - $escDbName = wfEscapeShellArg( wfEscapeShellArg( $dbName ) ); - $escTime = wfEscapeShellArg( wfEscapeShellArg( $time ) ); - $escUcsite = wfEscapeShellArg( wfEscapeShellArg( $ucsite ) ); - $escName = wfEscapeShellArg( wfEscapeShellArg( $name ) ); - $escLang = wfEscapeShellArg( wfEscapeShellArg( $lang ) ); - $escDomain = wfEscapeShellArg( wfEscapeShellArg( $domain ) ); - shell_exec( "echo notifyNewProjects $escDbName $escTime $escUcsite $escName $escLang $escDomain | at now + 15 minutes" ); - - $this->output( "Script ended. You still have to: - * Add any required settings in InitialiseSettings.php - * Run sync-common-all - * Run /home/wikipedia/conf/interwiki/update - " ); - } - - private function getFirstArticle( $ucsite, $name ) { - return << -[http://www.wikipedia.org Wikipedia] | -[http://www.wiktionary.org Wiktonary] | -[http://www.wikibooks.org Wikibooks] | -[http://www.wikinews.org Wikinews] | -[http://www.wikiquote.org Wikiquote] | -[http://www.wikisource.org Wikisource] -[http://www.wikiversity.org Wikiversity] - - -See Wikimedia's [[m:|Meta-Wiki]] for the coordination of these projects. - -[[aa:]] -[[ab:]] -[[ace:]] -[[af:]] -[[ak:]] -[[als:]] -[[am:]] -[[an:]] -[[ang:]] -[[ar:]] -[[arc:]] -[[arz:]] -[[as:]] -[[ast:]] -[[av:]] -[[ay:]] -[[az:]] -[[ba:]] -[[bar:]] -[[bat-smg:]] -[[bcl:]] -[[be:]] -[[be-x-old:]] -[[bg:]] -[[bh:]] -[[bi:]] -[[bm:]] -[[bn:]] -[[bo:]] -[[bpy:]] -[[br:]] -[[bs:]] -[[bug:]] -[[bxr:]] -[[ca:]] -[[cbk-zam:]] -[[cdo:]] -[[ce:]] -[[ceb:]] -[[ch:]] -[[cho:]] -[[chr:]] -[[chy:]] -[[ckb:]] -[[co:]] -[[cr:]] -[[crh:]] -[[cs:]] -[[csb:]] -[[cu:]] -[[cv:]] -[[cy:]] -[[da:]] -[[de:]] -[[diq:]] -[[dk:]] -[[dsb:]] -[[dv:]] -[[dz:]] -[[ee:]] -[[el:]] -[[eml:]] -[[en:]] -[[eo:]] -[[es:]] -[[et:]] -[[eu:]] -[[ext:]] -[[fa:]] -[[ff:]] -[[fi:]] -[[fiu-vro:]] -[[fj:]] -[[fo:]] -[[fr:]] -[[frp:]] -[[fur:]] -[[fy:]] -[[ga:]] -[[gan:]] -[[gd:]] -[[gl:]] -[[glk:]] -[[gn:]] -[[got:]] -[[gu:]] -[[gv:]] -[[ha:]] -[[hak:]] -[[haw:]] -[[he:]] -[[hi:]] -[[hif:]] -[[ho:]] -[[hr:]] -[[hsb:]] -[[ht:]] -[[hu:]] -[[hy:]] -[[hz:]] -[[ia:]] -[[id:]] -[[ie:]] -[[ig:]] -[[ii:]] -[[ik:]] -[[ilo:]] -[[io:]] -[[is:]] -[[it:]] -[[iu:]] -[[ja:]] -[[jbo:]] -[[jv:]] -[[ka:]] -[[kaa:]] -[[kab:]] -[[kg:]] -[[ki:]] -[[kj:]] -[[kk:]] -[[kl:]] -[[km:]] -[[kn:]] -[[ko:]] -[[kr:]] -[[ks:]] -[[ksh:]] -[[ku:]] -[[kv:]] -[[kw:]] -[[ky:]] -[[la:]] -[[lad:]] -[[lb:]] -[[lbe:]] -[[lg:]] -[[li:]] -[[lij:]] -[[lmo:]] -[[ln:]] -[[lo:]] -[[lt:]] -[[lv:]] -[[map-bms:]] -[[mdf:]] -[[mg:]] -[[mh:]] -[[mhr:]] -[[mi:]] -[[mk:]] -[[ml:]] -[[mn:]] -[[mo:]] -[[mr:]] -[[ms:]] -[[mt:]] -[[mus:]] -[[mwl:]] -[[my:]] -[[myv:]] -[[mzn:]] -[[na:]] -[[nan:]] -[[nap:]] -[[nds:]] -[[nds-nl:]] -[[ne:]] -[[new:]] -[[ng:]] -[[nl:]] -[[nn:]] -[[no:]] -[[nov:]] -[[nrm:]] -[[nv:]] -[[ny:]] -[[oc:]] -[[om:]] -[[or:]] -[[os:]] -[[pa:]] -[[pag:]] -[[pam:]] -[[pap:]] -[[pdc:]] -[[pi:]] -[[pih:]] -[[pl:]] -[[pms:]] -[[pnt:]] -[[pnb:]] -[[ps:]] -[[pt:]] -[[qu:]] -[[rm:]] -[[rmy:]] -[[rn:]] -[[ro:]] -[[roa-tara:]] -[[ru:]] -[[rup:]] -[[rw:]] -[[sa:]] -[[sah:]] -[[sc:]] -[[scn:]] -[[sco:]] -[[sd:]] -[[se:]] -[[sg:]] -[[sh:]] -[[si:]] -[[simple:]] -[[sk:]] -[[sl:]] -[[sm:]] -[[sn:]] -[[so:]] -[[sq:]] -[[sr:]] -[[srn:]] -[[ss:]] -[[st:]] -[[stq:]] -[[su:]] -[[sv:]] -[[sw:]] -[[szl:]] -[[ta:]] -[[te:]] -[[tet:]] -[[tg:]] -[[th:]] -[[ti:]] -[[tk:]] -[[tl:]] -[[tn:]] -[[to:]] -[[tpi:]] -[[tr:]] -[[ts:]] -[[tt:]] -[[tum:]] -[[tw:]] -[[ty:]] -[[udm:]] -[[ug:]] -[[uk:]] -[[ur:]] -[[uz:]] -[[ve:]] -[[vec:]] -[[vi:]] -[[vls:]] -[[vo:]] -[[wa:]] -[[war:]] -[[wo:]] -[[wuu:]] -[[xal:]] -[[xh:]] -[[yi:]] -[[yo:]] -[[za:]] -[[zea:]] -[[zh:]] -[[zh-classical:]] -[[zh-min-nan:]] -[[zh-yue:]] -[[zu:]] - -EOT; - } -} - -$maintClass = "AddWiki"; -require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/archives/patch-ar_sha1.sql b/maintenance/archives/patch-ar_sha1.sql new file mode 100644 index 00000000..1c7d8e91 --- /dev/null +++ b/maintenance/archives/patch-ar_sha1.sql @@ -0,0 +1,3 @@ +-- Adding ar_sha1 field +ALTER TABLE /*$wgDBprefix*/archive + ADD ar_sha1 varbinary(32) NOT NULL default ''; diff --git a/maintenance/archives/patch-drop-user_options.sql b/maintenance/archives/patch-drop-user_options.sql new file mode 100644 index 00000000..15b7d278 --- /dev/null +++ b/maintenance/archives/patch-drop-user_options.sql @@ -0,0 +1 @@ +ALTER TABLE /*_*/user DROP COLUMN user_options; \ No newline at end of file diff --git a/maintenance/archives/patch-jobs-add-timestamp.sql b/maintenance/archives/patch-jobs-add-timestamp.sql new file mode 100644 index 00000000..c5e6e711 --- /dev/null +++ b/maintenance/archives/patch-jobs-add-timestamp.sql @@ -0,0 +1,2 @@ +ALTER TABLE /*_*/job ADD COLUMN job_timestamp varbinary(14) NULL default NULL; +CREATE INDEX /*i*/job_timestamp ON /*_*/job(job_timestamp); diff --git a/maintenance/archives/patch-logging-type-action-index.sql b/maintenance/archives/patch-logging-type-action-index.sql new file mode 100644 index 00000000..5edc61a5 --- /dev/null +++ b/maintenance/archives/patch-logging-type-action-index.sql @@ -0,0 +1 @@ +CREATE INDEX /*i*/type_action ON /*_*/logging(log_type, log_action, log_timestamp); diff --git a/maintenance/archives/patch-page_redirect_namespace_len.sql b/maintenance/archives/patch-page_redirect_namespace_len.sql new file mode 100644 index 00000000..392945fb --- /dev/null +++ b/maintenance/archives/patch-page_redirect_namespace_len.sql @@ -0,0 +1,6 @@ +-- +-- Add the page_redirect_namespace_len index +-- + +CREATE INDEX /*i*/page_redirect_namespace_len ON /*_*/page (page_is_redirect, page_namespace, page_len); + diff --git a/maintenance/archives/patch-rev_sha1.sql b/maintenance/archives/patch-rev_sha1.sql new file mode 100644 index 00000000..0100c365 --- /dev/null +++ b/maintenance/archives/patch-rev_sha1.sql @@ -0,0 +1,3 @@ +-- Adding rev_sha1 field +ALTER TABLE /*$wgDBprefix*/revision + ADD rev_sha1 varbinary(32) NOT NULL default ''; diff --git a/maintenance/archives/patch-trackbacks.sql b/maintenance/archives/patch-trackbacks.sql deleted file mode 100644 index e3a47aae..00000000 --- a/maintenance/archives/patch-trackbacks.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TABLE /*$wgDBprefix*/trackbacks ( - tb_id INTEGER AUTO_INCREMENT PRIMARY KEY, - tb_page INTEGER REFERENCES page(page_id) ON DELETE CASCADE, - tb_title VARCHAR(255) NOT NULL, - tb_url BLOB NOT NULL, - tb_ex TEXT, - tb_name VARCHAR(255), - - INDEX (tb_page) -); diff --git a/maintenance/archives/patch-ufg_group-length-increase.sql b/maintenance/archives/patch-ufg_group-length-increase.sql new file mode 100644 index 00000000..e24cba02 --- /dev/null +++ b/maintenance/archives/patch-ufg_group-length-increase.sql @@ -0,0 +1,2 @@ +ALTER TABLE /*_*/user_former_groups + MODIFY COLUMN ufg_group varbinary(32) NOT NULL default ''; diff --git a/maintenance/archives/patch-ug_group-length-increase.sql b/maintenance/archives/patch-ug_group-length-increase.sql new file mode 100644 index 00000000..e944a858 --- /dev/null +++ b/maintenance/archives/patch-ug_group-length-increase.sql @@ -0,0 +1,2 @@ +ALTER TABLE /*_*/user_groups + MODIFY COLUMN ug_group varbinary(32) NOT NULL default ''; diff --git a/maintenance/archives/patch-uploadstash_chunk.sql b/maintenance/archives/patch-uploadstash_chunk.sql new file mode 100644 index 00000000..29e41870 --- /dev/null +++ b/maintenance/archives/patch-uploadstash_chunk.sql @@ -0,0 +1,3 @@ +-- Adding us_chunk_inx field +ALTER TABLE /*$wgDBprefix*/uploadstash + ADD us_chunk_inx int unsigned NULL; diff --git a/maintenance/archives/patch-user_former_groups.sql b/maintenance/archives/patch-user_former_groups.sql index ed18b2b6..ef56db06 100644 --- a/maintenance/archives/patch-user_former_groups.sql +++ b/maintenance/archives/patch-user_former_groups.sql @@ -3,7 +3,7 @@ CREATE TABLE /*_*/user_former_groups ( -- Key to user_id ufg_user int unsigned NOT NULL default 0, - ufg_group varbinary(16) NOT NULL default '' + ufg_group varbinary(32) NOT NULL default '' ) /*$wgDBTableOptions*/; CREATE UNIQUE INDEX /*i*/ufg_user_group ON /*_*/user_former_groups (ufg_user,ufg_group); diff --git a/maintenance/attachLatest.php b/maintenance/attachLatest.php index e6287f43..6e09671a 100644 --- a/maintenance/attachLatest.php +++ b/maintenance/attachLatest.php @@ -65,8 +65,8 @@ class AttachLatest extends Maintenance { $id = $revision->getId(); $this->output( wfWikiID() . " $pageId [[$name]] latest time $latestTime, rev id $id\n" ); if ( $this->hasOption( 'fix' ) ) { - $article = new Article( $title ); - $article->updateRevisionOn( $dbw, $revision ); + $page = WikiPage::factory( $title ); + $page->updateRevisionOn( $dbw, $revision ); } $n++; } diff --git a/maintenance/backup.inc b/maintenance/backup.inc index 4cb9c58a..9f67a1ac 100644 --- a/maintenance/backup.inc +++ b/maintenance/backup.inc @@ -47,6 +47,8 @@ class BackupDumper { 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; @@ -56,7 +58,14 @@ class BackupDumper { var $revCountLast = 0; var $ID = 0; - function BackupDumper( $args ) { + var $outputTypes = array(), $filterTypes = array(); + + /** + * @var LoadBalancer + */ + protected $lb; + + function __construct( $args ) { $this->stderr = fopen( "php://stderr", "wt" ); // Built-in output and filter plugins @@ -247,6 +256,7 @@ class BackupDumper { * @todo Fixme: the --server parameter is currently not respected, as it * doesn't seem terribly easy to ask the load balancer for a particular * connection by name. + * @return DatabaseBase */ function backupDb() { $this->lb = wfGetLBFactory()->newMainLB(); @@ -332,7 +342,7 @@ class BackupDumper { } class ExportProgressFilter extends DumpFilter { - function ExportProgressFilter( &$sink, &$progress ) { + function __construct( &$sink, &$progress ) { parent::__construct( $sink ); $this->progress = $progress; } diff --git a/maintenance/backupPrefetch.inc b/maintenance/backupPrefetch.inc index 93f75c65..0e12a1ce 100644 --- a/maintenance/backupPrefetch.inc +++ b/maintenance/backupPrefetch.inc @@ -133,6 +133,7 @@ class BaseDump { /** * @access private + * @return string */ function nextText() { $this->skipTo( 'text' ); @@ -141,6 +142,9 @@ class BaseDump { /** * @access private + * @param $name string + * @param $parent string + * @return bool|null */ function skipTo( $name, $parent = 'page' ) { if ( $this->atEnd ) { @@ -192,6 +196,7 @@ class BaseDump { /** * @access private + * @return null */ function close() { $this->reader->close(); diff --git a/maintenance/benchmarks/Benchmarker.php b/maintenance/benchmarks/Benchmarker.php index 57fb8759..0056c3c7 100644 --- a/maintenance/benchmarks/Benchmarker.php +++ b/maintenance/benchmarks/Benchmarker.php @@ -1,6 +1,7 @@ mDescription = 'Benchmark MediaWiki Hooks.'; + } + + public function execute() { + global $wgHooks; + $wgHooks['Test'] = array(); + + $time = $this->benchHooks(); + $this->output( 'Empty hook: ' . $time . "\n" ); + + $wgHooks['Test'][] = array( $this, 'test' ); + $time = $this->benchHooks(); + $this->output( 'Loaded (one) hook: ' . $time . "\n" ); + + 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++ ) { + $wgHooks['Test'][] = array( $this, 'test' ); + } + $time = $this->benchHooks(); + $this->output( 'Loaded (one hundred) hook: ' . $time . "\n" ); + $this->output( "\n" ); + } + + /** + * @param $trials int + * @return string + */ + private function benchHooks( $trials = 10 ) { + $start = wfTime(); + for ( $i = 0; $i < $trials; $i++ ) { + wfRunHooks( 'Test' ); + } + $delta = wfTime() - $start; + $pertrial = $delta / $trials; + return sprintf( "Took %6.2fs", + $pertrial ); + } + + /** + * @return bool + */ + public function test() { + return true; + } +} + +$maintClass = 'BenchmarkHooks'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/benchmarks/benchmarkPurge.php b/maintenance/benchmarks/benchmarkPurge.php index 4ab7aa10..e9d2ec7a 100644 --- a/maintenance/benchmarks/benchmarkPurge.php +++ b/maintenance/benchmarks/benchmarkPurge.php @@ -18,7 +18,7 @@ * http://www.gnu.org/copyleft/gpl.html * * @file - * @ingroup Maintenance + * @ingroup Benchmark */ require_once( dirname( __FILE__ ) . '/Benchmarker.php' ); @@ -54,6 +54,7 @@ class BenchmarkPurge extends Benchmarker { * to benchmark Squid response times. * @param $urls array A bunch of URLs to purge * @param $trials int How many times to run the test? + * @return string */ private function benchSquid( $urls, $trials = 1 ) { $start = wfTime(); @@ -70,6 +71,7 @@ class BenchmarkPurge extends Benchmarker { /** * Get an array of randomUrl()'s. * @param $length int How many urls to add to the array + * @return array */ private function randomUrlList( $length ) { $list = array(); @@ -82,6 +84,7 @@ class BenchmarkPurge extends Benchmarker { /** * Return a random URL of the wiki. Not necessarily an actual title in the * database, but at least a URL that looks like one. + * @return string */ private function randomUrl() { global $wgServer, $wgArticlePath; @@ -91,6 +94,7 @@ class BenchmarkPurge extends Benchmarker { /** * Create a random title string (not necessarily a Title object). * For use with randomUrl(). + * @return string */ private function randomTitle() { $str = ''; diff --git a/maintenance/changePassword.php b/maintenance/changePassword.php index ef87dfbd..cfcac406 100644 --- a/maintenance/changePassword.php +++ b/maintenance/changePassword.php @@ -43,7 +43,7 @@ class ChangePassword extends Maintenance { } else { $this->error( "A \"user\" or \"userid\" must be set to change the password for" , true ); } - if ( !$user->getId() ) { + if ( !$user || !$user->getId() ) { $this->error( "No such user: " . $this->getOption( 'user' ), true ); } try { diff --git a/maintenance/checkImages.php b/maintenance/checkImages.php index 96b93f22..484217d9 100644 --- a/maintenance/checkImages.php +++ b/maintenance/checkImages.php @@ -49,7 +49,9 @@ class CheckImages extends Maintenance { $this->output( "{$row->img_name}: not locally accessible\n" ); continue; } - $stat = @stat( $file->getPath() ); + wfSuppressWarnings(); + $stat = stat( $file->getPath() ); + wfRestoreWarnings(); if ( !$stat ) { $this->output( "{$row->img_name}: missing\n" ); continue; diff --git a/maintenance/checkSyntax.php b/maintenance/checkSyntax.php index 83f73be5..cc4e5af5 100644 --- a/maintenance/checkSyntax.php +++ b/maintenance/checkSyntax.php @@ -165,6 +165,8 @@ class CheckSyntax extends Maintenance { /** * Returns true if $file is of a type we can check + * @param $file string + * @return bool */ private function isSuitableFile( $file ) { $file = str_replace( '\\', '/', $file ); @@ -181,6 +183,8 @@ class CheckSyntax extends Maintenance { /** * Add given path to file list, searching it in include path if needed + * @param $path string + * @return bool */ private function addPath( $path ) { global $IP; @@ -188,8 +192,10 @@ class CheckSyntax extends Maintenance { } /** - * Add given file to file list, or, if it's a directory, add its content - */ + * Add given file to file list, or, if it's a directory, add its content + * @param $path string + * @return bool + */ private function addFileOrDir( $path ) { if ( is_dir( $path ) ) { $this->addDirectoryContent( $path ); diff --git a/maintenance/cleanupCaps.php b/maintenance/cleanupCaps.php index 2d945a52..6f8e180c 100644 --- a/maintenance/cleanupCaps.php +++ b/maintenance/cleanupCaps.php @@ -40,11 +40,15 @@ class CapsCleanup extends TableCleanup { public function execute() { global $wgCapitalLinks, $wgUser; + + if ( $wgCapitalLinks ) { + $this->error( "\$wgCapitalLinks is on -- no need for caps links cleanup.", true ); + } + + $wgUser = User::newFromName( 'Conversion script' ); + $this->namespace = intval( $this->getOption( 'namespace', 0 ) ); $this->dryrun = $this->hasOption( 'dry-run' ); - $wgUser->setName( 'Conversion script' ); - if ( $wgCapitalLinks ) - $this->error( "\$wgCapitalLinks is on -- no need for caps links cleanup.", true ); $this->runTable( array( 'table' => 'page', @@ -88,9 +92,8 @@ class CapsCleanup extends TableCleanup { return $this->processRow( $row ); } } - } else { - $this->progress( 0 ); } + return $this->progress( 0 ); } } diff --git a/maintenance/cleanupImages.php b/maintenance/cleanupImages.php index c8e90958..81d1c85b 100644 --- a/maintenance/cleanupImages.php +++ b/maintenance/cleanupImages.php @@ -73,8 +73,9 @@ class ImageCleanup extends TableCleanup { if ( is_null( $title ) ) { $this->output( "page $source ($cleaned) is illegal.\n" ); $safe = $this->buildSafeTitle( $cleaned ); - if ( $safe === false ) + if ( $safe === false ) { return $this->progress( 0 ); + } $this->pokeFile( $source, $safe ); return $this->progress( 1 ); } @@ -86,9 +87,12 @@ class ImageCleanup extends TableCleanup { return $this->progress( 1 ); } - $this->progress( 0 ); + return $this->progress( 0 ); } + /** + * @param $name string + */ private function killRow( $name ) { if ( $this->dryrun ) { $this->output( "DRY RUN: would delete bogus row '$name'\n" ); @@ -120,7 +124,8 @@ class ImageCleanup extends TableCleanup { $path = $this->filePath( $orig ); if ( !file_exists( $path ) ) { $this->output( "missing file: $path\n" ); - return $this->killRow( $orig ); + $this->killRow( $orig ); + return; } $db = wfGetDB( DB_MASTER ); @@ -135,7 +140,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" ); @@ -166,8 +171,8 @@ class ImageCleanup extends TableCleanup { __METHOD__ ); $dir = dirname( $finalPath ); if ( !file_exists( $dir ) ) { - if ( !wfMkdirParents( $dir ) ) { - $this->log( "RENAME FAILED, COULD NOT CREATE $dir" ); + if ( !wfMkdirParents( $dir, null, __METHOD__ ) ) { + $this->output( "RENAME FAILED, COULD NOT CREATE $dir" ); $db->rollback(); return; } diff --git a/maintenance/cleanupSpam.php b/maintenance/cleanupSpam.php index 8561281d..ca1e302b 100644 --- a/maintenance/cleanupSpam.php +++ b/maintenance/cleanupSpam.php @@ -36,6 +36,9 @@ class CleanupSpam extends Maintenance { $username = wfMsg( 'spambot_username' ); $wgUser = User::newFromName( $username ); + if ( !$wgUser ) { + $this->error( "Invalid username", true ); + } // Create the user if necessary if ( !$wgUser->getId() ) { $wgUser->addToDatabase(); @@ -91,36 +94,29 @@ class CleanupSpam extends Maintenance { $this->output( $title->getPrefixedDBkey() . " ..." ); $rev = Revision::newFromTitle( $title ); - $revId = $rev->getId(); - $currentRevId = $revId; + $currentRevId = $rev->getId(); - while ( $rev && LinkFilter::matchEntry( $rev->getText() , $domain ) ) { - # Revision::getPrevious can't be used in this way before MW 1.6 (Revision.php 1.26) - # $rev = $rev->getPrevious(); - $revId = $title->getPreviousRevisionID( $revId ); - if ( $revId ) { - $rev = Revision::newFromTitle( $title, $revId ); - } else { - $rev = false; - } + while ( $rev && ( $rev->isDeleted( Revision::DELETED_TEXT ) || LinkFilter::matchEntry( $rev->getText() , $domain ) ) ) { + $rev = $rev->getPrevious(); } - if ( $revId == $currentRevId ) { + + if ( $rev && $rev->getId() == $currentRevId ) { // The regex didn't match the current article text // This happens e.g. when a link comes from a template rather than the page itself $this->output( "False match\n" ); } else { $dbw = wfGetDB( DB_MASTER ); $dbw->begin(); + $page = WikiPage::factory( $title ); if ( !$rev ) { // Didn't find a non-spammy revision, blank the page $this->output( "blanking\n" ); - $article = new Article( $title ); - $article->doEdit( '', wfMsg( 'spam_blanking', $domain ) ); + $page->doEdit( '', wfMsgForContent( 'spam_blanking', $domain ) ); } else { // Revert to this revision $this->output( "reverting\n" ); - $article = new Article( $title ); - $article->doEdit( $rev->getText(), wfMsg( 'spam_reverting', $domain ), EDIT_UPDATE ); + $page->doEdit( $rev->getText(), wfMsgForContent( 'spam_reverting', $domain ), + EDIT_UPDATE, $rev->getId() ); } $dbw->commit(); } diff --git a/maintenance/cleanupTable.inc b/maintenance/cleanupTable.inc index f63c6d74..1c279762 100644 --- a/maintenance/cleanupTable.inc +++ b/maintenance/cleanupTable.inc @@ -43,7 +43,7 @@ class TableCleanup extends Maintenance { public function execute() { global $wgUser; - $wgUser->setName( 'Conversion script' ); + $wgUser = User::newFromName( 'Conversion script' ); $this->dryrun = $this->hasOption( 'dry-run' ); if ( $this->dryrun ) { $this->output( "Checking for bad titles...\n" ); diff --git a/maintenance/cleanupTitles.php b/maintenance/cleanupTitles.php index f03b7957..4fc6415e 100644 --- a/maintenance/cleanupTitles.php +++ b/maintenance/cleanupTitles.php @@ -145,8 +145,7 @@ class TitleCleanup extends TableCleanup { ), array( 'page_id' => $row->page_id ), __METHOD__ ); - $linkCache = LinkCache::singleton(); - $linkCache->clear(); + LinkCache::singleton()->clear(); } } } diff --git a/maintenance/cleanupUploadStash.php b/maintenance/cleanupUploadStash.php index 1926c05a..5f57ffdf 100644 --- a/maintenance/cleanupUploadStash.php +++ b/maintenance/cleanupUploadStash.php @@ -32,44 +32,52 @@ class UploadStashCleanup extends Maintenance { public function __construct() { parent::__construct(); $this->mDescription = "Clean up abandoned files in temporary uploaded file stash"; - } + } - public function execute() { + public function execute() { $repo = RepoGroup::singleton()->getLocalRepo(); - + $dbr = $repo->getSlaveDb(); - + + // how far back should this look for files to delete? + global $wgUploadStashMaxAge; + $this->output( "Getting list of files to clean up...\n" ); $res = $dbr->select( 'uploadstash', 'us_key', - 'us_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( time() - UploadStash::REPO_AGE * 3600 ) ), + 'us_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( time() - $wgUploadStashMaxAge ) ), __METHOD__ ); - + if( !is_object( $res ) || $res->numRows() == 0 ) { + $this->output( "No files to cleanup!\n" ); // nothing to do. - return false; + return; } // finish the read before starting writes. $keys = array(); - foreach($res as $row) { + foreach( $res as $row ) { array_push( $keys, $row->us_key ); } - + $this->output( 'Removing ' . count($keys) . " file(s)...\n" ); // this could be done some other, more direct/efficient way, but using // UploadStash's own methods means it's less likely to fall accidentally // out-of-date someday $stash = new UploadStash( $repo ); - + foreach( $keys as $key ) { - $stash->getFile( $key, true ); - $stash->removeFileNoAuth( $key ); + try { + $stash->getFile( $key, true ); + $stash->removeFileNoAuth( $key ); + } catch ( UploadStashBadPathException $ex ) { + $this->output( "Failed removing stashed upload with key: $key\n" ); + } } } } $maintClass = "UploadStashCleanup"; -require_once( RUN_MAINTENANCE_IF_MAIN ); \ No newline at end of file +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/clear_stats.php b/maintenance/clear_stats.php index 8f91864e..61314e67 100644 --- a/maintenance/clear_stats.php +++ b/maintenance/clear_stats.php @@ -36,7 +36,6 @@ class clear_stats extends Maintenance { $wgMemc->delete( "$db:stats:request_with_session" ); $wgMemc->delete( "$db:stats:request_without_session" ); $wgMemc->delete( "$db:stats:pcache_hit" ); - $wgMemc->delete( "$db:stats:pcache_miss_invalid" ); $wgMemc->delete( "$db:stats:pcache_miss_expired" ); $wgMemc->delete( "$db:stats:pcache_miss_absent" ); $wgMemc->delete( "$db:stats:pcache_miss_stub" ); @@ -46,6 +45,8 @@ class clear_stats extends Maintenance { $wgMemc->delete( "$db:stats:diff_cache_hit" ); $wgMemc->delete( "$db:stats:diff_cache_miss" ); $wgMemc->delete( "$db:stats:diff_uncacheable" ); + $wgMemc->delete( "$db:stats:job-insert" ); + $wgMemc->delete( "$db:stats:job-pop" ); } } } diff --git a/maintenance/commandLine.inc b/maintenance/commandLine.inc index f57c0b67..c7adbfbc 100644 --- a/maintenance/commandLine.inc +++ b/maintenance/commandLine.inc @@ -21,7 +21,7 @@ * @ingroup Maintenance */ -require( dirname( __FILE__ ) . '/Maintenance.php' ); +require_once( dirname( __FILE__ ) . '/Maintenance.php' ); global $optionsWithArgs; if ( !isset( $optionsWithArgs ) ) { @@ -37,19 +37,13 @@ class CommandLineInc extends Maintenance { } } - public function getDbType() { - global $wgUseNormalUser; - - return ( isset( $wgUseNormalUser ) && $wgUseNormalUser ) ? - Maintenance::DB_STD : Maintenance::DB_ADMIN; - } - /** * No help, it would just be misleading since it misses custom options */ protected function maybeHelp( $force = false ) { - if ( !$force ) + if ( !$force ) { return; + } parent::maybeHelp( true ); } diff --git a/maintenance/convertUserOptions.php b/maintenance/convertUserOptions.php index f46f710d..da6ff9b6 100644 --- a/maintenance/convertUserOptions.php +++ b/maintenance/convertUserOptions.php @@ -33,27 +33,38 @@ class ConvertUserOptions extends Maintenance { } public function execute() { - $this->output( "Beginning batch conversion of user options.\n" ); + $this->output( "...batch conversion of user_options: " ); $id = 0; $dbw = wfGetDB( DB_MASTER ); + 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 + $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' ) ); + array( $optCond, $idCond ), __METHOD__, + array( 'LIMIT' => 50, 'FOR UPDATE' ) + ); $id = $this->convertOptionBatch( $res, $dbw ); $dbw->commit(); wfWaitForSlaves(); - if ( $id ) + if ( $id ) { $this->output( "--Converted to ID $id\n" ); + } } - $this->output( "Conversion done. Converted " . $this->mConversionCount . " user records.\n" ); + $this->output( "done. Converted " . $this->mConversionCount . " user records.\n" ); } + /** + * @param $res + * @param $dbw DatabaseBase + * @return null|int + */ function convertOptionBatch( $res, $dbw ) { $id = null; foreach ( $res as $row ) { @@ -62,6 +73,14 @@ class ConvertUserOptions extends Maintenance { $u = User::newFromRow( $row ); $u->saveSettings(); + + // Do this here as saveSettings() doesn't set user_options to '' anymore! + $dbw->update( + 'user', + array( 'user_options' => '' ), + array( 'user_id' => $row->user_id ), + __METHOD__ + ); $id = $row->user_id; } diff --git a/maintenance/deleteBatch.php b/maintenance/deleteBatch.php index eb2e1f34..56fe13a4 100644 --- a/maintenance/deleteBatch.php +++ b/maintenance/deleteBatch.php @@ -49,9 +49,16 @@ class DeleteBatch extends Maintenance { chdir( $oldCwd ); # Options processing - $user = $this->getOption( 'u', 'Delete page script' ); + $username = $this->getOption( 'u', 'Delete page script' ); $reason = $this->getOption( 'r', '' ); $interval = $this->getOption( 'i', 0 ); + + $user = User::newFromName( $username ); + if ( !$user ) { + $this->error( "Invalid username", true ); + } + $wgUser = $user; + if ( $this->hasArg() ) { $file = fopen( $this->getArg(), 'r' ); } else { @@ -62,7 +69,7 @@ class DeleteBatch extends Maintenance { if ( !$file ) { $this->error( "Unable to read file, exiting", true ); } - $wgUser = User::newFromName( $user ); + $dbw = wfGetDB( DB_MASTER ); # Handle each entry @@ -71,31 +78,27 @@ class DeleteBatch extends Maintenance { if ( $line == '' ) { continue; } - $page = Title::newFromText( $line ); - if ( is_null( $page ) ) { + $title = Title::newFromText( $line ); + if ( is_null( $title ) ) { $this->output( "Invalid title '$line' on line $linenum\n" ); continue; } - if ( !$page->exists() ) { + if ( !$title->exists() ) { $this->output( "Skipping nonexistent page '$line'\n" ); continue; } - - $this->output( $page->getPrefixedText() ); + $this->output( $title->getPrefixedText() ); $dbw->begin(); - if ( $page->getNamespace() == NS_FILE ) { - $art = new ImagePage( $page ); - $img = wfFindFile( $art->mTitle ); - if ( !$img - || !$img->isLocal() - || !$img->delete( $reason ) ) { - $this->output( " FAILED to delete image file... " ); + if ( $title->getNamespace() == NS_FILE ) { + $img = wfFindFile( $title ); + if ( $img && $img->isLocal() && !$img->delete( $reason ) ) { + $this->output( " FAILED to delete associated file... " ); } - } else { - $art = new Article( $page ); } - $success = $art->doDeleteArticle( $reason ); + $page = WikiPage::factory( $title ); + $error = ''; + $success = $page->doDeleteArticle( $reason, false, 0, false, $error, $user ); $dbw->commit(); if ( $success ) { $this->output( " Deleted!\n" ); @@ -107,7 +110,7 @@ class DeleteBatch extends Maintenance { sleep( $interval ); } wfWaitForSlaves(); -} + } } } diff --git a/maintenance/deleteDefaultMessages.php b/maintenance/deleteDefaultMessages.php index a33921b1..21d7755f 100644 --- a/maintenance/deleteDefaultMessages.php +++ b/maintenance/deleteDefaultMessages.php @@ -31,9 +31,7 @@ class DeleteDefaultMessages extends Maintenance { } public function execute() { - - $user = 'MediaWiki default'; - $reason = 'No longer required'; + global $wgUser; $this->output( "Checking existence of old default messages..." ); $dbr = wfGetDB( DB_SLAVE ); @@ -54,9 +52,12 @@ class DeleteDefaultMessages extends Maintenance { # Deletions will be made by $user temporarly added to the bot group # in order to hide it in RecentChanges. - global $wgUser; - $wgUser = User::newFromName( $user ); - $wgUser->addGroup( 'bot' ); + $user = User::newFromName( 'MediaWiki default' ); + if ( !$user ) { + $this->error( "Invalid username", true ); + } + $user->addGroup( 'bot' ); + $wgUser = $user; # Handle deletion $this->output( "\n...deleting old default messages (this may take a long time!)...", 'msg' ); @@ -66,9 +67,10 @@ class DeleteDefaultMessages extends Maintenance { wfWaitForSlaves(); $dbw->ping(); $title = Title::makeTitle( $row->page_namespace, $row->page_title ); - $article = new Article( $title ); + $page = WikiPage::factory( $title ); $dbw->begin(); - $article->doDeleteArticle( $reason ); + $error = ''; // Passed by ref + $page->doDeleteArticle( 'No longer required', false, 0, false, $error, $user ); $dbw->commit(); } diff --git a/maintenance/dev/README b/maintenance/dev/README new file mode 100644 index 00000000..ca47d136 --- /dev/null +++ b/maintenance/dev/README @@ -0,0 +1,7 @@ +maintenance/dev/ scripts can help quickly setup a local MediaWiki for development purposes. + +Wikis setup in this way are NOT meant to be publicly available. They use a development database not acceptible for use in production. Place a sqlite database in an unsafe location a real wiki should never place it in. And use predictable default logins for the initial administrator user. + +Running maintenance/dev/install.sh will download and install a local copy of php 5.4, install a sqlite powered instance of MW for development, and then start up a local webserver to view the wiki. + +After installation you can bring the webserver back up at any time you want with maintenance/dev/start.sh diff --git a/maintenance/dev/includes/php.sh b/maintenance/dev/includes/php.sh new file mode 100644 index 00000000..3021b93b --- /dev/null +++ b/maintenance/dev/includes/php.sh @@ -0,0 +1,12 @@ +# Include-able script to determine the location of our php if any + +if [ -d "$DEV/php" -a -x "$DEV/php/bin/php" ]; then + # Quick local copy + PHP="$DEV/php/bin/php" +elif [ -d "$HOME/.mediawiki/php" -a -x "$HOME/.mediawiki/php/bin/php" ]; then + # Previous home directory location to install php in + PHP="$HOME/.mediawiki/php/bin/php" +elif [ -d "$HOME/.mwphp" -a -x "$HOME/.mwphp/bin/php" ]; then + # Previous home directory location to install php in + PHP="$HOME/.mwphp/bin/php" +fi diff --git a/maintenance/dev/includes/require-php.sh b/maintenance/dev/includes/require-php.sh new file mode 100644 index 00000000..470e6eb8 --- /dev/null +++ b/maintenance/dev/includes/require-php.sh @@ -0,0 +1,8 @@ +# Include-able script to require that we have a known php binary we can execute + +. "$DEV/includes/php.sh" + +if [ "x$PHP" == "x" -o ! -x "$PHP" ]; then + echo "Local copy of PHP is not installed" + exit 1 +fi diff --git a/maintenance/dev/includes/router.php b/maintenance/dev/includes/router.php new file mode 100644 index 00000000..f6a062b6 --- /dev/null +++ b/maintenance/dev/includes/router.php @@ -0,0 +1,82 @@ +/dev/null; then + echo "- using wget" + wget "$PHPURL" +elif command -v curl &>/dev/null; then + echo "- using curl" + curl -O "$PHPURL" +else + echo "- aborting" + echo "Could not find curl or wget." >&2; + exit 1; +fi + +echo "Extracting php 5.4" +tar -xzf "$TAR" + +cd php5.4-*/ + +echo "Configuring and installing php 5.4 in $PREFIX" +./configure --prefix="$PREFIX" +make +make install diff --git a/maintenance/dev/start.sh b/maintenance/dev/start.sh new file mode 100644 index 00000000..dd7363a8 --- /dev/null +++ b/maintenance/dev/start.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +if [ "x$BASH_SOURCE" == "x" ]; then echo '$BASH_SOURCE not set'; exit 1; fi +DEV=$(cd -P "$(dirname "${BASH_SOURCE[0]}" )" && pwd) + +. "$DEV/includes/require-php.sh" + +PORT=4881 + +echo "Starting up MediaWiki at http://localhost:$PORT/" +echo "" + +cd "$DEV/../../"; # $IP +"$PHP" -S "localhost:$PORT" "$DEV/includes/router.php" diff --git a/maintenance/doMaintenance.php b/maintenance/doMaintenance.php index 44e00032..6b29c5fd 100644 --- a/maintenance/doMaintenance.php +++ b/maintenance/doMaintenance.php @@ -74,18 +74,18 @@ require_once( MWInit::compiledPath( 'includes/DefaultSettings.php' ) ); if ( defined( 'MW_CONFIG_CALLBACK' ) ) { # Use a callback function to configure MediaWiki MWFunction::call( MW_CONFIG_CALLBACK ); -} elseif ( 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( MWInit::interpretedPath( '../wmf-config/wgConf.php' ) ); - $maintenance->loadWikimediaSettings(); - require( MWInit::interpretedPath( '../wmf-config/CommonSettings.php' ) ); } else { - require_once( $maintenance->loadSettings() ); + 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( MWInit::interpretedPath( '../wmf-config/wgConf.php' ) ); + } + // Require the configuration (probably LocalSettings.php) + require( $maintenance->loadSettings() ); } if ( $maintenance->getDbType() === Maintenance::DB_ADMIN && diff --git a/maintenance/dtrace/counts.d b/maintenance/dtrace/counts.d deleted file mode 100644 index bedb4547..00000000 --- a/maintenance/dtrace/counts.d +++ /dev/null @@ -1,23 +0,0 @@ -/* - * This software is in the public domain. - * - * $Id$ - */ - -#pragma D option quiet - -self int tottime; -BEGIN { - tottime = timestamp; -} - -php$target:::function-entry - @counts[copyinstr(arg0)] = count(); -} - -END { - printf("Total time: %dus\n", (timestamp - tottime) / 1000); - printf("# calls by function:\n"); - printa("%-40s %@d\n", @counts); -} - diff --git a/maintenance/dtrace/tree.d b/maintenance/dtrace/tree.d deleted file mode 100644 index a799cb12..00000000 --- a/maintenance/dtrace/tree.d +++ /dev/null @@ -1,26 +0,0 @@ -/* - * This software is in the public domain. - * - * $Id$ - */ - -#pragma D option quiet - -self int indent; -self int times[int]; - -php$target:::function-entry -{ - @counts[copyinstr(arg0)] = count(); - printf("%*s", self->indent, ""); - printf("-> %s\n", copyinstr(arg0)); - self->times[self->indent] = timestamp; - self->indent += 2; -} - -php$target:::function-return -{ - self->indent -= 2; - printf("%*s", self->indent, ""); - printf("<- %s %dus\n", copyinstr(arg0), (timestamp - self->times[self->indent]) / 1000); -} diff --git a/maintenance/dumpBackup.php b/maintenance/dumpBackup.php index 15189261..c49a2963 100644 --- a/maintenance/dumpBackup.php +++ b/maintenance/dumpBackup.php @@ -119,7 +119,7 @@ Options: Fancy stuff: (Works? Add examples please.) --plugin=[:] Load a dump plugin class --output=: Begin a filtered output stream; - s: file, gzip, bzip2, 7zip + s: file, gzip, bzip2, 7zip --filter=[:] Add a filter on an output branch ENDS diff --git a/maintenance/dumpHTML.php b/maintenance/dumpHTML.php deleted file mode 100644 index bd94958e..00000000 --- a/maintenance/dumpHTML.php +++ /dev/null @@ -1,7 +0,0 @@ -dumpHTML has moved to the DumpHTML extension. - -WebDAV/SVN: -http://svn.wikimedia.org/svnroot/mediawiki/trunk/extensions/DumpHTML/ - -Web: -http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/DumpHTML/ diff --git a/maintenance/dumpInterwiki.php b/maintenance/dumpInterwiki.php deleted file mode 100644 index 217afd88..00000000 --- a/maintenance/dumpInterwiki.php +++ /dev/null @@ -1,251 +0,0 @@ -mDescription = "Build constant slightly compact database of interwiki prefixes."; - $this->addOption( 'langlist', 'File with one language code per line', false, true ); - $this->addOption( 'dblist', 'File with one db per line', false, true ); - $this->addOption( 'specialdbs', "File with one 'special' db per line", false, true ); - $this->addOption( 'o', 'Cdb output file', false, true ); - $this->addOption( 'protocolrelative', 'Output wikimedia interwiki urls as protocol relative', false, false ); - } - - function execute() { - # List of language prefixes likely to be found in multi-language sites - $this->langlist = array_map( "trim", file( $this->getOption( 'langlist', "/home/wikipedia/common/langlist" ) ) ); - - # List of all database names - $this->dblist = array_map( "trim", file( $this->getOption( 'dblist', "/home/wikipedia/common/all.dblist" ) ) ); - - # Special-case databases - $this->specials = array_flip( array_map( "trim", file( $this->getOption( 'specialdbs', "/home/wikipedia/common/special.dblist" ) ) ) ); - - if ( $this->hasOption( 'o' ) ) { - $this->dbFile = CdbWriter::open( $this->getOption( 'o' ) ) ; - } else { - $this->dbFile = false; - } - - if ( $this->hasOption( 'protocolrelative' ) ) { - $this->urlprotocol = ''; - } else { - $this->urlprotocol = 'http:'; - } - - $this->getRebuildInterwikiDump(); - } - - function getRebuildInterwikiDump() { - global $wgContLang; - - # Multi-language sites - # db suffix => db suffix, iw prefix, hostname - $sites = array( - 'wiki' => new Site( 'wiki', 'w', 'wikipedia.org' ), - 'wiktionary' => new Site( 'wiktionary', 'wikt', 'wiktionary.org' ), - 'wikiquote' => new Site( 'wikiquote', 'q', 'wikiquote.org' ), - 'wikibooks' => new Site( 'wikibooks', 'b', 'wikibooks.org' ), - 'wikinews' => new Site( 'wikinews', 'n', 'wikinews.org' ), - 'wikisource' => new Site( 'wikisource', 's', 'wikisource.org' ), - 'wikimedia' => new Site( 'wikimedia', 'chapter', 'wikimedia.org' ), - 'wikiversity' => new Site( 'wikiversity', 'v', 'wikiversity.org' ), - ); - - # Extra interwiki links that can't be in the intermap for some reason - $extraLinks = array( - array( 'm', $this->urlprotocol . '//meta.wikimedia.org/wiki/$1', 1 ), - array( 'meta', $this->urlprotocol . '//meta.wikimedia.org/wiki/$1', 1 ), - array( 'sep11', $this->urlprotocol . '//sep11.wikipedia.org/wiki/$1', 1 ), - ); - - # Language aliases, usually configured as redirects to the real wiki in apache - # Interlanguage links are made directly to the real wiki - # Something horrible happens if you forget to list an alias here, I can't - # remember what - $this->languageAliases = array( - 'zh-cn' => 'zh', - 'zh-tw' => 'zh', - 'dk' => 'da', - 'nb' => 'no', - ); - - # Special case prefix rewrites, for the benefit of Swedish which uses s:t - # as an abbreviation for saint - $this->prefixRewrites = array( - 'svwiki' => array( 's' => 'src' ), - ); - - # Construct a list of reserved prefixes - $reserved = array(); - foreach ( $this->langlist as $lang ) { - $reserved[$lang] = 1; - } - foreach ( $this->languageAliases as $alias => $lang ) { - $reserved[$alias] = 1; - } - foreach ( $sites as $site ) { - $reserved[$site->lateral] = 1; - } - - # Extract the intermap from meta - $intermap = Http::get( 'http://meta.wikimedia.org/w/index.php?title=Interwiki_map&action=raw', 30 ); - $lines = array_map( 'trim', explode( "\n", trim( $intermap ) ) ); - - if ( !$lines || count( $lines ) < 2 ) { - $this->error( "m:Interwiki_map not found", true ); - } - - # Global iterwiki map - foreach ( $lines as $line ) { - if ( preg_match( '/^\|\s*(.*?)\s*\|\|\s*(.*?)\s*$/', $line, $matches ) ) { - $prefix = $wgContLang->lc( $matches[1] ); - $prefix = str_replace( ' ', '_', $prefix ); - - $url = $matches[2]; - if ( preg_match( '/(wikipedia|wiktionary|wikisource|wikiquote|wikibooks|wikimedia)\.org/', $url ) ) { - if ( $this->hasOption( 'protocolrelative' ) ) { - if ( substr( $url, 0, 5 ) == 'http:' ) { - $url = substr( $url, 5 ); - } else if ( substr( $url, 0, 6 ) == 'https:' ) { - $url = substr( $url, 6 ); - } - } - $local = 1; - } else { - $local = 0; - } - - if ( empty( $reserved[$prefix] ) ) { - $imap = array( "iw_prefix" => $prefix, "iw_url" => $url, "iw_local" => $local ); - $this->makeLink ( $imap, "__global" ); - } - } - } - - # Exclude Wikipedia for Wikipedia - $this->makeLink ( array ( 'iw_prefix' => 'wikipedia', 'is_url' => null ), "_wiki" ); - - # Multilanguage sites - foreach ( $sites as $site ) { - $this->makeLanguageLinks ( $site, "_" . $site->suffix ); - } - - foreach ( $this->dblist as $db ) { - if ( isset( $this->specials[$db] ) ) { - # Special wiki - # Has interwiki links and interlanguage links to wikipedia - - $this->makeLink( array( 'iw_prefix' => $db, 'iw_url' => "wiki" ), "__sites" ); - # Links to multilanguage sites - foreach ( $sites as $targetSite ) { - $this->makeLink( array( 'iw_prefix' => $targetSite->lateral, - 'iw_url' => $targetSite->getURL( 'en', $this->urlprotocol ), - 'iw_local' => 1 ), $db ); - } - } else { - # Find out which site this DB belongs to - $site = false; - foreach ( $sites as $candidateSite ) { - $suffix = $candidateSite->suffix; - if ( preg_match( "/(.*)$suffix$/", $db, $matches ) ) { - $site = $candidateSite; - break; - } - } - $this->makeLink( array( 'iw_prefix' => $db, 'iw_url' => $site->suffix ), "__sites" ); - if ( !$site ) { - $this->error( "Invalid database $db\n" ); - continue; - } - $lang = $matches[1]; - - # Lateral links - foreach ( $sites as $targetSite ) { - if ( $targetSite->suffix != $site->suffix ) { - $this->makeLink( array( 'iw_prefix' => $targetSite->lateral, - 'iw_url' => $targetSite->getURL( $lang, $this->urlprotocol ), - 'iw_local' => 1 ), $db ); - } - } - - if ( $site->suffix == "wiki" ) { - $this->makeLink( array( 'iw_prefix' => 'w', - 'iw_url' => $this->urlprotocol . "//en.wikipedia.org/wiki/$1", - 'iw_local' => 1 ), $db ); - } - - } - } - foreach ( $extraLinks as $link ) { - $this->makeLink( $link, "__global" ); - } - } - - # ------------------------------------------------------------------------------------------ - - # Executes part of an INSERT statement, corresponding to all interlanguage links to a particular site - function makeLanguageLinks( &$site, $source ) { - # Actual languages with their own databases - foreach ( $this->langlist as $targetLang ) { - $this->makeLink( array( $targetLang, $site->getURL( $targetLang, $this->urlprotocol ), 1 ), $source ); - } - - # Language aliases - foreach ( $this->languageAliases as $alias => $lang ) { - $this->makeLink( array( $alias, $site->getURL( $lang, $this->urlprotocol ), 1 ), $source ); - } - } - - function makeLink( $entry, $source ) { - if ( isset( $this->prefixRewrites[$source] ) && isset( $this->prefixRewrites[$source][$entry[0]] ) ) - $entry[0] = $this->prefixRewrites[$source][$entry[0]]; - - if ( !array_key_exists( "iw_prefix", $entry ) ) { - $entry = array( "iw_prefix" => $entry[0], "iw_url" => $entry[1], "iw_local" => $entry[2] ); - } - if ( array_key_exists( $source, $this->prefixRewrites ) && - array_key_exists( $entry['iw_prefix'], $this->prefixRewrites[$source] ) ) { - $entry['iw_prefix'] = $this->prefixRewrites[$source][$entry['iw_prefix']]; - } - - if ( $this->dbFile ) { - $this->dbFile->set( "{$source}:{$entry['iw_prefix']}", trim( "{$entry['iw_local']} {$entry['iw_url']}" ) ); - } else { - $this->output( "{$source}:{$entry['iw_prefix']} {$entry['iw_url']} {$entry['iw_local']}\n" ); - } - } -} - -$maintClass = "DumpInterwiki"; -require_once( RUN_MAINTENANCE_IF_MAIN ); - diff --git a/maintenance/dumpLinks.php b/maintenance/dumpLinks.php index 39a9e955..0101dc8d 100644 --- a/maintenance/dumpLinks.php +++ b/maintenance/dumpLinks.php @@ -26,7 +26,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html * - * @ingroup Mainatenance + * @ingroup Maintenance */ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); diff --git a/maintenance/dumpTextPass.php b/maintenance/dumpTextPass.php index 4e85e64e..0fed29fc 100644 --- a/maintenance/dumpTextPass.php +++ b/maintenance/dumpTextPass.php @@ -2,7 +2,7 @@ /** * Script that postprocesses XML dumps from dumpBackup.php to add page text * - * Copyright © 2005 Brion Vibber , 2010 Alexandre Emsenhuber + * Copyright (C) 2005 Brion Vibber * http://www.mediawiki.org/ * * This program is free software; you can redistribute it and/or modify @@ -66,6 +66,11 @@ class TextPassDumper extends BackupDumper { var $checkpointJustWritten = false; var $checkpointFiles = array(); + /** + * @var DatabaseBase + */ + protected $db; + function initProgress( $history ) { parent::initProgress(); $this->timeOfCheckpoint = $this->startTime; @@ -169,7 +174,8 @@ class TextPassDumper extends BackupDumper { */ function showReport() { if ( !$this->prefetch ) { - return parent::showReport(); + parent::showReport(); + return; } if ( $this->reporting ) { @@ -186,8 +192,7 @@ class TextPassDumper extends BackupDumper { $etats = wfTimestamp( TS_DB, intval( $eta ) ); if ( $this->fetchCount ) { $fetchRate = 100.0 * $this->prefetchCount / $this->fetchCount; - } - else { + } else { $fetchRate = '-'; } $pageRate = $this->pageCount / $deltaAll; @@ -201,8 +206,7 @@ class TextPassDumper extends BackupDumper { if ( $deltaPart ) { if ( $this->fetchCountLast ) { $fetchRatePart = 100.0 * $this->prefetchCountLast / $this->fetchCountLast; - } - else { + } else { $fetchRatePart = '-'; } $pageRatePart = $this->pageCountPart / $deltaPart; @@ -228,9 +232,9 @@ class TextPassDumper extends BackupDumper { function checkIfTimeExceeded() { if ( $this->maxTimeAllowed && ( $this->lastTime - $this->timeOfCheckpoint > $this->maxTimeAllowed ) ) { - return True; + return true; } - return False; + return false; } function finalOptionCheck() { @@ -286,7 +290,7 @@ class TextPassDumper extends BackupDumper { // we wrote some stuff after last checkpoint that needs renamed if (file_exists($filenameList[0])) { $newFilenames = array(); - # we might have just written the header and footer and had no + # we might have just written the header and footer and had no # pages or revisions written... perhaps they were all deleted # there's no pageID 0 so we use that. the caller is responsible # for deciding what to do with a file containing only the @@ -332,7 +336,6 @@ class TextPassDumper extends BackupDumper { } private function doGetText( $id ) { - $id = intval( $id ); $this->failures = 0; $ex = new MWException( "Graceful storage failure" ); @@ -345,9 +348,9 @@ class TextPassDumper extends BackupDumper { $this->closeSpawn(); $this->openSpawn(); } - $text = $this->getTextSpawned( $id ); + $text = $this->getTextSpawned( $id ); } else { - $text = $this->getTextDbSafe( $id ); + $text = $this->getTextDbSafe( $id ); } if ( $text === false ) { $this->failures++; @@ -359,11 +362,10 @@ class TextPassDumper extends BackupDumper { $this->failedTextRetrievals++; if ($this->failedTextRetrievals > $this->maxConsecutiveFailedTextRetrievals) { throw $ex; - } - else { + } else { // would be nice to return something better to the caller someday, // log what we know about the failure and about the revision - return(""); + return ""; } } else { $this->progress( "Error $this->failures " . @@ -373,16 +375,18 @@ class TextPassDumper extends BackupDumper { } } else { $this->failedTextRetrievals= 0; - return( $text ); + return $text; } } - + return ''; } /** * Fetch a text revision from the database, retrying in case of failure. * This may survive some transitory errors by reconnecting, but * may not survive a long-term server outage. + * + * FIXME: WTF? Why is it using a loop and then returning unconditionally? */ private function getTextDbSafe( $id ) { while ( true ) { @@ -397,6 +401,8 @@ class TextPassDumper extends BackupDumper { /** * May throw a database error if, say, the server dies during query. + * @param $id + * @return bool|string */ private function getTextDb( $id ) { global $wgContLang; @@ -584,15 +590,15 @@ class TextPassDumper extends BackupDumper { $this->egress->writeClosePage( $this->buffer ); // nasty hack, we can't just write the chardata after the // page tag, it will include leading blanks from the next line - $this->egress->sink->write("\n"); - + $this->egress->sink->write("\n"); + $this->buffer = $this->xmlwriterobj->closeStream(); $this->egress->writeCloseStream( $this->buffer ); $this->buffer = ""; $this->thisPage = ""; // this could be more than one file if we had more than one output arg - $checkpointFilenames = array(); + $filenameList = (array)$this->egress->getFilenames(); $newFilenames = array(); $firstPageID = str_pad($this->firstPageWritten,9,"0",STR_PAD_LEFT); @@ -669,10 +675,10 @@ Options: pressure on the database. (Requires the XMLReader extension) --maxtime= Write out checkpoint file after this many minutes (writing - out complete page, closing xml file properly, and opening new one + out complete page, closing xml file properly, and opening new one with header). This option requires the checkpointfile option. --checkpointfile= Use this string for checkpoint filenames, - substituting first pageid written for the first %s (required) and the + substituting first pageid written for the first %s (required) and the last pageid written for the second %s if it exists. --quiet Don't dump status reports to stderr. --report=n Report position and speed after every n pages processed. @@ -684,3 +690,5 @@ Options: ENDS ); } + + diff --git a/maintenance/edit.php b/maintenance/edit.php index fb462a40..88573714 100644 --- a/maintenance/edit.php +++ b/maintenance/edit.php @@ -58,14 +58,14 @@ class EditCLI extends Maintenance { $this->error( "Invalid title", true ); } - $article = new Article( $wgTitle ); + $page = WikiPage::factory( $wgTitle ); # Read the text $text = $this->getStdin( Maintenance::STDIN_ALL ); # Do the edit $this->output( "Saving... " ); - $status = $article->doEdit( $text, $summary, + $status = $page->doEdit( $text, $summary, ( $minor ? EDIT_MINOR : 0 ) | ( $bot ? EDIT_FORCE_BOT : 0 ) | ( $autoSummary ? EDIT_AUTOSUMMARY : 0 ) | diff --git a/maintenance/eval.php b/maintenance/eval.php index 1502ad3e..3bd164fd 100644 --- a/maintenance/eval.php +++ b/maintenance/eval.php @@ -31,12 +31,10 @@ * @ingroup Maintenance */ -$wgUseNormalUser = (bool)getenv( 'MW_WIKIUSER' ); - $optionsWithArgs = array( 'd' ); /** */ -require_once( "commandLine.inc" ); +require_once( dirname( __FILE__ ) . "/commandLine.inc" ); if ( isset( $options['d'] ) ) { $d = $options['d']; @@ -57,13 +55,8 @@ if ( isset( $options['d'] ) ) { } } -if ( function_exists( 'readline_add_history' ) - && Maintenance::posix_isatty( 0 /*STDIN*/ ) ) -{ - $useReadline = true; -} else { - $useReadline = false; -} +$useReadline = function_exists( 'readline_add_history' ) + && Maintenance::posix_isatty( 0 /*STDIN*/ ); if ( $useReadline ) { $historyFile = isset( $_ENV['HOME'] ) ? diff --git a/maintenance/fetchText.php b/maintenance/fetchText.php index 067ffe45..3b43bcd5 100644 --- a/maintenance/fetchText.php +++ b/maintenance/fetchText.php @@ -29,7 +29,7 @@ class FetchText extends Maintenance { $this->mDescription = "Fetch the revision text from an old_id"; } - /* + /** * returns a string containing the following in order: * textid * \n diff --git a/maintenance/findHooks.php b/maintenance/findHooks.php index 5996fd3a..cb582857 100644 --- a/maintenance/findHooks.php +++ b/maintenance/findHooks.php @@ -12,7 +12,7 @@ * * Any instance of wfRunHooks that doesn't meet these parameters will be noted. * - * Copyright © Ashar Voultoiz + * Copyright © Antoine Musso * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ * * @file * @ingroup Maintenance - * @author Ashar Voultoiz + * @author Antoine Musso */ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); @@ -59,6 +59,7 @@ class FindHooks extends Maintenance { $IP . '/includes/actions/', $IP . '/includes/api/', $IP . '/includes/cache/', + $IP . '/includes/context/', $IP . '/includes/db/', $IP . '/includes/diff/', $IP . '/includes/filerepo/', @@ -106,6 +107,29 @@ class FindHooks extends Maintenance { */ private function getHooksFromDoc( $doc ) { if ( $this->hasOption( 'online' ) ) { + return $this->getHooksFromOnlineDoc( ); + } else { + return $this->getHooksFromLocalDoc( $doc ); + } + } + + /** + * Get hooks from a local file (for example docs/hooks.txt) + * @param $doc string: filename to look in + * @return 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] ); + } + + /** + * Get hooks from www.mediawiki.org using the API + * @return 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 ); @@ -129,12 +153,6 @@ class FindHooks extends Maintenance { } } return array_diff( $allhooks, $removed ); - } else { - $m = array(); - $content = file_get_contents( $doc ); - preg_match_all( "/\n'(.*?)'/", $content, $m ); - return array_unique( $m[1] ); - } } /** diff --git a/maintenance/fixExtLinksProtocolRelative.php b/maintenance/fixExtLinksProtocolRelative.php index 1a7025ad..0cabe816 100644 --- a/maintenance/fixExtLinksProtocolRelative.php +++ b/maintenance/fixExtLinksProtocolRelative.php @@ -37,7 +37,7 @@ class FixExtLinksProtocolRelative extends LoggedUpdateMaintenance { protected function updateSkippedMessage() { return 'protocol-relative URLs in externallinks table already fixed.'; } - + protected function doDBUpdates() { $db = wfGetDB( DB_MASTER ); if ( !$db->tableExists( 'externallinks' ) ) { @@ -53,7 +53,7 @@ class FixExtLinksProtocolRelative extends LoggedUpdateMaintenance { foreach ( $res as $row ) { $count++; if ( $count % 100 == 0 ) { - $this->output( $count ); + $this->output( $count . "\n" ); wfWaitForSlaves(); } $db->insert( 'externallinks', diff --git a/maintenance/formatInstallDoc.php b/maintenance/formatInstallDoc.php index 9acc16a7..b3bb50ca 100644 --- a/maintenance/formatInstallDoc.php +++ b/maintenance/formatInstallDoc.php @@ -1,4 +1,7 @@ pagePath = "trackback.php"; - - $this->params = array ( - "url" => wikiFuzz::makeFuzz( 2 ), - "blog_name" => wikiFuzz::chooseInput( array( "80", wikiFuzz::randnum( 6000, -200 ), wikiFuzz::makeFuzz( 2 ) ) ), - "article" => wikiFuzz::chooseInput( array( "Main Page", wikiFuzz::makeFuzz( 2 ) ) ), - "title" => wikiFuzz::chooseInput( array( "Main Page", wikiFuzz::makeFuzz( 2 ) ) ), - "excerpt" => wikiFuzz::makeFuzz( 2 ), - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["title"] ); - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["excerpt"] ); - - // page does not produce HTML. - $this->tidyValidate = false; - } -} - - /** ** a test for profileinfo.php */ @@ -1891,52 +1870,6 @@ class specialFilepathPageTest extends pageTest { } -/** - ** a test for Special:Makebot (extension Special page). - */ -class specialMakebot extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Makebot"; - - $this->params = array ( - "username" => wikiFuzz::chooseInput( array( "Nickj2", "192.168.0.2", wikiFuzz::makeFuzz( 1 ) ) ), - "dosearch" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - "grant" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - "comment" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ), - "token" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ), - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["dosearch"] ); - if ( wikiFuzz::randnum( 2 ) == 0 ) unset( $this->params["grant"] ); - if ( wikiFuzz::randnum( 5 ) == 0 ) unset( $this->params["token"] ); - } -} - - -/** - ** a test for Special:Makesysop (extension Special page). - */ -class specialMakesysop extends pageTest { - function __construct() { - $this->pagePath = "index.php?title=Special:Makesysop"; - - $this->params = array ( - "wpMakesysopUser" => wikiFuzz::chooseInput( array( "Nickj2", "192.168.0.2", wikiFuzz::makeFuzz( 1 ) ) ), - "action" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - "wpMakesysopSubmit" => wikiFuzz::chooseInput( array( "0", "1", "++--34234", wikiFuzz::makeFuzz( 2 ) ) ), - "wpEditToken" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ), - "wpSetBureaucrat" => wikiFuzz::chooseInput( array( "20398702394", "", wikiFuzz::makeFuzz( 2 ) ) ), - ); - - // sometimes we don't want to specify certain parameters. - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpMakesysopSubmit"] ); - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpEditToken"] ); - if ( wikiFuzz::randnum( 3 ) == 0 ) unset( $this->params["wpSetBureaucrat"] ); - } -} - - /** ** a test for Special:Renameuser (extension Special page). */ @@ -2224,9 +2157,10 @@ class GeSHi_Test extends pageTest { } } - /** ** 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|\specialBlockmeTest|\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 ) { @@ -2275,17 +2209,14 @@ function selectPageTest( $count ) { case 33: return new specialRevisionDeletePageTest(); case 34: return new specialImportPageTest(); case 35: return new thumbTest(); - case 36: return new trackbackTest(); case 37: return new profileInfo(); case 38: return new specialCitePageTest(); case 39: return new specialFilepathPageTest(); - case 40: return new specialMakebot(); - case 41: return new specialMakesysop(); - case 42: return new specialRenameuserPageTest(); - case 43: return new specialLinksearch(); - case 44: return new specialCategoryTree(); - case 45: return new api(); - case 45: return new specialChemicalsourcesTest(); + 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(); } } @@ -2300,11 +2231,12 @@ 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 ); @@ -2356,11 +2288,12 @@ function saveTestAsPHP( pageTest $test, $filename ) { 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. + * 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(); @@ -2411,18 +2344,21 @@ function saveTest( pageTest $test, $testname ) { saveTestData ( $test, $base_name . DATA_FILE ); } - // ////////////////// MEDIAWIKI OUTPUT ///////////////////////// /** - ** Asks MediaWiki for the HTML output of a test. + * 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() ); + 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() ); @@ -2447,8 +2383,10 @@ function wikiTestOutput( pageTest $test ) { // ////////////////// HTML VALIDATION ///////////////////////// -/* - ** Asks the validator whether this is valid HTML, or not. +/** + * Asks the validator whether this is valid HTML, or not. + * @param $text string + * @return array */ function validateHTML( $text ) { @@ -2477,9 +2415,10 @@ function validateHTML( $text ) { return array( $valid, $result ); } - /** - ** Get tidy to check for no HTML errors in the output file (e.g. unescaped strings). + * Get tidy to check for no HTML errors in the output file (e.g. unescaped strings). + * @param $name + * @return bool */ function tidyCheckFile( $name ) { $file = DIRECTORY . "/" . $name; @@ -2498,10 +2437,10 @@ function tidyCheckFile( $name ) { } } - /** ** 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; @@ -2529,8 +2468,12 @@ function dbErrorLogged() { // //////////////// 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. + * 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 ) { diff --git a/maintenance/gearman/gearman.inc b/maintenance/gearman/gearman.inc deleted file mode 100644 index 15f80e62..00000000 --- a/maintenance/gearman/gearman.inc +++ /dev/null @@ -1,104 +0,0 @@ -complete( array( 'result' => true ) ); - socket_close( $this->conn ); - - # Close some more sockets - LBFactory::destroyInstance(); - global $wgMemc; - $wgMemc->disconnect_all(); - - # Find PHP - $php = readlink( '/proc/' . posix_getpid() . '/exe' ); - - # Run the worker script - $args = array( $_SERVER['PHP_SELF'], - '--wiki', $wiki, - '--fake-job', serialize( $params ) ); - $args = array_merge( $args, $GLOBALS['args'] ); - pcntl_exec( $php, $args, $_ENV ); - echo "Error running exec\n"; - } - - function run( $params ) { - if ( wfWikiID() !== $params['wiki'] ) { - $this->switchWiki( $params['wiki'], $params ); - } - return self::runNoSwitch( $params ); - } - - static function runNoSwitch( $params ) { - echo implode( ' ', $params ) . "\n"; - $title = Title::newFromText( $params['title'] ); - $mwJob = Job::factory( $params['command'], $title, $params['params'] ); - return $mwJob->run(); - } -} - -class NonScaryGearmanWorker extends Net_Gearman_Worker { - - /** - * Copied from Net_Gearman_Worker but with the scary "run any PHP file in - * the filesystem" feature removed. - */ - protected function doWork($socket) { - Net_Gearman_Connection::send($socket, 'grab_job'); - - $resp = array('function' => 'noop'); - while (count($resp) && $resp['function'] == 'noop') { - $resp = Net_Gearman_Connection::blockingRead($socket); - } - - if (in_array($resp['function'], array('noop', 'no_job'))) { - return false; - } - - if ($resp['function'] != 'job_assign') { - throw new Net_Gearman_Exception('Holy Cow! What are you doing?!'); - } - - $name = $resp['data']['func']; - $handle = $resp['data']['handle']; - $arg = array(); - - if (isset($resp['data']['arg']) && - Net_Gearman_Connection::stringLength($resp['data']['arg'])) { - $arg = json_decode($resp['data']['arg'], true); - } - - ### START MW DIFFERENT BIT - if ( $name != 'mw_job' ) { - throw new Net_Gearman_Job_Exception('Invalid function'); - } - $job = new MWGearmanJob($socket, $handle); - ### END MW DIFFERENT BIT - - try { - $this->start($handle, $name, $arg); - $res = $job->run($arg); - if (!is_array($res)) { - $res = array('result' => $res); - } - - $job->complete($res); - $this->complete($handle, $name, $res); - } catch (Net_Gearman_Job_Exception $e) { - $job->fail(); - $this->fail($handle, $name, $e); - } - - // Force the job's destructor to run - $job = null; - - return true; - } -} - diff --git a/maintenance/gearman/gearmanRefreshLinks.php b/maintenance/gearman/gearmanRefreshLinks.php deleted file mode 100644 index 730db96b..00000000 --- a/maintenance/gearman/gearmanRefreshLinks.php +++ /dev/null @@ -1,45 +0,0 @@ -selectField( 'page', 'MAX(page_id)', false, __METHOD__ ); -while ( true ) { - $res = $dbr->select( - 'page', - array( 'page_namespace', 'page_title', 'page_id' ), - array( 'page_id > ' . intval( $startId ) ), - __METHOD__, - array( 'LIMIT' => $batchSize ) - ); - - if ( $res->numRows() == 0 ) { - break; - } - $set = new Net_Gearman_Set; - foreach ( $res as $row ) { - $startId = $row->page_id; - $title = Title::makeTitle( $row->page_namespace, $row->page_title ); - $params = array( - 'wiki' => wfWikiID(), - 'title' => $title->getPrefixedDBkey(), - 'command' => 'refreshLinks', - 'params' => false, - ); - $task = new Net_Gearman_Task( 'mw_job', $params ); - $set->addTask( $task ); - } - $client->runSet( $set ); - print "$startId / $endId\n"; -} - diff --git a/maintenance/gearman/gearmanWorker.php b/maintenance/gearman/gearmanWorker.php deleted file mode 100644 index 3ea10081..00000000 --- a/maintenance/gearman/gearmanWorker.php +++ /dev/null @@ -1,43 +0,0 @@ - 1000 ) { - echo "Invalid number of processes, please specify a number between 1 and 1000\n"; - exit( 1 ); - } - $fc = new ForkController( $procs, ForkController::RESTART_ON_ERROR ); - if ( $fc->start() != 'child' ) { - exit( 0 ); - } -} - -if ( !$args ) { - $args = array( 'localhost' ); -} - -if ( isset( $options['fake-job'] ) ) { - $params = unserialize( $options['fake-job'] ); - MWGearmanJob::runNoSwitch( $params ); -} - -$worker = new NonScaryGearmanWorker( $args ); -$worker->addAbility( 'mw_job' ); -$worker->beginWork( 'wfGearmanMonitor' ); - -function wfGearmanMonitor( $idle, $lastJob ) { - static $lastSleep = 0; - $interval = 5; - $now = time(); - if ( $now - $lastSleep >= $interval ) { - wfWaitForSlaves(); - $lastSleep = $now; - } - return false; -} diff --git a/maintenance/generateSitemap.php b/maintenance/generateSitemap.php index 403e5a24..80d31f97 100644 --- a/maintenance/generateSitemap.php +++ b/maintenance/generateSitemap.php @@ -162,6 +162,8 @@ class GenerateSitemap extends Maintenance { } private function setNamespacePriorities() { + global $wgSitemapNamespacesPriorities; + // Custom main namespaces $this->priorities[self::GS_MAIN] = '0.5'; // Custom talk namesspaces @@ -183,10 +185,28 @@ class GenerateSitemap extends Maintenance { $this->priorities[NS_HELP_TALK] = '0.1'; $this->priorities[NS_CATEGORY] = '0.5'; $this->priorities[NS_CATEGORY_TALK] = '0.1'; + + // Custom priorities + if ( $wgSitemapNamespacesPriorities !== false ) { + /** + * @var $wgSitemapNamespacesPriorities array + */ + foreach ( $wgSitemapNamespacesPriorities as $namespace => $priority ) { + $float = floatval( $priority ); + if ( $float > 1.0 ) { + $priority = '1.0'; + } elseif ( $float < 0.0 ) { + $priority = '0.0'; + } + $this->priorities[$namespace] = $priority; + } + } } /** * Create directory if it does not exist and return pathname with a trailing slash + * @param $fspath string + * @return null|string */ private static function init_path( $fspath ) { if ( !isset( $fspath ) ) { @@ -194,7 +214,7 @@ class GenerateSitemap extends Maintenance { } # Create directory if needed if ( $fspath && !is_dir( $fspath ) ) { - wfMkdirParents( $fspath ) or die( "Can not create directory $fspath.\n" ); + wfMkdirParents( $fspath, null, __METHOD__ ) or die( "Can not create directory $fspath.\n" ); } return realpath( $fspath ) . DIRECTORY_SEPARATOR ; @@ -327,13 +347,20 @@ class GenerateSitemap extends Maintenance { * @return Resource */ function open( $file, $flags ) { - return $this->compress ? gzopen( $file, $flags ) : fopen( $file, $flags ); + $resource = $this->compress ? gzopen( $file, $flags ) : fopen( $file, $flags ); + if( $resource === false ) { + wfDebugDieBacktrace( __METHOD__ . " error opening file $file with flags $flags. Check permissions?" ); + } + return $resource; } /** * gzwrite() / fwrite() wrapper */ function write( &$handle, $str ) { + if( $handle === true || $handle === false ) { + wfDebugDieBacktrace( __METHOD__ . " was passed a boolean as a file handle.\n" ); + } if ( $this->compress ) gzwrite( $handle, $str ); else diff --git a/maintenance/getSlaveServer.php b/maintenance/getSlaveServer.php index a9d93f1d..3d13bc4e 100644 --- a/maintenance/getSlaveServer.php +++ b/maintenance/getSlaveServer.php @@ -32,15 +32,13 @@ class GetSlaveServer extends Maintenance { global $wgAllDBsAreLocalhost; if ( $wgAllDBsAreLocalhost ) { $host = 'localhost'; + } elseif ( $this->hasOption( 'group' ) ) { + $db = wfGetDB( DB_SLAVE, $this->getOption( 'group' ) ); + $host = $db->getServer(); } else { - if ( $this->hasOption( 'group' ) ) { - $db = wfGetDB( DB_SLAVE, $this->getOption( 'group' ) ); - $host = $db->getServer(); - } else { - $lb = wfGetLB(); - $i = $lb->getReaderIndex(); - $host = $lb->getServerName( $i ); - } + $lb = wfGetLB(); + $i = $lb->getReaderIndex(); + $host = $lb->getServerName( $i ); } $this->output( "$host\n" ); } diff --git a/maintenance/hiphop/extra-files b/maintenance/hiphop/extra-files index 15f48577..f07f7c7c 100644 --- a/maintenance/hiphop/extra-files +++ b/maintenance/hiphop/extra-files @@ -1,5 +1,4 @@ img_auth.php -includes/AjaxFunctions.php includes/AutoLoader.php includes/DefaultSettings.php includes/Defines.php diff --git a/maintenance/hiphop/make b/maintenance/hiphop/make index e792e08b..2bb9951f 100644 --- a/maintenance/hiphop/make +++ b/maintenance/hiphop/make @@ -1,9 +1,12 @@ #!/usr/bin/hphpi -f stderr = fopen( "php://stderr", "wt" ); $this->addOption( 'report', 'Report position and speed after every n pages processed', false, true ); - $this->addOption( 'namespaces', + $this->addOption( 'namespaces', 'Import only the pages from namespaces belonging to the list of ' . 'pipe-separated namespace names or namespace indexes', false, true ); $this->addOption( 'dry-run', 'Parse dump without actually importing pages' ); $this->addOption( 'debug', 'Output extra verbose debug information' ); $this->addOption( 'uploads', 'Process file upload data if included (experimental)' ); + $this->addOption( '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 ); } @@ -73,6 +74,10 @@ TEXT; } $this->reportingInterval = intval( $this->getOption( 'report', 100 ) ); + if ( !$this->reportingInterval ) { + $this->reportingInterval = 100; // avoid division by zero + } + $this->dryRun = $this->hasOption( 'dry-run' ); $this->uploads = $this->hasOption( 'uploads' ); // experimental! if ( $this->hasOption( 'image-base-path' ) ) { @@ -112,6 +117,10 @@ TEXT; $this->error( "Unknown namespace text / index specified: $namespace", true ); } + /** + * @param $obj Title|Revision + * @return bool + */ private function skippedNamespace( $obj ) { if ( $obj instanceof Title ) { $ns = $obj->getNamespace(); @@ -130,6 +139,10 @@ TEXT; $this->pageCount++; } + /** + * @param $rev Revision + * @return mixed + */ function handleRevision( $rev ) { $title = $rev->getTitle(); if ( !$title ) { @@ -149,6 +162,10 @@ TEXT; } } + /** + * @param $revision Revision + * @return bool + */ function handleUpload( $revision ) { if ( $this->uploads ) { if ( $this->skippedNamespace( $revision ) ) { @@ -186,7 +203,7 @@ TEXT; } function showReport() { - if ( $this->mQuiet ) { + if ( !$this->mQuiet ) { $delta = wfTime() - $this->startTime; if ( $delta ) { $rate = sprintf( "%.2f", $this->pageCount / $delta ); @@ -204,7 +221,7 @@ TEXT; } wfWaitForSlaves(); // XXX: Don't let deferred jobs array get absurdly large (bug 24375) - wfDoUpdates( 'commit' ); + DeferredUpdates::doUpdates( 'commit' ); } function progress( $string ) { @@ -214,11 +231,9 @@ TEXT; function importFromFile( $filename ) { if ( preg_match( '/\.gz$/', $filename ) ) { $filename = 'compress.zlib://' . $filename; - } - elseif ( preg_match( '/\.bz2$/', $filename ) ) { + } elseif ( preg_match( '/\.bz2$/', $filename ) ) { $filename = 'compress.bzip2://' . $filename; - } - elseif ( preg_match( '/\.7z$/', $filename ) ) { + } elseif ( preg_match( '/\.7z$/', $filename ) ) { $filename = 'mediawiki.compress.7z://' . $filename; } @@ -243,6 +258,9 @@ TEXT; if( $this->hasOption( 'debug' ) ) { $importer->setDebug( true ); } + if ( $this->hasOption( 'no-updates' ) ) { + $importer->setNoUpdates( true ); + } $importer->setPageCallback( array( &$this, 'reportPage' ) ); $this->importCallback = $importer->setRevisionCallback( array( &$this, 'handleRevision' ) ); diff --git a/maintenance/importImages.php b/maintenance/importImages.php index f3b10ea9..bd077ff9 100644 --- a/maintenance/importImages.php +++ b/maintenance/importImages.php @@ -40,255 +40,265 @@ $processed = $added = $ignored = $skipped = $overwritten = $failed = 0; echo( "Import Images\n\n" ); # Need a path -if ( count( $args ) > 0 ) { - - $dir = $args[0]; - - # Check Protection - if ( isset( $options['protect'] ) && isset( $options['unprotect'] ) ) - die( "Cannot specify both protect and unprotect. Only 1 is allowed.\n" ); - - if ( isset( $options['protect'] ) && $options['protect'] == 1 ) - die( "You must specify a protection option.\n" ); - - # Prepare the list of allowed extensions - global $wgFileExtensions; - $extensions = isset( $options['extensions'] ) - ? explode( ',', strtolower( $options['extensions'] ) ) - : $wgFileExtensions; - - # Search the path provided for candidates for import - $files = findFiles( $dir, $extensions ); - - # Initialise the user for this operation - $user = isset( $options['user'] ) - ? User::newFromName( $options['user'] ) - : User::newFromName( 'Maintenance script' ); - if ( !$user instanceof User ) - $user = User::newFromName( 'Maintenance script' ); - $wgUser = $user; - - # Get block check. If a value is given, this specified how often the check is performed - if ( isset( $options['check-userblock'] ) ) { - if ( !$options['check-userblock'] ) $checkUserBlock = 1; - else $checkUserBlock = (int)$options['check-userblock']; +if ( count( $args ) == 0 ) { + showUsage(); +} + +$dir = $args[0]; + +# Check Protection +if ( isset( $options['protect'] ) && isset( $options['unprotect'] ) ) { + die( "Cannot specify both protect and unprotect. Only 1 is allowed.\n" ); +} + +if ( isset( $options['protect'] ) && $options['protect'] == 1 ) { + die( "You must specify a protection option.\n" ); +} + +# Prepare the list of allowed extensions +global $wgFileExtensions; +$extensions = isset( $options['extensions'] ) + ? explode( ',', strtolower( $options['extensions'] ) ) + : $wgFileExtensions; + +# Search the path provided for candidates for import +$files = findFiles( $dir, $extensions ); + +# Initialise the user for this operation +$user = isset( $options['user'] ) + ? User::newFromName( $options['user'] ) + : User::newFromName( 'Maintenance script' ); +if ( !$user instanceof User ) { + $user = User::newFromName( 'Maintenance script' ); +} +$wgUser = $user; + +# Get block check. If a value is given, this specified how often the check is performed +if ( isset( $options['check-userblock'] ) ) { + if ( !$options['check-userblock'] ) { + $checkUserBlock = 1; } else { - $checkUserBlock = false; + $checkUserBlock = (int)$options['check-userblock']; } +} else { + $checkUserBlock = false; +} - # Get --from - $from = @$options['from']; +# Get --from +$from = @$options['from']; - # Get sleep time. - $sleep = @$options['sleep']; - if ( $sleep ) $sleep = (int)$sleep; +# Get sleep time. +$sleep = @$options['sleep']; +if ( $sleep ) { + $sleep = (int)$sleep; +} - # Get limit number - $limit = @$options['limit']; - if ( $limit ) $limit = (int)$limit; +# Get limit number +$limit = @$options['limit']; +if ( $limit ) { + $limit = (int)$limit; +} - # Get the upload comment. Provide a default one in case there's no comment given. - $comment = 'Importing image file'; +# Get the upload comment. Provide a default one in case there's no comment given. +$comment = 'Importing image file'; - if ( isset( $options['comment-file'] ) ) { - $comment = file_get_contents( $options['comment-file'] ); - if ( $comment === false || $comment === NULL ) { - die( "failed to read comment file: {$options['comment-file']}\n" ); - } - } - elseif ( isset( $options['comment'] ) ) { - $comment = $options['comment']; +if ( isset( $options['comment-file'] ) ) { + $comment = file_get_contents( $options['comment-file'] ); + if ( $comment === false || $comment === null ) { + die( "failed to read comment file: {$options['comment-file']}\n" ); } +} elseif ( isset( $options['comment'] ) ) { + $comment = $options['comment']; +} + +$commentExt = isset( $options['comment-ext'] ) ? $options['comment-ext'] : false; - $commentExt = isset( $options['comment-ext'] ) ? $options['comment-ext'] : false; +# Get the license specifier +$license = isset( $options['license'] ) ? $options['license'] : ''; - # Get the license specifier - $license = isset( $options['license'] ) ? $options['license'] : ''; +# Batch "upload" operation +$count = count( $files ); +if ( $count > 0 ) { - # Batch "upload" operation - if ( ( $count = count( $files ) ) > 0 ) { + foreach ( $files as $file ) { + $base = wfBaseName( $file ); - foreach ( $files as $file ) { - $base = wfBaseName( $file ); + # Validate a title + $title = Title::makeTitleSafe( NS_FILE, $base ); + if ( !is_object( $title ) ) { + echo( "{$base} could not be imported; a valid title cannot be produced\n" ); + continue; + } - # Validate a title - $title = Title::makeTitleSafe( NS_FILE, $base ); - if ( !is_object( $title ) ) { - echo( "{$base} could not be imported; a valid title cannot be produced\n" ); + if ( $from ) { + if ( $from == $title->getDBkey() ) { + $from = null; + } else { + $ignored++; continue; } + } - if ( $from ) { - if ( $from == $title->getDBkey() ) { - $from = NULL; - } else { - $ignored++; - continue; - } + if ( $checkUserBlock && ( ( $processed % $checkUserBlock ) == 0 ) ) { + $user->clearInstanceCache( 'name' ); // reload from DB! + if ( $user->isBlocked() ) { + echo( $user->getName() . " was blocked! Aborting.\n" ); + break; } + } - if ( $checkUserBlock && ( ( $processed % $checkUserBlock ) == 0 ) ) { - $user->clearInstanceCache( 'name' ); // reload from DB! - if ( $user->isBlocked() ) { - echo( $user->getName() . " was blocked! Aborting.\n" ); - break; - } + # Check existence + $image = wfLocalFile( $title ); + if ( $image->exists() ) { + if ( isset( $options['overwrite'] ) ) { + echo( "{$base} exists, overwriting..." ); + $svar = 'overwritten'; + } else { + echo( "{$base} exists, skipping\n" ); + $skipped++; + continue; } + } 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. - # Check existence - $image = wfLocalFile( $title ); - if ( $image->exists() ) { - if ( isset( $options['overwrite'] ) ) { - echo( "{$base} exists, overwriting..." ); - $svar = 'overwritten'; - } else { - echo( "{$base} exists, skipping\n" ); + $dupes = $repo->findBySha1( $sha1 ); + + if ( $dupes ) { + echo( "{$base} already exists as " . $dupes[0]->getName() . ", skipping\n" ); $skipped++; continue; } - } 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. + } - $dupes = $repo->findBySha1( $sha1 ); + echo( "Importing {$base}..." ); + $svar = 'added'; + } - if ( $dupes ) { - echo( "{$base} already exists as " . $dupes[0]->getName() . ", skipping\n" ); - $skipped++; - continue; - } + if ( isset( $options['source-wiki-url'] ) ) { + /* find comment text directly from source wiki, through MW's API */ + $real_comment = getFileCommentFromSourceWiki( $options['source-wiki-url'], $base ); + if ( $real_comment === false ) + $commentText = $comment; + else + $commentText = $real_comment; + + /* find user directly from source wiki, through MW's API */ + $real_user = getFileUserFromSourceWiki( $options['source-wiki-url'], $base ); + if ( $real_user === false ) { + $wgUser = $user; + } else { + $wgUser = User::newFromName( $real_user ); + if ( $wgUser === false ) { + # user does not exist in target wiki + echo ( "failed: user '$real_user' does not exist in target wiki." ); + continue; } - - echo( "Importing {$base}..." ); - $svar = 'added'; } - - if ( isset( $options['source-wiki-url'] ) ) { - /* find comment text directly from source wiki, through MW's API */ - $real_comment = getFileCommentFromSourceWiki( $options['source-wiki-url'], $base ); - if ( $real_comment === false ) - $commentText = $comment; - else - $commentText = $real_comment; - - /* find user directly from source wiki, through MW's API */ - $real_user = getFileUserFromSourceWiki( $options['source-wiki-url'], $base ); - if ( $real_user === false ) { - $wgUser = $user; + } else { + # Find comment text + $commentText = false; + + if ( $commentExt ) { + $f = findAuxFile( $file, $commentExt ); + if ( !$f ) { + echo( " No comment file with extension {$commentExt} found for {$file}, using default comment. " ); } else { - $wgUser = User::newFromName( $real_user ); - if ( $wgUser === false ) { - # user does not exist in target wiki - echo ( "failed: user '$real_user' does not exist in target wiki." ); - continue; + $commentText = file_get_contents( $f ); + if ( !$commentText ) { + echo( " Failed to load comment file {$f}, using default comment. " ); } } - } else { - # Find comment text - $commentText = false; - - if ( $commentExt ) { - $f = findAuxFile( $file, $commentExt ); - if ( !$f ) { - echo( " No comment file with extension {$commentExt} found for {$file}, using default comment. " ); - } else { - $commentText = file_get_contents( $f ); - if ( !$f ) { - echo( " Failed to load comment file {$f}, using default comment. " ); - } - } - } - - if ( !$commentText ) { - $commentText = $comment; - } } + if ( !$commentText ) { + $commentText = $comment; + } + } - # Import the file - if ( isset( $options['dry'] ) ) { - echo( " publishing {$file} by '" . $wgUser->getName() . "', comment '$commentText'... " ); - } else { - $archive = $image->publish( $file ); - if ( !$archive->isGood() ) { - echo( "failed. (" . - $archive->getWikiText() . - ")\n" ); - $failed++; - continue; - } + # Import the file + if ( isset( $options['dry'] ) ) { + echo( " publishing {$file} by '" . $wgUser->getName() . "', comment '$commentText'... " ); + } else { + $archive = $image->publish( $file ); + if ( !$archive->isGood() ) { + echo( "failed. (" . + $archive->getWikiText() . + ")\n" ); + $failed++; + continue; } + } + + if ( isset( $options['dry'] ) ) { + echo( "done.\n" ); + } elseif ( $image->recordUpload( $archive->value, $commentText, $license ) ) { + # We're done! + echo( "done.\n" ); $doProtect = false; - $restrictions = array(); global $wgRestrictionLevels; $protectLevel = isset( $options['protect'] ) ? $options['protect'] : null; if ( $protectLevel && in_array( $protectLevel, $wgRestrictionLevels ) ) { - $restrictions['move'] = $protectLevel; - $restrictions['edit'] = $protectLevel; - $doProtect = true; + $doProtect = true; } if ( isset( $options['unprotect'] ) ) { - $restrictions['move'] = ''; - $restrictions['edit'] = ''; - $doProtect = true; + $protectLevel = ''; + $doProtect = true; } + if ( $doProtect ) { + # Protect the file + echo "\nWaiting for slaves...\n"; + // Wait for slaves. + sleep( 2.0 ); # Why this sleep? + wfWaitForSlaves(); - if ( isset( $options['dry'] ) ) { - echo( "done.\n" ); - } elseif ( $image->recordUpload( $archive->value, $commentText, $license ) ) { - # We're done! - echo( "done.\n" ); - if ( $doProtect ) { - # Protect the file - $article = new Article( $title ); - echo "\nWaiting for slaves...\n"; - // Wait for slaves. - sleep( 2.0 ); # Why this sleep? - wfWaitForSlaves(); - - echo( "\nSetting image restrictions ... " ); - if ( $article->updateRestrictions( $restrictions ) ) - echo( "done.\n" ); - else - echo( "failed.\n" ); - } + echo( "\nSetting image restrictions ... " ); - } else { - echo( "failed. (at recordUpload stage)\n" ); - $svar = 'failed'; + $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" ); } - $$svar++; - $processed++; + } else { + echo( "failed. (at recordUpload stage)\n" ); + $svar = 'failed'; + } - if ( $limit && $processed >= $limit ) - break; + $$svar++; + $processed++; - if ( $sleep ) - sleep( $sleep ); + if ( $limit && $processed >= $limit ) { + break; } - # 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 ) { - if ( $$var > 0 ) - echo( "{$desc}: {$$var}\n" ); + if ( $sleep ) { + sleep( $sleep ); } + } - } else { - echo( "No suitable files could be found for import.\n" ); + # 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 ) { + if ( $$var > 0 ) + echo( "{$desc}: {$$var}\n" ); } } else { - showUsage(); + echo( "No suitable files could be found for import.\n" ); } exit( 0 ); diff --git a/maintenance/importSiteScripts.php b/maintenance/importSiteScripts.php index 849c7b1b..0dc200ec 100644 --- a/maintenance/importSiteScripts.php +++ b/maintenance/importSiteScripts.php @@ -1,8 +1,11 @@ getOption( 'username', 'ScriptImporter' ) ); + + $user = User::newFromName( $this->getOption( 'username', 'ScriptImporter' ) ); + $wgUser = $user; $baseUrl = $this->getArg( 1 ); $pageList = $this->fetchScriptList(); $this->output( 'Importing ' . count( $pageList ) . " pages\n" ); foreach ( $pageList as $page ) { + $title = Title::makeTitleSafe( NS_MEDIAWIKI, $page ); + if ( !$title ) { + $this->error( "$page is an invalid title; it will not be imported\n" ); + continue; + } + $this->output( "Importing $page\n" ); $url = wfAppendQuery( $baseUrl, array( 'action' => 'raw', 'title' => "MediaWiki:{$page}" ) ); $text = Http::get( $url ); - - $title = Title::makeTitleSafe( NS_MEDIAWIKI, $page ); - $article = new Article( $title ); - $article->doEdit( $text, "Importing from $url", 0 ); + + $wikiPage = WikiPage::factory( $title ); + $wikiPage->doEdit( $text, "Importing from $url", 0, false, $user ); } } diff --git a/maintenance/importTextFile.php b/maintenance/importTextFile.php index b78ae039..ec9ff001 100644 --- a/maintenance/importTextFile.php +++ b/maintenance/importTextFile.php @@ -57,8 +57,8 @@ if ( count( $args ) < 1 || isset( $options['help'] ) ) { $flags = 0 | ( isset( $options['norc'] ) ? EDIT_SUPPRESS_RC : 0 ); echo( "\nPerforming edit..." ); - $article = new Article( $title ); - $article->doEdit( $text, $comment, $flags ); + $page = WikiPage::factory( $title ); + $page->doEdit( $text, $comment, $flags, false, $user ); echo( "done.\n" ); } else { diff --git a/maintenance/importUseModWiki.php b/maintenance/importUseModWiki.php deleted file mode 100644 index a28d57a5..00000000 --- a/maintenance/importUseModWiki.php +++ /dev/null @@ -1,375 +0,0 @@ - - * Based loosely on Magnus's code from 2001-2002 - * - * Updated limited version to get something working temporarily - * 2003-10-09 - * Be sure to run the link & index rebuilding scripts! - * - * Some more munging for charsets etc - * 2003-11-28 - * - * Partial fix for pages starting with lowercase letters (??) - * and CamelCase and /Subpage link conversion - * 2004-11-17 - * - * Rewrite output to create Special:Export format for import - * instead of raw SQL. Should be 'future-proof' against future - * schema changes. - * 2005-03-14 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @todo document - * @file - * @ingroup Maintenance - */ - -require_once( "Maintenance.php" ); - -class ImportUseModWiki extends Maintenance { - - private $encoding, $rootDirectory = ''; - - /** - * Field separators - * @var String - */ - private $FS1, $FS2, $FS3 = ''; - - /** - * @var Array - */ - private $usercache, $nowiki = array(); - - public function __construct() { - parent::__construct(); - $this->mDescription = "Import pages from UseMod wikis"; - $this->addOption( 'encoding', 'Encoding of the imported text, default CP1252', false, true ); - /** - * If UseModWiki's New File System is used: - * $NewFS = 1; # 1 = new multibyte $FS, 0 = old $FS - * Use "\xb3"; for the Old File System - * Changed with UTF-8 UseModWiki - * http://www.usemod.com/cgi-bin/wiki.pl?SupportForUtf8 - * http://www.usemod.com/cgi-bin/wiki.pl?WikiBugs/NewFieldSeparatorWronglyTreated - * http://www.meatballwiki.org/wiki/WikiEngine#Q_amp_A - */ - $this->addOption( 'separator', 'Field separator to use, default \x1E\xFF\xFE\x1E', false, true ); - $this->addArg( 'path', 'Path to your UseMod wiki' ); - } - - public function execute() { - $this->rootDirectory = $this->getArg(); - $this->encoding = $this->getOption( 'encoding', 'CP1252' ); - $sep = $this->getOption( 'separator', "\x1E\xFF\xFE\x1E" ); - $this->FS1 = "{$sep}1"; - $this->FS2 = "{$sep}2"; - $this->FS3 = "{$sep}3"; - - echo << - - - -XML; - $letters = array( - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', - 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', - 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'other' ); - foreach ( $letters as $letter ) { - $dir = "{$this->rootDirectory}/page/$letter"; - if ( is_dir( $dir ) ) - $this->importPageDirectory( $dir ); - } - echo << - -XML; - } - - private function importPageDirectory( $dir, $prefix = "" ) { - echo "\n\n"; - $mydir = opendir( $dir ); - while ( $entry = readdir( $mydir ) ) { - $m = array(); - if ( preg_match( '/^(.+)\.db$/', $entry, $m ) ) { - echo $this->importPage( $prefix . $m[1] ); - } else { - if ( is_dir( "$dir/$entry" ) ) { - if ( $entry != '.' && $entry != '..' ) { - $this->importPageDirectory( "$dir/$entry", "$entry/" ); - } - } else { - echo "\n"; - } - } - } - } - - private function useModFilename( $title ) { - $c = substr( $title, 0, 1 ); - if ( preg_match( '/[A-Z]/i', $c ) ) { - return strtoupper( $c ) . "/$title"; - } - return "other/$title"; - } - - private function fetchPage( $title ) { - $fname = $this->rootDirectory . "/page/" . $this->useModFilename( $title ) . ".db"; - if ( !file_exists( $fname ) ) { - echo "Couldn't open file '$fname' for page '$title'.\n"; - die( -1 ); - } - - $page = $this->splitHash( $this->FS1, file_get_contents( $fname ) ); - $section = $this->splitHash( $this->FS2, $page["text_default"] ); - $text = $this->splitHash( $this->FS3, $section["data"] ); - - return $this->array2object( array( "text" => $text["text"] , "summary" => $text["summary"] , - "minor" => $text["minor"] , "ts" => $section["ts"] , - "username" => $section["username"] , "host" => $section["host"] ) ); - } - - private function fetchKeptPages( $title ) { - $fname = $this->rootDirectory . "/keep/" . $this->useModFilename( $title ) . ".kp"; - if ( !file_exists( $fname ) ) return array(); - - $keptlist = explode( $this->FS1, file_get_contents( $fname ) ); - array_shift( $keptlist ); # Drop the junk at beginning of file - - $revisions = array(); - foreach ( $keptlist as $rev ) { - $section = $this->splitHash( $this->FS2, $rev ); - $text = $this->splitHash( $this->FS3, $section["data"] ); - if ( $text["text"] && $text["minor"] != "" && ( $section["ts"] * 1 > 0 ) ) { - array_push( $revisions, $this->array2object( array ( "text" => $text["text"] , "summary" => $text["summary"] , - "minor" => $text["minor"] , "ts" => $section["ts"] , - "username" => $section["username"] , "host" => $section["host"] ) ) ); - } else { - echo "\n"; - } - } - return $revisions; - } - - private function splitHash( $sep , $str ) { - $temp = explode ( $sep , $str ) ; - $ret = array () ; - for ( $i = 0; $i + 1 < count ( $temp ) ; $i++ ) { - $ret[$temp[$i]] = $temp[++$i] ; - } - return $ret ; - } - - private function checkUserCache( $name, $host ) { - if ( $name ) { - if ( in_array( $name, $this->usercache ) ) { - $userid = $this->usercache[$name]; - } else { - # If we haven't imported user accounts - $userid = 0; - } - $username = str_replace( '_', ' ', $name ); - } else { - $userid = 0; - $username = $host; - } - return array( $userid, $username ); - } - - private function importPage( $title ) { - echo "\n\n"; - $page = $this->fetchPage( $title ); - - $newtitle = $this->xmlsafe( str_replace( '_', ' ', $this->recodeText( $title ) ) ); - - $munged = $this->mungeFormat( $page->text ); - if ( $munged != $page->text ) { - /** - * Save a *new* revision with the conversion, and put the - * previous last version into the history. - */ - $next = $this->array2object( array( - 'text' => $munged, - 'minor' => 1, - 'username' => 'Conversion script', - 'host' => '127.0.0.1', - 'ts' => time(), - 'summary' => 'link fix', - ) ); - $revisions = array( $page, $next ); - } else { - /** - * Current revision: - */ - $revisions = array( $page ); - } - $xml = << - $newtitle - -XML; - - # History - $revisions = array_merge( $revisions, $this->fetchKeptPages( $title ) ); - if ( count( $revisions ) == 0 ) { - return NULL; // Was "$sql", which does not appear to be defined. - } - - foreach ( $revisions as $rev ) { - $text = $this->xmlsafe( $this->recodeText( $rev->text ) ); - $minor = ( $rev->minor ? '' : '' ); - list( /* $userid */ , $username ) = $this->checkUserCache( $rev->username, $rev->host ); - $username = $this->xmlsafe( $this->recodeText( $username ) ); - $timestamp = $this->xmlsafe( $this->timestamp2ISO8601( $rev->ts ) ); - $comment = $this->xmlsafe( $this->recodeText( $rev->summary ) ); - - $xml .= << - $timestamp - $username - $minor - $comment - $text - - -XML; - } - $xml .= "\n\n"; - return $xml; - } - - private function recodeText( $string ) { - # For currently latin-1 wikis - $string = str_replace( "\r\n", "\n", $string ); - $string = @iconv( $this->encoding, "UTF-8", $string ); - $string = $this->mungeToUtf8( $string ); # Any old Ӓ stuff - return $string; - } - - /** - * @todo FIXME: Don't use /e - */ - private function mungeToUtf8( $string ) { - $string = preg_replace ( '/&#([0-9]+);/e', 'wfUtf8Sequence($1)', $string ); - $string = preg_replace ( '/&#x([0-9a-f]+);/ie', 'wfUtf8Sequence(0x$1)', $string ); - # Should also do named entities here - return $string; - } - - private function timestamp2ISO8601( $ts ) { - # 2003-08-05T18:30:02Z - return gmdate( 'Y-m-d', $ts ) . 'T' . gmdate( 'H:i:s', $ts ) . 'Z'; - } - - /** - * The page may contain old data which has not been properly normalized. - * Invalid UTF-8 sequences or forbidden control characters will make our - * XML output invalid, so be sure to strip them out. - * @param String $string Text to clean up - * @return String - */ - private function xmlsafe( $string ) { - $string = UtfNormal::cleanUp( $string ); - $string = htmlspecialchars( $string ); - return $string; - } - - private function xmlCommentSafe( $text ) { - return str_replace( '--', '\\-\\-', $this->xmlsafe( $this->recodeText( $text ) ) ); - } - - private function array2object( $arr ) { - $o = (object)0; - foreach ( $arr as $x => $y ) { - $o->$x = $y; - } - return $o; - } - - /** - * Make CamelCase and /Talk links work - */ - private function mungeFormat( $text ) { - $this->nowiki = array(); - $staged = preg_replace_callback( - '/(.*?<\\/nowiki>|(?:http|https|ftp):\\S+|\[\[[^]\\n]+]])/s', - array( $this, 'nowikiPlaceholder' ), $text ); - - # This is probably not 100% correct, I'm just - # glancing at the UseModWiki code. - $upper = "[A-Z]"; - $lower = "[a-z_0-9]"; - $any = "[A-Za-z_0-9]"; - $camel = "(?:$upper+$lower+$upper+$any*)"; - $subpage = "(?:\\/$any+)"; - $substart = "(?:\\/$upper$any*)"; - - $munged = preg_replace( "/(?!\\[\\[)($camel$subpage*|$substart$subpage*)\\b(?!\\]\\]|>)/", - '[[$1]]', $staged ); - - $final = preg_replace( '/' . preg_quote( $this->placeholder() ) . '/s', - array( $this, 'nowikiShift' ), $munged ); - return $final; - } - - private function placeholder( $x = null ) { - return '\xffplaceholder\xff'; - } - - public function nowikiPlaceholder( $matches ) { - $this->nowiki[] = $matches[1]; - return $this->placeholder(); - } - - public function nowikiShift() { - return array_shift( $this->nowiki ); - } -} - -function wfUtf8Sequence( $codepoint ) { - if ( $codepoint < 0x80 ) { - return chr( $codepoint ); - } - if ( $codepoint < 0x800 ) { - return chr( $codepoint >> 6 & 0x3f | 0xc0 ) . - chr( $codepoint & 0x3f | 0x80 ); - } - if ( $codepoint < 0x10000 ) { - return chr( $codepoint >> 12 & 0x0f | 0xe0 ) . - chr( $codepoint >> 6 & 0x3f | 0x80 ) . - chr( $codepoint & 0x3f | 0x80 ); - } - if ( $codepoint < 0x100000 ) { - return chr( $codepoint >> 18 & 0x07 | 0xf0 ) . # Double-check this - chr( $codepoint >> 12 & 0x3f | 0x80 ) . - chr( $codepoint >> 6 & 0x3f | 0x80 ) . - chr( $codepoint & 0x3f | 0x80 ); - } - # Doesn't yet handle outside the BMP - return "&#$codepoint;"; -} - -$maintClass = 'ImportUseModWiki'; -require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/importUseModWikipedia.php b/maintenance/importUseModWikipedia.php deleted file mode 100644 index c4b8112f..00000000 --- a/maintenance/importUseModWikipedia.php +++ /dev/null @@ -1,892 +0,0 @@ - 983862286, - 'TexaS' => 983918410, - 'HistoryOfUnitedStatesTalk' => 984795423, - 'MetallicA' => 985128533, - 'PythagoreanTheorem' => 985225545, - 'TheCanonofScripture' => 985368223, - 'TaoTehChing' => 985368222, - //'TheMostRemarkableFormulaInTheWorld' => 985368221, - 'TheRecorder' => 985368220, - 'GladstoneOregon' => 985368219, - 'PacificBeach' => '?', - 'AaRiver' => '?', - ); - - var $replacements = array(); - - var $renameTextLinksOps = array( - 983846265 => array( - 'TestIgnore' => 'IgnoreTest', - ), - 983848080 => array( - 'UnitedLocomotiveWorks' => 'Atlas Shrugged/United Locomotive Works' - ), - 983856376 => array( - 'WikiPedia' => 'Wikipedia', - ), - 983896152 => array( - 'John_F_Kennedy' => 'John_F._Kennedy', - ), - 983905871 => array( - 'LarrySanger' => 'Larry_Sanger' - ), - 984697068 => array( - 'UnitedStates' => 'United States', - ), - 984792748 => array( - 'LibertarianisM' => 'Libertarianism' - ), - 985327832 => array( - 'AnarchisM' => 'Anarchism', - ), - 985290063 => array( - 'HistoryOfUnitedStatesDiscussion' => 'History_Of_United_States_Discussion' - ), - 985290091 => array( - 'BritishEmpire' => 'British Empire' - ), - /* - 985468958 => array( - 'ScienceFiction' => 'Science fiction', - ),*/ - ); - - /** - * Hack for observed substitution issues - */ - var $skipSelfSubstitution = array( - 'Pythagorean_Theorem', - 'The_Most_Remarkable_Formula_In_The_World', - 'Wine', - ); - - var $unixLineEndingsOps = array( - 987743732 => 'Wikipedia_FAQ' - ); - - var $replacementsDone = array(); - - var $moveLog = array(); - var $moveDests = array(); - var $revId; - - var $rc = array(); - var $textCache = array(); - var $blacklist = array(); - - var $FS, $FS1, $FS2, $FS3; - var $FreeLinkPattern, $UrlPattern, $LinkPattern, $InterLinkPattern; - - var $cp1252Table = array( -0x80 => 0x20ac, -0x81 => 0x0081, -0x82 => 0x201a, -0x83 => 0x0192, -0x84 => 0x201e, -0x85 => 0x2026, -0x86 => 0x2020, -0x87 => 0x2021, -0x88 => 0x02c6, -0x89 => 0x2030, -0x8a => 0x0160, -0x8b => 0x2039, -0x8c => 0x0152, -0x8d => 0x008d, -0x8e => 0x017d, -0x8f => 0x008f, -0x90 => 0x0090, -0x91 => 0x2018, -0x92 => 0x2019, -0x93 => 0x201c, -0x94 => 0x201d, -0x95 => 0x2022, -0x96 => 0x2013, -0x97 => 0x2014, -0x98 => 0x02dc, -0x99 => 0x2122, -0x9a => 0x0161, -0x9b => 0x203a, -0x9c => 0x0153, -0x9d => 0x009d, -0x9e => 0x017e, -0x9f => 0x0178); - - public function __construct() { - parent::__construct(); - $this->addOption( 'datadir', 'the value of $DataDir from wiki.cgi', true, true ); - $this->addOption( 'outfile', 'the name of the output XML file', true, true ); - $this->initLinkPatterns(); - - $this->encodeMap = $this->decodeMap = array(); - - for ($source = 0; $source <= 0xff; $source++) { - if ( isset( $this->cp1252Table[$source] ) ) { - $dest = $this->cp1252Table[$source]; - } else { - $dest = $source; - } - $sourceChar = chr( $source ); - $destChar = codepointToUtf8( $dest ); - $this->encodeMap[$sourceChar] = $destChar; - $this->decodeMap[$destChar] = $sourceChar; - } - } - - function initLinkPatterns() { - # Field separators are used in the URL-style patterns below. - $this->FS = "\xb3"; # The FS character is a superscript "3" - $this->FS1 = $this->FS . "1"; # The FS values are used to separate fields - $this->FS2 = $this->FS . "2"; # in stored hashtables and other data structures. - $this->FS3 = $this->FS . "3"; # The FS character is not allowed in user data. - - $UpperLetter = "[A-Z"; - $LowerLetter = "[a-z"; - $AnyLetter = "[A-Za-z"; - $AnyLetter .= "_0-9"; - $UpperLetter .= "]"; $LowerLetter .= "]"; $AnyLetter .= "]"; - - # Main link pattern: lowercase between uppercase, then anything - $LpA = $UpperLetter . "+" . $LowerLetter . "+" . $UpperLetter - . $AnyLetter . "*"; - # Optional subpage link pattern: uppercase, lowercase, then anything - $LpB = $UpperLetter . "+" . $LowerLetter . "+" . $AnyLetter . "*"; - - # Loose pattern: If subpage is used, subpage may be simple name - $this->LinkPattern = "((?:(?:$LpA)?\\/$LpB)|$LpA)"; - $QDelim = '(?:"")?'; # Optional quote delimiter (not in output) - $this->LinkPattern .= $QDelim; - - # Inter-site convention: sites must start with uppercase letter - # (Uppercase letter avoids confusion with URLs) - $InterSitePattern = $UpperLetter . $AnyLetter . "+"; - $this->InterLinkPattern = "((?:$InterSitePattern:[^\\]\\s\"<>{$this->FS}]+)$QDelim)"; - - $AnyLetter = "[-,. _0-9A-Za-z]"; - $this->FreeLinkPattern = "($AnyLetter+)"; - $this->FreeLinkPattern = "((?:(?:$AnyLetter+)?\\/)?$AnyLetter+)"; - $this->FreeLinkPattern .= $QDelim; - - # Url-style links are delimited by one of: - # 1. Whitespace (kept in output) - # 2. Left or right angle-bracket (< or >) (kept in output) - # 3. Right square-bracket (]) (kept in output) - # 4. A single double-quote (") (kept in output) - # 5. A $FS (field separator) character (kept in output) - # 6. A double double-quote ("") (removed from output) - - $UrlProtocols = "http|https|ftp|afs|news|nntp|mid|cid|mailto|wais|" - . "prospero|telnet|gopher"; - $UrlProtocols .= '|file'; - $this->UrlPattern = "((?:(?:$UrlProtocols):[^\\]\\s\"<>{$this->FS}]+)$QDelim)"; - $ImageExtensions = "(gif|jpg|png|bmp|jpeg)"; - $RFCPattern = "RFC\\s?(\\d+)"; - $ISBNPattern = "ISBN:?([0-9- xX]{10,})"; - } - - function execute() { - $this->articleFileName = '/tmp/importUseMod.' . mt_rand( 0, 0x7ffffff ) . '.tmp'; - $this->patchFileName = '/tmp/importUseMod.' . mt_rand( 0, 0x7ffffff ) . '.tmp'; - $this->dataDir = $this->getOption( 'datadir' ); - $this->outFile = fopen( $this->getOption( 'outfile' ), 'w' ); - if ( !$this->outFile ) { - echo "Unable to open output file\n"; - return 1; - } - $this->writeXmlHeader(); - $this->readRclog(); - $this->writeMoveLog(); - $this->writeRevisions(); - $this->reconcileCurrentRevs(); - $this->writeXmlFooter(); - unlink( $this->articleFileName ); - unlink( $this->patchFileName ); - return 0; - } - - function writeXmlHeader() { - fwrite( $this->outFile, << - - Wikipedia - http://www.wikipedia.com/ - MediaWiki 1.18alpha importUseModWikipedia.php - case-sensitive - - - - - -EOT - ); - } - - function writeXmlFooter() { - fwrite( $this->outFile, "\n" ); - } - - function readRclog() { - $rcFile = fopen( "{$this->dataDir}/rclog", 'r' ); - while ( $line = fgets( $rcFile ) ) { - $bits = explode( $this->FS3, $line ); - if ( count( $bits ) !== 7 ) { - echo "Error reading rclog\n"; - return; - } - $params = array( - 'timestamp' => $bits[0], - 'rctitle' => $bits[1], - 'summary' => $bits[2], - 'minor' => $bits[3], - 'host' => $bits[4], - 'kind' => $bits[5], - 'extra' => array() - ); - $extraList = explode( $this->FS2, $bits[6] ); - - for ( $i = 0; $i < count( $extraList ); $i += 2 ) { - $params['extra'][$extraList[$i]] = $extraList[$i + 1]; - } - $this->rc[$params['timestamp']][] = $params; - } - } - - function writeMoveLog() { - $this->moveLog = array(); - $deepRenames = $this->deepRenames; - echo "Calculating move log...\n"; - $this->processDiffFile( array( $this, 'moveLogCallback' ) ); - - // We have the timestamp intervals, now make a guess at the actual timestamp - foreach ( $this->moveLog as $newTitle => $params ) { - // Is there a time specified? - $drTime = false; - if ( isset( $deepRenames[$params['old']] ) ) { - $drTime = $deepRenames[$params['old']]; - if ( $drTime !== '?' ) { - if ( ( !isset( $params['endTime'] ) || $drTime < $params['endTime'] ) - && $drTime > $params['startTime'] ) - { - $this->moveLog[$newTitle]['timestamp'] = $drTime; - $this->moveLog[$newTitle]['deep'] = true; - - echo "{$params['old']} -> $newTitle at $drTime\n"; - unset( $deepRenames[$params['old']] ); - continue; - } else { - echo "WARNING: deep rename time invalid: {$params['old']}\n"; - unset( $deepRenames[$params['old']] ); - } - } - } - - // Guess that it is one second after the last edit to the page before it was moved - $this->moveLog[$newTitle]['timestamp'] = $params['startTime'] + 1; - if ( $drTime === '?' ) { - $this->moveLog[$newTitle]['deep'] = true; - unset( $deepRenames[$params['old']] ); - } - if ( isset( $params['endTime'] ) ) { - $this->printLatin1( "{$params['old']} -> $newTitle between " . - "{$params['startTime']} and {$params['endTime']}\n" ); - } else { - $this->printLatin1( "{$params['old']} -> $newTitle after " . - "{$params['startTime']}\n" ); - } - } - - // Write the move log to the XML file - $id = 1; - foreach ( $this->moveLog as $newTitle => $params ) { - $out = "\n" . - $this->element( 'id', $id++ ) . - $this->element( 'timestamp', wfTimestamp( TS_ISO_8601, $params['timestamp'] ) ) . - "\n" . - $this->element( 'username', 'UseModWiki admin' ) . - "" . - $this->element( 'type', 'move' ) . - $this->element( 'action', 'move' ) . - $this->element( 'logtitle', $params['old'] ) . - "" . - htmlspecialchars( $this->encode( "{$newTitle}\n1" ) ) . - "\n" . - "\n"; - fwrite( $this->outFile, $out ); - } - - // Check for remaining deep rename entries - if ( $deepRenames ) { - echo "WARNING: the following entries in \$this->deepRenames are " . - "invalid, since no such move exists:\n" . - implode( "\n", array_keys( $deepRenames ) ) . - "\n\n"; - } - - } - - function element( $name, $value ) { - return "<$name>" . htmlspecialchars( $this->encode( $value ) ) . "\n"; - } - - function moveLogCallback( $entry ) { - $rctitle = $entry['rctitle']; - $title = $entry['title']; - $this->moveDests[$rctitle] = $title; - - if ( $rctitle === $title ) { - if ( isset( $this->moveLog[$rctitle] ) - && !isset( $this->moveLog[$rctitle]['endTime'] ) ) - { - // This is the latest time that the page could have been moved - $this->moveLog[$rctitle]['endTime'] = $entry['timestamp']; - } - } else { - if ( !isset( $this->moveLog[$rctitle] ) ) { - // Initialise the move log entry - $this->moveLog[$rctitle] = array( - 'old' => $title - ); - } - // Update the earliest time the page could have been moved - $this->moveLog[$rctitle]['startTime'] = $entry['timestamp']; - } - } - - function writeRevisions() { - $this->numGoodRevs = 0; - $this->revId = 1; - $this->processDiffFile( array( $this, 'revisionCallback' ) ); - echo "\n\nImported {$this->numGoodRevs} out of {$this->numRevs}\n"; - } - - function revisionCallback( $params ) { - $title = $params['rctitle']; - $editTime = $params['timestamp']; - - if ( isset( $this->blacklist[$title] ) ) { - return; - } - $this->doPendingOps( $editTime ); - - $origText = $this->getText( $title ); - $text = $this->patch( $origText, $params['diff'] ); - if ( $text === false ) { - echo "$editTime $title attempting resolution...\n"; - $linkSubstitutes = $this->resolveFailedDiff( $origText, $params['diff'] ); - if ( !$linkSubstitutes ) { - $this->printLatin1( "$editTime $title DIFF FAILED\n" ); - $this->blacklist[$title] = true; - return; - } - $this->printLatin1( "$editTime $title requires substitutions:\n" ); - $time = $editTime - 1; - foreach ( $linkSubstitutes as $old => $new ) { - $this->printLatin1( "SUBSTITUTE $old -> $new\n" ); - $this->renameTextLinks( $old, $new, $time-- ); - } - $origText = $this->getText( $title ); - $text = $this->patch( $origText, $params['diff'] ); - if ( $text === false ) { - $this->printLatin1( "$editTime $title STILL FAILS!\n" ); - $this->blacklist[$title] = true; - return; - } - - echo "\n"; - } - - $params['text'] = $text; - $this->saveRevision( $params ); - $this->numGoodRevs++; - #$this->printLatin1( "$editTime $title\n" ); - } - - function doPendingOps( $editTime ) { - foreach ( $this->moveLog as $newTitle => $entry ) { - if ( $entry['timestamp'] <= $editTime ) { - unset( $this->moveLog[$newTitle] ); - if ( isset( $entry['deep'] ) ) { - $this->renameTextLinks( $entry['old'], $newTitle, $entry['timestamp'] ); - } - } - } - - foreach ( $this->renameTextLinksOps as $renameTime => $replacements ) { - if ( $editTime >= $renameTime ) { - foreach ( $replacements as $old => $new ) { - $this->printLatin1( "SUBSTITUTE $old -> $new\n" ); - $this->renameTextLinks( $old, $new, $renameTime ); - } - unset( $this->renameTextLinksOps[$renameTime] ); - } - } - - foreach ( $this->unixLineEndingsOps as $fixTime => $title ) { - if ( $editTime >= $fixTime ) { - $this->printLatin1( "$fixTime $title FIXING LINE ENDINGS\n" ); - $text = $this->getText( $title ); - $text = str_replace( "\r", '', $text ); - $this->saveRevision( array( - 'rctitle' => $title, - 'timestamp' => $fixTime, - 'extra' => array( 'name' => 'UseModWiki admin' ), - 'text' => $text, - 'summary' => 'Fixing line endings', - ) ); - unset( $this->unixLineEndingsOps[$fixTime] ); - } - } - } - - function patch( $source, $diff ) { - file_put_contents( $this->articleFileName, $source ); - file_put_contents( $this->patchFileName, $diff ); - $error = wfShellExec( - wfEscapeShellArg( - 'patch', - '-n', - '-r', '-', - '--no-backup-if-mismatch', - '--binary', - $this->articleFileName, - $this->patchFileName - ) . ' 2>&1', - $status - ); - $text = file_get_contents( $this->articleFileName ); - if ( $status || $text === false ) { - return false; - } else { - return $text; - } - } - - function resolveFailedDiff( $origText, $diff ) { - $context = array(); - $diffLines = explode( "\n", $diff ); - for ( $i = 0; $i < count( $diffLines ); $i++ ) { - $diffLine = $diffLines[$i]; - if ( !preg_match( '/^(\d+)(?:,\d+)?[acd]\d+(?:,\d+)?$/', $diffLine, $m ) ) { - continue; - } - - $sourceIndex = intval( $m[1] ); - $i++; - while ( $i < count( $diffLines ) && substr( $diffLines[$i], 0, 1 ) === '<' ) { - $context[$sourceIndex - 1] = substr( $diffLines[$i], 2 ); - $sourceIndex++; - $i++; - } - $i--; - } - - $changedLinks = array(); - $origLines = explode( "\n", $origText ); - foreach ( $context as $i => $contextLine ) { - $origLine = isset( $origLines[$i] ) ? $origLines[$i] : ''; - if ( $contextLine === $origLine ) { - continue; - } - $newChanges = $this->resolveTextChange( $origLine, $contextLine ); - if ( is_array( $newChanges ) ) { - $changedLinks += $newChanges; - } else { - echo "Resolution failure on line " . ( $i + 1 ) . "\n"; - $this->printLatin1( $newChanges ); - } - } - - return $changedLinks; - } - - function resolveTextChange( $source, $dest ) { - $changedLinks = array(); - $sourceLinks = $this->getLinkList( $source ); - $destLinks = $this->getLinkList( $dest ); - $newLinks = array_diff( $destLinks, $sourceLinks ); - $removedLinks = array_diff( $sourceLinks, $destLinks ); - - // Match up the removed links with the new links - foreach ( $newLinks as $newLink ) { - $minDistance = 100000000; - $bestRemovedLink = false; - foreach ( $removedLinks as $removedLink ) { - $editDistance = levenshtein( $newLink, $removedLink ); - if ( $editDistance < $minDistance ) { - $minDistance = $editDistance; - $bestRemovedLink = $removedLink; - } - } - if ( $bestRemovedLink !== false ) { - $changedLinks[$bestRemovedLink] = $newLink; - $newLinks = array_diff( $newLinks, array( $newLink ) ); - $removedLinks = array_diff( $removedLinks, array( $bestRemovedLink ) ); - } - } - - $proposal = $source; - foreach ( $changedLinks as $removedLink => $newLink ) { - $proposal = $this->substituteTextLinks( $removedLink, $newLink, $proposal ); - } - if ( $proposal !== $dest ) { - // Resolution failed - $msg = "Source line: $source\n" . - "Source links: " . implode( ', ', $sourceLinks ) . "\n" . - "Context line: $dest\n" . - "Context links: " . implode( ', ', $destLinks ) . "\n" . - "Proposal: $proposal\n"; - return $msg; - } - return $changedLinks; - } - - function processDiffFile( $callback ) { - $diffFile = fopen( "{$this->dataDir}/diff_log", 'r' ); - - $delimiter = "------\n"; - file_put_contents( $this->articleFileName, "Describe the new page here.\n" ); - - $line = fgets( $diffFile ); - $lineNum = 1; - if ( $line !== $delimiter ) { - echo "Invalid diff file\n"; - return false; - } - $lastReportLine = 0; - $this->numRevs = 0; - - while ( true ) { - $line = fgets( $diffFile ); - $lineNum++; - if ( $line === false ) { - break; - } - if ( $lineNum > $lastReportLine + 1000 ) { - $lastReportLine = $lineNum; - fwrite( STDERR, "$lineNum \r" ); - fflush( STDERR ); - } - $line = trim( $line ); - if ( !preg_match( '/^([^|]+)\|(\d+)$/', $line, $matches ) ) { - echo "Invalid header on line $lineNum\n"; - return true; - } - list( , $title, $editTime ) = $matches; - - $diff = ''; - $diffStartLine = $lineNum; - while ( true ) { - $line = fgets( $diffFile ); - $lineNum++; - if ( $line === $delimiter ) { - break; - } - if ( $line === false ) { - break 2; - } - $diff .= $line; - } - - $this->numRevs++; - - if ( !isset( $this->rc[$editTime] ) ) { - $this->printLatin1( "$editTime $title DELETED, skipping\n" ); - continue; - } - - if ( count( $this->rc[$editTime] ) == 1 ) { - $params = $this->rc[$editTime][0]; - } else { - $params = false; - $candidates = ''; - foreach ( $this->rc[$editTime] as $rc ) { - if ( $rc['rctitle'] === $title ) { - $params = $rc; - break; - } - if ( $candidates === '' ) { - $candidates = $rc['rctitle']; - } else { - $candidates .= ', ' . $rc['rctitle']; - } - } - if ( !$params ) { - $this->printLatin1( "$editTime $title ERROR cannot resolve rclog\n" ); - $this->printLatin1( "$editTime $title CANDIDATES: $candidates\n" ); - continue; - } - } - $params['diff'] = $diff; - $params['title'] = $title; - $params['diffStartLine'] = $diffStartLine; - call_user_func( $callback, $params ); - } - echo "\n"; - - if ( !feof( $diffFile ) ) { - echo "Stopped at line $lineNum\n"; - } - return true; - } - - function reconcileCurrentRevs() { - foreach ( $this->textCache as $title => $text ) { - $fileName = "{$this->dataDir}/page/"; - if ( preg_match( '/^[A-Z]/', $title, $m ) ) { - $fileName .= $m[0]; - } else { - $fileName .= 'other'; - } - $fileName .= "/$title.db"; - - if ( !file_exists( $fileName ) ) { - $this->printLatin1( "ERROR: Cannot find page file for {$title}\n" ); - continue; - } - - $fileContents = file_get_contents( $fileName ); - $page = $this->unserializeUseMod( $fileContents, $this->FS1 ); - $section = $this->unserializeUseMod( $page['text_default'], $this->FS2 ); - $data = $this->unserializeUseMod( $section['data'], $this->FS3 ); - $pageText = $data['text']; - if ( $text !== $pageText ) { - $substs = $this->resolveTextChange( $text, $pageText ); - if ( is_array( $substs ) ) { - foreach ( $substs as $source => $dest ) { - if ( isset( $this->moveLog[$dest] ) ) { - $this->printLatin1( "ERROR: need deep rename: $source\n" ); - } else { - $this->printLatin1( "ERROR: need substitute: $source -> $dest\n" ); - } - } - } else { - $this->printLatin1( "ERROR: unresolved diff in $title:\n" ); - wfSuppressWarnings(); - $diff = xdiff_string_diff( $text, $pageText ) . ''; - wfRestoreWarnings(); - $this->printLatin1( "$diff\n" ); - } - } - } - } - - function makeTitle( $titleText ) { - return Title::newFromText( $this->encode( $titleText ) ); - } - - function getText( $titleText ) { - if ( !isset( $this->textCache[$titleText] ) ) { - return "Describe the new page here.\n"; - } else { - return $this->textCache[$titleText]; - } - } - - function saveRevision( $params ) { - $this->textCache[$params['rctitle']] = $params['text']; - - $out = "\n" . - $this->element( 'title', $params['rctitle'] ) . - "\n" . - $this->element( 'id', $this->revId ++ ) . - $this->element( 'timestamp', wfTimestamp( TS_ISO_8601, $params['timestamp'] ) ) . - "\n"; - if ( isset( $params['extra']['name'] ) ) { - $out .= $this->element( 'username', $params['extra']['name'] ); - } - if ( isset( $params['extra']['id'] ) ) { - $out .= $this->element( 'id', $params['extra']['id'] ); - } - if ( isset( $params['host'] ) ) { - $out .= $this->element( 'ip', $params['host'] ); - } - $out .= - "\n" . - $this->element( 'comment', $params['summary'] ) . - "" . - htmlspecialchars( $this->encode( $params['text'] ) ) . - "\n" . - "\n" . - "\n"; - fwrite( $this->outFile, $out ); - } - - function renameTextLinks( $old, $new, $timestamp ) { - $newWithUnderscores = $new; - $old = str_replace( '_', ' ', $old ); - $new = str_replace( '_', ' ', $new ); - - foreach ( $this->textCache as $title => $oldText ) { - if ( $newWithUnderscores === $title - && in_array( $title, $this->skipSelfSubstitution ) ) - { - // Hack to make Pythagorean_Theorem etc. work - continue; - } - - $newText = $this->substituteTextLinks( $old, $new, $oldText ); - if ( $oldText !== $newText ) { - $this->saveRevision( array( - 'rctitle' => $title, - 'timestamp' => $timestamp, - 'text' => $newText, - 'extra' => array( 'name' => 'Page move link fixup script' ), - 'summary' => '', - 'minor' => true - ) ); - } - } - } - - function substituteTextLinks( $old, $new, $text ) { - $this->saveUrl = array(); - $this->old = $old; - $this->new = $new; - - $text = str_replace( $this->FS, '', $text ); # Remove separators (paranoia) - $text = preg_replace_callback( '/(
(.*?)<\/pre>)/is', 
-			array( $this, 'storeRaw' ), $text );
-		$text = preg_replace_callback( '/((.*?)<\/code>)/is', 
-			array( $this, 'storeRaw' ), $text );
-		$text = preg_replace_callback( '/((.*?)<\/nowiki>)/s', 
-			array( $this, 'storeRaw' ), $text );
-
-		$text = preg_replace_callback( "/\[\[{$this->FreeLinkPattern}\|([^\]]+)\]\]/",
-			array( $this, 'subFreeLink' ), $text );
-		$text = preg_replace_callback( "/\[\[{$this->FreeLinkPattern}\]\]/",
-			array( $this, 'subFreeLink' ), $text );
-		$text = preg_replace_callback( "/(\[{$this->UrlPattern}\s+([^\]]+?)\])/", 
-			array( $this, 'storeRaw' ), $text );
-		$text = preg_replace_callback( "/(\[{$this->InterLinkPattern}\s+([^\]]+?)\])/", 
-			array( $this, 'storeRaw' ), $text );
-		$text = preg_replace_callback( "/(\[?{$this->UrlPattern}\]?)/", 
-			array( $this, 'storeRaw' ), $text );
-		$text = preg_replace_callback( "/(\[?{$this->InterLinkPattern}\]?)/",
-			array( $this, 'storeRaw' ), $text );
-		$text = preg_replace_callback( "/{$this->LinkPattern}/", 
-			array( $this, 'subWikiLink' ), $text );
-
-		$text = preg_replace_callback( "/{$this->FS}(\d+){$this->FS}/", 
-			array( $this, 'restoreRaw' ), $text );   # Restore saved text
-		return $text;
-	}
-
-	function getLinkList( $text ) {
-		$this->saveUrl = array();
-		$this->linkList = array();
-
-		$text = str_replace( $this->FS, '', $text ); # Remove separators (paranoia)
-		$text = preg_replace_callback( '/(
(.*?)<\/pre>)/is', 
-			array( $this, 'storeRaw' ), $text );
-		$text = preg_replace_callback( '/((.*?)<\/code>)/is', 
-			array( $this, 'storeRaw' ), $text );
-		$text = preg_replace_callback( '/((.*?)<\/nowiki>)/s', 
-			array( $this, 'storeRaw' ), $text );
-
-		$text = preg_replace_callback( "/\[\[{$this->FreeLinkPattern}\|([^\]]+)\]\]/",
-			array( $this, 'storeLink' ), $text );
-		$text = preg_replace_callback( "/\[\[{$this->FreeLinkPattern}\]\]/",
-			array( $this, 'storeLink' ), $text );
-		$text = preg_replace_callback( "/(\[{$this->UrlPattern}\s+([^\]]+?)\])/", 
-			array( $this, 'storeRaw' ), $text );
-		$text = preg_replace_callback( "/(\[{$this->InterLinkPattern}\s+([^\]]+?)\])/", 
-			array( $this, 'storeRaw' ), $text );
-		$text = preg_replace_callback( "/(\[?{$this->UrlPattern}\]?)/", 
-			array( $this, 'storeRaw' ), $text );
-		$text = preg_replace_callback( "/(\[?{$this->InterLinkPattern}\]?)/",
-			array( $this, 'storeRaw' ), $text );
-		$text = preg_replace_callback( "/{$this->LinkPattern}/", 
-			array( $this, 'storeLink' ), $text );
-
-		return $this->linkList;
-	}
-
-	function storeRaw( $m ) {
-		$this->saveUrl[] = $m[1];
-		return $this->FS . (count( $this->saveUrl ) - 1) . $this->FS;
-	}
-
-	function subFreeLink( $m ) {
-		$link = $m[1];
-		if ( isset( $m[2] ) ) {
-			$name = $m[2];
-		} else {
-			$name = '';
-		}
-		$oldlink = $link;
-		$link = preg_replace( '/^\s+/', '', $link );
-		$link = preg_replace( '/\s+$/', '', $link );
-		if ( $link == $this->old ) {
-			$link = $this->new;
-		} else {
-			$link = $oldlink;  # Preserve spaces if no match
-		}
-		$link = "[[$link";
-		if ( $name !== "" ) {
-			$link .= "|$name";
-		}
-		$link .= "]]";
-		return $this->storeRaw( array( 1 => $link ) );
-	}
-
-	function subWikiLink( $m ) {
-		$link = $m[1];
-		if ( $link == $this->old ) {
-			$link = $this->new;
-			if ( !preg_match( "/^{$this->LinkPattern}$/", $this->new ) ) {
-				$link = "[[$link]]";
-			}
-		}
-		return $this->storeRaw( array( 1 => $link ) );
-	}
-
-	function restoreRaw( $m ) {
-		return $this->saveUrl[$m[1]];
-	}
-
-	function storeLink( $m ) {
-		$this->linkList[] = $m[1];
-		return $this->storeRaw( $m );
-	}
-
-	function encode( $s ) {
-		return strtr( $s, $this->encodeMap );
-	}
-
-	function decode( $s ) {
-		return strtr( $s, $this->decodeMap );
-	}
-
-	function printLatin1( $s ) {
-		echo $this->encode( $s );
-	}
-
-	function unserializeUseMod( $s, $sep ) {
-		$parts = explode( $sep, $s );
-		$result = array();
-		for ( $i = 0; $i < count( $parts ); $i += 2 ) {
-			$result[$parts[$i]] = $parts[$i+1];
-		}
-		return $result;
-	}
-}
-
-$maintClass = 'ImportUseModWikipedia';
-require_once( RUN_MAINTENANCE_IF_MAIN );
diff --git a/maintenance/install.php b/maintenance/install.php
index dba43400..9a408aa6 100644
--- a/maintenance/install.php
+++ b/maintenance/install.php
@@ -51,6 +51,7 @@ class CommandLineInstaller extends Maintenance {
 		$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( '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( 'dbuser', 'The user to use for normal operations (wikiuser)', false, true );
diff --git a/maintenance/jsparse.php b/maintenance/jsparse.php
index ae6f1f1d..da6798e1 100644
--- a/maintenance/jsparse.php
+++ b/maintenance/jsparse.php
@@ -32,7 +32,6 @@ class JSParseHelper extends Maintenance {
 	}
 
 	public function execute() {
-		$iterations = $this->getOption( 'i', 100 );
 		if ( $this->hasArg() ) {
 			$files = $this->mArgs;
 		} else {
diff --git a/maintenance/language/StatOutputs.php b/maintenance/language/StatOutputs.php
index c6d03641..befd0d72 100644
--- a/maintenance/language/StatOutputs.php
+++ b/maintenance/language/StatOutputs.php
@@ -21,7 +21,7 @@ if ( !defined( 'MEDIAWIKI' ) ) die();
  * @file
  * @ingroup MaintenanceLanguage
  * @author Ævar Arnfjörð Bjarmason 
- * @author Ashar Voultoiz 
+ * @author Antoine Musso 
  */
 
 /** A general output object. Need to be overriden */
@@ -54,7 +54,7 @@ class wikiStatsOutput extends statsOutput {
 		echo 'English (en) is excluded because it is the default localization';
 		if( is_array( $wgDummyLanguageCodes ) ) {
 			$dummyCodes = array();
-			foreach( $wgDummyLanguageCodes as $dummyCode ) {
+			foreach( $wgDummyLanguageCodes as $dummyCode => $correctCode ) {
 				$dummyCodes[] = $wgContLang->getLanguageName( $dummyCode ) . ' (' . $dummyCode . ')';
 			}
 			echo ', as well as the following languages that are not intended for system message translations, usually because they redirect to other language codes: ' . implode( ', ', $dummyCodes );
diff --git a/maintenance/language/checkLanguage.inc b/maintenance/language/checkLanguage.inc
index 1e4b94a2..8ab6125b 100644
--- a/maintenance/language/checkLanguage.inc
+++ b/maintenance/language/checkLanguage.inc
@@ -304,17 +304,17 @@ ENDS;
 
 	/**
 	 * Check a language.
-	 * @param $code The language code.
-	 * @return The results.
+	 * @param $code string The language code.
+	 * @return array The results.
 	 */
 	protected function checkLanguage( $code ) {
 		# Syntax check only
+		$results = array();
 		if ( $this->level === 0 ) {
 			$this->L->getMessages( $code );
-			return;
+			return $results;
 		}
 
-		$results = array();
 		$checkFunctions = $this->getChecks();
 		$checkBlacklist = $this->getCheckBlacklist();
 		foreach ( $this->checks as $check ) {
@@ -643,7 +643,7 @@ ENDS;
 
 	/**
 	 * Check a language and show the results.
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 */
 	protected function checkLanguage( $code ) {
 		foreach( $this->extensions as $extension ) {
diff --git a/maintenance/language/function-list.php b/maintenance/language/function-list.php
deleted file mode 100644
index 7b0e57c2..00000000
--- a/maintenance/language/function-list.php
+++ /dev/null
@@ -1,63 +0,0 @@
-addOption( 'data-dir', 'A directory on the local filesystem ' .
-			'containing allkeys.txt and ucd.all.grouped.xml from unicode.org', 
+			'containing allkeys.txt and ucd.all.grouped.xml from unicode.org',
 			false, true );
 		$this->addOption( 'debug-output', 'Filename for sending debug output to',
 			false, true );
@@ -72,7 +76,7 @@ class GenerateCollationData extends Maintenance {
 		// but do not skip a normal space (U+0020) since
 		// people like to use that as a fake no header symbol.
 		$category = substr( $data['gc'], 0, 1 );
-		if ( strpos( 'LNPS', $category ) === false 
+		if ( strpos( 'LNPS', $category ) === false
 			&& $data['cp'] !== '0020' ) {
 			return;
 		}
@@ -84,7 +88,7 @@ class GenerateCollationData extends Maintenance {
 			return;
 		}
 
-		// Skip the composed Hangul syllables, we will use the bare Jamo 
+		// Skip the composed Hangul syllables, we will use the bare Jamo
 		// as first letters
 		if ( $data['block'] == 'Hangul Syllables' ) {
 			return;
@@ -163,7 +167,7 @@ class GenerateCollationData extends Maintenance {
 			}
 			$this->weights[$cp] = $primary;
 			if ( $tertiary === '.0008'
-				|| $tertiary === '.000E' ) 
+				|| $tertiary === '.000E' )
 			{
 				$goodTertiaryChars[$cp] = true;
 			}
@@ -192,8 +196,8 @@ class GenerateCollationData extends Maintenance {
 		}
 
 		// If one character has a given primary weight sequence, and a second
-		// character has a longer primary weight sequence with an initial 
-		// portion equal to the first character, then remove the second 
+		// character has a longer primary weight sequence with an initial
+		// portion equal to the first character, then remove the second
 		// character. This avoids having characters like U+A732 (double A)
 		// polluting the basic latin sort area.
 
@@ -306,11 +310,12 @@ class UcdXmlReader {
 		while ( $this->xml->name !== 'ucd' && $this->xml->read() );
 		$this->xml->read();
 		return $this->xml;
-	}	
+	}
 
 	/**
-	 * Read the attributes of the current element node and return them 
+	 * Read the attributes of the current element node and return them
 	 * as an array
+	 * @return array
 	 */
 	protected function readAttributes() {
 		$attrs = array();
diff --git a/maintenance/language/languages.inc b/maintenance/language/languages.inc
index 60b1112f..06102ac1 100644
--- a/maintenance/language/languages.inc
+++ b/maintenance/language/languages.inc
@@ -61,7 +61,7 @@ class languages {
 	/**
 	 * Get the language list.
 	 *
-	 * @return The language list.
+	 * @return array The language list.
 	 */
 	public function getLanguages() {
 		return $this->mLanguages;
@@ -70,7 +70,7 @@ class languages {
 	/**
 	 * Get the ignored messages list.
 	 *
-	 * @return The ignored messages list.
+	 * @return array The ignored messages list.
 	 */
 	public function getIgnoredMessages() {
 		return $this->mIgnoredMessages;
@@ -79,7 +79,7 @@ class languages {
 	/**
 	 * Get the optional messages list.
 	 *
-	 * @return The optional messages list.
+	 * @return array The  optional messages list.
 	 */
 	public function getOptionalMessages() {
 		return $this->mOptionalMessages;
@@ -137,7 +137,7 @@ class languages {
 	 * obsolete - messages which should not be translated, either because they do not exist, or they are ignored messages.
 	 * translated - messages which are either required or optional, but translated from English and needed.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 */
 	private function loadMessages( $code ) {
 		if ( isset( $this->mMessages[$code] ) ) {
@@ -203,7 +203,7 @@ class languages {
 	 * obsolete - messages which should not be translated, either because they do not exist, or they are ignored messages.
 	 * translated - messages which are either required or optional, but translated from English and needed.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
 	 * @return string The messages in this language.
 	 */
@@ -220,7 +220,7 @@ class languages {
 	 * ignored - messages which should not be translated to other languages.
 	 * translatable - messages which are either required or optional, but can be translated from English.
 	 *
-	 * @return The general English messages.
+	 * @return array The general English messages.
 	 */
 	public function getGeneralMessages() {
 		$this->loadGeneralMessages();
@@ -230,9 +230,9 @@ class languages {
 	/**
 	 * Get fallback language code for a specific language.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return Fallback code.
+	 * @return string Fallback code.
 	 */
 	public function getFallback( $code ) {
 		$this->loadFile( $code );
@@ -242,7 +242,7 @@ class languages {
 	/**
 	 * Get namespace names for a specific language.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
 	 * @return Namespace names.
 	 */
@@ -254,7 +254,7 @@ class languages {
 	/**
 	 * Get namespace aliases for a specific language.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
 	 * @return Namespace aliases.
 	 */
@@ -266,7 +266,7 @@ class languages {
 	/**
 	 * Get magic words for a specific language.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
 	 * @return Magic words.
 	 */
@@ -278,7 +278,7 @@ class languages {
 	/**
 	 * Get special page aliases for a specific language.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
 	 * @return Special page aliases.
 	 */
@@ -290,9 +290,9 @@ class languages {
 	/**
 	 * Get the untranslated messages for a specific language.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The untranslated messages for this language.
+	 * @return array The untranslated messages for this language.
 	 */
 	public function getUntranslatedMessages( $code ) {
 		$this->loadGeneralMessages();
@@ -303,9 +303,9 @@ class languages {
 	/**
 	 * Get the duplicate messages for a specific language.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The duplicate messages for this language.
+	 * @return array The duplicate messages for this language.
 	 */
 	public function getDuplicateMessages( $code ) {
 		$this->loadGeneralMessages();
@@ -322,9 +322,9 @@ class languages {
 	/**
 	 * Get the obsolete messages for a specific language.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The obsolete messages for this language.
+	 * @return array The obsolete messages for this language.
 	 */
 	public function getObsoleteMessages( $code ) {
 		$this->loadGeneralMessages();
@@ -335,9 +335,9 @@ class languages {
 	/**
 	 * Get the messages whose variables do not match the original ones.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The messages whose variables do not match the original ones.
+	 * @return array The messages whose variables do not match the original ones.
 	 */
 	public function getMessagesWithMismatchVariables( $code ) {
 		$this->loadGeneralMessages();
@@ -366,9 +366,9 @@ class languages {
 	/**
 	 * Get the messages which do not use plural.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The messages which do not use plural in this language.
+	 * @return array The messages which do not use plural in this language.
 	 */
 	public function getMessagesWithoutPlural( $code ) {
 		$this->loadGeneralMessages();
@@ -385,9 +385,9 @@ class languages {
 	/**
 	 * Get the empty messages.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The empty messages for this language.
+	 * @return array The empty messages for this language.
 	 */
 	public function getEmptyMessages( $code ) {
 		$this->loadGeneralMessages();
@@ -404,9 +404,9 @@ class languages {
 	/**
 	 * Get the messages with trailing whitespace.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The messages with trailing whitespace in this language.
+	 * @return array The messages with trailing whitespace in this language.
 	 */
 	public function getMessagesWithWhitespace( $code ) {
 		$this->loadGeneralMessages();
@@ -423,9 +423,9 @@ class languages {
 	/**
 	 * Get the non-XHTML messages.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The non-XHTML messages for this language.
+	 * @return array The non-XHTML messages for this language.
 	 */
 	public function getNonXHTMLMessages( $code ) {
 		$this->loadGeneralMessages();
@@ -451,9 +451,9 @@ class languages {
 	/**
 	 * Get the messages which include wrong characters.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The messages which include wrong characters in this language.
+	 * @return array The messages which include wrong characters in this language.
 	 */
 	public function getMessagesWithWrongChars( $code ) {
 		$this->loadGeneralMessages();
@@ -488,9 +488,9 @@ class languages {
 	/**
 	 * Get the messages which include dubious links.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The messages which include dubious links in this language.
+	 * @return array The messages which include dubious links in this language.
 	 */
 	public function getMessagesWithDubiousLinks( $code ) {
 		$this->loadGeneralMessages();
@@ -517,9 +517,9 @@ class languages {
 	/**
 	 * Get the messages which include unbalanced brackets.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The messages which include unbalanced brackets in this language.
+	 * @return array The messages which include unbalanced brackets in this language.
 	 */
 	public function getMessagesWithUnbalanced( $code ) {
 		$this->loadGeneralMessages();
@@ -547,7 +547,7 @@ class languages {
 			if ( $a !== $b || $c !== $d ) {
 				$messages[$key] = "$a, $b, $c, $d";
 			}
-			
+
 		}
 		return $messages;
 	}
@@ -555,9 +555,9 @@ class languages {
 	/**
 	 * Get the untranslated namespace names.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The untranslated namespace names in this language.
+	 * @return array The untranslated namespace names in this language.
 	 */
 	public function getUntranslatedNamespaces( $code ) {
 		$this->loadFile( 'en' );
@@ -570,9 +570,9 @@ class languages {
 	/**
 	 * Get the project talk namespace names with no $1.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The problematic project talk namespaces in this language.
+	 * @return array The problematic project talk namespaces in this language.
 	 */
 	public function getProblematicProjectTalks( $code ) {
 		$this->loadFile( $code );
@@ -599,9 +599,9 @@ class languages {
 	/**
 	 * Get the untranslated magic words.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The untranslated magic words in this language.
+	 * @return array The untranslated magic words in this language.
 	 */
 	public function getUntranslatedMagicWords( $code ) {
 		$this->loadFile( 'en' );
@@ -618,9 +618,9 @@ class languages {
 	/**
 	 * Get the obsolete magic words.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The obsolete magic words in this language.
+	 * @return array The obsolete magic words in this language.
 	 */
 	public function getObsoleteMagicWords( $code ) {
 		$this->loadFile( 'en' );
@@ -637,9 +637,9 @@ class languages {
 	/**
 	 * Get the magic words that override the original English magic word.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The overriding magic words in this language.
+	 * @return array The overriding magic words in this language.
 	 */
 	public function getOverridingMagicWords( $code ) {
 		$this->loadFile( 'en' );
@@ -666,9 +666,9 @@ class languages {
 	/**
 	 * Get the magic words which do not match the case-sensitivity of the original words.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The magic words whose case does not match in this language.
+	 * @return array The magic words whose case does not match in this language.
 	 */
 	public function getCaseMismatchMagicWords( $code ) {
 		$this->loadFile( 'en' );
@@ -689,9 +689,9 @@ class languages {
 	/**
 	 * Get the untranslated special page names.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The untranslated special page names in this language.
+	 * @return array The untranslated special page names in this language.
 	 */
 	public function getUntraslatedSpecialPages( $code ) {
 		$this->loadFile( 'en' );
@@ -708,9 +708,9 @@ class languages {
 	/**
 	 * Get the obsolete special page names.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 *
-	 * @return The obsolete special page names in this language.
+	 * @return array The obsolete special page names in this language.
 	 */
 	public function getObsoleteSpecialPages( $code ) {
 		$this->loadFile( 'en' );
@@ -726,7 +726,11 @@ class languages {
 }
 
 class extensionLanguages extends languages {
-	private $mMessageGroup; # The message group
+
+	/**
+	 * @var MessageGroup
+	 */
+	private $mMessageGroup;
 
 	/**
 	 * Load the messages group.
@@ -743,7 +747,7 @@ class extensionLanguages extends languages {
 	/**
 	 * Get the extension name.
 	 *
-	 * @return The extension name.
+	 * @return string The extension name.
 	 */
 	public function name() {
 		return $this->mMessageGroup->getLabel();
@@ -752,7 +756,7 @@ class extensionLanguages extends languages {
 	/**
 	 * Load the language file.
 	 *
-	 * @param $code The language code.
+	 * @param $code string The language code.
 	 */
 	protected function loadFile( $code ) {
 		if( !isset( $this->mRawMessages[$code] ) ) {
diff --git a/maintenance/language/messageTypes.inc b/maintenance/language/messageTypes.inc
index 62374500..1c33667a 100644
--- a/maintenance/language/messageTypes.inc
+++ b/maintenance/language/messageTypes.inc
@@ -90,6 +90,8 @@ $wgIgnoredMessages = array(
 	'accesskey-blockip-block',
 	'accesskey-export',
 	'accesskey-import',
+	'accesskey-watchlistedit-normal-submit',
+	'accesskey-watchlistedit-raw-submit',
 	'addsection',
 	'talkpageheader',
 	'anonnotice',
@@ -125,7 +127,6 @@ $wgIgnoredMessages = array(
 	'revision-nav',
 	'rfcurl',
 	'shareddescriptionfollows',
-	'signature',
 	'signature-anon',
 	'signupstart',
 	'signupend',
@@ -181,7 +182,6 @@ $wgIgnoredMessages = array(
 	'unusedtemplates-summary',
 	'fewestrevisions-summary',
 	'upload-summary',
-	'newuserlogentry',
 	'wantedtemplates-summary',
 	'activeusers-summary',
 	'search-summary',
@@ -189,6 +189,8 @@ $wgIgnoredMessages = array(
 	'addsection-preload',
 	'addsection-editintro',
 	'longpage-hint',
+	'javascripttest-backlink',
+	'javascripttest-qunit-name',
 );
 
 /** Optional messages, which may be translated only if changed in the target language. */
@@ -197,7 +199,6 @@ $wgOptionalMessages = array(
 	'editsection-brackets',
 	'feed-atom',
 	'feed-rss',
-	'sectionlink',
 	'unit-pixel',
 	'userrights-irreversible-marker',
 	'tog-nolangconversion',
@@ -233,6 +234,9 @@ $wgOptionalMessages = array(
 	'variantname-ike-cans',
 	'variantname-ike-latn',
 	'variantname-iu',
+	'variantname-shi-tfng',
+	'variantname-shi-latn',
+	'variantname-shi',
 	'rc-change-size',
 	'resetpass_text',
 	'image_sample',
@@ -314,10 +318,6 @@ $wgOptionalMessages = array(
 	'minutes-abbrev',
 	'hours-abbrev',
 	'days-abbrev',
-	'filerevert-backlink',
-	'filedelete-backlink',
-	'delete-backlink',
-	'protect-backlink',
 	'pagetitle',
 	'filename-prefix-blacklist',
 	'edittools',
@@ -326,6 +326,20 @@ $wgOptionalMessages = array(
 	'size-kilobytes',
 	'size-megabytes',
 	'size-gigabytes',
+	'size-terabytes',
+	'size-petabytes',
+	'size-exabytes',
+	'size-zetabytes',
+	'size-yottabytes',
+	'bitrate-bits',
+	'bitrate-kilobits',
+	'bitrate-megabits',
+	'bitrate-gigabits',
+	'bitrate-terabits',
+	'bitrate-petabits',
+	'bitrate-exabits',
+	'bitrate-zetabits',
+	'bitrate-yottabits',
 	'iranian-calendar-m1',
 	'iranian-calendar-m2',
 	'iranian-calendar-m3',
@@ -392,13 +406,10 @@ $wgOptionalMessages = array(
 	'listgrouprights-right-display',
 	'listgrouprights-right-revoked',
 	'timezone-utc',
-	'whatlinkshere-backlink',
-	'recentchangeslinked-backlink',
 	'unpatrolledletter',
 	'diff-with-additional',
 	'pagetitle-view-mainpage',
-	'trackback',
-	'trackbackexcerpt',
+	'backlinksubtitle',
 	'prefs-registration-date-time',
 	'prefs-memberingroups-type',
 	'shared-repo-name-wikimediacommons',
diff --git a/maintenance/language/messages.inc b/maintenance/language/messages.inc
index d707cf5d..bdd4d397 100644
--- a/maintenance/language/messages.inc
+++ b/maintenance/language/messages.inc
@@ -301,6 +301,7 @@ $wgMessageStructure = array(
 		'ok',
 		'pagetitle',
 		'pagetitle-view-mainpage',
+		'backlinksubtitle',
 		'retrievedfrom',
 		'youhavenewmessages',
 		'newmessageslink',
@@ -385,6 +386,7 @@ $wgMessageStructure = array(
 		'formerror',
 		'badarticleerror',
 		'cannotdelete',
+		'cannotdelete-title',
 		'badtitle',
 		'badtitletext',
 		'perfcached',
@@ -392,11 +394,12 @@ $wgMessageStructure = array(
 		'querypage-no-updates',
 		'wrong_wfQuery_params',
 		'viewsource',
-		'viewsourcefor',
+		'viewsource-title',
 		'actionthrottled',
 		'actionthrottledtext',
 		'protectedpagetext',
 		'viewsourcetext',
+		'viewyourtext',
 		'protectedinterface',
 		'editinginterface',
 		'sqlhidden',
@@ -480,6 +483,7 @@ $wgMessageStructure = array(
 		'noemailprefs',
 		'emailconfirmlink',
 		'invalidemailaddress',
+		'cannotchangeemail',
 		'accountcreated',
 		'accountcreatedtext',
 		'createaccount-title',
@@ -495,6 +499,7 @@ $wgMessageStructure = array(
 		'pear-mail-error',
 		'php-mail-error',
 		'php-mail-error-unknown',
+		'user-mail-no-addy',
 	),
 	'resetpass' => array(
 		'resetpass',
@@ -521,12 +526,27 @@ $wgMessageStructure = array(
 		'passwordreset-pretext',
 		'passwordreset-username',
 		'passwordreset-domain',
+		'passwordreset-capture',
+		'passwordreset-capture-help',
 		'passwordreset-email',
 		'passwordreset-emailtitle',
 		'passwordreset-emailtext-ip',
 		'passwordreset-emailtext-user',
 		'passwordreset-emailelement',
 		'passwordreset-emailsent',
+		'passwordreset-emailsent-capture',
+		'passwordreset-emailerror-capture',
+	),
+	'changeemail' => array(
+		'changeemail',
+		'changeemail-header',
+		'changeemail-text',
+		'changeemail-no-info',
+		'changeemail-oldemail',
+		'changeemail-newemail',
+		'changeemail-none',
+		'changeemail-submit',
+		'changeemail-cancel',
 	),
 	'toolbar' => array(
 		'bold_sample',
@@ -569,9 +589,6 @@ $wgMessageStructure = array(
 		'blockedtext',
 		'autoblockedtext',
 		'blockednoreason',
-		'blockedoriginalsource',
-		'blockededitsource',
-		'whitelistedittitle',
 		'whitelistedittext',
 		'confirmedittext',
 		'nosuchsectiontitle',
@@ -755,10 +772,6 @@ $wgMessageStructure = array(
 		'revdelete-unsuppress',
 		'revdelete-log',
 		'revdelete-submit',
-		'revdelete-logentry',
-		'logdelete-logentry',
-		'revdelete-logaction',
-		'logdelete-logaction',
 		'revdelete-success',
 		'revdelete-failure',
 		'logdelete-success',
@@ -768,15 +781,6 @@ $wgMessageStructure = array(
 		'revdel-restore-visible',
 		'pagehist',
 		'deletedhist',
-		'revdelete-content',
-		'revdelete-summary',
-		'revdelete-uname',
-		'revdelete-restricted',
-		'revdelete-unrestricted',
-		'revdelete-hid',
-		'revdelete-unhid',
-		'revdelete-log-message',
-		'logdelete-log-message',
 		'revdelete-hide-current',
 		'revdelete-show-no-access',
 		'revdelete-modify-no-access',
@@ -938,6 +942,8 @@ $wgMessageStructure = array(
 		'prefs-watchlist-token',
 		'prefs-misc', // continue checking if used from here on (r49916)
 		'prefs-resetpass',
+		'prefs-changeemail',
+		'prefs-setemail',
 		'prefs-email',
 		'prefs-rendering',
 		'saveprefs',
@@ -997,6 +1003,7 @@ $wgMessageStructure = array(
 		'yourrealname',
 		'yourlanguage',
 		'yourvariant',
+		'prefs-help-variant',
 		'yournick',
 		'prefs-help-signature',
 		'badsig',
@@ -1009,7 +1016,7 @@ $wgMessageStructure = array(
 		'email',
 		'prefs-help-realname',
 
-		# 3 messages depending upon wgEmailConfirmToEdit and $wgEnableUserEmail
+		# 3 messages depending upon $wgEmailConfirmToEdit and $wgEnableUserEmail
 		'prefs-help-email',
 		'prefs-help-email-others',
 		'prefs-help-email-required',
@@ -1133,13 +1140,13 @@ $wgMessageStructure = array(
 		'right-autopatrol',
 		'right-patrolmarks',
 		'right-unwatchedpages',
-		'right-trackback',
 		'right-mergehistory',
 		'right-userrights',
 		'right-userrights-interwiki',
 		'right-siteadmin',
 		'right-override-export-depth',
 		'right-sendemail',
+		'right-passwordreset',
 	),
 	'rightslog' => array(
 		'rightslog',
@@ -1173,16 +1180,17 @@ $wgMessageStructure = array(
 		'action-suppressionlog',
 		'action-block',
 		'action-protect',
+		'action-rollback',
 		'action-import',
 		'action-importupload',
 		'action-patrol',
 		'action-autopatrol',
 		'action-unwatchedpages',
-		'action-trackback',
 		'action-mergehistory',
 		'action-userrights',
 		'action-userrights-interwiki',
 		'action-siteadmin',
+		'action-sendemail',
 	),
 	'recentchanges' => array(
 		'nchanges',
@@ -1213,12 +1221,12 @@ $wgMessageStructure = array(
 		'newpageletter',
 		'boteditletter',
 		'unpatrolledletter',
-		'sectionlink',
 		'number_of_watching_users_RCview',
 		'number_of_watching_users_pageview',
 		'rc_categories',
 		'rc_categories_any',
 		'rc-change-size',
+		'rc-change-size-new',
 		'newsectionsummary',
 		'rc-enhanced-expand',
 		'rc-enhanced-hide',
@@ -1228,7 +1236,6 @@ $wgMessageStructure = array(
 		'recentchangeslinked-feed',
 		'recentchangeslinked-toolbox',
 		'recentchangeslinked-title',
-		'recentchangeslinked-backlink',
 		'recentchangeslinked-noresult',
 		'recentchangeslinked-summary',
 		'recentchangeslinked-page',
@@ -1265,6 +1272,7 @@ $wgMessageStructure = array(
 		'ignorewarnings',
 		'minlength1',
 		'illegalfilename',
+		'filename-toolong',
 		'badfilename',
 		'filetype-mime-mismatch',
 		'filetype-badmime',
@@ -1339,6 +1347,43 @@ $wgMessageStructure = array(
 		'upload-http-error',
 	),
 
+	'filebackend-errors' => array(
+		'backend-fail-stream',
+		'backend-fail-backup',
+		'backend-fail-notexists',
+		'backend-fail-hashes',
+		'backend-fail-notsame',
+		'backend-fail-invalidpath',
+		'backend-fail-delete',
+		'backend-fail-alreadyexists',
+		'backend-fail-store',
+		'backend-fail-copy',
+		'backend-fail-move',
+		'backend-fail-opentemp',
+		'backend-fail-writetemp',
+		'backend-fail-closetemp',
+		'backend-fail-read',
+		'backend-fail-create',
+		'backend-fail-readonly',
+		'backend-fail-synced',
+		'backend-fail-connect',
+		'backend-fail-internal',
+		'backend-fail-contenttype',
+		'backend-fail-batchsize'
+	),
+
+	'lockmanager-errors' => array(
+		'lockmanager-notlocked',
+		'lockmanager-fail-closelock',
+		'lockmanager-fail-deletelock',
+		'lockmanager-fail-acquirelock',
+		'lockmanager-fail-openlock',
+		'lockmanager-fail-releaselock',
+		'lockmanager-fail-db-bucket',
+		'lockmanager-fail-db-release',
+		'lockmanager-fail-svr-release'
+	),
+
 	'zip' => array(
 		'zip-file-open-error',
 		'zip-wrong-format',
@@ -1354,6 +1399,7 @@ $wgMessageStructure = array(
 		'uploadstash-badtoken',
 		'uploadstash-errclear',
 		'uploadstash-refresh',
+		'invalid-chunk-offset',
 	),
 
 	'img-auth' => array(
@@ -1448,7 +1494,6 @@ $wgMessageStructure = array(
 	),
 	'filerevert' => array(
 		'filerevert',
-		'filerevert-backlink',
 		'filerevert-legend',
 		'filerevert-intro',
 		'filerevert-comment',
@@ -1459,7 +1504,6 @@ $wgMessageStructure = array(
 	),
 	'filedelete' => array(
 		'filedelete',
-		'filedelete-backlink',
 		'filedelete-legend',
 		'filedelete-intro',
 		'filedelete-intro-old',
@@ -1474,6 +1518,7 @@ $wgMessageStructure = array(
 		'filedelete-reason-dropdown',
 		'filedelete-edit-reasonlist',
 		'filedelete-maintenance',
+		'filedelete-maintenance-title',
 	),
 	'mimesearch' => array(
 		'mimesearch',
@@ -1591,6 +1636,8 @@ $wgMessageStructure = array(
 		'wantedpages-badtitle',
 		'wantedfiles',
 		'wantedfiles-summary',
+		'wantedfiletext-cat',
+		'wantedfiletext-nocat',
 		'wantedtemplates',
 		'wantedtemplates-summary',
 		'mostlinked',
@@ -1606,6 +1653,7 @@ $wgMessageStructure = array(
 		'mostrevisions',
 		'mostrevisions-summary',
 		'prefixindex',
+		'prefixindex-namespace',
 		'prefixindex-summary',
 		'shortpages',
 		'shortpages-summary',
@@ -1730,11 +1778,6 @@ $wgMessageStructure = array(
 	'newuserlog' => array(
 		'newuserlogpage',
 		'newuserlogpagetext',
-		'newuserlogentry',
-		'newuserlog-byemail',
-		'newuserlog-create-entry',
-		'newuserlog-create2-entry',
-		'newuserlog-autocreate-entry',
 	),
 	'listgrouprights' => array(
 		'listgrouprights',
@@ -1847,15 +1890,12 @@ $wgMessageStructure = array(
 		'exbeforeblank',
 		'exblank',
 		'delete-confirm',
-		'delete-backlink',
 		'delete-legend',
 		'historywarning',
 		'confirmdeletetext',
 		'actioncomplete',
 		'actionfailed',
 		'deletedtext',
-		'deletedarticle',
-		'suppressedarticle',
 		'dellogpage',
 		'dellogpagetext',
 		'deletionlog',
@@ -1892,8 +1932,10 @@ $wgMessageStructure = array(
 		'unprotectedarticle',
 		'movedarticleprotection',
 		'protect-title',
+		'protect-title-notallowed',
 		'prot_1movedto2',
-		'protect-backlink',
+		'protect-badnamespace-title',
+		'protect-badnamespace-text',
 		'protect-legend',
 		'protectcomment',
 		'protectexpiry',
@@ -1911,6 +1953,7 @@ $wgMessageStructure = array(
 		'protect-level-sysop',
 		'protect-summary-cascade',
 		'protect-expiring',
+		'protect-expiring-local',
 		'protect-expiry-indefinite',
 		'protect-cascade',
 		'protect-cantedit',
@@ -1960,13 +2003,13 @@ $wgMessageStructure = array(
 		'undeletereset',
 		'undeleteinvert',
 		'undeletecomment',
-		'undeletedarticle',
 		'undeletedrevisions',
 		'undeletedrevisions-files',
 		'undeletedfiles',
 		'cannotundelete',
 		'undeletedpage',
 		'undelete-header',
+		'undelete-search-title',
 		'undelete-search-box',
 		'undelete-search-prefix',
 		'undelete-search-submit',
@@ -1975,6 +2018,7 @@ $wgMessageStructure = array(
 		'undelete-bad-store-key',
 		'undelete-cleanup-error',
 		'undelete-missing-filearchive',
+		'undelete-error',
 		'undelete-error-short',
 		'undelete-error-long',
 		'undelete-show-file-confirm',
@@ -2023,7 +2067,6 @@ $wgMessageStructure = array(
 		'whatlinkshere-title',
 		'whatlinkshere-summary',
 		'whatlinkshere-page',
-		'whatlinkshere-backlink',
 		'linkshere',
 		'nolinkshere',
 		'nolinkshere-ns',
@@ -2088,6 +2131,7 @@ $wgMessageStructure = array(
 		'blocklist-userblocks',
 		'blocklist-tempblocks',
 		'blocklist-addressblocks',
+		'blocklist-rangeblocks',
 		'blocklist-timestamp',
 		'blocklist-target',
 		'blocklist-expiry',
@@ -2112,6 +2156,7 @@ $wgMessageStructure = array(
 		'unblocklink',
 		'change-blocklink',
 		'contribslink',
+		'emaillink',
 		'autoblocker',
 		'blocklogpage',
 		'blocklog-showlog',
@@ -2202,9 +2247,6 @@ $wgMessageStructure = array(
 		'movepage-page-moved',
 		'movepage-page-unmoved',
 		'movepage-max-pages',
-		'1movedto2',
-		'1movedto2_redir',
-		'move-redirect-suppressed',
 		'movelogpage',
 		'movelogpagetext',
 		'movesubpage',
@@ -2237,8 +2279,10 @@ $wgMessageStructure = array(
 	'export' => array(
 		'export',
 		'exporttext',
+		'exportall',
 		'exportcuronly',
 		'exportnohistory',
+		'exportlistauthors',
 		'export-submit',
 		'export-addcattext',
 		'export-addcat',
@@ -2270,6 +2314,8 @@ $wgMessageStructure = array(
 		'thumbnail_error',
 		'djvu_page_error',
 		'djvu_no_xml',
+		'thumbnail-temp-create',
+		'thumbnail-dest-create',
 		'thumbnail_invalid_params',
 		'thumbnail_dest_directory',
 		'thumbnail_image-type',
@@ -2311,6 +2357,11 @@ $wgMessageStructure = array(
 		'import-upload',
 		'import-token-mismatch',
 		'import-invalid-interwiki',
+		'import-error-edit',
+		'import-error-create',
+		'import-error-interwiki',
+		'import-error-special',
+		'import-error-invalid',
 	),
 	'importlog' => array(
 		'importlogpage',
@@ -2320,6 +2371,19 @@ $wgMessageStructure = array(
 		'import-logentry-interwiki',
 		'import-logentry-interwiki-detail',
 	),
+	'javaccripttest' => array(
+		'javascripttest',
+		'javascripttest-backlink',
+		'javascripttest-disabled',
+		'javascripttest-title',
+		'javascripttest-pagetext-noframework',
+		'javascripttest-pagetext-unknownframework',
+		'javascripttest-pagetext-frameworks',
+		'javascripttest-pagetext-skins' ,
+		'javascripttest-qunit-name',
+		'javascripttest-qunit-intro',
+		'javascripttest-qunit-heading',
+	),
 	'accesskeys' => array(
 		'accesskey-pt-userpage',
 		'accesskey-pt-anonuserpage',
@@ -2388,6 +2452,8 @@ $wgMessageStructure = array(
 		'accesskey-blockip-block',
 		'accesskey-export',
 		'accesskey-import',
+		'accesskey-watchlistedit-normal-submit',
+		'accesskey-watchlistedit-raw-submit',
 	),
 	'tooltips' => array(
 		'tooltip-pt-userpage',
@@ -2450,6 +2516,8 @@ $wgMessageStructure = array(
 		'tooltip-diff',
 		'tooltip-compareselectedversions',
 		'tooltip-watch',
+		'tooltip-watchlistedit-normal-submit',
+		'tooltip-watchlistedit-raw-submit',
 		'tooltip-recreate',
 		'tooltip-upload',
 		'tooltip-rollback',
@@ -2554,9 +2622,6 @@ $wgMessageStructure = array(
 	'patrol-log' => array(
 		'patrol-log-page',
 		'patrol-log-header',
-		'patrol-log-line',
-		'patrol-log-auto',
-		'patrol-log-diff',
 		'log-show-hide-patrol',
 	),
 	'imagedeletion' => array(
@@ -2611,6 +2676,11 @@ $wgMessageStructure = array(
 		'minutes-abbrev',
 		'hours-abbrev',
 		'days-abbrev',
+		'seconds',
+		'minutes',
+		'hours',
+		'days',
+		'ago',
 	),
 	'badimagelist' => array(
 		'bad_image_list',
@@ -2660,6 +2730,11 @@ $wgMessageStructure = array(
 		'variantname-ike-latn',
 		'variantname-iu',
 	),
+	'variantname-shi' => array(
+		'variantname-shi-tfng',
+		'variantname-shi-latn',
+		'variantname-shi',
+	),
 	'metadata' => array(
 		'metadata',
 		'metadata-help',
@@ -3168,14 +3243,6 @@ $wgMessageStructure = array(
 		'scarytranscludefailed',
 		'scarytranscludetoolong',
 	),
-	'trackbacks' => array(
-		'trackbackbox',
-		'trackback',
-		'trackbackexcerpt',
-		'trackbackremove',
-		'trackbacklink',
-		'trackbackdeleteok',
-	),
 	'deleteconflict' => array(
 		'deletedwhileediting',
 		'confirmrecreate',
@@ -3239,6 +3306,22 @@ $wgMessageStructure = array(
 		'size-kilobytes',
 		'size-megabytes',
 		'size-gigabytes',
+		'size-terabytes',
+		'size-petabytes',
+		'size-exabytes',
+		'size-zetabytes',
+		'size-yottabytes',
+	),
+	'bitrateunits' => array(
+		'bitrate-bits',
+		'bitrate-kilobits',
+		'bitrate-megabits',
+		'bitrate-gigabits',
+		'bitrate-terabits',
+		'bitrate-petabits',
+		'bitrate-exabits',
+		'bitrate-zetabits',
+		'bitrate-yottabits',
 	),
 	'livepreview' => array(
 		'livepreview-loading',
@@ -3431,6 +3514,9 @@ $wgMessageStructure = array(
 		'compare-rev1',
 		'compare-rev2',
 		'compare-submit',
+		'compare-invalid-title',
+		'compare-title-not-exists',
+		'compare-revision-not-exists',
 	),
 	'db-error-messages' => array(
 		'dberr-header',
@@ -3460,6 +3546,91 @@ $wgMessageStructure = array(
 	'unwatch' => array(
 		'confirm-unwatch-button',
 	),
+	'logging' => array(
+		'logentry-delete-delete',
+		'logentry-delete-restore',
+		'logentry-delete-event',
+		'logentry-delete-revision',
+		'logentry-delete-event-legacy',
+		'logentry-delete-revision-legacy',
+		'logentry-suppress-delete',
+		'logentry-suppress-event',
+		'logentry-suppress-revision',
+		'logentry-suppress-event-legacy',
+		'logentry-suppress-revision-legacy',
+		'revdelete-content-hid',
+		'revdelete-summary-hid',
+		'revdelete-uname-hid',
+		'revdelete-content-unhid',
+		'revdelete-summary-unhid',
+		'revdelete-uname-unhid',
+		'revdelete-restricted',
+		'revdelete-unrestricted',
+		'logentry-move-move',
+		'logentry-move-move-noredirect',
+		'logentry-move-move_redir',
+		'logentry-move-move_redir-noredirect',
+		'logentry-patrol-patrol',
+		'logentry-patrol-patrol-auto',
+		'logentry-newusers-newusers',
+		'logentry-newusers-create',
+		'logentry-newusers-create2',
+		'logentry-newusers-autocreate',
+		'newuserlog-byemail',
+	),
+	'feedback' => array(
+		'feedback-bugornote',
+		'feedback-subject',
+		'feedback-message',
+		'feedback-cancel',
+		'feedback-submit',
+		'feedback-adding',
+		'feedback-error1',
+		'feedback-error2',
+		'feedback-error3',
+		'feedback-thanks',
+		'feedback-close',
+		'feedback-bugcheck',
+		'feedback-bugnew',
+	),
+	'apierrors' => array(
+		'api-error-badaccess-groups',
+		'api-error-badtoken',
+		'api-error-copyuploaddisabled',
+		'api-error-duplicate',
+		'api-error-duplicate-archive',
+		'api-error-duplicate-archive-popup-title',
+		'api-error-duplicate-popup-title',
+		'api-error-empty-file',
+		'api-error-emptypage',
+		'api-error-fetchfileerror',
+		'api-error-file-too-large',
+		'api-error-filename-tooshort',
+		'api-error-filetype-banned',
+		'api-error-filetype-missing',
+		'api-error-hookaborted',
+		'api-error-http',
+		'api-error-illegal-filename',
+		'api-error-internal-error',
+		'api-error-invalid-file-key',
+		'api-error-missingparam',
+		'api-error-missingresult',
+		'api-error-mustbeloggedin',
+		'api-error-mustbeposted',
+		'api-error-noimageinfo',
+		'api-error-nomodule',
+		'api-error-ok-but-empty',
+		'api-error-overwrite',
+		'api-error-stashfailed',
+		'api-error-timeout',
+		'api-error-unclassified',
+		'api-error-unknown-code',
+		'api-error-unknown-error',
+		'api-error-unknown-warning',
+		'api-error-unknownerror',
+		'api-error-uploaddisabled',
+		'api-error-verification-error',
+	),
 );
 
 /** Comments for each block */
@@ -3493,6 +3664,7 @@ XHTML id names.",
 	'passwordstrength'    => 'JavaScript password checks',
 	'resetpass'           => 'Change password dialog',
 	'passwordreset'       => 'Special:PasswordReset',
+	'changeemail'         => 'Special:ChangeEmail',
 	'toolbar'             => 'Edit page toolbar',
 	'edit'                => 'Edit pages',
 	'parserwarnings'      => 'Parser/template warnings',
@@ -3522,6 +3694,8 @@ XHTML id names.",
 	'upload'              => 'Upload',
 	'zip'                 => 'ZipDirectoryReader',
 	'upload-errors'       => '',
+	'filebackend-errors'  => 'File backend',
+	'lockmanager-errors'  => 'Lock manager',
 	'uploadstash'         => 'Special:UploadStash',
 	'img-auth'            => 'img_auth script messages',
 	'http-errors'         => 'HTTP errors',
@@ -3579,6 +3753,7 @@ XHTML id names.",
 	'thumbnails'          => 'Thumbnails',
 	'import'              => 'Special:Import',
 	'importlog'           => 'Import log',
+	'javaccripttest'      => 'JavaScriptTest',
 	'accesskeys'          => 'Keyboard access keys for power users',
 	'tooltips'            => 'Tooltip help for the actions',
 	'stylesheets'         => 'Stylesheets',
@@ -3605,6 +3780,7 @@ Variants for Chinese language",
 	'variantname-ku'      => 'Variants for Kurdish language',
 	'variantname-tg'      => 'Variants for Tajiki language',
 	'variantname-iu'      => 'Variants for Inuktitut language',
+	'variantname-shi'     => 'Variants for Tachelhit language',
 	'media-info'          => 'Media information',
 	'metadata'            => 'Metadata',
 	'exif'                           => 'EXIF tags',
@@ -3657,7 +3833,6 @@ Variants for Chinese language",
 	'all'                   => "'all' in various places, this might be different for inflected languages",
 	'confirmemail'          => 'E-mail address confirmation',
 	'scarytransclusion'     => 'Scary transclusion',
-	'trackbacks'            => 'Trackbacks',
 	'deleteconflict'        => 'Delete conflict',
 	'unit-pixel'            => '',
 	'purge'                 => 'action=purge',
@@ -3668,6 +3843,7 @@ Variants for Chinese language",
 	'autosumm'              => 'Auto-summaries',
 	'autoblock_whitelist'   => 'Autoblock whitelist',
 	'sizeunits'             => 'Size units',
+	'bitrateunits'          => 'Bitrate units',
 	'livepreview'           => 'Live preview',
 	'lagwarning'            => 'Friendlier slave lag warnings',
 	'watchlisteditor'       => 'Watchlist editor',
@@ -3688,5 +3864,7 @@ Variants for Chinese language",
 	'db-error-messages'     => 'Database error messages',
 	'html-forms'            => 'HTML forms',
 	'sqlite'                => 'SQLite database support',
-	'ajax-category'         => 'Add categories per AJAX',
+	'logging'               => 'New logging system',
+	'feedback'              => 'Feedback',
+	'apierrors'             => 'API errors',
 );
diff --git a/maintenance/language/transstat.php b/maintenance/language/transstat.php
index a3213266..fb08b7d0 100644
--- a/maintenance/language/transstat.php
+++ b/maintenance/language/transstat.php
@@ -21,7 +21,7 @@
  * @ingroup MaintenanceLanguage
  *
  * @author Ævar Arnfjörð Bjarmason 
- * @author Ashar Voultoiz 
+ * @author Antoine Musso 
  *
  * Output is posted from time to time on:
  * http://www.mediawiki.org/wiki/Localisation_statistics
@@ -97,7 +97,7 @@ $wgRequiredMessagesNumber = count( $wgGeneralMessages['required'] );
 foreach ( $wgLanguages->getLanguages() as $code ) {
 	# Don't check English, RTL English or dummy language codes
 	if ( $code == 'en' || $code == 'enRTL' || (is_array( $wgDummyLanguageCodes ) &&
-		in_array( $code, $wgDummyLanguageCodes ) ) ) {
+		isset( $wgDummyLanguageCodes[$code] ) ) ) {
 		continue;
 	}
 
diff --git a/maintenance/language/writeMessagesArray.inc b/maintenance/language/writeMessagesArray.inc
index 093359ca..524c2ba6 100644
--- a/maintenance/language/writeMessagesArray.inc
+++ b/maintenance/language/writeMessagesArray.inc
@@ -106,6 +106,9 @@ class MessageWriter {
 		# Sort messages to blocks
 		$sortedMessages['unknown'] = $messages;
 		foreach( self::$messageStructure as $blockName => $block ) {
+			/**
+			 * @var $block array
+			 */
 			foreach( $block as $key ) {
 				if( array_key_exists( $key, $sortedMessages['unknown'] ) ) {
 					$sortedMessages[$blockName][$key] = $sortedMessages['unknown'][$key];
diff --git a/maintenance/locking/LockServerDaemon.php b/maintenance/locking/LockServerDaemon.php
new file mode 100644
index 00000000..1a4a928e
--- /dev/null
+++ b/maintenance/locking/LockServerDaemon.php
@@ -0,0 +1,617 @@
+main();
+
+/**
+ * Simple lock server daemon that accepts lock/unlock requests
+ */
+class LockServerDaemon {
+	/** @var resource */
+	protected $sock; // socket to listen/accept on
+	/** @var Array */
+	protected $sessions = array(); // (session => resource)
+	/** @var Array */
+	protected $deadSessions = array(); // (session => UNIX timestamp)
+
+	/** @var LockHolder */
+	protected $lockHolder;
+
+	protected $address; // string IP address
+	protected $port; // integer
+	protected $authKey; // string key
+	protected $lockTimeout; // integer number of seconds
+	protected $maxBacklog; // integer
+	protected $maxClients; // integer
+
+	protected $startTime; // integer UNIX timestamp
+	protected $ticks = 0; // integer counter
+
+	/* @var LockServerDaemon */
+	protected static $instance = null;
+
+	/**
+	 * @params $config Array
+	 * @return LockServerDaemon
+	 */
+	public static function init( array $config ) {
+		if ( self::$instance ) {
+			throw new Exception( 'LockServer already initialized.' );
+		}
+		foreach ( array( 'address', 'port', 'authKey' ) as $par ) {
+			if ( !isset( $config[$par] ) ) {
+				die( "Usage: php LockServerDaemon.php " .
+					"--address 
--port --authkey " . + "[--lockTimeout ] " . + "[--maxLocks ] [--maxClients ] [--maxBacklog ]" + ); + } + } + self::$instance = new self( $config ); + return self::$instance; + } + + /** + * @params $config Array + */ + protected function __construct( array $config ) { + // Required parameters... + $this->address = $config['address']; + $this->port = $config['port']; + $this->authKey = $config['authKey']; + // Parameters with defaults... + $this->lockTimeout = isset( $config['lockTimeout'] ) + ? (int)$config['lockTimeout'] + : 60; + $this->maxClients = isset( $config['maxClients'] ) + ? (int)$config['maxClients'] + : 1000; // less than default FD_SETSIZE + $this->maxBacklog = isset( $config['maxBacklog'] ) + ? (int)$config['maxBacklog'] + : 100; + $maxLocks = isset( $config['maxLocks'] ) + ? (int)$config['maxLocks'] + : 10000; + + $this->lockHolder = new LockHolder( $maxLocks ); + } + + /** + * @return void + */ + protected function setupServerSocket() { + if ( !function_exists( 'socket_create' ) ) { + throw new Exception( "PHP sockets extension missing from PHP CLI mode." ); + } + $sock = socket_create( AF_INET, SOCK_STREAM, SOL_TCP ); + if ( $sock === false ) { + throw new Exception( "socket_create(): " . socket_strerror( socket_last_error() ) ); + } + socket_set_option( $sock, SOL_SOCKET, SO_REUSEADDR, 1 ); // bypass 2MLS + socket_set_nonblock( $sock ); // don't block on accept() + if ( socket_bind( $sock, $this->address, $this->port ) === false ) { + throw new Exception( "socket_bind(): " . + socket_strerror( socket_last_error( $sock ) ) ); + } elseif ( socket_listen( $sock, $this->maxBacklog ) === false ) { + throw new Exception( "socket_listen(): " . + socket_strerror( socket_last_error( $sock ) ) ); + } + $this->sock = $sock; + $this->startTime = time(); + } + + /** + * Entry-point function that listens to the server socket, accepts + * new clients, and recieves/responds to requests to lock resources. + */ + public function main() { + $this->setupServerSocket(); // setup listening socket + $socketArray = new SocketArray(); // sockets being serviced + $socketArray->addSocket( $this->sock ); // add listening socket + do { + list( $read, $write ) = $socketArray->socketsForSelect(); + if ( socket_select( $read, $write, $except = NULL, NULL ) < 1 ) { + continue; // wait + } + // Check if there is a client trying to connect... + if ( in_array( $this->sock, $read ) && $socketArray->size() < $this->maxClients ) { + $newSock = socket_accept( $this->sock ); + if ( $newSock ) { + socket_set_option( $newSock, SOL_SOCKET, SO_KEEPALIVE, 1 ); + socket_set_nonblock( $newSock ); // don't block on read()/write() + $socketArray->addSocket( $newSock ); + } + } + // Loop through all the clients that have data to read... + foreach ( $read as $read_sock ) { + if ( $read_sock === $this->sock ) { + continue; // skip listening socket + } + // Avoids PHP_NORMAL_READ per https://bugs.php.net/bug.php?id=33471 + $data = socket_read( $read_sock, 65535 ); + // Check if the client is disconnected + if ( $data === false || $data === '' ) { + $socketArray->closeSocket( $read_sock ); + $this->recordDeadSocket( $read_sock ); // remove session + // Check if we reached the end of a message + } elseif ( substr( $data, -1 ) === "\n" ) { + // Newline is the last char (given ping-pong message usage) + $cmd = $socketArray->readRcvBuffer( $read_sock ) . $data; + // Perform the requested command... + $response = $this->doCommand( rtrim( $cmd ), $read_sock ); + // Send the response to the client... + $socketArray->appendSndBuffer( $read_sock, $response . "\n" ); + // Otherwise, we just have more message data to append + } elseif ( !$socketArray->appendRcvBuffer( $read_sock, $data ) ) { + $socketArray->closeSocket( $read_sock ); // too big + $this->recordDeadSocket( $read_sock ); // remove session + } + } + // Loop through all the clients that have data to write... + foreach ( $write as $write_sock ) { + $bytes = socket_write( $write_sock, $socketArray->readSndBuffer( $write_sock ) ); + // Check if the client is disconnected + if ( $bytes === false ) { + $socketArray->closeSocket( $write_sock ); + $this->recordDeadSocket( $write_sock ); // remove session + // Otherwise, truncate these bytes from the start of the write buffer + } else { + $socketArray->consumeSndBuffer( $write_sock, $bytes ); + } + } + // Prune dead locks every few socket events... + if ( ++$this->ticks >= 9 ) { + $this->ticks = 0; + $this->purgeExpiredLocks(); + } + } while ( true ); + } + + /** + * @param $data string + * @param $sourceSock resource + * @return string + */ + protected function doCommand( $data, $sourceSock ) { + $cmdArr = $this->getCommand( $data ); + if ( is_string( $cmdArr ) ) { + return $cmdArr; // error + } + list( $function, $session, $type, $resources ) = $cmdArr; + // On first command, track the session => sock correspondence + if ( !isset( $this->sessions[$session] ) ) { + $this->sessions[$session] = $sourceSock; + unset( $this->deadSessions[$session] ); // renew if dead + } + if ( $function === 'ACQUIRE' ) { + return $this->lockHolder->lock( $session, $type, $resources ); + } elseif ( $function === 'RELEASE' ) { + return $this->lockHolder->unlock( $session, $type, $resources ); + } elseif ( $function === 'RELEASE_ALL' ) { + return $this->lockHolder->release( $session ); + } elseif ( $function === 'STAT' ) { + return $this->stat(); + } + return 'INTERNAL_ERROR'; + } + + /** + * @param $data string + * @return Array + */ + protected function getCommand( $data ) { + $m = explode( ':', $data ); // + if ( count( $m ) == 5 ) { + list( $session, $key, $command, $type, $values ) = $m; + if ( sha1( $session . $command . $type . $values . $this->authKey ) !== $key ) { + return 'BAD_KEY'; + } elseif ( strlen( $session ) !== 31 ) { + return 'BAD_SESSION'; + } + $values = explode( '|', $values ); + if ( $command === 'ACQUIRE' ) { + $needsLockArgs = true; + } elseif ( $command === 'RELEASE' ) { + $needsLockArgs = true; + } elseif ( $command === 'RELEASE_ALL' ) { + $needsLockArgs = false; + } elseif ( $command === 'STAT' ) { + $needsLockArgs = false; + } else { + return 'BAD_COMMAND'; + } + if ( $needsLockArgs ) { + if ( $type !== 'SH' && $type !== 'EX' ) { + return 'BAD_TYPE'; + } + foreach ( $values as $value ) { + if ( strlen( $value ) !== 31 ) { + return 'BAD_FORMAT'; + } + } + } + return array( $command, $session, $type, $values ); + } + return 'BAD_FORMAT'; + } + + /** + * Remove a socket's corresponding session from tracking and + * store it in the dead session tracking if it still has locks. + * + * @param $socket resource + * @return book + */ + protected function recordDeadSocket( $socket ) { + $session = array_search( $socket, $this->sessions ); + if ( $session !== false ) { + unset( $this->sessions[$session] ); + // Record recently killed sessions that still have locks + if ( $this->lockHolder->sessionHasLocks( $session ) ) { + $this->deadSessions[$session] = time(); + } + return true; + } + return false; + } + + /** + * Clear locks for sessions that have been dead for a while + * + * @return integer Number of sessions purged + */ + protected function purgeExpiredLocks() { + $count = 0; + $now = time(); + foreach ( $this->deadSessions as $session => $timestamp ) { + if ( ( $now - $timestamp ) > $this->lockTimeout ) { + $this->lockHolder->release( $session ); + unset( $this->deadSessions[$session] ); + ++$count; + } + } + return $count; + } + + /** + * Get the current timestamp and memory usage + * + * @return string + */ + protected function stat() { + return ( time() - $this->startTime ) . ':' . memory_get_usage(); + } +} + +/** + * LockServerDaemon helper class that keeps track socket states + */ +class SocketArray { + /* @var Array */ + protected $clients = array(); // array of client sockets + /* @var Array */ + protected $rBuffers = array(); // corresponding socket read buffers + /* @var Array */ + protected $wBuffers = array(); // corresponding socket write buffers + + const BUFFER_SIZE = 65535; + + /** + * @return Array (list of sockets to read, list of sockets to write) + */ + public function socketsForSelect() { + $rSockets = array(); + $wSockets = array(); + foreach ( $this->clients as $key => $socket ) { + if ( $this->wBuffers[$key] !== '' ) { + $wSockets[] = $socket; // wait for writing to unblock + } else { + $rSockets[] = $socket; // wait for reading to unblock + } + } + return array( $rSockets, $wSockets ); + } + + /** + * @return integer Number of client sockets + */ + public function size() { + return count( $this->clients ); + } + + /** + * @param $sock resource + * @return bool + */ + public function addSocket( $sock ) { + $this->clients[] = $sock; + $this->rBuffers[] = ''; + $this->wBuffers[] = ''; + return true; + } + + /** + * @param $sock resource + * @return bool + */ + public function closeSocket( $sock ) { + $key = array_search( $sock, $this->clients ); + if ( $key === false ) { + return false; + } + socket_close( $sock ); + unset( $this->clients[$key] ); + unset( $this->rBuffers[$key] ); + unset( $this->wBuffers[$key] ); + return true; + } + + /** + * @param $sock resource + * @param $data string + * @return bool + */ + public function appendRcvBuffer( $sock, $data ) { + $key = array_search( $sock, $this->clients ); + if ( $key === false ) { + return false; + } elseif ( ( strlen( $this->rBuffers[$key] ) + strlen( $data ) ) > self::BUFFER_SIZE ) { + return false; + } + $this->rBuffers[$key] .= $data; + return true; + } + + /** + * @param $sock resource + * @return string|false + */ + public function readRcvBuffer( $sock ) { + $key = array_search( $sock, $this->clients ); + if ( $key === false ) { + return false; + } + $data = $this->rBuffers[$key]; + $this->rBuffers[$key] = ''; // consume data + return $data; + } + + /** + * @param $sock resource + * @param $data string + * @return bool + */ + public function appendSndBuffer( $sock, $data ) { + $key = array_search( $sock, $this->clients ); + if ( $key === false ) { + return false; + } elseif ( ( strlen( $this->wBuffers[$key] ) + strlen( $data ) ) > self::BUFFER_SIZE ) { + return false; + } + $this->wBuffers[$key] .= $data; + return true; + } + + /** + * @param $sock resource + * @return bool + */ + public function readSndBuffer( $sock ) { + $key = array_search( $sock, $this->clients ); + if ( $key === false ) { + return false; + } + return $this->wBuffers[$key]; + } + + /** + * @param $sock resource + * @param $bytes integer + * @return bool + */ + public function consumeSndBuffer( $sock, $bytes ) { + $key = array_search( $sock, $this->clients ); + if ( $key === false ) { + return false; + } + $this->wBuffers[$key] = (string)substr( $this->wBuffers[$key], $bytes ); + return true; + } +} + +/** + * LockServerDaemon helper class that keeps track of the locks + */ +class LockHolder { + /** @var Array */ + protected $shLocks = array(); // (key => session => 1) + /** @var Array */ + protected $exLocks = array(); // (key => session) + + /** @var Array */ + protected $sessionIndexSh = array(); // (session => key => 1) + /** @var Array */ + protected $sessionIndexEx = array(); // (session => key => 1) + protected $lockCount = 0; // integer + + protected $maxLocks; // integer + + /** + * @params $maxLocks integer Maximum number of locks to allow + */ + public function __construct( $maxLocks ) { + $this->maxLocks = $maxLocks; + } + + /** + * @param $session string + * @return bool + */ + public function sessionHasLocks( $session ) { + return isset( $this->sessionIndexSh[$session] ) + || isset( $this->sessionIndexEx[$session] ); + } + + /** + * @param $session string + * @param $type string + * @param $keys Array + * @return string + */ + public function lock( $session, $type, array $keys ) { + if ( ( $this->lockCount + count( $keys ) ) > $this->maxLocks ) { + return 'TOO_MANY_LOCKS'; + } + if ( $type === 'SH' ) { + // Check if any keys are already write-locked... + foreach ( $keys as $key ) { + if ( isset( $this->exLocks[$key] ) && $this->exLocks[$key] !== $session ) { + return 'CANT_ACQUIRE'; + } + } + // Acquire the read-locks... + foreach ( $keys as $key ) { + $this->set_sh_lock( $key, $session ); + } + return 'ACQUIRED'; + } elseif ( $type === 'EX' ) { + // Check if any keys are already read-locked or write-locked... + foreach ( $keys as $key ) { + if ( isset( $this->exLocks[$key] ) && $this->exLocks[$key] !== $session ) { + return 'CANT_ACQUIRE'; + } + if ( isset( $this->shLocks[$key] ) ) { + foreach ( $this->shLocks[$key] as $otherSession => $x ) { + if ( $otherSession !== $session ) { + return 'CANT_ACQUIRE'; + } + } + } + } + // Acquire the write-locks... + foreach ( $keys as $key ) { + $this->set_ex_lock( $key, $session ); + } + return 'ACQUIRED'; + } + return 'INTERNAL_ERROR'; + } + + /** + * @param $session string + * @param $type string + * @param $keys Array + * @return string + */ + public function unlock( $session, $type, array $keys ) { + if ( $type === 'SH' ) { + foreach ( $keys as $key ) { + $this->unset_sh_lock( $key, $session ); + } + return 'RELEASED'; + } elseif ( $type === 'EX' ) { + foreach ( $keys as $key ) { + $this->unset_ex_lock( $key, $session ); + } + return 'RELEASED'; + } + return 'INTERNAL_ERROR'; + } + + /** + * @param $session string + * @return string + */ + public function release( $session ) { + if ( isset( $this->sessionIndexSh[$session] ) ) { + foreach ( $this->sessionIndexSh[$session] as $key => $x ) { + $this->unset_sh_lock( $key, $session ); + } + } + if ( isset( $this->sessionIndexEx[$session] ) ) { + foreach ( $this->sessionIndexEx[$session] as $key => $x ) { + $this->unset_ex_lock( $key, $session ); + } + } + return 'RELEASED_ALL'; + } + + /** + * @param $key string + * @param $session string + * @return void + */ + protected function set_sh_lock( $key, $session ) { + if ( !isset( $this->shLocks[$key][$session] ) ) { + $this->shLocks[$key][$session] = 1; + $this->sessionIndexSh[$session][$key] = 1; + ++$this->lockCount; // we are adding a lock + } + } + + /** + * @param $key string + * @param $session string + * @return void + */ + protected function set_ex_lock( $key, $session ) { + if ( !isset( $this->exLocks[$key][$session] ) ) { + $this->exLocks[$key] = $session; + $this->sessionIndexEx[$session][$key] = 1; + ++$this->lockCount; // we are adding a lock + } + } + + /** + * @param $key string + * @param $session string + * @return void + */ + protected function unset_sh_lock( $key, $session ) { + if ( isset( $this->shLocks[$key][$session] ) ) { + unset( $this->shLocks[$key][$session] ); + if ( !count( $this->shLocks[$key] ) ) { + unset( $this->shLocks[$key] ); + } + unset( $this->sessionIndexSh[$session][$key] ); + if ( !count( $this->sessionIndexSh[$session] ) ) { + unset( $this->sessionIndexSh[$session] ); + } + --$this->lockCount; + } + } + + /** + * @param $key string + * @param $session string + * @return void + */ + protected function unset_ex_lock( $key, $session ) { + if ( isset( $this->exLocks[$key] ) && $this->exLocks[$key] === $session ) { + unset( $this->exLocks[$key] ); + unset( $this->sessionIndexEx[$session][$key] ); + if ( !count( $this->sessionIndexEx[$session] ) ) { + unset( $this->sessionIndexEx[$session] ); + } + --$this->lockCount; + } + } +} diff --git a/maintenance/locking/file_locks.sql b/maintenance/locking/file_locks.sql new file mode 100644 index 00000000..f51d06b3 --- /dev/null +++ b/maintenance/locking/file_locks.sql @@ -0,0 +1,11 @@ +-- Table to handle resource locking (EX) with row-level locking +CREATE TABLE /*_*/filelocks_exclusive ( + fle_key binary(31) NOT NULL PRIMARY KEY +) ENGINE=InnoDB, CHECKSUM=0; + +-- Table to handle resource locking (SH) with row-level locking +CREATE TABLE /*_*/filelocks_shared ( + fls_key binary(31) NOT NULL, + fls_session binary(31) NOT NULL, + PRIMARY KEY (fls_key,fls_session) +) ENGINE=InnoDB, CHECKSUM=0; diff --git a/maintenance/mctest.php b/maintenance/mctest.php index 651b2958..359cd248 100644 --- a/maintenance/mctest.php +++ b/maintenance/mctest.php @@ -29,15 +29,16 @@ class mcTest extends Maintenance { $this->mDescription = "Makes several 'set', 'incr' and 'get' requests on every" . " memcached server and shows a report"; $this->addOption( 'i', 'Number of iterations', false, true ); - $this->addArg( 'server', 'Memcached server to test', false ); + $this->addArg( 'server[:port]', 'Memcached server to test, with optional port', false ); } public function execute() { global $wgMemCachedServers; $iterations = $this->getOption( 'i', 100 ); - if ( $this->hasArg() ) + if ( $this->hasArg() ) { $wgMemCachedServers = array( $this->getArg() ); + } foreach ( $wgMemCachedServers as $server ) { $this->output( $server . " ", $server ); diff --git a/maintenance/mergeMessageFileList.php b/maintenance/mergeMessageFileList.php index 8cfefcbd..b5a911aa 100644 --- a/maintenance/mergeMessageFileList.php +++ b/maintenance/mergeMessageFileList.php @@ -26,25 +26,18 @@ define( 'MW_NO_EXTENSION_MESSAGES', 1 ); require_once( dirname( __FILE__ ) . '/Maintenance.php' ); -$maintClass = 'MergeMessageFileList'; -$mmfl = false; + class MergeMessageFileList extends Maintenance { function __construct() { parent::__construct(); - $this->addOption( 'list-file', 'A file containing a list of extension setup files, one per line.', false, true ); + $this->addOption( 'list-file', 'A file containing a list of extension setup files, one per line.', true, true ); $this->addOption( 'output', 'Send output to this file (omit for stdout)', false, true ); $this->mDescription = 'Merge $wgExtensionMessagesFiles from various extensions to produce a ' . 'single array containing all message files.'; } public function execute() { - global $mmfl; - if ( !$this->hasOption( 'list-file' ) ) { - $this->error( 'The --list-file option must be specified.' ); - return; - } - $lines = file( $this->getOption( 'list-file' ) ); if ( $lines === false ) { $this->error( 'Unable to open list file.' ); @@ -53,43 +46,43 @@ class MergeMessageFileList extends Maintenance { if ( $this->hasOption( 'output' ) ) { $mmfl['output'] = $this->getOption( 'output' ); } - } -} - -require_once( RUN_MAINTENANCE_IF_MAIN ); -foreach ( $mmfl['setupFiles'] as $fileName ) { - if ( strval( $fileName ) === '' ) { - continue; - } - $fileName = str_replace( '$IP', $IP, $fileName ); - fwrite( STDERR, "Loading data from $fileName\n" ); - include_once( $fileName ); -} -fwrite( STDERR, "\n" ); -$s = - "<" . "?php\n" . - "## This file is generated by mergeMessageFileList.php. Do not edit it directly.\n\n" . - "if ( defined( 'MW_NO_EXTENSION_MESSAGES' ) ) return;\n\n" . - '$wgExtensionMessagesFiles = ' . var_export( $wgExtensionMessagesFiles, true ) . ";\n\n" . - '$wgExtensionAliasesFiles = ' . var_export( $wgExtensionAliasesFiles, true ) . ";\n"; + global $IP, $wgExtensionMessagesFiles; + foreach ( $mmfl['setupFiles'] as $fileName ) { + if ( strval( $fileName ) === '' ) { + continue; + } + $fileName = str_replace( '$IP', $IP, $fileName ); + fwrite( STDERR, "Loading data from $fileName\n" ); + include_once( $fileName ); + } + fwrite( STDERR, "\n" ); + $s = + "<" . "?php\n" . + "## This file is generated by mergeMessageFileList.php. Do not edit it directly.\n\n" . + "if ( defined( 'MW_NO_EXTENSION_MESSAGES' ) ) return;\n\n" . + '$wgExtensionMessagesFiles = ' . var_export( $wgExtensionMessagesFiles, true ) . ";\n"; -$dirs = array( - $IP, - dirname( dirname( __FILE__ ) ), - realpath( $IP ) -); + $dirs = array( + $IP, + dirname( dirname( __FILE__ ) ), + realpath( $IP ) + ); -foreach ( $dirs as $dir ) { - $s = preg_replace( - "/'" . preg_quote( $dir, '/' ) . "([^']*)'/", - '"$IP\1"', - $s ); -} + foreach ( $dirs as $dir ) { + $s = preg_replace( + "/'" . preg_quote( $dir, '/' ) . "([^']*)'/", + '"$IP\1"', + $s ); + } -if ( isset( $mmfl['output'] ) ) { - file_put_contents( $mmfl['output'], $s ); -} else { - echo $s; + if ( isset( $mmfl['output'] ) ) { + file_put_contents( $mmfl['output'], $s ); + } else { + echo $s; + } + } } +$maintClass = 'MergeMessageFileList'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/moveBatch.php b/maintenance/moveBatch.php index 9b9f9105..6ecc775e 100644 --- a/maintenance/moveBatch.php +++ b/maintenance/moveBatch.php @@ -67,6 +67,9 @@ class MoveBatch extends Maintenance { $this->error( "Unable to read file, exiting", true ); } $wgUser = User::newFromName( $user ); + if ( !$wgUser ) { + $this->error( "Invalid username", true ); + } # Setup complete, now start $dbw = wfGetDB( DB_MASTER ); diff --git a/maintenance/mssql/tables.sql b/maintenance/mssql/tables.sql index dca64f17..8c4d5008 100644 --- a/maintenance/mssql/tables.sql +++ b/maintenance/mssql/tables.sql @@ -642,16 +642,6 @@ CREATE UNIQUE INDEX /*$wgDBprefix*/ls_field_val ON /*$wgDBprefix*/log_search (ls CREATE INDEX /*$wgDBprefix*/ls_log_id ON /*$wgDBprefix*/log_search (ls_log_id); -CREATE TABLE /*$wgDBprefix*/trackbacks ( - tb_id INT PRIMARY KEY, - tb_page INT REFERENCES /*$wgDBprefix*/page(page_id) ON DELETE CASCADE, - tb_title NVARCHAR(255) NOT NULL, - tb_url NVARCHAR(255) NOT NULL, - tb_ex NVARCHAR(MAX), - tb_name NVARCHAR(255), -); -CREATE INDEX /*$wgDBprefix*/trackbacks_page ON /*$wgDBprefix*/trackbacks(tb_page); - -- Jobs performed by parallel apache threads or a command-line daemon CREATE TABLE /*$wgDBprefix*/job ( job_id INT NOT NULL PRIMARY KEY, diff --git a/maintenance/mwdocgen.php b/maintenance/mwdocgen.php index ed511f74..0c3b262e 100644 --- a/maintenance/mwdocgen.php +++ b/maintenance/mwdocgen.php @@ -33,7 +33,7 @@ * @todo document * @ingroup Maintenance * - * @author Ashar Voultoiz + * @author Antoine Musso * @author Brion Vibber * @author Alexandre Emsenhuber * @version first release @@ -89,6 +89,7 @@ require_once( "$mwPath/includes/GlobalFunctions.php" ); /** * Read a line from the shell * @param $prompt String + * @return string */ function readaline( $prompt = '' ) { print $prompt; @@ -151,9 +152,12 @@ function getSvnRevision( $dir ) { * @param $exclude String: Additionals path regex to exclude * @param $exclude_patterns String: Additionals path regex to exclude * (LocalSettings.php, AdminSettings.php, .svn and .git directories are always excluded) + * @return string */ function generateConfigFile( $doxygenTemplate, $outputDirectory, $stripFromPath, $currentVersion, $svnstat, $input, $exclude, $exclude_patterns ) { + global $wgDoxyGenerateMan; + $template = file_get_contents( $doxygenTemplate ); // Replace template placeholders by correct values. @@ -166,6 +170,7 @@ function generateConfigFile( $doxygenTemplate, $outputDirectory, $stripFromPath, '{{EXCLUDE}}' => $exclude, '{{EXCLUDE_PATTERNS}}' => $exclude_patterns, '{{HAVE_DOT}}' => `which dot` ? 'YES' : 'NO', + '{{GENERATE_MAN}}' => $wgDoxyGenerateMan ? 'YES' : 'NO', ); $tmpCfg = str_replace( array_keys( $replacements ), array_values( $replacements ), $template ); $tmpFileName = tempnam( wfTempDir(), 'mwdocgen-' ); @@ -180,20 +185,56 @@ function generateConfigFile( $doxygenTemplate, $outputDirectory, $stripFromPath, unset( $file ); -if ( is_array( $argv ) && isset( $argv[1] ) ) { - switch( $argv[1] ) { - case '--all': $input = 0; break; - case '--includes': $input = 1; break; - case '--languages': $input = 2; break; - case '--maintenance': $input = 3; break; - case '--skins': $input = 4; break; - case '--file': - $input = 5; - if ( isset( $argv[2] ) ) { - $file = $argv[2]; +if ( is_array( $argv ) ) { + for ($i = 0; $i < count($argv); $i++ ) { + switch( $argv[$i] ) { + case '--all': $input = 0; break; + case '--includes': $input = 1; break; + case '--languages': $input = 2; break; + case '--maintenance': $input = 3; break; + case '--skins': $input = 4; break; + case '--file': + $input = 5; + $i++; + if ( isset( $argv[$i] ) ) { + $file = $argv[$i]; + } + break; + case '--no-extensions': $input = 6; break; + case '--output': + $i++; + if ( isset( $argv[$i] ) ) { + $doxyOutput = realpath( $argv[$i] ); + } + break; + case '--generate-man': + $wgDoxyGenerateMan = true; + break; + case '--help': + print <<] [] + +Commands: + --all Process entire codebase + --includes Process only files in includes/ dir + --languages Process only files in languages/ dir + --maintenance Process only files in maintenance/ dir + --skins Process only files in skins/ dir + --file Process only the given file + --no-extensions Process everything but extensions directorys + +If no command is given, you will be prompted. + +Other options: + --output Set output directory (default $doxyOutput) + --generate-man Generates man page documentation + --help Show this help and exit. + + +END; + exit(0); + break; } - break; - case '--no-extensions': $input = 6; break; } } diff --git a/maintenance/namespaceDupes.php b/maintenance/namespaceDupes.php index b883ea68..0a5fec33 100644 --- a/maintenance/namespaceDupes.php +++ b/maintenance/namespaceDupes.php @@ -26,6 +26,12 @@ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); class NamespaceConflictChecker extends Maintenance { + + /** + * @var DatabaseBase + */ + protected $db; + public function __construct() { parent::__construct(); $this->mDescription = ""; @@ -64,6 +70,8 @@ class NamespaceConflictChecker extends Maintenance { * @todo Document * @param $fix Boolean: whether or not to fix broken entries * @param $suffix String: suffix to append to renamed articles + * + * @return bool */ private function checkAll( $fix, $suffix = '' ) { global $wgContLang, $wgNamespaceAliases, $wgCapitalLinks; @@ -150,6 +158,7 @@ class NamespaceConflictChecker extends Maintenance { * @param $name String * @param $fix Boolean: whether to fix broken entries * @param $suffix String: suffix to append to renamed articles + * @return bool */ private function checkNamespace( $ns, $name, $fix, $suffix = '' ) { $conflicts = $this->getConflicts( $ns, $name ); @@ -171,6 +180,11 @@ class NamespaceConflictChecker extends Maintenance { /** * @todo: do this for reals + * @param $key + * @param $prefix + * @param $fix + * @param $suffix string + * @return bool */ private function checkPrefix( $key, $prefix, $fix, $suffix = '' ) { $this->output( "Checking prefix \"$prefix\" vs namespace $key\n" ); @@ -183,6 +197,8 @@ class NamespaceConflictChecker extends Maintenance { * * @param $ns Integer: namespace id (id for new namespace?) * @param $name String: prefix that is being made a namespace + * + * @return array */ private function getConflicts( $ns, $name ) { $page = 'page'; @@ -217,6 +233,8 @@ class NamespaceConflictChecker extends Maintenance { /** * Report any conflicts we find + * + * @return bool */ private function reportConflict( $row, $suffix ) { $newTitle = Title::makeTitleSafe( $row->namespace, $row->title ); @@ -254,6 +272,7 @@ class NamespaceConflictChecker extends Maintenance { * @param $row Object: row from the page table to fix * @param $resolvable Boolean * @param $suffix String: suffix to append to the fixed page + * @return bool */ private function resolveConflict( $row, $resolvable, $suffix ) { if ( !$resolvable ) { @@ -285,6 +304,7 @@ class NamespaceConflictChecker extends Maintenance { * @param $row Object: row from the old broken entry * @param $table String: table to update * @param $prefix String: prefix for column name, like page or ar + * @return bool */ private function resolveConflictOn( $row, $table, $prefix ) { $this->output( "... resolving on $table... " ); diff --git a/maintenance/nextJobDB.php b/maintenance/nextJobDB.php index 2d3608d5..67aa3088 100644 --- a/maintenance/nextJobDB.php +++ b/maintenance/nextJobDB.php @@ -37,7 +37,7 @@ class nextJobDB extends Maintenance { $memcKey = 'jobqueue:dbs:v2'; $pendingDBs = $wgMemc->get( $memcKey ); - // If the cache entry wasn't present, or in 1% of cases otherwise, + // If the cache entry wasn't present, or in 1% of cases otherwise, // regenerate the cache. if ( !$pendingDBs || mt_rand( 0, 100 ) == 0 ) { $pendingDBs = $this->getPendingDbs(); @@ -65,7 +65,7 @@ class nextJobDB extends Maintenance { $candidates = array_values( $candidates ); $db = $candidates[ mt_rand( 0, count( $candidates ) - 1 ) ]; if ( !$this->checkJob( $type, $db ) ) { - // This job is not available in the current database. Remove it from + // This job is not available in the current database. Remove it from // the cache. if ( $type === false ) { foreach ( $pendingDBs as $type2 => $dbs ) { @@ -85,7 +85,10 @@ class nextJobDB extends Maintenance { /** * Check if the specified database has a job of the specified type in it. - * The type may be false to indicate "all". + * The type may be false to indicate "all". + * @param $type string + * @param $dbName string + * @return bool */ function checkJob( $type, $dbName ) { $lb = wfGetLB( $dbName ); @@ -103,7 +106,6 @@ class nextJobDB extends Maintenance { /** * Get all databases that have a pending job - * @param $type String Job type * @return array */ private function getPendingDbs() { diff --git a/maintenance/nukeNS.php b/maintenance/nukeNS.php index c89fa94b..57417508 100644 --- a/maintenance/nukeNS.php +++ b/maintenance/nukeNS.php @@ -41,11 +41,13 @@ class NukeNS extends Maintenance { $this->mDescription = "Remove pages with only 1 revision from any namespace"; $this->addOption( 'delete', "Actually delete the page" ); $this->addOption( 'ns', 'Namespace to delete from, default NS_MEDIAWIKI', false, true ); + $this->addOption( 'all', 'Delete everything regardless of revision count' ); } public function execute() { $ns = $this->getOption( 'ns', NS_MEDIAWIKI ); $delete = $this->getOption( 'delete', false ); + $all = $this->getOption( 'all', false ); $dbw = wfGetDB( DB_MASTER ); $dbw->begin(); @@ -70,7 +72,7 @@ class NukeNS extends Maintenance { $count = count( $revs ); // skip anything that looks modified (i.e. multiple revs) - if ( $count == 1 ) { + if ( $all || $count == 1 ) { # echo $title->getPrefixedText(), "\t", $count, "\n"; $this->output( "delete: " . $title->getPrefixedText() . "\n" ); @@ -80,7 +82,7 @@ class NukeNS extends Maintenance { $dbw->query( "DELETE FROM $tbl_pag WHERE page_id = $id" ); $dbw->commit(); // Delete revisions as appropriate - $child = $this->runChild( 'NukePage', 'NukePage.php' ); + $child = $this->runChild( 'NukePage', 'nukePage.php' ); $child->deleteRevisions( $revs ); $this->purgeRedundantText( true ); $n_deleted ++; diff --git a/maintenance/oracle/archives/patch-ar_sha1_field.sql b/maintenance/oracle/archives/patch-ar_sha1_field.sql new file mode 100644 index 00000000..de723ce7 --- /dev/null +++ b/maintenance/oracle/archives/patch-ar_sha1_field.sql @@ -0,0 +1,3 @@ +define mw_prefix='{$wgDBprefix}'; + +ALTER TABLE &mw_prefix.archive ADD ar_sha1 VARCHAR2(32); diff --git a/maintenance/oracle/archives/patch-config.sql b/maintenance/oracle/archives/patch-config.sql deleted file mode 100644 index 66714a73..00000000 --- a/maintenance/oracle/archives/patch-config.sql +++ /dev/null @@ -1,8 +0,0 @@ -define mw_prefix='{$wgDBprefix}'; - -CREATE TABLE &mw_prefix.config ( - cf_name VARCHAR2(255) NOT NULL, - cf_value blob NOT NULL -); -ALTER TABLE &mw_prefix.config ADD CONSTRAINT &mw_prefix.config_pk PRIMARY KEY (cf_name); - diff --git a/maintenance/oracle/archives/patch-job_timestamp_field.sql b/maintenance/oracle/archives/patch-job_timestamp_field.sql new file mode 100644 index 00000000..4901c87c --- /dev/null +++ b/maintenance/oracle/archives/patch-job_timestamp_field.sql @@ -0,0 +1,4 @@ +define mw_prefix='{$wgDBprefix}'; + +ALTER TABLE &mw_prefix.job ADD job_timestamp TIMESTAMP(6) WITH TIME ZONE NULL; + diff --git a/maintenance/oracle/archives/patch-job_timestamp_index.sql b/maintenance/oracle/archives/patch-job_timestamp_index.sql new file mode 100644 index 00000000..6db43046 --- /dev/null +++ b/maintenance/oracle/archives/patch-job_timestamp_index.sql @@ -0,0 +1,4 @@ +define mw_prefix='{$wgDBprefix}'; + +CREATE INDEX &mw_prefix.job_i02 ON &mw_prefix.job (job_timestamp); + diff --git a/maintenance/oracle/archives/patch-logging_type_action_index.sql b/maintenance/oracle/archives/patch-logging_type_action_index.sql new file mode 100644 index 00000000..d30e0cfc --- /dev/null +++ b/maintenance/oracle/archives/patch-logging_type_action_index.sql @@ -0,0 +1,4 @@ +define mw_prefix='{$wgDBprefix}'; + +CREATE INDEX &mw_prefix.logging_i05 ON &mw_prefix.logging (log_type, log_action, log_timestamp); + diff --git a/maintenance/oracle/archives/patch-page_redirect_namespace_len.sql b/maintenance/oracle/archives/patch-page_redirect_namespace_len.sql new file mode 100644 index 00000000..1f8b9d9a --- /dev/null +++ b/maintenance/oracle/archives/patch-page_redirect_namespace_len.sql @@ -0,0 +1,4 @@ +define mw_prefix='{$wgDBprefix}'; + +CREATE INDEX &mw_prefix.page_i03 ON &mw_prefix.page (page_is_redirect, page_namespace, page_len); + diff --git a/maintenance/oracle/archives/patch-rev_sha1_field.sql b/maintenance/oracle/archives/patch-rev_sha1_field.sql new file mode 100644 index 00000000..80544e89 --- /dev/null +++ b/maintenance/oracle/archives/patch-rev_sha1_field.sql @@ -0,0 +1,4 @@ +define mw_prefix='{$wgDBprefix}'; + +ALTER TABLE &mw_prefix.revision ADD rev_sha1 VARCHAR2(32); + diff --git a/maintenance/oracle/archives/patch-ug_group-length-increase.sql b/maintenance/oracle/archives/patch-ug_group-length-increase.sql new file mode 100644 index 00000000..00a3d0c9 --- /dev/null +++ b/maintenance/oracle/archives/patch-ug_group-length-increase.sql @@ -0,0 +1,3 @@ +define mw_prefix='{$wgDBprefix}'; + +ALTER TABLE &mw_prefix.user_groups MODIFY ug_group VARCHAR2(32) NOT NULL; diff --git a/maintenance/oracle/archives/patch-us_chunk_inx_field.sql b/maintenance/oracle/archives/patch-us_chunk_inx_field.sql new file mode 100644 index 00000000..43ee16ec --- /dev/null +++ b/maintenance/oracle/archives/patch-us_chunk_inx_field.sql @@ -0,0 +1,4 @@ +define mw_prefix='{$wgDBprefix}'; + +ALTER TABLE &mw_prefix.uploadstash ADD us_chunk_inx NUMBER; + diff --git a/maintenance/oracle/archives/patch_fk_rename_deferred.sql b/maintenance/oracle/archives/patch_fk_rename_deferred.sql index ce5be9af..ca9c997f 100644 --- a/maintenance/oracle/archives/patch_fk_rename_deferred.sql +++ b/maintenance/oracle/archives/patch_fk_rename_deferred.sql @@ -35,7 +35,6 @@ ALTER TABLE &mw_prefix.recentchanges ADD CONSTRAINT &mw_prefix.recentchanges_fk1 ALTER TABLE &mw_prefix.recentchanges ADD CONSTRAINT &mw_prefix.recentchanges_fk2 FOREIGN KEY (rc_cur_id) REFERENCES &mw_prefix.page(page_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED; ALTER TABLE &mw_prefix.watchlist ADD CONSTRAINT &mw_prefix.watchlist_fk1 FOREIGN KEY (wl_user) REFERENCES &mw_prefix.mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED; ALTER TABLE &mw_prefix.logging ADD CONSTRAINT &mw_prefix.logging_fk1 FOREIGN KEY (log_user) REFERENCES &mw_prefix.mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED; -ALTER TABLE &mw_prefix.trackbacks ADD CONSTRAINT &mw_prefix.trackbacks_fk1 FOREIGN KEY (tb_page) REFERENCES &mw_prefix.page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED; ALTER TABLE &mw_prefix.redirect ADD CONSTRAINT &mw_prefix.redirect_fk1 FOREIGN KEY (rd_from) REFERENCES &mw_prefix.page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED; ALTER TABLE &mw_prefix.page_restrictions ADD CONSTRAINT &mw_prefix.page_restrictions_fk1 FOREIGN KEY (pr_page) REFERENCES &mw_prefix.page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED; diff --git a/maintenance/oracle/archives/patch_rebuild_dupfunc.sql b/maintenance/oracle/archives/patch_rebuild_dupfunc.sql index 0a232dbc..56ee5b3e 100644 --- a/maintenance/oracle/archives/patch_rebuild_dupfunc.sql +++ b/maintenance/oracle/archives/patch_rebuild_dupfunc.sql @@ -10,7 +10,7 @@ CREATE OR REPLACE PROCEDURE duplicate_table(p_tabname IN VARCHAR2, BEGIN BEGIN EXECUTE IMMEDIATE 'DROP TABLE ' || p_newprefix || p_tabname || - ' CASCADE CONSTRAINTS'; + ' CASCADE CONSTRAINTS PURGE'; EXCEPTION WHEN e_table_not_exist THEN NULL; @@ -20,8 +20,9 @@ BEGIN END IF; IF (l_temporary) THEN EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE ' || p_newprefix || - p_tabname || ' AS SELECT * FROM ' || p_oldprefix || - p_tabname || ' WHERE ROWNUM = 0'; + p_tabname || + ' ON COMMIT PRESERVE ROWS AS SELECT * FROM ' || + p_oldprefix || p_tabname || ' WHERE ROWNUM = 0'; ELSE EXECUTE IMMEDIATE 'CREATE TABLE ' || p_newprefix || p_tabname || ' AS SELECT * FROM ' || p_oldprefix || p_tabname || @@ -68,7 +69,8 @@ BEGIN FROM user_constraints uc WHERE table_name = p_oldprefix || p_tabname AND constraint_type = 'R') LOOP - IF nvl(length(l_temp_ei_sql), 0) > 0 THEN + IF nvl(length(l_temp_ei_sql), 0) > 0 AND + INSTR(l_temp_ei_sql, 'PRIMARY KEY') = 0 THEN EXECUTE IMMEDIATE l_temp_ei_sql; END IF; END LOOP; @@ -142,5 +144,6 @@ BEGIN END IF; END LOOP; END; + /*$mw$*/ diff --git a/maintenance/oracle/archives/patch_remove_not_null_empty_defs2.sql b/maintenance/oracle/archives/patch_remove_not_null_empty_defs2.sql new file mode 100644 index 00000000..f7a38a05 --- /dev/null +++ b/maintenance/oracle/archives/patch_remove_not_null_empty_defs2.sql @@ -0,0 +1,3 @@ +define mw_prefix='{$wgDBprefix}'; + +ALTER TABLE &mw_prefix.ipblocks MODIFY ipb_by_text DEFAULT NULL NULL; diff --git a/maintenance/oracle/tables.sql b/maintenance/oracle/tables.sql index c6162753..9f875aed 100644 --- a/maintenance/oracle/tables.sql +++ b/maintenance/oracle/tables.sql @@ -31,12 +31,19 @@ INSERT INTO &mw_prefix.mwuser CREATE TABLE &mw_prefix.user_groups ( ug_user NUMBER DEFAULT 0 NOT NULL, - ug_group VARCHAR2(16) NOT NULL + ug_group VARCHAR2(32) NOT NULL ); ALTER TABLE &mw_prefix.user_groups ADD CONSTRAINT &mw_prefix.user_groups_fk1 FOREIGN KEY (ug_user) REFERENCES &mw_prefix.mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED; CREATE UNIQUE INDEX &mw_prefix.user_groups_u01 ON &mw_prefix.user_groups (ug_user,ug_group); CREATE INDEX &mw_prefix.user_groups_i01 ON &mw_prefix.user_groups (ug_group); +CREATE TABLE &mw_prefix.user_former_groups ( + ufg_user NUMBER DEFAULT 0 NOT NULL, + ufg_group VARCHAR2(16) NOT NULL +); +ALTER TABLE &mw_prefix.user_former_groups ADD CONSTRAINT &mw_prefix.user_former_groups_fk1 FOREIGN KEY (ufg_user) REFERENCES &mw_prefix.mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED; +CREATE UNIQUE INDEX &mw_prefix.user_former_groups_u01 ON &mw_prefix.user_former_groups (ufg_user,ufg_group); + CREATE TABLE &mw_prefix.user_newtalk ( user_id NUMBER DEFAULT 0 NOT NULL, user_ip VARCHAR2(40) NULL, @@ -72,6 +79,7 @@ ALTER TABLE &mw_prefix.page ADD CONSTRAINT &mw_prefix.page_pk PRIMARY KEY (page_ CREATE UNIQUE INDEX &mw_prefix.page_u01 ON &mw_prefix.page (page_namespace,page_title); CREATE INDEX &mw_prefix.page_i01 ON &mw_prefix.page (page_random); CREATE INDEX &mw_prefix.page_i02 ON &mw_prefix.page (page_len); +CREATE INDEX &mw_prefix.page_i03 ON &mw_prefix.page (page_is_redirect, page_namespace, page_len); -- Create a dummy page to satisfy fk contraints especially with revisions INSERT INTO &mw_prefix.page @@ -97,7 +105,8 @@ CREATE TABLE &mw_prefix.revision ( rev_minor_edit CHAR(1) DEFAULT '0' NOT NULL, rev_deleted CHAR(1) DEFAULT '0' NOT NULL, rev_len NUMBER NULL, - rev_parent_id NUMBER DEFAULT NULL + rev_parent_id NUMBER DEFAULT NULL, + rev_sha1 VARCHAR2(32) NULL ); ALTER TABLE &mw_prefix.revision ADD CONSTRAINT &mw_prefix.revision_pk PRIMARY KEY (rev_id); ALTER TABLE &mw_prefix.revision ADD CONSTRAINT &mw_prefix.revision_fk1 FOREIGN KEY (rev_page) REFERENCES &mw_prefix.page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED; @@ -131,7 +140,8 @@ CREATE TABLE &mw_prefix.archive ( ar_deleted CHAR(1) DEFAULT '0' NOT NULL, ar_len NUMBER, ar_page_id NUMBER, - ar_parent_id NUMBER + ar_parent_id NUMBER, + ar_sha1 VARCHAR2(32) NULL ); ALTER TABLE &mw_prefix.archive ADD CONSTRAINT &mw_prefix.archive_fk1 FOREIGN KEY (ar_user) REFERENCES &mw_prefix.mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED; CREATE INDEX &mw_prefix.archive_i01 ON &mw_prefix.archive (ar_namespace,ar_title,ar_timestamp); @@ -250,7 +260,7 @@ CREATE TABLE &mw_prefix.ipblocks ( ipb_address VARCHAR2(255) NULL, ipb_user NUMBER DEFAULT 0 NOT NULL, ipb_by NUMBER DEFAULT 0 NOT NULL, - ipb_by_text VARCHAR2(255) NOT NULL, + ipb_by_text VARCHAR2(255) NULL, ipb_reason VARCHAR2(255) NOT NULL, ipb_timestamp TIMESTAMP(6) WITH TIME ZONE NOT NULL, ipb_auto CHAR(1) DEFAULT '0' NOT NULL, @@ -355,6 +365,31 @@ CREATE INDEX &mw_prefix.filearchive_i02 ON &mw_prefix.filearchive (fa_storage_gr CREATE INDEX &mw_prefix.filearchive_i03 ON &mw_prefix.filearchive (fa_deleted_timestamp); CREATE INDEX &mw_prefix.filearchive_i04 ON &mw_prefix.filearchive (fa_user_text,fa_timestamp); +CREATE SEQUENCE uploadstash_us_id_seq; +CREATE TABLE &mw_prefix.uploadstash ( + us_id NUMBER NOT NULL, + us_user NUMBER DEFAULT 0 NOT NULL, + us_key VARCHAR2(255) NOT NULL, + us_orig_path VARCHAR2(255) NOT NULL, + us_path VARCHAR2(255) NOT NULL, + us_source_type VARCHAR2(50), + us_timestamp TIMESTAMP(6) WITH TIME ZONE, + us_status VARCHAR2(50) NOT NULL, + us_chunk_inx NUMBER, + us_size NUMBER NOT NULL, + us_sha1 VARCHAR2(32) NOT NULL, + us_mime VARCHAR2(255), + us_media_type VARCHAR2(32) DEFAULT NULL, + us_image_width NUMBER, + us_image_height NUMBER, + us_image_bits NUMBER +); +ALTER TABLE &mw_prefix.uploadstash ADD CONSTRAINT &mw_prefix.uploadstash_pk PRIMARY KEY (us_id); +ALTER TABLE &mw_prefix.uploadstash ADD CONSTRAINT &mw_prefix.uploadstash_fk1 FOREIGN KEY (us_user) REFERENCES &mw_prefix.mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED; +CREATE INDEX &mw_prefix.uploadstash_i01 ON &mw_prefix.uploadstash (us_user); +CREATE INDEX &mw_prefix.uploadstash_i02 ON &mw_prefix.uploadstash (us_timestamp); +CREATE UNIQUE INDEX &mw_prefix.uploadstash_u01 ON &mw_prefix.uploadstash (us_key); + CREATE SEQUENCE recentchanges_rc_id_seq; CREATE TABLE &mw_prefix.recentchanges ( rc_id NUMBER NOT NULL, @@ -467,6 +502,7 @@ CREATE INDEX &mw_prefix.logging_i01 ON &mw_prefix.logging (log_type, log_timesta CREATE INDEX &mw_prefix.logging_i02 ON &mw_prefix.logging (log_user, log_timestamp); CREATE INDEX &mw_prefix.logging_i03 ON &mw_prefix.logging (log_namespace, log_title, log_timestamp); CREATE INDEX &mw_prefix.logging_i04 ON &mw_prefix.logging (log_timestamp); +CREATE INDEX &mw_prefix.logging_i05 ON &mw_prefix.logging (log_type, log_action, log_timestamp); CREATE TABLE &mw_prefix.log_search ( ls_field VARCHAR2(32) NOT NULL, @@ -476,18 +512,6 @@ CREATE TABLE &mw_prefix.log_search ( ALTER TABLE &mw_prefix.log_search ADD CONSTRAINT log_search_pk PRIMARY KEY (ls_field,ls_value,ls_log_id); CREATE INDEX &mw_prefix.log_search_i01 ON &mw_prefix.log_search (ls_log_id); -CREATE SEQUENCE trackbacks_tb_id_seq; -CREATE TABLE &mw_prefix.trackbacks ( - tb_id NUMBER NOT NULL, - tb_page NUMBER, - tb_title VARCHAR2(255) NOT NULL, - tb_url VARCHAR2(255) NOT NULL, - tb_ex CLOB, - tb_name VARCHAR2(255) -); -ALTER TABLE &mw_prefix.trackbacks ADD CONSTRAINT &mw_prefix.trackbacks_pk PRIMARY KEY (tb_id); -ALTER TABLE &mw_prefix.trackbacks ADD CONSTRAINT &mw_prefix.trackbacks_fk1 FOREIGN KEY (tb_page) REFERENCES &mw_prefix.page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED; -CREATE INDEX &mw_prefix.trackbacks_i01 ON &mw_prefix.trackbacks (tb_page); CREATE SEQUENCE job_job_id_seq; CREATE TABLE &mw_prefix.job ( @@ -495,10 +519,12 @@ CREATE TABLE &mw_prefix.job ( job_cmd VARCHAR2(60) NOT NULL, job_namespace NUMBER DEFAULT 0 NOT NULL, job_title VARCHAR2(255) NOT NULL, + job_timestamp TIMESTAMP(6) WITH TIME ZONE NULL, job_params CLOB NOT NULL ); ALTER TABLE &mw_prefix.job ADD CONSTRAINT &mw_prefix.job_pk PRIMARY KEY (job_id); CREATE INDEX &mw_prefix.job_i01 ON &mw_prefix.job (job_cmd, job_namespace, job_title); +CREATE INDEX &mw_prefix.job_i02 ON &mw_prefix.job (job_timestamp); CREATE TABLE &mw_prefix.querycache_info ( qci_type VARCHAR2(32) NOT NULL, @@ -642,8 +668,7 @@ CREATE TABLE &mw_prefix.config ( cf_value blob NOT NULL ); ALTER TABLE &mw_prefix.config ADD CONSTRAINT &mw_prefix.config_pk PRIMARY KEY (cf_name); --- leaving index out for now ... - +-- leaving index out for now ... -- do not prefix this table as it breaks parserTests CREATE TABLE wiki_field_info_full ( @@ -653,9 +678,9 @@ data_default VARCHAR2(4000), data_length NUMBER NOT NULL, data_type VARCHAR2(106), not_null CHAR(1) NOT NULL, -prim NUMBER(1), +prim NUMBER(1), uniq NUMBER(1), -nonuniq NUMBER(1) +nonuniq NUMBER(1) ); ALTER TABLE wiki_field_info_full ADD CONSTRAINT wiki_field_info_full_pk PRIMARY KEY (table_name, column_name); @@ -722,7 +747,7 @@ CREATE OR REPLACE PROCEDURE duplicate_table(p_tabname IN VARCHAR2, BEGIN BEGIN EXECUTE IMMEDIATE 'DROP TABLE ' || p_newprefix || p_tabname || - ' CASCADE CONSTRAINTS'; + ' CASCADE CONSTRAINTS PURGE'; EXCEPTION WHEN e_table_not_exist THEN NULL; @@ -732,8 +757,9 @@ BEGIN END IF; IF (l_temporary) THEN EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE ' || p_newprefix || - p_tabname || ' AS SELECT * FROM ' || p_oldprefix || - p_tabname || ' WHERE ROWNUM = 0'; + p_tabname || + ' ON COMMIT PRESERVE ROWS AS SELECT * FROM ' || + p_oldprefix || p_tabname || ' WHERE ROWNUM = 0'; ELSE EXECUTE IMMEDIATE 'CREATE TABLE ' || p_newprefix || p_tabname || ' AS SELECT * FROM ' || p_oldprefix || p_tabname || @@ -780,7 +806,8 @@ BEGIN FROM user_constraints uc WHERE table_name = p_oldprefix || p_tabname AND constraint_type = 'R') LOOP - IF nvl(length(l_temp_ei_sql), 0) > 0 THEN + IF nvl(length(l_temp_ei_sql), 0) > 0 AND + INSTR(l_temp_ei_sql, 'PRIMARY KEY') = 0 THEN EXECUTE IMMEDIATE l_temp_ei_sql; END IF; END LOOP; diff --git a/maintenance/orphans.php b/maintenance/orphans.php index 1986ff35..faaadd37 100644 --- a/maintenance/orphans.php +++ b/maintenance/orphans.php @@ -53,10 +53,11 @@ class Orphans extends Maintenance { * @param $db DatabaseBase object * @param $extraTable String The name of any extra tables to lock (eg: text) */ - private function lockTables( &$db, $extraTable = null ) { + private function lockTables( $db, $extraTable = array() ) { $tbls = array( 'page', 'revision', 'redirect' ); - if ( $extraTable ) - $tbls[] = $extraTable; + if ( $extraTable ) { + $tbls = array_merge( $tbls, $extraTable ); + } $db->lockTables( array(), $tbls, __METHOD__, false ); } @@ -167,7 +168,7 @@ class Orphans extends Maintenance { $revision = $dbw->tableName( 'revision' ); if ( $fix ) { - $dbw->lockTables( $dbw, 'text', __METHOD__ ); + $this->lockTables( $dbw, array( 'user', 'text' ) ); } $this->output( "\nChecking for pages whose page_latest links are incorrect... (this may take a while on a large wiki)\n" ); @@ -206,7 +207,7 @@ class Orphans extends Maintenance { $this->output( "... updating to revision $maxId\n" ); $maxRev = Revision::newFromId( $maxId ); $title = Title::makeTitle( $row->page_namespace, $row->page_title ); - $article = new Article( $title ); + $article = WikiPage::factory( $title ); $article->updateRevisionOn( $dbw, $maxRev ); } } diff --git a/maintenance/ourusers.php b/maintenance/ourusers.php deleted file mode 100644 index c8578e24..00000000 --- a/maintenance/ourusers.php +++ /dev/null @@ -1,70 +0,0 @@ -foo + *

+ * @endcode + * + * Example2: + * @code + * $ echo "'''bold'''" > /tmp/foo.txt + * $ php parse.php /tmp/foo.txt + *

bold + *

$ + * @endcode + * + * Example3: + * @code + * $ cat /tmp/foo | php parse.php + *

bold + *

$ + * @endcode + * + * @ingroup Maintenance + * @author Antoine Musso + * @license GNU General Public License 2.0 or later + */ +require_once( dirname(__FILE__) . '/Maintenance.php' ); + +class CLIParser extends Maintenance { + protected $parser; + + public function __construct() { + parent::__construct(); + $this->mDescription = "Parse a given wikitext"; + $this->addOption( 'title', 'Title name for the given wikitext (Default: \'CLIParser\')', false, true ); + $this->addArg( 'file', 'File containing wikitext (Default: stdin)', false ); + } + + public function execute() { + $this->initParser(); + print $this->render( $this->WikiText() ); + } + + /** + * @param string $wikitext Wikitext to get rendered + * @return string HTML Rendering + */ + public function render( $wikitext ) { + return $this->parse( $wikitext )->getText(); + } + + /** + * Get wikitext from a the file passed as argument or STDIN + * @return string Wikitext + */ + protected function Wikitext() { + + $php_stdin = 'php://stdin'; + $input_file = $this->getArg( 0, $php_stdin ); + + if( $input_file === $php_stdin ) { + $this->error( basename(__FILE__) .": warning: reading wikitext from STDIN\n" ); + } + + return file_get_contents( $input_file ); + } + + protected function initParser() { + global $wgParserConf; + $parserClass = $wgParserConf['class']; + $this->parser = new $parserClass(); + } + + /** + * Title object to use for CLI parsing. + * Default title is 'CLIParser', it can be overriden with the option + * --title + * + * @return Title object + */ + protected function getTitle( ) { + $title = + $this->getOption( 'title' ) + ? $this->getOption( 'title' ) + : 'CLIParser' ; + return Title::newFromText( $title ); + } + + /** + * @param string $wikitext Wikitext to parse + * @return ParserOutput + */ + protected function parse( $wikitext ) { + return $this->parser->parse( + $wikitext + , $this->getTitle() + , new ParserOptions() + ); + } +} + +$maintClass = "CLIParser"; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/populateCategory.php b/maintenance/populateCategory.php index 4e9c44c4..0b45493e 100644 --- a/maintenance/populateCategory.php +++ b/maintenance/populateCategory.php @@ -22,8 +22,6 @@ * @author Simetrical */ -$optionsWithArgs = array( 'begin', 'max-slave-lag', 'throttle' ); - require_once( dirname( __FILE__ ) . '/Maintenance.php' ); diff --git a/maintenance/populateImageSha1.php b/maintenance/populateImageSha1.php new file mode 100644 index 00000000..4cd2747c --- /dev/null +++ b/maintenance/populateImageSha1.php @@ -0,0 +1,121 @@ +mDescription = "Populate the img_sha1 field"; + $this->addOption( 'method', "Use 'pipe' to pipe to mysql command line,\n" . + "\t\tdefault uses Database class", false, true ); + $this->addOption( 'file', 'Fix for a specific file, without File: namespace prefixed', false, true ); + } + + protected function getUpdateKey() { + return 'populate img_sha1'; + } + + protected function updateSkippedMessage() { + return 'img_sha1 column of image table already populated.'; + } + + public function execute() { + if ( $this->getOption( 'file' ) ) { + $this->doDBUpdates(); // skip update log checks/saves + } else { + parent::execute(); + } + } + + public function doDBUpdates() { + $method = $this->getOption( 'method', 'normal' ); + $file = $this->getOption( 'file' ); + + $t = -microtime( true ); + $dbw = wfGetDB( DB_MASTER ); + if ( $file ) { + $res = $dbw->select( + 'image', + array( 'img_name' ), + array( 'img_name' => $file ), + __METHOD__ + ); + if ( !$res ) { + $this->error( "No such file: $file", true ); + return false; + } + $this->output( "Populating img_sha1 field for specified files\n" ); + } else { + $res = $dbw->select( 'image', + array( 'img_name' ), array( 'img_sha1' => '' ), __METHOD__ ); + $this->output( "Populating img_sha1 field\n" ); + } + + $imageTable = $dbw->tableName( 'image' ); + + if ( $method == 'pipe' ) { + // @todo FIXME: Kill this and replace with a second unbuffered DB connection. + global $wgDBuser, $wgDBserver, $wgDBpassword, $wgDBname; + $cmd = 'mysql -u' . wfEscapeShellArg( $wgDBuser ) . + ' -h' . wfEscapeShellArg( $wgDBserver ) . + ' -p' . wfEscapeShellArg( $wgDBpassword, $wgDBname ); + $this->output( "Using pipe method\n" ); + $pipe = popen( $cmd, 'w' ); + } + + $numRows = $res->numRows(); + $i = 0; + foreach ( $res as $row ) { + if ( $i % $this->mBatchSize == 0 ) { + $this->output( sprintf( "Done %d of %d, %5.3f%% \r", $i, $numRows, $i / $numRows * 100 ) ); + wfWaitForSlaves(); + } + $file = wfLocalFile( $row->img_name ); + if ( !$file ) { + continue; + } + $sha1 = $file->getRepo()->getFileSha1( $file->getPath() ); + if ( strval( $sha1 ) !== '' ) { + $sql = "UPDATE $imageTable SET img_sha1=" . $dbw->addQuotes( $sha1 ) . + " WHERE img_name=" . $dbw->addQuotes( $row->img_name ); + if ( $method == 'pipe' ) { + fwrite( $pipe, "$sql;\n" ); + } else { + $dbw->query( $sql, __METHOD__ ); + } + } + $i++; + } + if ( $method == 'pipe' ) { + fflush( $pipe ); + pclose( $pipe ); + } + $t += microtime( true ); + $this->output( sprintf( "\nDone %d files in %.1f seconds\n", $numRows, $t ) ); + + return !$file; // we only updated *some* files, don't log + } +} + +$maintClass = "PopulateImageSha1"; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/populateLogSearch.php b/maintenance/populateLogSearch.php index f13873cb..e3f6067f 100644 --- a/maintenance/populateLogSearch.php +++ b/maintenance/populateLogSearch.php @@ -23,21 +23,28 @@ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); -class PopulateLogSearch extends Maintenance { - - const LOG_SEARCH_BATCH_SIZE = 100; - +class PopulateLogSearch extends LoggedUpdateMaintenance { static $tableMap = array( 'rev' => 'revision', 'fa' => 'filearchive', 'oi' => 'oldimage', 'ar' => 'archive' ); public function __construct() { parent::__construct(); $this->mDescription = "Migrate log params to new table and index for searching"; + $this->setBatchSize( 100 ); + } + + protected function getUpdateKey() { + return 'populate log_search'; + } + + protected function updateSkippedMessage() { + return 'log_search table already populated.'; } - public function execute() { + protected function doDBUpdates() { $db = $this->getDB( DB_MASTER ); if ( !$db->tableExists( 'log_search' ) ) { - $this->error( "log_search does not exist", true ); + $this->error( "log_search does not exist" ); + return false; } $start = $db->selectField( 'logging', 'MIN(log_id)', false, __FUNCTION__ ); if ( !$start ) { @@ -47,9 +54,9 @@ class PopulateLogSearch extends Maintenance { $end = $db->selectField( 'logging', 'MAX(log_id)', false, __FUNCTION__ ); # Do remaining chunk - $end += self::LOG_SEARCH_BATCH_SIZE - 1; + $end += $this->mBatchSize - 1; $blockStart = $start; - $blockEnd = $start + self::LOG_SEARCH_BATCH_SIZE - 1; + $blockEnd = $start + $this->mBatchSize - 1; $delTypes = array( 'delete', 'suppress' ); // revisiondelete types while ( $blockEnd <= $end ) { @@ -128,23 +135,12 @@ class PopulateLogSearch extends Maintenance { $log->addRelations( 'target_author_ip', $userIPs, $row->log_id ); } } - $blockStart += self::LOG_SEARCH_BATCH_SIZE; - $blockEnd += self::LOG_SEARCH_BATCH_SIZE; + $blockStart += $this->mBatchSize; + $blockEnd += $this->mBatchSize; wfWaitForSlaves(); } - if ( $db->insert( - 'updatelog', - array( 'ul_key' => 'populate log_search' ), - __FUNCTION__, - 'IGNORE' - ) - ) { - $this->output( "log_search population complete.\n" ); - return true; - } else { - $this->output( "Could not insert log_search population row.\n" ); - return false; - } + $this->output( "Done populating log_search table.\n" ); + return true; } } diff --git a/maintenance/populateLogUsertext.php b/maintenance/populateLogUsertext.php index e9e6926e..2b0e2d64 100644 --- a/maintenance/populateLogUsertext.php +++ b/maintenance/populateLogUsertext.php @@ -25,14 +25,22 @@ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); -class PopulateLogUsertext extends Maintenance { +class PopulateLogUsertext extends LoggedUpdateMaintenance { public function __construct() { parent::__construct(); - $this->mDescription = "Populates the log_user_text"; + $this->mDescription = "Populates the log_user_text field"; $this->setBatchSize( 100 ); } - public function execute() { + protected function getUpdateKey() { + return 'populate log_usertext'; + } + + protected function updateSkippedMessage() { + return 'log_user_text column of logging table already populated.'; + } + + protected function doDBUpdates() { $db = $this->getDB( DB_MASTER ); $start = $db->selectField( 'logging', 'MIN(log_id)', false, __METHOD__ ); if ( !$start ) { @@ -61,19 +69,8 @@ class PopulateLogUsertext extends Maintenance { $blockEnd += $this->mBatchSize; wfWaitForSlaves(); } - if ( $db->insert( - 'updatelog', - array( 'ul_key' => 'populate log_usertext' ), - __METHOD__, - 'IGNORE' - ) - ) { - $this->output( "log_usertext population complete.\n" ); - return true; - } else { - $this->output( "Could not insert log_usertext population row.\n" ); - return false; - } + $this->output( "Done populating log_user_text field.\n" ); + return true; } } diff --git a/maintenance/populateParentId.php b/maintenance/populateParentId.php index 7cbc267b..14f158c9 100644 --- a/maintenance/populateParentId.php +++ b/maintenance/populateParentId.php @@ -1,5 +1,5 @@ mDescription = "Populates rev_parent_id"; - $this->setBatchSize( 200 ); } - public function execute() { + protected function getUpdateKey() { + return 'populate rev_parent_id'; + } + + protected function updateSkippedMessage() { + return 'rev_parent_id column of revision table already populated.'; + } + + protected function doDBUpdates() { $db = wfGetDB( DB_MASTER ); if ( !$db->tableExists( 'revision' ) ) { - $this->error( "revision table does not exist", true ); + $this->error( "revision table does not exist" ); + return false; } $this->output( "Populating rev_parent_id column\n" ); $start = $db->selectField( 'revision', 'MIN(rev_id)', false, __FUNCTION__ ); $end = $db->selectField( 'revision', 'MAX(rev_id)', false, __FUNCTION__ ); if ( is_null( $start ) || is_null( $end ) ) { - $this->output( "...revision table seems to be empty.\n" ); - $db->insert( 'updatelog', - array( 'ul_key' => 'populate rev_parent_id' ), - __METHOD__, - 'IGNORE' ); - return; + $this->output( "...revision table seems to be empty, nothing to do.\n" ); + return true; } # Do remaining chunk $blockStart = intval( $start ); @@ -100,17 +104,8 @@ class PopulateParentId extends Maintenance { $blockEnd += $this->mBatchSize; wfWaitForSlaves(); } - $logged = $db->insert( 'updatelog', - array( 'ul_key' => 'populate rev_parent_id' ), - __METHOD__, - 'IGNORE' ); - if ( $logged ) { - $this->output( "rev_parent_id population complete ... {$count} rows [{$changed} changed]\n" ); - return true; - } else { - $this->output( "Could not insert rev_parent_id population row.\n" ); - return false; - } + $this->output( "rev_parent_id population complete ... {$count} rows [{$changed} changed]\n" ); + return true; } } diff --git a/maintenance/populateRevisionLength.php b/maintenance/populateRevisionLength.php index d020b4cb..6626cbc1 100644 --- a/maintenance/populateRevisionLength.php +++ b/maintenance/populateRevisionLength.php @@ -1,5 +1,5 @@ mDescription = "Populates rev_len"; + $this->mDescription = "Populates the rev_len field"; $this->setBatchSize( 200 ); } - public function execute() { + protected function getUpdateKey() { + return 'populate rev_len'; + } + + protected function updateSkippedMessage() { + return 'rev_len column of revision table already populated.'; + } + + public function doDBUpdates() { $db = $this->getDB( DB_MASTER ); if ( !$db->tableExists( 'revision' ) ) { $this->error( "revision table does not exist", true ); } $this->output( "Populating rev_len column\n" ); - $start = $db->selectField( 'revision', 'MIN(rev_id)', false, __FUNCTION__ ); - $end = $db->selectField( 'revision', 'MAX(rev_id)', false, __FUNCTION__ ); - if ( is_null( $start ) || is_null( $end ) ) { + + $start = $db->selectField( 'revision', 'MIN(rev_id)', false, __METHOD__ ); + $end = $db->selectField( 'revision', 'MAX(rev_id)', false, __METHOD__ ); + if ( !$start || !$end ) { $this->output( "...revision table seems to be empty.\n" ); - $db->insert( 'updatelog', - array( 'ul_key' => 'populate rev_len' ), - __METHOD__, - 'IGNORE' ); - return; + return true; } + # Do remaining chunks $blockStart = intval( $start ); $blockEnd = intval( $start ) + $this->mBatchSize - 1; @@ -80,17 +86,9 @@ class PopulateRevisionLength extends Maintenance { $blockEnd += $this->mBatchSize; wfWaitForSlaves(); } - $logged = $db->insert( 'updatelog', - array( 'ul_key' => 'populate rev_len' ), - __METHOD__, - 'IGNORE' ); - if ( $logged ) { - $this->output( "rev_len population complete ... {$count} rows changed ({$missing} missing)\n" ); - return true; - } else { - $this->output( "Could not insert rev_len population row.\n" ); - return false; - } + + $this->output( "rev_len population complete ... {$count} rows changed ({$missing} missing)\n" ); + return true; } } diff --git a/maintenance/populateRevisionSha1.php b/maintenance/populateRevisionSha1.php new file mode 100644 index 00000000..55e2a93d --- /dev/null +++ b/maintenance/populateRevisionSha1.php @@ -0,0 +1,185 @@ +mDescription = "Populates the rev_sha1 and ar_sha1 fields"; + $this->setBatchSize( 200 ); + } + + protected function getUpdateKey() { + return 'populate rev_sha1'; + } + + protected function doDBUpdates() { + $db = $this->getDB( DB_MASTER ); + + if ( !$db->tableExists( 'revision' ) ) { + $this->error( "revision table does not exist", true ); + } elseif ( !$db->tableExists( 'archive' ) ) { + $this->error( "archive table does not exist", true ); + } + + $this->output( "Populating rev_sha1 column\n" ); + $rc = $this->doSha1Updates( 'revision', 'rev_id', 'rev' ); + + $this->output( "Populating ar_sha1 column\n" ); + $ac = $this->doSha1Updates( 'archive', 'ar_rev_id', 'ar' ); + $this->output( "Populating ar_sha1 column legacy rows\n" ); + $ac += $this->doSha1LegacyUpdates(); + + $this->output( "rev_sha1 and ar_sha1 population complete [$rc revision rows, $ac archive rows].\n" ); + return true; + } + + /** + * @param $table string + * @param $idCol + * @param $prefix string + * @return Integer Rows changed + */ + protected function doSha1Updates( $table, $idCol, $prefix ) { + $db = $this->getDB( DB_MASTER ); + $start = $db->selectField( $table, "MIN($idCol)", false, __METHOD__ ); + $end = $db->selectField( $table, "MAX($idCol)", false, __METHOD__ ); + if ( !$start || !$end ) { + $this->output( "...$table table seems to be empty.\n" ); + return 0; + } + + $count = 0; + # Do remaining chunk + $end += $this->mBatchSize - 1; + $blockStart = $start; + $blockEnd = $start + $this->mBatchSize - 1; + while ( $blockEnd <= $end ) { + $this->output( "...doing $idCol from $blockStart to $blockEnd\n" ); + $cond = "$idCol BETWEEN $blockStart AND $blockEnd + AND $idCol IS NOT NULL AND {$prefix}_sha1 = ''"; + $res = $db->select( $table, '*', $cond, __METHOD__ ); + + $db->begin(); + foreach ( $res as $row ) { + if ( $this->upgradeRow( $row, $table, $idCol, $prefix ) ) { + $count++; + } + } + $db->commit(); + + $blockStart += $this->mBatchSize; + $blockEnd += $this->mBatchSize; + wfWaitForSlaves(); + } + return $count; + } + + /** + * @return int + */ + protected function doSha1LegacyUpdates() { + $count = 0; + $db = $this->getDB( DB_MASTER ); + $res = $db->select( 'archive', '*', array( 'ar_rev_id IS NULL' ), __METHOD__ ); + + $updateSize = 0; + $db->begin(); + foreach ( $res as $row ) { + if ( $this->upgradeLegacyArchiveRow( $row ) ) { + ++$count; + } + if ( ++$updateSize >= 100 ) { + $updateSize = 0; + $db->commit(); + $this->output( "Commited row with ar_timestamp={$row->ar_timestamp}\n" ); + wfWaitForSlaves(); + $db->begin(); + } + } + $db->commit(); + return $count; + } + + /** + * @param $row + * @param $table + * @param $idCol + * @param $prefix + * @return bool + */ + protected function upgradeRow( $row, $table, $idCol, $prefix ) { + $db = $this->getDB( DB_MASTER ); + if ( $table === 'archive' ) { + $rev = Revision::newFromArchiveRow( $row ); + } else { + $rev = new Revision( $row ); + } + $text = $rev->getRawText(); + if ( !is_string( $text ) ) { + # This should not happen, but sometimes does (bug 20757) + $this->output( "Text of revision with {$idCol}={$row->$idCol} unavailable!\n" ); + return false; + } else { + $db->update( $table, + array( "{$prefix}_sha1" => Revision::base36Sha1( $text ) ), + array( $idCol => $row->$idCol ), + __METHOD__ + ); + return true; + } + } + + /** + * @param $row + * @return bool + */ + protected function upgradeLegacyArchiveRow( $row ) { + $db = $this->getDB( DB_MASTER ); + $rev = Revision::newFromArchiveRow( $row ); + $text = $rev->getRawText(); + if ( !is_string( $text ) ) { + # This should not happen, but sometimes does (bug 20757) + $this->output( "Text of revision with timestamp {$row->ar_timestamp} unavailable!\n" ); + return false; + } else { + # Archive table as no PK, but (NS,title,time) should be near unique. + # Any duplicates on those should also have duplicated text anyway. + $db->update( 'archive', + array( 'ar_sha1' => Revision::base36Sha1( $text ) ), + array( + 'ar_namespace' => $row->ar_namespace, + 'ar_title' => $row->ar_title, + 'ar_timestamp' => $row->ar_timestamp, + 'ar_len' => $row->ar_len // extra sanity + ), + __METHOD__ + ); + return true; + } + } +} + +$maintClass = "PopulateRevisionSha1"; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/populateSha1.php b/maintenance/populateSha1.php deleted file mode 100644 index 1ab9109d..00000000 --- a/maintenance/populateSha1.php +++ /dev/null @@ -1,99 +0,0 @@ -mDescription = "Populate the img_sha1 field"; - $this->addOption( 'method', "Use 'pipe' to pipe to mysql command line,\n" . - "\t\tdefault uses Database class", false, true ); - $this->addOption( 'file', 'Fix for a specific file, without File: namespace prefixed', false, true ); - } - - public function execute() { - $method = $this->getOption( 'method', 'normal' ); - $file = $this->getOption( 'file' ); - - $t = -microtime( true ); - $dbw = wfGetDB( DB_MASTER ); - if ( $file ) { - $res = $dbw->selectRow( - 'image', - array( 'img_name' ), - array( 'img_name' => $dbw->addQuotes( $file ) ), - __METHOD__ - ); - if ( !$res ) { - $this->error( "No such file: $file", true ); - return; - } - } else { - $res = $dbw->select( 'image', array( 'img_name' ), array( 'img_sha1' => '' ), __METHOD__ ); - } - $imageTable = $dbw->tableName( 'image' ); - - if ( $method == 'pipe' ) { - // @todo FIXME: Kill this and replace with a second unbuffered DB connection. - global $wgDBuser, $wgDBserver, $wgDBpassword, $wgDBname; - $cmd = 'mysql -u' . wfEscapeShellArg( $wgDBuser ) . - ' -h' . wfEscapeShellArg( $wgDBserver ) . - ' -p' . wfEscapeShellArg( $wgDBpassword, $wgDBname ); - $this->output( "Using pipe method\n" ); - $pipe = popen( $cmd, 'w' ); - } - - $numRows = $res->numRows(); - $i = 0; - foreach ( $res as $row ) { - if ( $i % 100 == 0 ) { - $this->output( sprintf( "Done %d of %d, %5.3f%% \r", $i, $numRows, $i / $numRows * 100 ) ); - wfWaitForSlaves(); - } - $file = wfLocalFile( $row->img_name ); - if ( !$file ) { - continue; - } - $sha1 = File::sha1Base36( $file->getPath() ); - if ( strval( $sha1 ) !== '' ) { - $sql = "UPDATE $imageTable SET img_sha1=" . $dbw->addQuotes( $sha1 ) . - " WHERE img_name=" . $dbw->addQuotes( $row->img_name ); - if ( $method == 'pipe' ) { - fwrite( $pipe, "$sql;\n" ); - } else { - $dbw->query( $sql, __METHOD__ ); - } - } - $i++; - } - if ( $method == 'pipe' ) { - fflush( $pipe ); - pclose( $pipe ); - } - $t += microtime( true ); - $this->output( sprintf( "\nDone %d files in %.1f seconds\n", $numRows, $t ) ); - } -} - -$maintClass = "PopulateSha1"; -require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/postgres/mediawiki_mysql2postgres.pl b/maintenance/postgres/mediawiki_mysql2postgres.pl index 9133477e..8f170abc 100644 --- a/maintenance/postgres/mediawiki_mysql2postgres.pl +++ b/maintenance/postgres/mediawiki_mysql2postgres.pl @@ -417,7 +417,6 @@ SELECT setval('page_restrictions_pr_id_seq', 1+coalesce(max(pr_id) ,0),false) F SELECT setval('recentchanges_rc_id_seq', 1+coalesce(max(rc_id) ,0),false) FROM recentchanges; SELECT setval('revision_rev_id_seq', 1+coalesce(max(rev_id) ,0),false) FROM revision; SELECT setval('text_old_id_seq', 1+coalesce(max(old_id) ,0),false) FROM pagecontent; -SELECT setval('trackbacks_tb_id_seq', 1+coalesce(max(tb_id) ,0),false) FROM trackbacks; SELECT setval('user_user_id_seq', 1+coalesce(max(user_id),0),false) FROM mwuser; }; @@ -437,7 +436,6 @@ profiling objectcache ## Which tables to ignore depending on the version -VERSION 1.5: trackback VERSION 1.6: externallinks job templatelinks transcache VERSION 1.7: filearchive langlinks querycache_info VERSION 1.9: querycachetwo page_restrictions redirect diff --git a/maintenance/postgres/tables.sql b/maintenance/postgres/tables.sql index 9f6b8049..6890df91 100644 --- a/maintenance/postgres/tables.sql +++ b/maintenance/postgres/tables.sql @@ -16,7 +16,6 @@ DROP SEQUENCE IF EXISTS page_restrictions_id_seq CASCADE; DROP SEQUENCE IF EXISTS ipblocks_ipb_id_seq CASCADE; DROP SEQUENCE IF EXISTS recentchanges_rc_id_seq CASCADE; DROP SEQUENCE IF EXISTS logging_log_id_seq CASCADE; -DROP SEQUENCE IF EXISTS trackbacks_tb_id_seq CASCADE; DROP SEQUENCE IF EXISTS job_job_id_seq CASCADE; DROP SEQUENCE IF EXISTS category_cat_id_seq CASCADE; DROP FUNCTION IF EXISTS page_deleted() CASCADE; @@ -116,7 +115,8 @@ CREATE TABLE revision ( rev_minor_edit SMALLINT NOT NULL DEFAULT 0, rev_deleted SMALLINT NOT NULL DEFAULT 0, rev_len INTEGER NULL, - rev_parent_id INTEGER NULL + rev_parent_id INTEGER NULL, + rev_sha1 TEXT NOT NULL DEFAULT '' ); CREATE UNIQUE INDEX revision_unique ON revision (rev_page, rev_id); CREATE INDEX rev_text_id_idx ON revision (rev_text_id); @@ -159,6 +159,7 @@ CREATE TABLE archive ( ar_text TEXT, -- technically should be bytea, but not used anymore ar_page_id INTEGER NULL, ar_parent_id INTEGER NULL, + ar_sha1 TEXT NOT NULL DEFAULT '', ar_comment TEXT, ar_user INTEGER NULL REFERENCES mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED, ar_user_text TEXT NOT NULL, @@ -358,11 +359,11 @@ CREATE INDEX fa_dupe ON filearchive (fa_storage_group, fa_storage_key); CREATE INDEX fa_notime ON filearchive (fa_deleted_timestamp); CREATE INDEX fa_nouser ON filearchive (fa_deleted_user); -CREATE SEQUENCE us_id_seq; +CREATE SEQUENCE uploadstash_us_id_seq; CREATE TYPE media_type AS ENUM ('UNKNOWN','BITMAP','DRAWING','AUDIO','VIDEO','MULTIMEDIA','OFFICE','TEXT','EXECUTABLE','ARCHIVE'); CREATE TABLE uploadstash ( - us_id INTEGER PRIMARY KEY NOT NULL DEFAULT nextval('us_id_seq'), + us_id INTEGER PRIMARY KEY NOT NULL DEFAULT nextval('uploadstash_us_id_seq'), us_user INTEGER, us_key TEXT, us_orig_path TEXT, @@ -370,6 +371,7 @@ CREATE TABLE uploadstash ( us_source_type TEXT, us_timestamp TIMESTAMPTZ, us_status TEXT, + us_chunk_inx INTEGER NULL, us_size INTEGER, us_sha1 TEXT, us_mime TEXT, @@ -510,17 +512,6 @@ CREATE TABLE log_search ( ); CREATE INDEX ls_log_id ON log_search (ls_log_id); -CREATE SEQUENCE trackbacks_tb_id_seq; -CREATE TABLE trackbacks ( - tb_id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('trackbacks_tb_id_seq'), - tb_page INTEGER REFERENCES page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, - tb_title TEXT NOT NULL, - tb_url TEXT NOT NULL, - tb_ex TEXT, - tb_name TEXT -); -CREATE INDEX trackback_page ON trackbacks (tb_page); - CREATE SEQUENCE job_job_id_seq; CREATE TABLE job ( @@ -528,9 +519,11 @@ CREATE TABLE job ( job_cmd TEXT NOT NULL, job_namespace SMALLINT NOT NULL, job_title TEXT NOT NULL, + job_timestamp TIMESTAMPTZ, job_params TEXT NOT NULL ); CREATE INDEX job_cmd_namespace_title ON job (job_cmd, job_namespace, job_title); +CREATE INDEX job_timestamp_idx ON job (job_timestamp); -- Tsearch2 2 stuff. Will fail if we don't have proper access to the tsearch2 tables -- Version 8.3 or higher only. Previous versions would need another parmeter for to_tsvector. diff --git a/maintenance/protect.php b/maintenance/protect.php index aab84d68..56958ea7 100644 --- a/maintenance/protect.php +++ b/maintenance/protect.php @@ -28,8 +28,9 @@ class Protect extends Maintenance { $this->mDescription = "Protect or unprotect an article from the command line."; $this->addOption( 'unprotect', 'Removes protection' ); $this->addOption( 'semiprotect', 'Adds semi-protection' ); - $this->addOption( 'u', 'Username to protect with', false, true ); - $this->addOption( 'r', 'Reason for un/protection', false, true ); + $this->addOption( 'cascade', 'Add cascading protection' ); + $this->addOption( 'user', 'Username to protect with', false, true, 'u' ); + $this->addOption( 'reason', 'Reason for un/protection', false, true, 'r' ); $this->addArg( 'title', 'Title to protect', true ); } @@ -39,6 +40,8 @@ class Protect extends Maintenance { $userName = $this->getOption( 'u', 'Maintenance script' ); $reason = $this->getOption( 'r', '' ); + $cascade = $this->hasOption( 'cascade' ); + $protection = "sysop"; if ( $this->hasOption( 'semiprotect' ) ) { $protection = "autoconfirmed"; @@ -46,7 +49,11 @@ class Protect extends Maintenance { $protection = ""; } - $wgUser = User::newFromName( $userName ); + $user = User::newFromName( $userName ); + if ( !$user ) { + $this->error( "Invalid username", true ); + } + $restrictions = array( 'edit' => $protection, 'move' => $protection ); $t = Title::newFromText( $this->getArg() ); @@ -54,12 +61,18 @@ class Protect extends Maintenance { $this->error( "Invalid title", true ); } - $article = new Article( $t ); + $restrictions = array(); + foreach( $t->getRestrictionTypes() as $type ) { + $restrictions[$type] = $protection; + } # un/protect the article $this->output( "Updating protection status... " ); - $success = $article->updateRestrictions( $restrictions, $reason ); - if ( $success ) { + + $page = WikiPage::factory( $t ); + $status = $page->doUpdateRestrictions( $restrictions, array(), $cascade, $reason, $user ); + + if ( $status->isOK() ) { $this->output( "done\n" ); } else { $this->output( "failed\n" ); diff --git a/maintenance/proxy_check.php b/maintenance/proxy_check.php index 2bc46c0d..9c5f32b2 100644 --- a/maintenance/proxy_check.php +++ b/maintenance/proxy_check.php @@ -3,6 +3,7 @@ * Command line script to check for an open proxy at a specified location * * @file + * @ingroup Maintenance */ if( php_sapi_name() != 'cli' ) { diff --git a/maintenance/pruneFileCache.php b/maintenance/pruneFileCache.php new file mode 100644 index 00000000..f1a1cfd4 --- /dev/null +++ b/maintenance/pruneFileCache.php @@ -0,0 +1,105 @@ +mDescription = "Build file cache for content pages"; + $this->addOption( 'agedays', 'How many days old files must be in order to delete', true, true ); + $this->addOption( 'subdir', 'Prune one $wgFileCacheDirectory subdirectory name', false, true ); + } + + public function execute() { + global $wgUseFileCache, $wgFileCacheDirectory; + + if ( !$wgUseFileCache ) { + $this->error( "Nothing to do -- \$wgUseFileCache is disabled.", true ); + } + + $age = $this->getOption( 'agedays' ); + if ( !ctype_digit( $age ) ) { + $this->error( "Non-integer 'age' parameter given.", true ); + } + // Delete items with a TS older than this + $this->minSurviveTimestamp = time() - ( 86400 * $age ); + + $dir = $wgFileCacheDirectory; + if ( !is_dir( $dir ) ) { + $this->error( "Nothing to do -- \$wgFileCacheDirectory directory not found.", true ); + } + + $subDir = $this->getOption( 'subdir' ); + if ( $subDir !== null ) { + if ( !is_dir( "$dir/$subDir" ) ) { + $this->error( "The specified subdirectory `$subDir` does not exist.", true ); + } + $this->output( "Pruning `$dir/$subDir` directory...\n" ); + $this->prune_directory( "$dir/$subDir", 'report' ); + $this->output( "Done pruning `$dir/$subDir` directory\n" ); + } else { + $this->output( "Pruning `$dir` directory...\n" ); + // Note: don't prune things like .cdb files on the top level! + $this->prune_directory( $dir, 'report' ); + $this->output( "Done pruning `$dir` directory\n" ); + } + } + + /** + * @param $dir string + * @param $report string|bool Use 'report' to report the directories being scanned + */ + protected function prune_directory( $dir, $report = false ) { + $tsNow = time(); + $dirHandle = opendir( $dir ); + while ( false !== ( $file = readdir( $dirHandle ) ) ) { + // Skip ".", "..", and also any dirs or files like ".svn" or ".htaccess" + if ( $file[0] != "." ) { + $path = $dir . '/' . $file; // absolute + if ( is_dir( $path ) ) { + if ( $report === 'report' ) { + $this->output( "Scanning `$path`...\n" ); + } + $this->prune_directory( $path ); + } else { + $mts = filemtime( $path ); + // Sanity check the file extension against known cache types + if ( $mts < $this->minSurviveTimestamp + && preg_match( '/\.(?:html|cache)(?:\.gz)?$/', $file ) + && unlink( $path ) ) + { + $daysOld = round( ( $tsNow - $mts ) / 86400, 2 ); + $this->output( "Deleted `$path` [days=$daysOld]\n" ); + } + } + } + } + closedir( $dirHandle ); + } +} + +$maintClass = "PruneFileCache"; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/purgeDeletedFiles.php b/maintenance/purgeDeletedFiles.php new file mode 100644 index 00000000..c8d4fc07 --- /dev/null +++ b/maintenance/purgeDeletedFiles.php @@ -0,0 +1,89 @@ +mDescription = "Scan the logging table and purge files that where deleted."; + $this->addOption( 'starttime', 'Starting timestamp', false, true ); + $this->addOption( 'endtime', 'Ending timestamp', false, true ); + } + + public function execute() { + $this->output( "Purging cache and thumbnails for deleted files...\n" ); + $this->purgeFromLogType( 'delete' ); + $this->output( "...deleted files purged.\n\n" ); + + $this->output( "Purging cache and thumbnails for suppressed files...\n" ); + $this->purgeFromLogType( 'suppress' ); + $this->output( "...suppressed files purged.\n" ); + } + + protected function purgeFromLogType( $logType ) { + $repo = RepoGroup::singleton()->getLocalRepo(); + $db = $repo->getSlaveDB(); + + $conds = array( + 'log_namespace' => NS_FILE, + 'log_type' => $logType, + 'log_action' => array( 'delete', 'revision' ) + ); + $start = $this->getOption( 'starttime' ); + if ( $start ) { + $conds[] = 'log_timestamp >= ' . $db->addQuotes( $db->timestamp( $start ) ); + } + $end = $this->getOption( 'endtime' ); + if ( $end ) { + $conds[] = 'log_timestamp <= ' . $db->addQuotes( $db->timestamp( $end ) ); + } + + $res = $db->select( 'logging', array( 'log_title', 'log_timestamp' ), $conds, __METHOD__ ); + foreach ( $res as $row ) { + $file = $repo->newFile( Title::makeTitle( NS_FILE, $row->log_title ) ); + + // Purge current version and any versions in oldimage table + $file->purgeCache(); + $file->purgeHistory(); + // Purge items from fileachive table (rows are likely here) + $this->purgeFromArchiveTable( $file ); + + $this->output( "Purged file {$row->log_title}; deleted on {$row->log_timestamp}.\n" ); + } + } + + protected function purgeFromArchiveTable( LocalFile $file ) { + $db = $file->getRepo()->getSlaveDB(); + $res = $db->select( 'filearchive', + array( 'fa_archive_name' ), + array( 'fa_name' => $file->getName() ), + __METHOD__ + ); + foreach ( $res as $row ) { + $file->purgeOldThumbnails( $row->fa_archive_name ); + } + } +} + +$maintClass = "PurgeDeletedFiles"; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/purgeList.php b/maintenance/purgeList.php index 85a7aae2..f1452bca 100644 --- a/maintenance/purgeList.php +++ b/maintenance/purgeList.php @@ -47,7 +47,7 @@ class PurgeList extends Maintenance { while ( !feof( $stdin ) ) { $page = trim( fgets( $stdin ) ); - if ( substr( $page, 0, 7 ) == 'http://' ) { + if ( preg_match( '%^https?://%', $page ) ) { $urls[] = $page; } elseif ( $page !== '' ) { $title = Title::newFromText( $page ); diff --git a/maintenance/purgeParserCache.php b/maintenance/purgeParserCache.php index 4b550b6e..4d775f95 100644 --- a/maintenance/purgeParserCache.php +++ b/maintenance/purgeParserCache.php @@ -1,8 +1,14 @@ addDescription( "Remove old objects from the parser cache. " . @@ -31,12 +37,26 @@ class PurgeParserCache extends Maintenance { echo "Deleting objects expiring before " . $english->timeanddate( $date ) . "\n"; $pc = wfGetParserCacheStorage(); - $success = $pc->deleteObjectsExpiringBefore( $date ); + $success = $pc->deleteObjectsExpiringBefore( $date, array( $this, 'showProgress' ) ); if ( !$success ) { - echo "Cannot purge this kind of parser cache.\n"; + echo "\nCannot purge this kind of parser cache.\n"; exit( 1 ); } - echo "Done\n"; + $this->showProgress( 100 ); + echo "\nDone\n"; + } + + function showProgress( $percent ) { + $percentString = sprintf( "%.2f", $percent ); + if ( $percentString === $this->lastProgress ) { + return; + } + $this->lastProgress = $percentString; + + $stars = floor( $percent / 2 ); + echo '[' . str_repeat( '*', $stars ), str_repeat( '.', 50 - $stars ) . '] ' . + "$percentString%\r"; + } } $maintClass = 'PurgeParserCache'; diff --git a/maintenance/purgeStaleMemcachedText.php b/maintenance/purgeStaleMemcachedText.php index c4f5006f..fc9a6a89 100644 --- a/maintenance/purgeStaleMemcachedText.php +++ b/maintenance/purgeStaleMemcachedText.php @@ -1,4 +1,8 @@ id, user_text => text * * @param $user User for the spec - * @param $idfield Field name containing the identifier - * @param $utfield Field name containing the user text + * @param $idfield string Field name containing the identifier + * @param $utfield string Field name containing the user text * @return array */ private function userSpecification( &$user, $idfield, $utfield ) { @@ -160,6 +160,9 @@ class ReassignEdits extends Maintenance { $user->setName( $username ); } else { $user = User::newFromName( $username ); + if ( !$user ) { + $this->error( "Invalid username", true ); + } } $user->load(); return $user; diff --git a/maintenance/rebuildFileCache.php b/maintenance/rebuildFileCache.php index 84ada11c..e7ce3521 100644 --- a/maintenance/rebuildFileCache.php +++ b/maintenance/rebuildFileCache.php @@ -26,29 +26,52 @@ class RebuildFileCache extends Maintenance { public function __construct() { parent::__construct(); $this->mDescription = "Build file cache for content pages"; - $this->addArg( 'start', 'Page_id to start from', true ); - $this->addArg( 'overwrite', 'Refresh page cache', false ); + $this->addOption( 'start', 'Page_id to start from', false, true ); + $this->addOption( 'end', 'Page_id to end on', false, true ); + $this->addOption( 'overwrite', 'Refresh page cache' ); $this->setBatchSize( 100 ); } + public function finalSetup() { + global $wgDebugToolbar; + + // Debug toolbar makes content uncacheable so we disable it. + // Has to be done before Setup.php initialize MWDebug + $wgDebugToolbar = false; + parent::finalSetup(); + } + public function execute() { - global $wgUseFileCache, $wgDisableCounters, $wgContentNamespaces, $wgRequestTime; + global $wgUseFileCache, $wgReadOnly, $wgContentNamespaces, $wgRequestTime; global $wgTitle, $wgOut; if ( !$wgUseFileCache ) { $this->error( "Nothing to do -- \$wgUseFileCache is disabled.", true ); } - $wgDisableCounters = false; - $start = $this->getArg( 0, "0" ); + + $wgReadOnly = 'Building cache'; // avoid DB writes (like enotif/counters) + + $start = $this->getOption( 'start', "0" ); if ( !ctype_digit( $start ) ) { $this->error( "Invalid value for start parameter.", true ); } $start = intval( $start ); - $overwrite = $this->hasArg( 1 ) && $this->getArg( 1 ) === 'overwrite'; + + $end = $this->getOption( 'end', "0" ); + if ( !ctype_digit( $end ) ) { + $this->error( "Invalid value for end parameter.", true ); + } + $end = intval( $end ); + $this->output( "Building content page file cache from page {$start}!\n" ); $dbr = wfGetDB( DB_SLAVE ); - $start = $start > 0 ? $start : $dbr->selectField( 'page', 'MIN(page_id)', false, __FUNCTION__ ); - $end = $dbr->selectField( 'page', 'MAX(page_id)', false, __FUNCTION__ ); + $overwrite = $this->getOption( 'overwrite', false ); + $start = ( $start > 0 ) + ? $start + : $dbr->selectField( 'page', 'MIN(page_id)', false, __FUNCTION__ ); + $end = ( $end > 0 ) + ? $end + : $dbr->selectField( 'page', 'MAX(page_id)', false, __FUNCTION__ ); if ( !$start ) { $this->error( "Nothing to do.", true ); } @@ -69,22 +92,29 @@ class RebuildFileCache extends Maintenance { "page_id BETWEEN $blockStart AND $blockEnd" ), array( 'ORDER BY' => 'page_id ASC', 'USE INDEX' => 'PRIMARY' ) ); + + $dbw->begin(); // for any changes foreach ( $res as $row ) { $rebuilt = false; - $wgRequestTime = wfTime(); # bug 22852 - $context = new RequestContext; + $wgRequestTime = microtime( true ); # bug 22852 + $wgTitle = Title::makeTitleSafe( $row->page_namespace, $row->page_title ); - $context->setTitle( $wgTitle ); if ( null == $wgTitle ) { $this->output( "Page {$row->page_id} has bad title\n" ); continue; // broken title? } + + $context = new RequestContext; + $context->setTitle( $wgTitle ); + $article = Article::newFromTitle( $wgTitle, $context ); + $context->setWikiPage( $article->getPage() ); + $wgOut = $context->getOutput(); // set display title - $article = new Article( $wgTitle ); + // If the article is cacheable, then load it if ( $article->isFileCacheable() ) { - $cache = new HTMLFileCache( $wgTitle ); - if ( $cache->isFileCacheGood() ) { + $cache = HTMLFileCache::newFromTitle( $wgTitle, 'view' ); + if ( $cache->isCacheGood() ) { if ( $overwrite ) { $rebuilt = true; } else { @@ -100,18 +130,19 @@ class RebuildFileCache extends Maintenance { wfRestoreWarnings(); $wgUseFileCache = true; ob_end_clean(); // clear buffer - if ( $rebuilt ) + if ( $rebuilt ) { $this->output( "Re-cached page {$row->page_id}\n" ); - else + } else { $this->output( "Cached page {$row->page_id}\n" ); + } } else { $this->output( "Page {$row->page_id} not cacheable\n" ); } - $dbw->commit(); // commit any changes } + $dbw->commit(); // commit any changes (just for sanity) + $blockStart += $this->mBatchSize; $blockEnd += $this->mBatchSize; - wfWaitForSlaves(); } $this->output( "Done!\n" ); diff --git a/maintenance/rebuildImages.php b/maintenance/rebuildImages.php index 17111831..fe3b35c9 100644 --- a/maintenance/rebuildImages.php +++ b/maintenance/rebuildImages.php @@ -27,7 +27,7 @@ * * @file * @author Brion Vibber - * @ingroup maintenance + * @ingroup Maintenance */ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); @@ -148,8 +148,7 @@ class ImageBuilder extends Maintenance { } function buildOldImage() { - $this->buildTable( 'oldimage', 'oi_archive_name', - array( $this, 'oldimageCallback' ) ); + $this->buildTable( 'oldimage', 'oi_archive_name', array( $this, 'oldimageCallback' ) ); } function oldimageCallback( $row, $copy ) { @@ -164,42 +163,33 @@ class ImageBuilder extends Maintenance { } function crawlMissing() { - $repo = RepoGroup::singleton()->getLocalRepo(); - $repo->enumFilesInFS( array( $this, 'checkMissingImage' ) ); + $this->getRepo()->enumFiles( array( $this, 'checkMissingImage' ) ); } function checkMissingImage( $fullpath ) { $filename = wfBaseName( $fullpath ); - if ( is_dir( $fullpath ) ) { - return; - } - if ( is_link( $fullpath ) ) { - $this->output( "skipping symlink at $fullpath\n" ); - return; - } $row = $this->dbw->selectRow( 'image', array( 'img_name' ), array( 'img_name' => $filename ), __METHOD__ ); - if ( $row ) { - // already known, move on - return; - } else { + if ( !$row ) { // file not registered $this->addMissingImage( $filename, $fullpath ); } } function addMissingImage( $filename, $fullpath ) { - $timestamp = $this->dbw->timestamp( filemtime( $fullpath ) ); - global $wgContLang; + + $timestamp = $this->dbw->timestamp( $this->getRepo()->getFileTimestamp( $fullpath ) ); + $altname = $wgContLang->checkTitleEncoding( $filename ); if ( $altname != $filename ) { if ( $this->dryrun ) { $filename = $altname; $this->output( "Estimating transcoding... $altname\n" ); } else { + # @FIXME: create renameFile() $filename = $this->renameFile( $filename ); } } diff --git a/maintenance/rebuildInterwiki.php b/maintenance/rebuildInterwiki.php deleted file mode 100644 index 25aea2de..00000000 --- a/maintenance/rebuildInterwiki.php +++ /dev/null @@ -1,275 +0,0 @@ -mDescription = "Rebuild the interwiki table using the file on meta and the language list."; - $this->addOption( 'langlist', 'File with one language code per line', false, true ); - $this->addOption( 'dblist', 'File with one db per line', false, true ); - $this->addOption( 'd', 'Output folder', false, true ); - } - - function execute() { - # List of language prefixes likely to be found in multi-language sites - $this->langlist = array_map( "trim", file( $this->getOption( 'langlist', "/home/wikipedia/common/langlist" ) ) ); - - # List of all database names - $this->dblist = array_map( "trim", file( $this->getOption( 'dblist', "/home/wikipedia/common/all.dblist" ) ) ); - - # Special-case databases - //$this->specials = array_flip( array_map( "trim", file( $this->getOption( 'specialdbs', "/home/wikipedia/common/special.dblist" ) ) ) ); - - $this->makeInterwikiSQL( $this->getOption( 'd', '/home/wikipedia/conf/interwiki/sql' ) ); - } - - function makeInterwikiSQL( $destDir ) { - $this->output( "Making new interwiki SQL files in $destDir\n" ); - - # Multi-language sites - # db suffix => db suffix, iw prefix, hostname - $sites = array( - 'wiki' => new Site( 'wiki', 'w', 'wikipedia.org' ), - 'wiktionary' => new Site( 'wiktionary', 'wikt', 'wiktionary.org' ), - 'wikiquote' => new Site( 'wikiquote', 'q', 'wikiquote.org' ), - 'wikibooks' => new Site( 'wikibooks', 'b', 'wikibooks.org' ), - 'wikinews' => new Site( 'wikinews', 'n', 'wikinews.org' ), - 'wikisource' => new Site( 'wikisource', 's', 'wikisource.org' ), - 'wikimedia' => new Site( 'wikimedia', 'chapter', 'wikimedia.org' ), - 'wikiversity' => new Site( 'wikiversity', 'v', 'wikiversity.org' ), - ); - - # Special-case hostnames - $this->specials = array( - 'sourceswiki' => 'sources.wikipedia.org', - 'quotewiki' => 'wikiquote.org', - 'textbookwiki' => 'wikibooks.org', - 'sep11wiki' => 'sep11.wikipedia.org', - 'metawiki' => 'meta.wikimedia.org', - 'commonswiki' => 'commons.wikimedia.org', - 'specieswiki' => 'species.wikimedia.org', - ); - - # Extra interwiki links that can't be in the intermap for some reason - $extraLinks = array( - array( 'm', 'http://meta.wikimedia.org/wiki/$1', 1 ), - array( 'meta', 'http://meta.wikimedia.org/wiki/$1', 1 ), - array( 'sep11', 'http://sep11.wikipedia.org/wiki/$1', 1 ), - ); - - # Language aliases, usually configured as redirects to the real wiki in apache - # Interlanguage links are made directly to the real wiki - # Something horrible happens if you forget to list an alias here, I can't - # remember what - $this->languageAliases = array( - 'zh-cn' => 'zh', - 'zh-tw' => 'zh', - 'dk' => 'da', - 'nb' => 'no', - ); - - # Special case prefix rewrites, for the benefit of Swedish which uses s:t - # as an abbreviation for saint - $this->prefixRewrites = array( - 'svwiki' => array( 's' => 'src' ), - ); - - # Construct a list of reserved prefixes - $reserved = array(); - foreach ( $this->langlist as $lang ) { - $reserved[$lang] = 1; - } - foreach ( $this->languageAliases as $alias => $lang ) { - $reserved[$alias] = 1; - } - foreach ( $sites as $site ) { - $reserved[$site->lateral] = 1; - } - - # Extract the intermap from meta - $intermap = Http::get( 'http://meta.wikimedia.org/w/index.php?title=Interwiki_map&action=raw', 30 ); - $lines = array_map( 'trim', explode( "\n", trim( $intermap ) ) ); - - if ( !$lines || count( $lines ) < 2 ) { - $this->error( "m:Interwiki_map not found", true ); - } - - $iwArray = array(); - - foreach ( $lines as $line ) { - $matches = array(); - if ( preg_match( '/^\|\s*(.*?)\s*\|\|\s*(https?:\/\/.*?)\s*$/', $line, $matches ) ) { - $prefix = strtolower( $matches[1] ); - $url = $matches[2]; - if ( preg_match( '/(wikipedia|wiktionary|wikisource|wikiquote|wikibooks|wikimedia)\.org/', $url ) ) { - $local = 1; - } else { - $local = 0; - } - - if ( empty( $reserved[$prefix] ) ) { - $iwArray[$prefix] = array( "iw_prefix" => $prefix, "iw_url" => $url, "iw_local" => $local ); - } - } - } - - foreach ( $this->dblist as $db ) { - $sql = "-- Generated by rebuildInterwiki.php"; - if ( isset( $this->specials[$db] ) ) { - # Special wiki - # Has interwiki links and interlanguage links to wikipedia - - $host = $this->specials[$db]; - $sql .= "\n--$host\n\n"; - $sql .= "USE $db;\n" . - "TRUNCATE TABLE interwiki;\n" . - "INSERT INTO interwiki (iw_prefix, iw_url, iw_local) VALUES \n"; - $first = true; - - # Intermap links - foreach ( $iwArray as $iwEntry ) { - $sql .= $this->makeLink( $iwEntry, $first, $db ); - } - - # Links to multilanguage sites - foreach ( $sites as $targetSite ) { - $sql .= $this->makeLink( array( $targetSite->lateral, $targetSite->getURL( 'en' ), 1 ), $first, $db ); - } - - # Interlanguage links to wikipedia - $sql .= $this->makeLanguageLinks( $sites['wiki'], $first, $db ); - - # Extra links - foreach ( $extraLinks as $link ) { - $sql .= $this->makeLink( $link, $first, $db ); - } - - $sql .= ";\n"; - } else { - # Find out which site this DB belongs to - $site = false; - foreach ( $sites as $candidateSite ) { - $suffix = $candidateSite->suffix; - if ( preg_match( "/(.*)$suffix$/", $db, $matches ) ) { - $site = $candidateSite; - break; - } - } - if ( !$site ) { - print "Invalid database $db\n"; - continue; - } - $lang = $matches[1]; - $host = "$lang." . $site->url; - $sql .= "\n--$host\n\n"; - - $sql .= "USE $db;\n" . - "TRUNCATE TABLE interwiki;\n" . - "INSERT INTO interwiki (iw_prefix,iw_url,iw_local) VALUES\n"; - $first = true; - - # Intermap links - foreach ( $iwArray as $iwEntry ) { - # Suppress links with the same name as the site - if ( ( $suffix == 'wiki' && $iwEntry['iw_prefix'] != 'wikipedia' ) || - ( $suffix != 'wiki' && $suffix != $iwEntry['iw_prefix'] ) ) - { - $sql .= $this->makeLink( $iwEntry, $first, $db ); - } - } - - # Lateral links - foreach ( $sites as $targetSite ) { - # Suppress link to self - if ( $targetSite->suffix != $site->suffix ) { - $sql .= $this->makeLink( array( $targetSite->lateral, $targetSite->getURL( $lang ), 1 ), $first, $db ); - } - } - - # Interlanguage links - $sql .= $this->makeLanguageLinks( $site, $first, $db ); - - # w link within wikipedias - # Other sites already have it as a lateral link - if ( $site->suffix == "wiki" ) { - $sql .= $this->makeLink( array( "w", "http://en.wikipedia.org/wiki/$1", 1 ), $first, $db ); - } - - # Extra links - foreach ( $extraLinks as $link ) { - $sql .= $this->makeLink( $link, $first, $db ); - } - $sql .= ";\n"; - } - file_put_contents( "$destDir/$db.sql", $sql ); - } - } - - # ------------------------------------------------------------------------------------------ - - # Returns part of an INSERT statement, corresponding to all interlanguage links to a particular site - function makeLanguageLinks( &$site, &$first, $source ) { - $sql = ""; - - # Actual languages with their own databases - foreach ( $this->langlist as $targetLang ) { - $sql .= $this->makeLink( array( $targetLang, $site->getURL( $targetLang ), 1 ), $first, $source ); - } - - # Language aliases - foreach ( $this->languageAliases as $alias => $lang ) { - $sql .= $this->makeLink( array( $alias, $site->getURL( $lang ), 1 ), $first, $source ); - } - return $sql; - } - - # Make SQL for a single link from an array - function makeLink( $entry, &$first, $source ) { - - if ( isset( $this->prefixRewrites[$source] ) && isset($entry[0]) && isset( $this->prefixRewrites[$source][$entry[0]] ) ) { - $entry[0] = $this->prefixRewrites[$source][$entry[0]]; - } - - $sql = ""; - # Add comma - if ( $first ) { - $first = false; - } else { - $sql .= ",\n"; - } - $dbr = wfGetDB( DB_SLAVE ); - $sql .= "(" . $dbr->makeList( $entry ) . ")"; - return $sql; - } -} - -$maintClass = "RebuildInterwiki"; -require_once( RUN_MAINTENANCE_IF_MAIN ); - diff --git a/maintenance/rebuildrecentchanges.php b/maintenance/rebuildrecentchanges.php index 5cacac65..92d60902 100644 --- a/maintenance/rebuildrecentchanges.php +++ b/maintenance/rebuildrecentchanges.php @@ -119,11 +119,11 @@ class RebuildRecentchanges extends Maintenance { if ( $row ) { $lastOldId = intval( $row->rev_id ); # Grab the last text size if available - $lastSize = !is_null( $row->rev_len ) ? intval( $row->rev_len ) : 'NULL'; + $lastSize = !is_null( $row->rev_len ) ? intval( $row->rev_len ) : null; } else { # No previous edit $lastOldId = 0; - $lastSize = 'NULL'; + $lastSize = null; $new = 1; // probably true } } diff --git a/maintenance/rebuildtextindex.php b/maintenance/rebuildtextindex.php index 04606c75..494e0a75 100644 --- a/maintenance/rebuildtextindex.php +++ b/maintenance/rebuildtextindex.php @@ -112,7 +112,7 @@ class RebuildTextIndex extends Maintenance { */ private function dropMysqlTextIndex() { $searchindex = $this->db->tableName( 'searchindex' ); - if ( $this->db->indexExists( 'searchindex', 'si_title' ) ) { + if ( $this->db->indexExists( 'searchindex', 'si_title', __METHOD__ ) ) { $this->output( "Dropping index...\n" ); $sql = "ALTER TABLE $searchindex DROP INDEX si_title, DROP INDEX si_text"; $this->db->query( $sql, __METHOD__ ); diff --git a/maintenance/refreshImageMetadata.php b/maintenance/refreshImageMetadata.php index ec612183..958b2ba1 100644 --- a/maintenance/refreshImageMetadata.php +++ b/maintenance/refreshImageMetadata.php @@ -24,7 +24,7 @@ * * @file * @author Brian Wolff - * @ingroup maintenance + * @ingroup Maintenance */ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); @@ -156,6 +156,10 @@ class RefreshImageMetadata extends Maintenance { } } + /** + * @param $dbw DatabaseBase + * @return array + */ function getConditions( $dbw ) { $conds = array(); @@ -179,8 +183,12 @@ class RefreshImageMetadata extends Maintenance { return $conds; } + /** + * @param $force bool + * @param $brokenOnly bool + */ function setupParameters( $force, $brokenOnly ) { - global $wgUpdateCompatibleMetadata, $wgReadOnly; + global $wgUpdateCompatibleMetadata; if ( $brokenOnly ) { $wgUpdateCompatibleMetadata = false; diff --git a/maintenance/refreshLinks.php b/maintenance/refreshLinks.php index 0515ef1b..c16b6963 100644 --- a/maintenance/refreshLinks.php +++ b/maintenance/refreshLinks.php @@ -184,17 +184,15 @@ class RefreshLinks extends Maintenance { __METHOD__ ); return; } - $article = new Article( $title ); - $rt = $article->followRedirect(); + $page = WikiPage::factory( $title ); + $rt = $page->getRedirectTarget(); - if ( !$rt || !is_object( $rt ) ) { + if ( $rt === null ) { // $title is not a redirect // Delete any redirect table entry for it $dbw->delete( 'redirect', array( 'rd_from' => $id ), __METHOD__ ); - } else { - $article->updateRedirectOn( $dbw, $rt ); } } @@ -213,13 +211,14 @@ class RefreshLinks extends Maintenance { if ( is_null( $title ) ) { return; } - $dbw->begin(); $revision = Revision::newFromTitle( $title ); if ( !$revision ) { return; } + $dbw->begin(); + $options = new ParserOptions; $parserOutput = $wgParser->parse( $revision->getText(), $title, $options, true, true, $revision->getId() ); $update = new LinksUpdate( $title, $parserOutput, false ); @@ -229,7 +228,7 @@ class RefreshLinks extends Maintenance { /** * Removes non-existing links from pages from pagelinks, imagelinks, - * categorylinks, templatelinks and externallinks tables. + * categorylinks, templatelinks, externallinks, interwikilinks, langlinks and redirect tables. * * @param $maxLag * @param $batchSize The size of deletion batches @@ -254,6 +253,7 @@ class RefreshLinks extends Maintenance { 'iwlinks' => 'iwl_from', 'langlinks' => 'll_from', 'redirect' => 'rd_from', + 'page_props' => 'pp_page', ); foreach ( $linksTables as $table => $field ) { @@ -271,7 +271,6 @@ class RefreshLinks extends Maintenance { $counter = 0; $list = array(); $this->output( "0.." ); - foreach ( $results as $row ) { $counter++; $list[] = $row->$field; diff --git a/maintenance/removeUnusedAccounts.php b/maintenance/removeUnusedAccounts.php index 44c27b35..ba25efdd 100644 --- a/maintenance/removeUnusedAccounts.php +++ b/maintenance/removeUnusedAccounts.php @@ -92,7 +92,7 @@ class RemoveUnusedAccounts extends Maintenance { private function isInactiveAccount( $id, $master = false ) { $dbo = wfGetDB( $master ? DB_MASTER : DB_SLAVE ); $checks = array( 'revision' => 'rev', 'archive' => 'ar', 'logging' => 'log', - 'image' => 'img', 'oldimage' => 'oi' ); + 'image' => 'img', 'oldimage' => 'oi', 'filearchive' => 'fa' ); $count = 0; $dbo->begin(); diff --git a/maintenance/renamewiki.php b/maintenance/renamewiki.php deleted file mode 100644 index 4146525e..00000000 --- a/maintenance/renamewiki.php +++ /dev/null @@ -1,89 +0,0 @@ -mDescription = "Rename external storage dbs and leave a new one"; - $this->addArg( 'olddb', 'Old DB name' ); - $this->addArg( 'newdb', 'New DB name' ); - } - - public function getDbType() { - return Maintenance::DB_ADMIN; - } - - public function execute() { - global $wgDefaultExternalStore; - - # Setup - $from = $this->getArg( 0 ); - $to = $this->getArg( 1 ); - $this->output( "Renaming blob tables in ES from $from to $to...\n" ); - $this->output( "Sleeping 5 seconds...\n" ); - sleep( 5 ); - - # Initialise external storage - if ( is_array( $wgDefaultExternalStore ) ) { - $stores = $wgDefaultExternalStore; - } elseif ( $wgDefaultExternalStore ) { - $stores = array( $wgDefaultExternalStore ); - } else { - $stores = array(); - } - - if ( count( $stores ) ) { - $this->output( "Initialising external storage...\n" ); - global $wgDBuser, $wgDBpassword, $wgExternalServers; - foreach ( $stores as $storeURL ) { - $m = array(); - if ( !preg_match( '!^DB://(.*)$!', $storeURL, $m ) ) { - continue; - } - - $cluster = $m[1]; - - # Hack - $wgExternalServers[$cluster][0]['user'] = $wgDBuser; - $wgExternalServers[$cluster][0]['password'] = $wgDBpassword; - - $store = new ExternalStoreDB; - $extdb =& $store->getMaster( $cluster ); - $extdb->query( "SET table_type=InnoDB" ); - $extdb->query( "CREATE DATABASE {$to}" ); - $extdb->query( "ALTER TABLE {$from}.blobs RENAME TO {$to}.blobs" ); - $extdb->selectDB( $from ); - $extdb->sourceFile( $this->getDir() . '/storage/blobs.sql' ); - $extdb->commit(); - } - } - $this->output( "done.\n" ); - } -} - -$maintClass = "RenameWiki"; -require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/rollbackEdits.php b/maintenance/rollbackEdits.php index e1c126e1..3e57e01f 100644 --- a/maintenance/rollbackEdits.php +++ b/maintenance/rollbackEdits.php @@ -62,10 +62,12 @@ class RollbackEdits extends Maintenance { return; } + $doer = User::newFromName( 'Maintenance script' ); + foreach ( $titles as $t ) { - $a = new Article( $t ); - $this->output( 'Processing ' . $t->getPrefixedText() . '...' ); - if ( !$a->commitRollback( $user, $summary, $bot, $results ) ) { + $page = WikiPage::factory( $t ); + $this->output( 'Processing ' . $t->getPrefixedText() . '... ' ); + if ( !$page->commitRollback( $user, $summary, $bot, $results, $doer ) ) { $this->output( "done\n" ); } else { $this->output( "failed\n" ); @@ -76,6 +78,7 @@ class RollbackEdits extends Maintenance { /** * Get all pages that should be rolled back for a given user * @param $user String a name to check against rev_user_text + * @return array */ private function getRollbackTitles( $user ) { $dbr = wfGetDB( DB_SLAVE ); diff --git a/maintenance/runBatchedQuery.php b/maintenance/runBatchedQuery.php index 9a6b6383..83e0cab8 100644 --- a/maintenance/runBatchedQuery.php +++ b/maintenance/runBatchedQuery.php @@ -36,7 +36,6 @@ class BatchedQueryRunner extends Maintenance { $this->error( "No query specified. Specify the query as a command line parameter.", true ); $query = $this->getArg(); - $wait = $this->getOption( 'wait', 5 ); $n = 1; $dbw = wfGetDB( DB_MASTER ); do { diff --git a/maintenance/runJobs.php b/maintenance/runJobs.php index 0edf7ac9..6068311e 100644 --- a/maintenance/runJobs.php +++ b/maintenance/runJobs.php @@ -61,16 +61,18 @@ class RunJobs extends Maintenance { $dbw = wfGetDB( DB_MASTER ); $n = 0; $conds = ''; - if ( $type !== false ) + if ( $type !== false ) { $conds = "job_cmd = " . $dbw->addQuotes( $type ); + } while ( $dbw->selectField( 'job', 'job_id', $conds, 'runJobs.php' ) ) { $offset = 0; for ( ; ; ) { $job = !$type ? Job::pop( $offset ) : Job::pop_type( $type ); - if ( !$job ) + if ( !$job ) { break; + } wfWaitForSlaves(); $t = microtime( true ); diff --git a/maintenance/showJobs.php b/maintenance/showJobs.php index 886d7d45..ff7d3fc0 100644 --- a/maintenance/showJobs.php +++ b/maintenance/showJobs.php @@ -21,7 +21,7 @@ * * @ingroup Maintenance * @author Tim Starling - * @author Ashar Voultoiz + * @author Antoine Musso */ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); diff --git a/maintenance/showStats.php b/maintenance/showStats.php index f695c440..b284a9ac 100644 --- a/maintenance/showStats.php +++ b/maintenance/showStats.php @@ -20,7 +20,7 @@ * http://www.gnu.org/copyleft/gpl.html * * @ingroup Maintenance - * @author Ashar Voultoiz + * @author Antoine Musso * Based on initStats.php by: * @author Brion Vibber * @author Rob Church @@ -42,7 +42,6 @@ class ShowStats extends Maintenance { 'ss_good_articles' => 'Number of articles', 'ss_total_pages' => 'Total pages', 'ss_users' => 'Number of users', - 'ss_admins' => 'Number of admins', 'ss_images' => 'Number of images', ); diff --git a/maintenance/sql.php b/maintenance/sql.php index e1f2bb5f..c4af6307 100644 --- a/maintenance/sql.php +++ b/maintenance/sql.php @@ -30,32 +30,59 @@ class MwSql extends Maintenance { } public function execute() { + $dbw = wfGetDB( DB_MASTER ); if ( $this->hasArg() ) { $fileName = $this->getArg(); $file = fopen( $fileName, 'r' ); - $promptCallback = false; - } else { - $file = $this->getStdin(); - $promptObject = new SqlPromptPrinter( "> " ); - $promptCallback = $promptObject->cb(); + if ( !$file ) { + $this->error( "Unable to open input file", true ); + } + + $error = $dbw->sourceStream( $file, false, array( $this, 'sqlPrintResult' ) ); + if ( $error !== true ) { + $this->error( $error, true ); + } else { + exit( 0 ); + } } - if ( !$file ) - $this->error( "Unable to open input file", true ); + $useReadline = function_exists( 'readline_add_history' ) + && Maintenance::posix_isatty( 0 /*STDIN*/ ); - $dbw = wfGetDB( DB_MASTER ); - $error = $dbw->sourceStream( $file, $promptCallback, array( $this, 'sqlPrintResult' ) ); - if ( $error !== true ) { - $this->error( $error, true ); - } else { - exit( 0 ); + if ( $useReadline ) { + global $IP; + $historyFile = isset( $_ENV['HOME'] ) ? + "{$_ENV['HOME']}/.mwsql_history" : "$IP/maintenance/.mwsql_history"; + readline_read_history( $historyFile ); + } + + $wholeLine = ''; + while ( ( $line = Maintenance::readconsole() ) !== false ) { + $done = $dbw->streamStatementEnd( $wholeLine, $line ); + + $wholeLine .= $line; + + if ( !$done ) { + continue; + } + if ( $useReadline ) { + readline_add_history( $wholeLine ); + readline_write_history( $historyFile ); + } + try{ + $res = $dbw->query( $wholeLine ); + $this->sqlPrintResult( $res, $dbw ); + $wholeLine = ''; + } catch (DBQueryError $e) { + $this->error( $e, true ); + } } } /** * Print the results, callback for $db->sourceStream() - * @param $res The results object - * @param $db Database object + * @param $res ResultWrapper The results object + * @param $db DatabaseBase object */ public function sqlPrintResult( $res, $db ) { if ( !$res ) { @@ -70,24 +97,13 @@ class MwSql extends Maintenance { } } + /** + * @return int DB_TYPE constant + */ public function getDbType() { return Maintenance::DB_ADMIN; } } -class SqlPromptPrinter { - function __construct( $prompt ) { - $this->prompt = $prompt; - } - - function cb() { - return array( $this, 'printPrompt' ); - } - - function printPrompt() { - echo $this->prompt; - } -} - $maintClass = "MwSql"; require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/sqlite.php b/maintenance/sqlite.php index dc8a430e..864d5ab6 100644 --- a/maintenance/sqlite.php +++ b/maintenance/sqlite.php @@ -35,6 +35,8 @@ class SqliteMaintenance extends Maintenance { /** * While we use database connection, this simple lie prevents useless --dbpass and * --dbuser options from appearing in help message for this script. + * + * @return int DB constant */ public function getDbType() { return Maintenance::DB_NONE; @@ -129,4 +131,4 @@ class SqliteMaintenance extends Maintenance { } $maintClass = "SqliteMaintenance"; -require_once( RUN_MAINTENANCE_IF_MAIN ); \ No newline at end of file +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/sqlite/archives/initial-indexes.sql b/maintenance/sqlite/archives/initial-indexes.sql index 2573ca13..73b008cc 100644 --- a/maintenance/sqlite/archives/initial-indexes.sql +++ b/maintenance/sqlite/archives/initial-indexes.sql @@ -3,7 +3,7 @@ -- Unique indexes need to be handled with INSERT SELECT since just running -- the CREATE INDEX statement will fail if there are duplicate values. -- --- Ignore duplicates, several tables will have them (e.g. bug 16966) but in +-- Ignore duplicates, several tables will have them (e.g. bug 16966) but in -- most cases it's harmless to discard them. -------------------------------------------------------------------------------- @@ -218,7 +218,7 @@ CREATE TABLE /*_*/math_tmp ( math_outputhash varbinary(16) NOT NULL, math_html_conservativeness tinyint NOT NULL, math_html text, - math_mathml text + math_mathml text ); CREATE UNIQUE INDEX /*i*/math_inputhash ON /*_*/math_tmp (math_inputhash); @@ -406,7 +406,6 @@ CREATE INDEX /*i*/type_time ON /*_*/logging (log_type, log_timestamp); CREATE INDEX /*i*/user_time ON /*_*/logging (log_user, log_timestamp); CREATE INDEX /*i*/page_time ON /*_*/logging (log_namespace, log_title, log_timestamp); CREATE INDEX /*i*/times ON /*_*/logging (log_timestamp); -CREATE INDEX /*i*/tb_page ON /*_*/trackbacks (tb_page); CREATE INDEX /*i*/job_cmd_namespace_title ON /*_*/job (job_cmd, job_namespace, job_title); CREATE INDEX /*i*/rd_ns_title ON /*_*/redirect (rd_namespace,rd_title,rd_from); CREATE INDEX /*i*/qcc_type ON /*_*/querycachetwo (qcc_type,qcc_value); diff --git a/maintenance/sqlite/archives/patch-drop-user_options.sql b/maintenance/sqlite/archives/patch-drop-user_options.sql new file mode 100644 index 00000000..5bc6a47c --- /dev/null +++ b/maintenance/sqlite/archives/patch-drop-user_options.sql @@ -0,0 +1,31 @@ +-- Remove user_options field from user table + +CREATE TABLE /*_*/user_tmp ( + user_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT, + user_name varchar(255) binary NOT NULL default '', + user_real_name varchar(255) binary NOT NULL default '', + user_password tinyblob NOT NULL, + user_newpassword tinyblob NOT NULL, + user_newpass_time binary(14), + user_email tinytext NOT NULL, + user_touched binary(14) NOT NULL default '', + user_token binary(32) NOT NULL default '', + user_email_authenticated binary(14), + user_email_token binary(32), + user_email_token_expires binary(14), + user_registration binary(14), + user_editcount int +) /*$wgDBTableOptions*/; + +INSERT INTO /*_*/user_tmp + SELECT user_id, user_name, user_real_name, user_password, user_newpassword, user_newpass_time, user_email, user_touched, + user_token, user_email_authenticated, user_email_token, user_email_token_expires, user_registration, user_editcount + FROM /*_*/user; + +DROP TABLE /*_*/user; + +ALTER TABLE /*_*/user_tmp RENAME TO /*_*/user; + +CREATE UNIQUE INDEX /*i*/user_name ON /*_*/user (user_name); +CREATE INDEX /*i*/user_email_token ON /*_*/user (user_email_token); +CREATE INDEX /*i*/user_email ON /*_*/user (user_email(50)); diff --git a/maintenance/sqlite/archives/patch-jobs-add-timestamp.sql b/maintenance/sqlite/archives/patch-jobs-add-timestamp.sql new file mode 100644 index 00000000..c5e6e711 --- /dev/null +++ b/maintenance/sqlite/archives/patch-jobs-add-timestamp.sql @@ -0,0 +1,2 @@ +ALTER TABLE /*_*/job ADD COLUMN job_timestamp varbinary(14) NULL default NULL; +CREATE INDEX /*i*/job_timestamp ON /*_*/job(job_timestamp); diff --git a/maintenance/sqlite/archives/patch-page_redirect_namespace_len.sql b/maintenance/sqlite/archives/patch-page_redirect_namespace_len.sql new file mode 100644 index 00000000..d9eedadd --- /dev/null +++ b/maintenance/sqlite/archives/patch-page_redirect_namespace_len.sql @@ -0,0 +1,7 @@ +-- +-- Add the page_redirect_namespace_len index +-- + +CREATE INDEX /*i*/page_redirect_namespace_len ON /*_*/page (page_is_redirect, page_namespace, page_len); + + diff --git a/maintenance/sqlite/archives/patch-ufg_group-length-increase.sql b/maintenance/sqlite/archives/patch-ufg_group-length-increase.sql new file mode 100644 index 00000000..c6dcea5e --- /dev/null +++ b/maintenance/sqlite/archives/patch-ufg_group-length-increase.sql @@ -0,0 +1,15 @@ +CREATE TABLE /*_*/user_former_groups_tmp ( + ug_user int unsigned NOT NULL default 0, + ug_group varbinary(32) NOT NULL default '' +) /*$wgDBTableOptions*/; + +INSERT INTO /*_*/user_former_groups_tmp + SELECT ug_user, ug_group + FROM /*_*/user_groups; + +DROP TABLE /*_*/user_former_groups; + +ALTER TABLE /*_*/user_former_groups_tmp RENAME TO /*_*/user_former_groups; + +CREATE UNIQUE INDEX /*i*/ufg_user_group ON /*_*/user_former_groups (ufg_user,ufg_group); + diff --git a/maintenance/sqlite/archives/patch-ug_group-length-increase.sql b/maintenance/sqlite/archives/patch-ug_group-length-increase.sql new file mode 100644 index 00000000..5e810937 --- /dev/null +++ b/maintenance/sqlite/archives/patch-ug_group-length-increase.sql @@ -0,0 +1,15 @@ +CREATE TABLE /*_*/user_groups_tmp ( + ug_user int unsigned NOT NULL default 0, + ug_group varbinary(32) NOT NULL default '' +) /*$wgDBTableOptions*/; + +INSERT INTO /*_*/user_groups_tmp + SELECT ug_user, ug_group + FROM /*_*/user_groups; + +DROP TABLE /*_*/user_groups; + +ALTER TABLE /*_*/user_groups_tmp RENAME TO /*_*/user_groups; + +CREATE UNIQUE INDEX /*i*/ug_user_group ON /*_*/user_groups (ug_user,ug_group); +CREATE INDEX /*i*/ug_group ON /*_*/user_groups (ug_group); diff --git a/maintenance/stats.php b/maintenance/stats.php index 1fb46fa9..46926dd0 100644 --- a/maintenance/stats.php +++ b/maintenance/stats.php @@ -54,14 +54,12 @@ class CacheStats extends Maintenance { $this->output( "\nParser cache\n" ); $hits = intval( $wgMemc->get( wfMemcKey( 'stats', 'pcache_hit' ) ) ); - $invalid = intval( $wgMemc->get( wfMemcKey( 'stats', 'pcache_miss_invalid' ) ) ); $expired = intval( $wgMemc->get( wfMemcKey( 'stats', 'pcache_miss_expired' ) ) ); $absent = intval( $wgMemc->get( wfMemcKey( 'stats', 'pcache_miss_absent' ) ) ); $stub = intval( $wgMemc->get( wfMemcKey( 'stats', 'pcache_miss_stub' ) ) ); - $total = $hits + $invalid + $expired + $absent + $stub; + $total = $hits + $expired + $absent + $stub; if ( $total ) { $this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits / $total * 100 ) ); - $this->output( sprintf( "invalid: %-10d %6.2f%%\n", $invalid, $invalid / $total * 100 ) ); $this->output( sprintf( "expired: %-10d %6.2f%%\n", $expired, $expired / $total * 100 ) ); $this->output( sprintf( "absent: %-10d %6.2f%%\n", $absent, $absent / $total * 100 ) ); $this->output( sprintf( "stub threshold: %-10d %6.2f%%\n", $stub, $stub / $total * 100 ) ); diff --git a/maintenance/storage/checkStorage.php b/maintenance/storage/checkStorage.php index c372b9c4..af1f9ee2 100644 --- a/maintenance/storage/checkStorage.php +++ b/maintenance/storage/checkStorage.php @@ -21,8 +21,6 @@ * @ingroup Maintenance ExternalStorage */ -define( 'CONCAT_HEADER', 'O:27:"concatenatedgziphistoryblob"' ); - if ( !defined( 'MEDIAWIKI' ) ) { require_once( dirname( __FILE__ ) . '/../commandLine.inc' ); @@ -43,6 +41,7 @@ if ( !defined( 'MEDIAWIKI' ) ) { * @ingroup Maintenance ExternalStorage */ class CheckStorage { + const CONCAT_HEADER = 'O:27:"concatenatedgziphistoryblob"'; var $oldIdMap, $errors; var $dbStore = null; @@ -55,15 +54,13 @@ class CheckStorage { ); function check( $fix = false, $xml = '' ) { - $fname = 'checkStorage'; $dbr = wfGetDB( DB_SLAVE ); if ( $fix ) { - $dbw = wfGetDB( DB_MASTER ); print "Checking, will fix errors if possible...\n"; } else { print "Checking...\n"; } - $maxRevId = $dbr->selectField( 'revision', 'MAX(rev_id)', false, $fname ); + $maxRevId = $dbr->selectField( 'revision', 'MAX(rev_id)', false, __METHOD__ ); $chunkSize = 1000; $flagStats = array(); $objectStats = array(); @@ -84,7 +81,7 @@ class CheckStorage { $this->oldIdMap = array(); $dbr->ping(); $res = $dbr->select( 'revision', array( 'rev_id', 'rev_text_id' ), - array( "rev_id BETWEEN $chunkStart AND $chunkEnd" ), $fname ); + array( "rev_id BETWEEN $chunkStart AND $chunkEnd" ), __METHOD__ ); foreach ( $res as $row ) { $this->oldIdMap[$row->rev_id] = $row->rev_text_id; } @@ -99,8 +96,11 @@ class CheckStorage { $externalRevs = array(); $objectRevs = array(); $res = $dbr->select( 'text', array( 'old_id', 'old_flags' ), - 'old_id IN (' . implode( ',', $this->oldIdMap ) . ')', $fname ); + 'old_id IN (' . implode( ',', $this->oldIdMap ) . ')', __METHOD__ ); foreach ( $res as $row ) { + /** + * @var $flags int + */ $flags = $row->old_flags; $id = $row->old_id; @@ -133,7 +133,7 @@ class CheckStorage { $dbw = wfGetDB( DB_MASTER ); $dbw->ping(); $dbw->update( 'text', array( 'old_flags' => '' ), - array( 'old_id' => $id ), $fname ); + array( 'old_id' => $id ), __METHOD__ ); echo "Fixed\n"; } else { $this->error( 'fixable', "Warning: old_flags set to 0", $id ); @@ -154,7 +154,7 @@ class CheckStorage { $externalNormalBlobs = array(); if ( count( $externalRevs ) ) { $res = $dbr->select( 'text', array( 'old_id', 'old_flags', 'old_text' ), - array( 'old_id IN (' . implode( ',', $externalRevs ) . ')' ), $fname ); + array( 'old_id IN (' . implode( ',', $externalRevs ) . ')' ), __METHOD__ ); foreach ( $res as $row ) { $urlParts = explode( '://', $row->old_text, 2 ); if ( count( $urlParts ) !== 2 || $urlParts[1] == '' ) { @@ -192,7 +192,7 @@ class CheckStorage { $blobsTable = $this->dbStore->getTable( $extDb ); $res = $extDb->select( $blobsTable, array( 'blob_id' ), - array( 'blob_id IN( ' . implode( ',', $blobIds ) . ')' ), $fname ); + array( 'blob_id IN( ' . implode( ',', $blobIds ) . ')' ), __METHOD__ ); foreach ( $res as $row ) { unset( $xBlobIds[$row->blob_id] ); } @@ -211,7 +211,7 @@ class CheckStorage { if ( count( $objectRevs ) ) { $headerLength = 300; $res = $dbr->select( 'text', array( 'old_id', 'old_flags', "LEFT(old_text, $headerLength) AS header" ), - array( 'old_id IN (' . implode( ',', $objectRevs ) . ')' ), $fname ); + array( 'old_id IN (' . implode( ',', $objectRevs ) . ')' ), __METHOD__ ); foreach ( $res as $row ) { $oldId = $row->old_id; $matches = array(); @@ -262,7 +262,7 @@ class CheckStorage { if ( count( $concatBlobs ) ) { $headerLength = 300; $res = $dbr->select( 'text', array( 'old_id', 'old_flags', "LEFT(old_text, $headerLength) AS header" ), - array( 'old_id IN (' . implode( ',', array_keys( $concatBlobs ) ) . ')' ), $fname ); + array( 'old_id IN (' . implode( ',', array_keys( $concatBlobs ) ) . ')' ), __METHOD__ ); foreach ( $res as $row ) { $flags = explode( ',', $row->old_flags ); if ( in_array( 'external', $flags ) ) { @@ -285,7 +285,7 @@ class CheckStorage { $this->error( 'unfixable', "Error: invalid flags \"{$row->old_flags}\" on concat bulk row {$row->old_id}", $concatBlobs[$row->old_id] ); } - } elseif ( strcasecmp( substr( $row->header, 0, strlen( CONCAT_HEADER ) ), CONCAT_HEADER ) ) { + } elseif ( strcasecmp( substr( $row->header, 0, strlen( self::CONCAT_HEADER ) ), self::CONCAT_HEADER ) ) { $this->error( 'restore text', "Error: Incorrect object header for concat bulk row {$row->old_id}", $concatBlobs[$row->old_id] ); } # else good @@ -354,7 +354,6 @@ class CheckStorage { } function checkExternalConcatBlobs( $externalConcatBlobs ) { - $fname = 'CheckStorage::checkExternalConcatBlobs'; if ( !count( $externalConcatBlobs ) ) { return; } @@ -367,12 +366,12 @@ class CheckStorage { $blobIds = array_keys( $oldIds ); $extDb =& $this->dbStore->getSlave( $cluster ); $blobsTable = $this->dbStore->getTable( $extDb ); - $headerLength = strlen( CONCAT_HEADER ); + $headerLength = strlen( self::CONCAT_HEADER ); $res = $extDb->select( $blobsTable, array( 'blob_id', "LEFT(blob_text, $headerLength) AS header" ), - array( 'blob_id IN( ' . implode( ',', $blobIds ) . ')' ), $fname ); + array( 'blob_id IN( ' . implode( ',', $blobIds ) . ')' ), __METHOD__ ); foreach ( $res as $row ) { - if ( strcasecmp( $row->header, CONCAT_HEADER ) ) { + if ( strcasecmp( $row->header, self::CONCAT_HEADER ) ) { $this->error( 'restore text', "Error: invalid header on target $cluster/{$row->blob_id} of two-part ES URL", $oldIds[$row->blob_id] ); } @@ -440,8 +439,6 @@ class CheckStorage { } function importRevision( &$revision, &$importer ) { - $fname = 'CheckStorage::importRevision'; - $id = $revision->getID(); $text = $revision->getText(); if ( $text === '' ) { @@ -462,7 +459,7 @@ class CheckStorage { // Find text row again $dbr = wfGetDB( DB_SLAVE ); - $oldId = $dbr->selectField( 'revision', 'rev_text_id', array( 'rev_id' => $id ), $fname ); + $oldId = $dbr->selectField( 'revision', 'rev_text_id', array( 'rev_id' => $id ), __METHOD__ ); if ( !$oldId ) { echo "Missing revision row for rev_id $id\n"; return; @@ -476,7 +473,7 @@ class CheckStorage { $dbw->update( 'text', array( 'old_flags' => $flags, 'old_text' => $text ), array( 'old_id' => $oldId ), - $fname, array( 'LIMIT' => 1 ) + __METHOD__, array( 'LIMIT' => 1 ) ); // Remove it from the unfixed list and add it to the fixed list diff --git a/maintenance/storage/compressOld.php b/maintenance/storage/compressOld.php index da82d93b..afe01458 100644 --- a/maintenance/storage/compressOld.php +++ b/maintenance/storage/compressOld.php @@ -70,7 +70,7 @@ class CompressOld extends Maintenance { $type = $this->getOption( 'type', 'concat' ); $chunkSize = $this->getOption( 'chunksize', 20 ); - $startId = $this->getOption( 'start-id', 0 ); + $startId = $this->getOption( 'startid', 0 ); $beginDate = $this->getOption( 'begin-date', '' ); $endDate = $this->getOption( 'end-date', '' ); $extDB = $this->getOption( 'extdb', '' ); @@ -123,7 +123,12 @@ class CompressOld extends Maintenance { } while( true ); } - /** @todo document */ + /** + * @todo document + * @param $row + * @param $extdb + * @return bool + */ private function compressPage( $row, $extdb ) { if ( false !== strpos( $row->old_flags, 'gzip' ) || false !== strpos( $row->old_flags, 'object' ) ) { #print "Already compressed row {$row->old_id}\n"; @@ -156,7 +161,15 @@ class CompressOld extends Maintenance { return true; } - /** @todo document */ + /** + * @param $startId + * @param $maxChunkSize + * @param $beginDate + * @param $endDate + * @param $extdb string + * @param $maxPageId bool|int + * @return bool + */ private function compressWithConcat( $startId, $maxChunkSize, $beginDate, $endDate, $extdb = "", $maxPageId = false ) { diff --git a/maintenance/storage/fixBug20757.php b/maintenance/storage/fixBug20757.php index b6def2da..cb2663d1 100644 --- a/maintenance/storage/fixBug20757.php +++ b/maintenance/storage/fixBug20757.php @@ -302,6 +302,9 @@ class FixBug20757 extends Maintenance { /** * This is based on part of HistoryBlobStub::getText(). * Determine if the text can be retrieved from the row in the normal way. + * @param $stub + * @param $secondaryRow + * @return bool */ function isUnbrokenStub( $stub, $secondaryRow ) { $flags = explode( ',', $secondaryRow->old_flags ); diff --git a/maintenance/storage/recompressTracked.php b/maintenance/storage/recompressTracked.php index 09ab3e57..c8aac64b 100644 --- a/maintenance/storage/recompressTracked.php +++ b/maintenance/storage/recompressTracked.php @@ -169,6 +169,7 @@ class RecompressTracked { /** * Make sure the tracking table exists and isn't empty + * @return bool */ function checkTrackingTable() { $dbr = wfGetDB( DB_SLAVE ); @@ -588,6 +589,7 @@ class RecompressTracked { /** * Returns the name of the next target cluster + * @return string */ function getTargetCluster() { $cluster = next( $this->destClusters ); @@ -599,6 +601,8 @@ class RecompressTracked { /** * Gets a DB master connection for the given external cluster name + * @param $cluster string + * @return DatabaseBase */ function getExtDB( $cluster ) { $lb = wfGetLBFactory()->getExternalLB( $cluster ); @@ -684,6 +688,9 @@ class CgzCopyTransaction { /** * Add text. * Returns false if it's ready to commit. + * @param $text string + * @param $textId + * @return bool */ function addItem( $text, $textId ) { if ( !$this->cgz ) { diff --git a/maintenance/tables.sql b/maintenance/tables.sql index f879f02f..db89c37e 100644 --- a/maintenance/tables.sql +++ b/maintenance/tables.sql @@ -86,12 +86,6 @@ CREATE TABLE /*_*/user ( -- Same with passwords. user_email tinytext NOT NULL, - -- Newline-separated list of name=value defining the user - -- preferences - -- Now obsolete in favour of user_properties table; - -- old values will be migrated from here transparently. - user_options blob NOT NULL, - -- This is a timestamp which is updated when a user -- logs in, logs out, changes preferences, or performs -- some other action requiring HTML cache invalidation @@ -158,7 +152,7 @@ CREATE TABLE /*_*/user_groups ( -- with particular permissions. A user will have the combined -- permissions of any group they're explicitly in, plus -- the implicit '*' and 'user' groups. - ug_group varbinary(16) NOT NULL default '' + ug_group varbinary(32) NOT NULL default '' ) /*$wgDBTableOptions*/; CREATE UNIQUE INDEX /*i*/ug_user_group ON /*_*/user_groups (ug_user,ug_group); @@ -169,7 +163,7 @@ CREATE INDEX /*i*/ug_group ON /*_*/user_groups (ug_group); CREATE TABLE /*_*/user_former_groups ( -- Key to user_id ufg_user int unsigned NOT NULL default 0, - ufg_group varbinary(16) NOT NULL default '' + ufg_group varbinary(32) NOT NULL default '' ) /*$wgDBTableOptions*/; CREATE UNIQUE INDEX /*i*/ufg_user_group ON /*_*/user_former_groups (ufg_user,ufg_group); @@ -271,7 +265,7 @@ CREATE TABLE /*_*/page ( CREATE UNIQUE INDEX /*i*/name_title ON /*_*/page (page_namespace,page_title); CREATE INDEX /*i*/page_random ON /*_*/page (page_random); CREATE INDEX /*i*/page_len ON /*_*/page (page_len); - +CREATE INDEX /*i*/page_redirect_namespace_len ON /*_*/page (page_is_redirect, page_namespace, page_len); -- -- Every edit of a page creates also a revision row. @@ -279,6 +273,7 @@ CREATE INDEX /*i*/page_len ON /*_*/page (page_len); -- to the text storage backend. -- CREATE TABLE /*_*/revision ( + -- Unique ID to identify each revision rev_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT, -- Key to page_id. This should _never_ be invalid. @@ -302,14 +297,14 @@ CREATE TABLE /*_*/revision ( -- Text username or IP address of the editor. rev_user_text varchar(255) binary NOT NULL default '', - -- Timestamp + -- Timestamp of when revision was created rev_timestamp binary(14) NOT NULL default '', -- Records whether the user marked the 'minor edit' checkbox. -- Many automated edits are marked as minor. rev_minor_edit tinyint unsigned NOT NULL default 0, - -- Not yet used; reserved for future changes to the deletion system. + -- Restrictions on who can access this revision rev_deleted tinyint unsigned NOT NULL default 0, -- Length of this revision in bytes @@ -317,7 +312,10 @@ CREATE TABLE /*_*/revision ( -- Key to revision.rev_id -- This field is used to add support for a tree structure (The Adjacency List Model) - rev_parent_id int unsigned default NULL + rev_parent_id int unsigned default NULL, + + -- SHA-1 text content hash in base-36 + rev_sha1 varbinary(32) NOT NULL default '' ) /*$wgDBTableOptions*/ MAX_ROWS=10000000 AVG_ROW_LENGTH=1024; -- In case tables are created as MyISAM, use row hints for MySQL <5.0 to avoid 4GB limit @@ -424,7 +422,10 @@ CREATE TABLE /*_*/archive ( ar_page_id int unsigned, -- Original previous revision - ar_parent_id int unsigned default NULL + ar_parent_id int unsigned default NULL, + + -- SHA-1 text content hash in base-36 + ar_sha1 varbinary(32) NOT NULL default '' ) /*$wgDBTableOptions*/; CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp); @@ -966,6 +967,9 @@ CREATE TABLE /*_*/uploadstash ( us_status varchar(50) not null, + -- chunk counter starts at 0, current offset is stored in us_size + us_chunk_inx int unsigned NULL, + -- file properties from File::getPropsFromPath. these may prove unnecessary. -- us_size int unsigned NOT NULL, @@ -997,6 +1001,8 @@ CREATE INDEX /*i*/us_timestamp ON /*_*/uploadstash (us_timestamp); CREATE TABLE /*_*/recentchanges ( rc_id int NOT NULL PRIMARY KEY AUTO_INCREMENT, rc_timestamp varbinary(14) NOT NULL default '', + + -- This is no longer used rc_cur_time varbinary(14) NOT NULL default '', -- As in revision @@ -1016,6 +1022,7 @@ CREATE TABLE /*_*/recentchanges ( -- default view. rc_bot tinyint unsigned NOT NULL default 0, + -- Set if this change corresponds to a page creation rc_new tinyint unsigned NOT NULL default 0, -- Key to page_id (was cur_id prior to 1.5). @@ -1029,8 +1036,10 @@ CREATE TABLE /*_*/recentchanges ( -- rev_id of the prior revision, for generating diff links. rc_last_oldid int unsigned NOT NULL default 0, - -- These may no longer be used, with the new move log. + -- The type of change entry (RC_EDIT,RC_NEW,RC_LOG) rc_type tinyint unsigned NOT NULL default 0, + + -- These may no longer be used, with the new move log. rc_moved_to_ns tinyint unsigned NOT NULL default 0, rc_moved_to_title varchar(255) binary NOT NULL default '', @@ -1226,6 +1235,7 @@ CREATE INDEX /*i*/page_time ON /*_*/logging (log_namespace, log_title, log_times CREATE INDEX /*i*/times ON /*_*/logging (log_timestamp); CREATE INDEX /*i*/log_user_type_time ON /*_*/logging (log_user, log_type, log_timestamp); CREATE INDEX /*i*/log_page_id_time ON /*_*/logging (log_page,log_timestamp); +CREATE INDEX /*i*/type_action ON /*_*/logging(log_type, log_action, log_timestamp); CREATE TABLE /*_*/log_search ( @@ -1240,17 +1250,6 @@ CREATE UNIQUE INDEX /*i*/ls_field_val ON /*_*/log_search (ls_field,ls_value,ls_l CREATE INDEX /*i*/ls_log_id ON /*_*/log_search (ls_log_id); -CREATE TABLE /*_*/trackbacks ( - tb_id int PRIMARY KEY AUTO_INCREMENT, - tb_page int REFERENCES /*_*/page(page_id) ON DELETE CASCADE, - tb_title varchar(255) NOT NULL, - tb_url blob NOT NULL, - tb_ex text, - tb_name varchar(255) -) /*$wgDBTableOptions*/; -CREATE INDEX /*i*/tb_page ON /*_*/trackbacks (tb_page); - - -- Jobs performed by parallel apache threads or a command-line daemon CREATE TABLE /*_*/job ( job_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT, @@ -1264,12 +1263,17 @@ CREATE TABLE /*_*/job ( job_namespace int NOT NULL, job_title varchar(255) binary NOT NULL, + -- Timestamp of when the job was inserted + -- NULL for jobs added before addition of the timestamp + job_timestamp varbinary(14) NULL default NULL, + -- Any other parameters to the command -- Stored as a PHP serialized array, or an empty string if there are no parameters job_params blob NOT NULL ) /*$wgDBTableOptions*/; CREATE INDEX /*i*/job_cmd ON /*_*/job (job_cmd, job_namespace, job_title, job_params(128)); +CREATE INDEX /*i*/job_timestamp ON /*_*/job(job_timestamp); -- Details of updates to cached special pages diff --git a/maintenance/term/MWTerm.php b/maintenance/term/MWTerm.php new file mode 100644 index 00000000..36fb8eec --- /dev/null +++ b/maintenance/term/MWTerm.php @@ -0,0 +1,50 @@ +color( 0 ); + } +} + +/* A colour-less terminal */ +class DummyTermColorer { + public function color( $color ) { + return ''; + } + + public function reset() { + return ''; + } +} + diff --git a/maintenance/undelete.php b/maintenance/undelete.php index 7213d096..1c3b14a5 100644 --- a/maintenance/undelete.php +++ b/maintenance/undelete.php @@ -44,6 +44,9 @@ class Undelete extends Maintenance { $this->error( "Invalid title", true ); } $wgUser = User::newFromName( $user ); + if ( !$wgUser ) { + $this->error( "Invalid username", true ); + } $archive = new PageArchive( $title ); $this->output( "Undeleting " . $title->getPrefixedDBkey() . '...' ); $archive->undelete( array(), $reason ); diff --git a/maintenance/update.php b/maintenance/update.php index 6173befd..c4bf3b22 100644 --- a/maintenance/update.php +++ b/maintenance/update.php @@ -89,6 +89,8 @@ class UpdateMediaWiki extends Maintenance { $this->output( "MediaWiki {$wgVersion} Updater\n\n" ); + wfWaitForSlaves( 5 ); // let's not kill databases, shall we? ;) --tor + if ( !$this->hasOption( 'skip-compat-checks' ) ) { $this->compatChecks(); } else { @@ -119,8 +121,12 @@ class UpdateMediaWiki extends Maintenance { $updater->doUpdates( $updates ); foreach( $updater->getPostDatabaseUpdateMaintenance() as $maint ) { + if ( $updater->updateRowExists( $maint ) ) { + continue; + } $child = $this->runChild( $maint ); $child->execute(); + $updater->insertUpdateRow( $maint ); } $this->output( "\nDone.\n" ); diff --git a/maintenance/updateCollation.php b/maintenance/updateCollation.php index 7e8a7ee0..023409fa 100644 --- a/maintenance/updateCollation.php +++ b/maintenance/updateCollation.php @@ -43,7 +43,7 @@ using the page title and cl_sortkey_prefix. If everything's collation is up-to-date, it will do nothing. TEXT; - $this->addOption( 'force', 'Run on all rows, even if the collation is ' . + $this->addOption( 'force', 'Run on all rows, even if the collation is ' . 'supposed to be up-to-date.' ); $this->addOption( 'previous-collation', 'Set the previous value of ' . '$wgCategoryCollation here to speed up this script, especially if your ' . @@ -111,7 +111,7 @@ TEXT; # This is an old-style row, so the sortkey needs to be # converted. if ( $row->cl_sortkey == $title->getText() - || $row->cl_sortkey == $title->getPrefixedText() ) { + || $row->cl_sortkey == $title->getPrefixedText() ) { $prefix = ''; } else { # Custom sortkey, use it as a prefix @@ -148,14 +148,14 @@ TEXT; if ( $force && $row ) { $encFrom = $dbw->addQuotes( $row->cl_from ); $encTo = $dbw->addQuotes( $row->cl_to ); - $batchConds = array( + $batchConds = array( "(cl_from = $encFrom AND cl_to > $encTo) " . " OR cl_from > $encFrom" ); } $count += $res->numRows(); $this->output( "$count done.\n" ); - + if ( ++$batchCount % self::SYNC_INTERVAL == 0 ) { $this->output( "Waiting for slaves ... " ); wfWaitForSlaves(); diff --git a/maintenance/upgrade1_5.php b/maintenance/upgrade1_5.php index 730e7637..1577c237 100644 --- a/maintenance/upgrade1_5.php +++ b/maintenance/upgrade1_5.php @@ -39,6 +39,12 @@ define( 'MW_UPGRADE_CALLBACK', null ); // for self-documentation only * @ingroup Maintenance */ class FiveUpgrade extends Maintenance { + + /** + * @var DatabaseBase + */ + protected $db; + function __construct() { parent::__construct(); @@ -321,6 +327,8 @@ class FiveUpgrade extends Maintenance { /** * Helper function for copyTable array_filter + * @param $x + * @return bool */ static private function notUpgradeNull( $x ) { return $x !== MW_UPGRADE_NULL; @@ -800,8 +808,10 @@ END; * Rename a given image or archived image file to the converted filename, * leaving a symlink for URL compatibility. * - * @param string $oldname pre-conversion filename - * @param string $basename pre-conversion base filename for dir hashing, if an archive + * @param $oldname string pre-conversion filename + * @param $subdirCallback string + * @param $basename string pre-conversion base filename for dir hashing, if an archive + * @return bool|string * @access private */ function renameFile( $oldname, $subdirCallback = 'wfImageDir', $basename = null ) { diff --git a/maintenance/userOptions.inc b/maintenance/userOptions.inc index 6edef9d9..a6659fe7 100644 --- a/maintenance/userOptions.inc +++ b/maintenance/userOptions.inc @@ -50,7 +50,14 @@ class userOptions { } - /** This is used to check options. Only needed on construction */ + /** + * This is used to check options. Only needed on construction + * + * @param $opts array + * @param $args array + * + * @return bool + */ private function checkOpts( $opts, $args ) { // The three possible ways to run the script: $list = isset( $opts['list'] ); @@ -63,7 +70,14 @@ class userOptions { return $isValid; } - /** load script options in the object */ + /** + * load script options in the object + * + * @param $opts array + * @param $args array + * + * @return true + */ private function initializeOpts( $opts, $args ) { $this->mQuick = isset( $opts['nowarn'] ); @@ -94,8 +108,8 @@ class userOptions { return false; } - $this-> { $this->mMode } ( ); - + $this->{ $this->mMode } ( ); + return true; } # @@ -206,8 +220,10 @@ class userOptions { } } - - /** Return an array of option names */ + /** + * Return an array of option names + * @return array + */ public static function getDefaultOptionsNames() { $def = User::getDefaultOptions(); $ret = array(); @@ -217,7 +233,6 @@ class userOptions { return $ret; } - # # Helper methods # @@ -250,7 +265,10 @@ USAGE; exit( 0 ); } - /** The warning message and countdown */ + /** + * The warning message and countdown + * @return bool + */ public function warn() { if ( $this->mQuick ) { diff --git a/maintenance/userOptions.php b/maintenance/userOptions.php index 6e1681c3..64368f63 100644 --- a/maintenance/userOptions.php +++ b/maintenance/userOptions.php @@ -23,7 +23,7 @@ * * @file * @ingroup Maintenance - * @author Ashar Voultoiz + * @author Antoine Musso */ // This is a command line script, load tools and parse args diff --git a/maintenance/wikipedia-interwiki.sql b/maintenance/wikipedia-interwiki.sql deleted file mode 100644 index 6a213166..00000000 --- a/maintenance/wikipedia-interwiki.sql +++ /dev/null @@ -1,289 +0,0 @@ --- For convenience, here are the *in-project* interwiki prefixes --- for Wikipedia. - -REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local) VALUES --- Non-Wikipedia sites: -('q','http://en.wikiquote.org/wiki/$1',1), -('b','http://en.wikibooks.org/wiki/$1',1), -('s','http://en.wikisource.org/wiki/$1',1), -('n','http://en.wikinews.org/wiki/$1',1), -('v','http://en.wikiversity.org/wiki/$1',1), -('meta','http://meta.wikimedia.org/wiki/$1',1), -('m','http://meta.wikimedia.org/wiki/$1',1), --- An alphabetical list of Wikipedia sites: -('aa','http://aa.wikipedia.org/wiki/$1',1), -('ab','http://ab.wikipedia.org/wiki/$1',1), -('ace','http://ace.wikipedia.org/wiki/$1',1), -('af','http://af.wikipedia.org/wiki/$1',1), -('ak','http://ak.wikipedia.org/wiki/$1',1), -('als','http://als.wikipedia.org/wiki/$1',1), -('am','http://am.wikipedia.org/wiki/$1',1), -('an','http://an.wikipedia.org/wiki/$1',1), -('ang','http://ang.wikipedia.org/wiki/$1',1), -('ar','http://ar.wikipedia.org/wiki/$1',1), -('arc','http://arc.wikipedia.org/wiki/$1',1), -('arz','http://arz.wikipedia.org/wiki/$1',1), -('as','http://as.wikipedia.org/wiki/$1',1), -('ast','http://ast.wikipedia.org/wiki/$1',1), -('av','http://av.wikipedia.org/wiki/$1',1), -('ay','http://ay.wikipedia.org/wiki/$1',1), -('az','http://az.wikipedia.org/wiki/$1',1), -('ba','http://ba.wikipedia.org/wiki/$1',1), -('bar','http://bar.wikipedia.org/wiki/$1',1), -('bat-smg','http://bat-smg.wikipedia.org/wiki/$1',1), -('bcl','http://bcl.wikipedia.org/wiki/$1',1), -('be','http://be.wikipedia.org/wiki/$1',1), -('be-x-old','http://be-x-old.wikipedia.org/wiki/$1',1), -('bg','http://bg.wikipedia.org/wiki/$1',1), -('bh','http://bh.wikipedia.org/wiki/$1',1), -('bi','http://bi.wikipedia.org/wiki/$1',1), -('bm','http://bm.wikipedia.org/wiki/$1',1), -('bn','http://bn.wikipedia.org/wiki/$1',1), -('bo','http://bo.wikipedia.org/wiki/$1',1), -('bpy','http://bpy.wikipedia.org/wiki/$1',1), -('br','http://br.wikipedia.org/wiki/$1',1), -('bs','http://bs.wikipedia.org/wiki/$1',1), -('bug','http://bug.wikipedia.org/wiki/$1',1), -('bxr','http://bxr.wikipedia.org/wiki/$1',1), -('ca','http://ca.wikipedia.org/wiki/$1',1), -('cbk-zam','http://cbk-zam.wikipedia.org/wiki/$1',1), -('cdo','http://cdo.wikipedia.org/wiki/$1',1), -('ce','http://ce.wikipedia.org/wiki/$1',1), -('ceb','http://ceb.wikipedia.org/wiki/$1',1), -('ch','http://ch.wikipedia.org/wiki/$1',1), -('cho','http://cho.wikipedia.org/wiki/$1',1), -('chr','http://chr.wikipedia.org/wiki/$1',1), -('chy','http://chy.wikipedia.org/wiki/$1',1), -('ckb','http://ckb.wikipedia.org/wiki/$1',1), -('co','http://co.wikipedia.org/wiki/$1',1), -('cr','http://cr.wikipedia.org/wiki/$1',1), -('crh','http://crh.wikipedia.org/wiki/$1',1), -('cs','http://cs.wikipedia.org/wiki/$1',1), -('csb','http://csb.wikipedia.org/wiki/$1',1), -('cu','http://cu.wikipedia.org/wiki/$1',1), -('cv','http://cv.wikipedia.org/wiki/$1',1), -('cy','http://cy.wikipedia.org/wiki/$1',1), -('da','http://da.wikipedia.org/wiki/$1',1), -('de','http://de.wikipedia.org/wiki/$1',1), -('diq','http://diq.wikipedia.org/wiki/$1',1), -('dk','http://da.wikipedia.org/wiki/$1',1), -('dsb','http://dsb.wikipedia.org/wiki/$1',1), -('dv','http://dv.wikipedia.org/wiki/$1',1), -('dz','http://dz.wikipedia.org/wiki/$1',1), -('ee','http://ee.wikipedia.org/wiki/$1',1), -('el','http://el.wikipedia.org/wiki/$1',1), -('en','http://en.wikipedia.org/wiki/$1',1), -('eo','http://eo.wikipedia.org/wiki/$1',1), -('es','http://es.wikipedia.org/wiki/$1',1), -('et','http://et.wikipedia.org/wiki/$1',1), -('eu','http://eu.wikipedia.org/wiki/$1',1), -('ext','http://ext.wikipedia.org/wiki/$1',1), -('fa','http://fa.wikipedia.org/wiki/$1',1), -('ff','http://ff.wikipedia.org/wiki/$1',1), -('fi','http://fi.wikipedia.org/wiki/$1',1), -('fiu-vro','http://fiu-vro.wikipedia.org/wiki/$1',1), -('fj','http://fj.wikipedia.org/wiki/$1',1), -('fo','http://fo.wikipedia.org/wiki/$1',1), -('fr','http://fr.wikipedia.org/wiki/$1',1), -('frp','http://frp.wikipedia.org/wiki/$1',1), -('fur','http://fur.wikipedia.org/wiki/$1',1), -('fy','http://fy.wikipedia.org/wiki/$1',1), -('ga','http://ga.wikipedia.org/wiki/$1',1), -('gan','http://gan.wikipedia.org/wiki/$1',1), -('gd','http://gd.wikipedia.org/wiki/$1',1), -('gl','http://gl.wikipedia.org/wiki/$1',1), -('glk','http://glk.wikipedia.org/wiki/$1',1), -('gn','http://gn.wikipedia.org/wiki/$1',1), -('got','http://got.wikipedia.org/wiki/$1',1), -('gu','http://gu.wikipedia.org/wiki/$1',1), -('gv','http://gv.wikipedia.org/wiki/$1',1), -('ha','http://ha.wikipedia.org/wiki/$1',1), -('hak','http://hak.wikipedia.org/wiki/$1',1), -('haw','http://haw.wikipedia.org/wiki/$1',1), -('he','http://he.wikipedia.org/wiki/$1',1), -('hi','http://hi.wikipedia.org/wiki/$1',1), -('hif','http://hif.wikipedia.org/wiki/$1',1), -('ho','http://ho.wikipedia.org/wiki/$1',1), -('hr','http://hr.wikipedia.org/wiki/$1',1), -('hsb','http://hsb.wikipedia.org/wiki/$1',1), -('ht','http://ht.wikipedia.org/wiki/$1',1), -('hu','http://hu.wikipedia.org/wiki/$1',1), -('hy','http://hy.wikipedia.org/wiki/$1',1), -('hz','http://hz.wikipedia.org/wiki/$1',1), -('ia','http://ia.wikipedia.org/wiki/$1',1), -('id','http://id.wikipedia.org/wiki/$1',1), -('ie','http://ie.wikipedia.org/wiki/$1',1), -('ig','http://ig.wikipedia.org/wiki/$1',1), -('ii','http://ii.wikipedia.org/wiki/$1',1), -('ik','http://ik.wikipedia.org/wiki/$1',1), -('ilo','http://ilo.wikipedia.org/wiki/$1',1), -('io','http://io.wikipedia.org/wiki/$1',1), -('is','http://is.wikipedia.org/wiki/$1',1), -('it','http://it.wikipedia.org/wiki/$1',1), -('iu','http://iu.wikipedia.org/wiki/$1',1), -('ja','http://ja.wikipedia.org/wiki/$1',1), -('jbo','http://jbo.wikipedia.org/wiki/$1',1), -('jv','http://jv.wikipedia.org/wiki/$1',1), -('ka','http://ka.wikipedia.org/wiki/$1',1), -('kab','http://kab.wikipedia.org/wiki/$1',1), -('kg','http://kg.wikipedia.org/wiki/$1',1), -('ki','http://ki.wikipedia.org/wiki/$1',1), -('kj','http://kj.wikipedia.org/wiki/$1',1), -('kk','http://kk.wikipedia.org/wiki/$1',1), -('kl','http://kl.wikipedia.org/wiki/$1',1), -('km','http://km.wikipedia.org/wiki/$1',1), -('kn','http://kn.wikipedia.org/wiki/$1',1), -('ko','http://ko.wikipedia.org/wiki/$1',1), -('kr','http://kr.wikipedia.org/wiki/$1',1), -('ks','http://ks.wikipedia.org/wiki/$1',1), -('ksh','http://ksh.wikipedia.org/wiki/$1',1), -('ku','http://ku.wikipedia.org/wiki/$1',1), -('kv','http://kv.wikipedia.org/wiki/$1',1), -('kw','http://kw.wikipedia.org/wiki/$1',1), -('ky','http://ky.wikipedia.org/wiki/$1',1), -('la','http://la.wikipedia.org/wiki/$1',1), -('lad','http://lad.wikipedia.org/wiki/$1',1), -('lb','http://lb.wikipedia.org/wiki/$1',1), -('lbe','http://lbe.wikipedia.org/wiki/$1',1), -('lg','http://lg.wikipedia.org/wiki/$1',1), -('li','http://li.wikipedia.org/wiki/$1',1), -('lij','http://lij.wikipedia.org/wiki/$1',1), -('lmo','http://lmo.wikipedia.org/wiki/$1',1), -('ln','http://ln.wikipedia.org/wiki/$1',1), -('lo','http://lo.wikipedia.org/wiki/$1',1), -('lt','http://lt.wikipedia.org/wiki/$1',1), -('lv','http://lv.wikipedia.org/wiki/$1',1), -('map-bms','http://map-bms.wikipedia.org/wiki/$1',1), -('mdf','http://mdf.wikipedia.org/wiki/$1',1), -('mg','http://mg.wikipedia.org/wiki/$1',1), -('mh','http://mh.wikipedia.org/wiki/$1',1), -('mhr','http://mhr.wikipedia.org/wiki/$1',1), -('mi','http://mi.wikipedia.org/wiki/$1',1), -('minnan','http://zh-min-nan.wikipedia.org/wiki/$1',1), -('mk','http://mk.wikipedia.org/wiki/$1',1), -('ml','http://ml.wikipedia.org/wiki/$1',1), -('mn','http://mn.wikipedia.org/wiki/$1',1), -('mo','http://mo.wikipedia.org/wiki/$1',1), -('mr','http://mr.wikipedia.org/wiki/$1',1), -('ms','http://ms.wikipedia.org/wiki/$1',1), -('mt','http://mt.wikipedia.org/wiki/$1',1), -('mus','http://mus.wikipedia.org/wiki/$1',1), -('mwl','http://mwl.wikipedia.org/wiki/$1',1), -('my','http://my.wikipedia.org/wiki/$1',1), -('myv','http://myv.wikipedia.org/wiki/$1',1), -('mzn','http://mzn.wikipedia.org/wiki/$1',1), -('na','http://na.wikipedia.org/wiki/$1',1), -('nah','http://nah.wikipedia.org/wiki/$1',1), -('nan','http://zh-min-nan.wikipedia.org/wiki/$1',1), -('nap','http://nap.wikipedia.org/wiki/$1',1), -('nb','http://nb.wikipedia.org/wiki/$1',1), -('nds','http://nds.wikipedia.org/wiki/$1',1), -('nds-nl','http://nds-nl.wikipedia.org/wiki/$1',1), -('ne','http://ne.wikipedia.org/wiki/$1',1), -('ng','http://ng.wikipedia.org/wiki/$1',1), -('nl','http://nl.wikipedia.org/wiki/$1',1), -('nn','http://nn.wikipedia.org/wiki/$1',1), -('no','http://no.wikipedia.org/wiki/$1',1), -('nov','http://nov.wikipedia.org/wiki/$1',1), -('nrm','http://nrm.wikipedia.org/wiki/$1',1), -('nv','http://nv.wikipedia.org/wiki/$1',1), -('ny','http://ny.wikipedia.org/wiki/$1',1), -('oc','http://oc.wikipedia.org/wiki/$1',1), -('om','http://om.wikipedia.org/wiki/$1',1), -('or','http://or.wikipedia.org/wiki/$1',1), -('os','http://os.wikipedia.org/wiki/$1',1), -('pa','http://pa.wikipedia.org/wiki/$1',1), -('pag','http://pag.wikipedia.org/wiki/$1',1), -('pam','http://pam.wikipedia.org/wiki/$1',1), -('pap','http://pap.wikipedia.org/wiki/$1',1), -('pdc','http://pdc.wikipedia.org/wiki/$1',1), -('pi','http://pi.wikipedia.org/wiki/$1',1), -('pih','http://pih.wikipedia.org/wiki/$1',1), -('pl','http://pl.wikipedia.org/wiki/$1',1), -('pms','http://pms.wikipedia.org/wiki/$1',1), -('pnb','http://pnb.wikipedia.org/wiki/$1',1), -('ps','http://ps.wikipedia.org/wiki/$1',1), -('pt','http://pt.wikipedia.org/wiki/$1',1), -('qu','http://qu.wikipedia.org/wiki/$1',1), -('rm','http://rm.wikipedia.org/wiki/$1',1), -('rmy','http://rmy.wikipedia.org/wiki/$1',1), -('rn','http://rn.wikipedia.org/wiki/$1',1), -('ro','http://ro.wikipedia.org/wiki/$1',1), -('roa-rup','http://roa-rup.wikipedia.org/wiki/$1',1), -('roa-tara','http://roa-tara.wikipedia.org/wiki/$1',1), -('ru','http://ru.wikipedia.org/wiki/$1',1), -('rw','http://rw.wikipedia.org/wiki/$1',1), -('sa','http://sa.wikipedia.org/wiki/$1',1), -('sah','http://sah.wikipedia.org/wiki/$1',1), -('sc','http://sc.wikipedia.org/wiki/$1',1), -('scn','http://scn.wikipedia.org/wiki/$1',1), -('sco','http://sco.wikipedia.org/wiki/$1',1), -('sd','http://sd.wikipedia.org/wiki/$1',1), -('se','http://se.wikipedia.org/wiki/$1',1), -('sep11','http://sep11.wikipedia.org/wiki/$1',1), -('sg','http://sg.wikipedia.org/wiki/$1',1), -('sh','http://sh.wikipedia.org/wiki/$1',1), -('si','http://si.wikipedia.org/wiki/$1',1), -('simple','http://simple.wikipedia.org/wiki/$1',1), -('sk','http://sk.wikipedia.org/wiki/$1',1), -('sl','http://sl.wikipedia.org/wiki/$1',1), -('sm','http://sm.wikipedia.org/wiki/$1',1), -('sn','http://sn.wikipedia.org/wiki/$1',1), -('so','http://so.wikipedia.org/wiki/$1',1), -('sq','http://sq.wikipedia.org/wiki/$1',1), -('sr','http://sr.wikipedia.org/wiki/$1',1), -('srn','http://srn.wikipedia.org/wiki/$1',1), -('ss','http://ss.wikipedia.org/wiki/$1',1), -('st','http://st.wikipedia.org/wiki/$1',1), -('stq','http://stq.wikipedia.org/wiki/$1',1), -('su','http://su.wikipedia.org/wiki/$1',1), -('sv','http://sv.wikipedia.org/wiki/$1',1), -('sw','http://sw.wikipedia.org/wiki/$1',1), -('szl','http://szl.wikipedia.org/wiki/$1',1), -('ta','http://ta.wikipedia.org/wiki/$1',1), -('te','http://te.wikipedia.org/wiki/$1',1), -('tet','http://tet.wikipedia.org/wiki/$1',1), -('tg','http://tg.wikipedia.org/wiki/$1',1), -('th','http://th.wikipedia.org/wiki/$1',1), -('ti','http://ti.wikipedia.org/wiki/$1',1), -('tk','http://tk.wikipedia.org/wiki/$1',1), -('tl','http://tl.wikipedia.org/wiki/$1',1), -('tn','http://tn.wikipedia.org/wiki/$1',1), -('to','http://to.wikipedia.org/wiki/$1',1), -('tpi','http://tpi.wikipedia.org/wiki/$1',1), -('tr','http://tr.wikipedia.org/wiki/$1',1), -('ts','http://ts.wikipedia.org/wiki/$1',1), -('tt','http://tt.wikipedia.org/wiki/$1',1), -('tum','http://tum.wikipedia.org/wiki/$1',1), -('tw','http://tw.wikipedia.org/wiki/$1',1), -('ty','http://ty.wikipedia.org/wiki/$1',1), -('udm','http://udm.wikipedia.org/wiki/$1',1), -('ug','http://ug.wikipedia.org/wiki/$1',1), -('uk','http://uk.wikipedia.org/wiki/$1',1), -('ur','http://ur.wikipedia.org/wiki/$1',1), -('uz','http://uz.wikipedia.org/wiki/$1',1), -('ve','http://ve.wikipedia.org/wiki/$1',1), -('vec','http://vec.wikipedia.org/wiki/$1',1), -('vi','http://vi.wikipedia.org/wiki/$1',1), -('vls','http://vls.wikipedia.org/wiki/$1',1), -('vo','http://vo.wikipedia.org/wiki/$1',1), -('w','http://en.wikipedia.org/wiki/$1',1), -('wa','http://wa.wikipedia.org/wiki/$1',1), -('war','http://war.wikipedia.org/wiki/$1',1), -('wo','http://wo.wikipedia.org/wiki/$1',1), -('wuu','http://wuu.wikipedia.org/wiki/$1',1), -('xal','http://xal.wikipedia.org/wiki/$1',1), -('xh','http://xh.wikipedia.org/wiki/$1',1), -('yi','http://yi.wikipedia.org/wiki/$1',1), -('yo','http://yo.wikipedia.org/wiki/$1',1), -('yue','http://zh-yue.wikipedia.org/wiki/$1',1), -('za','http://za.wikipedia.org/wiki/$1',1), -('zea','http://zea.wikipedia.org/wiki/$1',1), -('zh','http://zh.wikipedia.org/wiki/$1',1), -('zh-cfr','http://zh-min-nan.wikipedia.org/wiki/$1',1), -('zh-classical','http://zh-classical.wikipedia.org/wiki/$1',1), -('zh-cn','http://zh.wikipedia.org/wiki/$1',1), -('zh-min-nan','http://zh-min-nan.wikipedia.org/wiki/$1',1), -('zh-tw','http://zh.wikipedia.org/wiki/$1',1), -('zh-yue','http://zh-yue.wikipedia.org/wiki/$1',1), -('zu','http://zu.wikipedia.org/wiki/$1',1); diff --git a/maintenance/wiktionary-interwiki.sql b/maintenance/wiktionary-interwiki.sql deleted file mode 100644 index f55235b1..00000000 --- a/maintenance/wiktionary-interwiki.sql +++ /dev/null @@ -1,184 +0,0 @@ --- For convenience, here are the *in-project* interwiki prefixes --- for Wiktionary. - -REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local) VALUES -('w','http://www.wikipedia.org/wiki/$1',1), -('m','http://meta.wikipedia.org/wiki/$1',1), -('meta','http://meta.wikipedia.org/wiki/$1',1), -('sep11','http://sep11.wikipedia.org/wiki/$1',1), -('simple','http://simple.wiktionary.org/wiki/$1',1), -('aa','http://aa.wiktionary.org/wiki/$1',1), -('ab','http://ab.wiktionary.org/wiki/$1',1), -('af','http://af.wiktionary.org/wiki/$1',1), -('ak','http://ak.wiktionary.org/wiki/$1',1), -('als','http://als.wiktionary.org/wiki/$1',1), -('am','http://am.wiktionary.org/wiki/$1',1), -('an','http://an.wiktionary.org/wiki/$1',1), -('ang','http://ang.wiktionary.org/wiki/$1',1), -('ar','http://ar.wiktionary.org/wiki/$1',1), -('as','http://as.wiktionary.org/wiki/$1',1), -('ast','http://ast.wiktionary.org/wiki/$1',1), -('av','http://av.wiktionary.org/wiki/$1',1), -('ay','http://ay.wiktionary.org/wiki/$1',1), -('az','http://az.wiktionary.org/wiki/$1',1), -('ba','http://ba.wiktionary.org/wiki/$1',1), -('be','http://be.wiktionary.org/wiki/$1',1), -('bg','http://bg.wiktionary.org/wiki/$1',1), -('bh','http://bh.wiktionary.org/wiki/$1',1), -('bi','http://bi.wiktionary.org/wiki/$1',1), -('bm','http://bm.wiktionary.org/wiki/$1',1), -('bn','http://bn.wiktionary.org/wiki/$1',1), -('bo','http://bo.wiktionary.org/wiki/$1',1), -('br','http://br.wiktionary.org/wiki/$1',1), -('bs','http://bs.wiktionary.org/wiki/$1',1), -('ca','http://ca.wiktionary.org/wiki/$1',1), -('ch','http://ch.wiktionary.org/wiki/$1',1), -('chr','http://chr.wiktionary.org/wiki/$1',1), -('co','http://co.wiktionary.org/wiki/$1',1), -('cr','http://cr.wiktionary.org/wiki/$1',1), -('cs','http://cs.wiktionary.org/wiki/$1',1), -('csb','http://csb.wiktionary.org/wiki/$1',1), -('cy','http://cy.wiktionary.org/wiki/$1',1), -('da','http://da.wiktionary.org/wiki/$1',1), -('de','http://de.wiktionary.org/wiki/$1',1), -('dk','http://da.wiktionary.org/wiki/$1',1), -('dv','http://dv.wiktionary.org/wiki/$1',1), -('dz','http://dz.wiktionary.org/wiki/$1',1), -('el','http://el.wiktionary.org/wiki/$1',1), -('en','http://en.wiktionary.org/wiki/$1',1), -('eo','http://eo.wiktionary.org/wiki/$1',1), -('es','http://es.wiktionary.org/wiki/$1',1), -('et','http://et.wiktionary.org/wiki/$1',1), -('eu','http://eu.wiktionary.org/wiki/$1',1), -('fa','http://fa.wiktionary.org/wiki/$1',1), -('fi','http://fi.wiktionary.org/wiki/$1',1), -('fj','http://fj.wiktionary.org/wiki/$1',1), -('fo','http://fo.wiktionary.org/wiki/$1',1), -('fr','http://fr.wiktionary.org/wiki/$1',1), -('fy','http://fy.wiktionary.org/wiki/$1',1), -('ga','http://ga.wiktionary.org/wiki/$1',1), -('gd','http://gd.wiktionary.org/wiki/$1',1), -('gl','http://gl.wiktionary.org/wiki/$1',1), -('gn','http://gn.wiktionary.org/wiki/$1',1), -('gu','http://gu.wiktionary.org/wiki/$1',1), -('gv','http://gv.wiktionary.org/wiki/$1',1), -('ha','http://ha.wiktionary.org/wiki/$1',1), -('he','http://he.wiktionary.org/wiki/$1',1), -('hi','http://hi.wiktionary.org/wiki/$1',1), -('hr','http://hr.wiktionary.org/wiki/$1',1), -('hsb','http://hsb.wiktionary.org/wiki/$1',1), -('hu','http://hu.wiktionary.org/wiki/$1',1), -('hy','http://hy.wiktionary.org/wiki/$1',1), -('ia','http://ia.wiktionary.org/wiki/$1',1), -('id','http://id.wiktionary.org/wiki/$1',1), -('ie','http://ie.wiktionary.org/wiki/$1',1), -('ik','http://ik.wiktionary.org/wiki/$1',1), -('io','http://io.wiktionary.org/wiki/$1',1), -('is','http://is.wiktionary.org/wiki/$1',1), -('it','http://it.wiktionary.org/wiki/$1',1), -('iu','http://iu.wiktionary.org/wiki/$1',1), -('ja','http://ja.wiktionary.org/wiki/$1',1), -('jbo','http://jbo.wiktionary.org/wiki/$1',1), -('jv','http://jv.wiktionary.org/wiki/$1',1), -('ka','http://ka.wiktionary.org/wiki/$1',1), -('kk','http://kk.wiktionary.org/wiki/$1',1), -('kl','http://kl.wiktionary.org/wiki/$1',1), -('km','http://km.wiktionary.org/wiki/$1',1), -('kn','http://kn.wiktionary.org/wiki/$1',1), -('ko','http://ko.wiktionary.org/wiki/$1',1), -('ks','http://ks.wiktionary.org/wiki/$1',1), -('ku','http://ku.wiktionary.org/wiki/$1',1), -('kw','http://kw.wiktionary.org/wiki/$1',1), -('ky','http://ky.wiktionary.org/wiki/$1',1), -('la','http://la.wiktionary.org/wiki/$1',1), -('lb','http://lb.wiktionary.org/wiki/$1',1), -('li','http://li.wiktionary.org/wiki/$1',1), -('ln','http://ln.wiktionary.org/wiki/$1',1), -('lo','http://lo.wiktionary.org/wiki/$1',1), -('lt','http://lt.wiktionary.org/wiki/$1',1), -('lv','http://lv.wiktionary.org/wiki/$1',1), -('mg','http://mg.wiktionary.org/wiki/$1',1), -('mh','http://mh.wiktionary.org/wiki/$1',1), -('mi','http://mi.wiktionary.org/wiki/$1',1), -('mk','http://mk.wiktionary.org/wiki/$1',1), -('ml','http://ml.wiktionary.org/wiki/$1',1), -('mn','http://mn.wiktionary.org/wiki/$1',1), -('mo','http://mo.wiktionary.org/wiki/$1',1), -('mr','http://mr.wiktionary.org/wiki/$1',1), -('ms','http://ms.wiktionary.org/wiki/$1',1), -('mt','http://mt.wiktionary.org/wiki/$1',1), -('my','http://my.wiktionary.org/wiki/$1',1), -('na','http://na.wiktionary.org/wiki/$1',1), -('nah','http://nah.wiktionary.org/wiki/$1',1), -('nb', 'http://no.wiktionary.org/wiki/$1',1), -('nds','http://nds.wiktionary.org/wiki/$1',1), -('ne','http://ne.wiktionary.org/wiki/$1',1), -('nl','http://nl.wiktionary.org/wiki/$1',1), -('nn','http://nn.wiktionary.org/wiki/$1',1), -('no','http://no.wiktionary.org/wiki/$1',1), -('oc','http://oc.wiktionary.org/wiki/$1',1), -('om','http://om.wiktionary.org/wiki/$1',1), -('or','http://or.wiktionary.org/wiki/$1',1), -('pa','http://pa.wiktionary.org/wiki/$1',1), -('pi','http://pi.wiktionary.org/wiki/$1',1), -('pl','http://pl.wiktionary.org/wiki/$1',1), -('ps','http://ps.wiktionary.org/wiki/$1',1), -('pt','http://pt.wiktionary.org/wiki/$1',1), -('qu','http://qu.wiktionary.org/wiki/$1',1), -('rm','http://rm.wiktionary.org/wiki/$1',1), -('rn','http://rn.wiktionary.org/wiki/$1',1), -('ro','http://ro.wiktionary.org/wiki/$1',1), -('roa-rup','http://roa-rup.wiktionary.org/wiki/$1',1), -('ru','http://ru.wiktionary.org/wiki/$1',1), -('rw','http://rw.wiktionary.org/wiki/$1',1), -('sa','http://sa.wiktionary.org/wiki/$1',1), -('sc','http://sc.wiktionary.org/wiki/$1',1), -('scn','http://scn.wiktionary.org/wiki/$1',1), -('sd','http://sd.wiktionary.org/wiki/$1',1), -('sg','http://sg.wiktionary.org/wiki/$1',1), -('sh','http://sh.wiktionary.org/wiki/$1',1), -('si','http://si.wiktionary.org/wiki/$1',1), -('sk','http://sk.wiktionary.org/wiki/$1',1), -('sl','http://sl.wiktionary.org/wiki/$1',1), -('sm','http://sm.wiktionary.org/wiki/$1',1), -('sn','http://sn.wiktionary.org/wiki/$1',1), -('so','http://so.wiktionary.org/wiki/$1',1), -('sq','http://sq.wiktionary.org/wiki/$1',1), -('sr','http://sr.wiktionary.org/wiki/$1',1), -('ss','http://ss.wiktionary.org/wiki/$1',1), -('st','http://st.wiktionary.org/wiki/$1',1), -('su','http://su.wiktionary.org/wiki/$1',1), -('sv','http://sv.wiktionary.org/wiki/$1',1), -('sw','http://sw.wiktionary.org/wiki/$1',1), -('ta','http://ta.wiktionary.org/wiki/$1',1), -('te','http://te.wiktionary.org/wiki/$1',1), -('tg','http://tg.wiktionary.org/wiki/$1',1), -('th','http://th.wiktionary.org/wiki/$1',1), -('ti','http://ti.wiktionary.org/wiki/$1',1), -('tk','http://tk.wiktionary.org/wiki/$1',1), -('tl','http://tl.wiktionary.org/wiki/$1',1), -('tn','http://tn.wiktionary.org/wiki/$1',1), -('to','http://to.wiktionary.org/wiki/$1',1), -('tpi','http://tpi.wiktionary.org/wiki/$1',1), -('tr','http://tr.wiktionary.org/wiki/$1',1), -('ts','http://ts.wiktionary.org/wiki/$1',1), -('tt','http://tt.wiktionary.org/wiki/$1',1), -('tw','http://tw.wiktionary.org/wiki/$1',1), -('ug','http://ug.wiktionary.org/wiki/$1',1), -('uk','http://uk.wiktionary.org/wiki/$1',1), -('ur','http://ur.wiktionary.org/wiki/$1',1), -('uz','http://uz.wiktionary.org/wiki/$1',1), -('vi','http://vi.wiktionary.org/wiki/$1',1), -('vo','http://vo.wiktionary.org/wiki/$1',1), -('wa','http://wa.wiktionary.org/wiki/$1',1), -('wo','http://wo.wiktionary.org/wiki/$1',1), -('xh','http://xh.wiktionary.org/wiki/$1',1), -('yi','http://yi.wiktionary.org/wiki/$1',1), -('yo','http://yo.wiktionary.org/wiki/$1',1), -('za','http://za.wiktionary.org/wiki/$1',1), -('zh','http://zh.wiktionary.org/wiki/$1',1), -('zh-cn','http://zh.wiktionary.org/wiki/$1',1), -('zh-min-nan','http://zh-min-nan.wiktionary.org/wiki/$1',1), -('zh-tw','http://zh.wiktionary.org/wiki/$1',1), -('zu','http://zu.wiktionary.org/wiki/$1',1); - -- cgit v1.2.2