diff options
Diffstat (limited to 'maintenance')
170 files changed, 3875 insertions, 4786 deletions
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}=<b> \1 </b>:" \ + "types{2}=<b> \1 </b> or <b> \2 </b>:" \ + "types{3}=<b> \1 </b>, <b> \2 </b>, or <b> \3 </b>:" \ + "arrayof{2}=<b> Array </b> 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}=<b> \1 </b>:" \ - "types{2}=<b> \1 </b> or <b> \2 </b>:" \ - "types{3}=<b> \1 </b>, <b> \2 </b>, or <b> \3 </b>:" \ - "arrayof{2}=<b> Array </b> 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; @@ -890,57 +912,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 @@ -<?php -/** - * @todo document - * @ingroup Maintenance - */ -class Site { - var $suffix, $lateral, $url; - - function __construct( $s, $l, $u ) { - $this->suffix = $s; - $this->lateral = $l; - $this->url = $u; - } - - function getURL( $lang, $urlprotocol ) { - $xlang = str_replace( '_', '-', $lang ); - return "$urlprotocol//$xlang.{$this->url}/wiki/\$1"; - } -} diff --git a/maintenance/addwiki.php b/maintenance/addwiki.php deleted file mode 100644 index 43f42be5..00000000 --- a/maintenance/addwiki.php +++ /dev/null @@ -1,479 +0,0 @@ -<?php -/** - * @defgroup Wikimedia Wikimedia - */ - -/** - * Add a new wiki - * Wikimedia specific! - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - * @ingroup Maintenance - * @ingroup Wikimedia - */ - -require_once( dirname( __FILE__ ) . '/Maintenance.php' ); - -class AddWiki extends Maintenance { - public function __construct() { - global $wgNoDBParam; - - parent::__construct(); - $this->mDescription = "Add a new wiki to the family. Wikimedia specific!"; - $this->addArg( 'language', 'Language code of new site, e.g. en' ); - $this->addArg( 'site', 'Type of site, e.g. wikipedia' ); - $this->addArg( 'dbname', 'Name of database to create, e.g. enwiki' ); - $this->addArg( 'domain', 'Domain name of the wiki, e.g. en.wikipedia.org' ); - - $wgNoDBParam = true; - } - - public function getDbType() { - 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 <<<EOT -==This subdomain is reserved for the creation of a [[wikimedia:Our projects|$ucsite]] in '''[[w:en:{$name}|{$name}]]''' language== - -* Please '''do not start editing''' this new site. This site has a test project on the [[incubator:|Wikimedia Incubator]] (or on the [[betawikiversity:|BetaWikiversity]] or on the [[oldwikisource:|Old Wikisource]]) and it will be imported to here. - -* If you would like to help translating the interface to this language, please do not translate here, but go to [[translatewiki:|translatewiki]], a special wiki for translating the interface. That way everyone can use it on every wiki using the [[mw:|same software]]. - -* For information about how to edit and for other general help, see [[m:Help:Contents|Help on Wikimedia's Meta-Wiki]] or [[mw:Help:Contents|Help on MediaWiki.org]]. - -== Sister projects == -<span class="plainlinks"> -[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] -</span> - -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 </mediawiki> 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 @@ <?php /** * @defgroup Benchmark Benchmark + * @ingroup Maintenance */ /** @@ -23,7 +24,7 @@ * * @todo Report PHP version, OS .. * @file - * @ingroup Maintenance Benchmark + * @ingroup Benchmark */ require_once( dirname( __FILE__ ) . '/../Maintenance.php' ); diff --git a/maintenance/benchmarks/bench_HTTP_HTTPS.php b/maintenance/benchmarks/bench_HTTP_HTTPS.php index 0038b2d1..cf62aadb 100644 --- a/maintenance/benchmarks/bench_HTTP_HTTPS.php +++ b/maintenance/benchmarks/bench_HTTP_HTTPS.php @@ -18,8 +18,8 @@ * http://www.gnu.org/copyleft/gpl.html * * @file - * @ingroup Maintenance - * @author Platonides + * @ingroup Benchmark + * @author Platonides */ require_once( dirname( __FILE__ ) . '/Benchmarker.php' ); diff --git a/maintenance/benchmarks/bench_delete_truncate.php b/maintenance/benchmarks/bench_delete_truncate.php index 9fe9bea9..71385520 100644 --- a/maintenance/benchmarks/bench_delete_truncate.php +++ b/maintenance/benchmarks/bench_delete_truncate.php @@ -1,4 +1,8 @@ <?php +/** + * @file + * @ingroup Benchmark + */ require_once( dirname( __FILE__ ) . '/Benchmarker.php' ); diff --git a/maintenance/benchmarks/bench_if_switch.php b/maintenance/benchmarks/bench_if_switch.php index 11c00b3c..dafce050 100644 --- a/maintenance/benchmarks/bench_if_switch.php +++ b/maintenance/benchmarks/bench_if_switch.php @@ -18,8 +18,8 @@ * http://www.gnu.org/copyleft/gpl.html * * @file - * @ingroup Maintenance - * @author Platonides + * @ingroup Benchmark + * @author Platonides */ require_once( dirname( __FILE__ ) . '/Benchmarker.php' ); diff --git a/maintenance/benchmarks/bench_strtr_str_replace.php b/maintenance/benchmarks/bench_strtr_str_replace.php index ae576981..73ace2bd 100644 --- a/maintenance/benchmarks/bench_strtr_str_replace.php +++ b/maintenance/benchmarks/bench_strtr_str_replace.php @@ -1,4 +1,8 @@ <?php +/** + * @file + * @ingroup Benchmark + */ require_once( dirname( __FILE__ ) . '/Benchmarker.php' ); diff --git a/maintenance/benchmarks/bench_wfIsWindows.php b/maintenance/benchmarks/bench_wfIsWindows.php index 4c35221d..4caebc5e 100644 --- a/maintenance/benchmarks/bench_wfIsWindows.php +++ b/maintenance/benchmarks/bench_wfIsWindows.php @@ -18,8 +18,8 @@ * http://www.gnu.org/copyleft/gpl.html * * @file - * @ingroup Maintenance - * @author Platonides + * @ingroup Benchmark + * @author Platonides */ require_once( dirname( __FILE__ ) . '/Benchmarker.php' ); diff --git a/maintenance/benchmarks/benchmarkHooks.php b/maintenance/benchmarks/benchmarkHooks.php new file mode 100644 index 00000000..4ec26168 --- /dev/null +++ b/maintenance/benchmarks/benchmarkHooks.php @@ -0,0 +1,80 @@ +<?php +/** + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @ingroup Benchmark + */ + +require_once( dirname( __FILE__ ) . '/Benchmarker.php' ); + +class BenchmarkHooks extends Benchmarker { + + public function __construct() { + parent::__construct(); + $this->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 @@ +<?php + +# Router for the php cli-server built-in webserver +# http://ca2.php.net/manual/en/features.commandline.webserver.php + +if ( php_sapi_name() != 'cli-server' ) { + die( "This script can only be run by php's cli-server sapi." ); +} + +ini_set('display_errors', 1); +error_reporting(E_ALL); + +if ( isset( $_SERVER["SCRIPT_FILENAME"] ) ) { + # Known resource, sometimes a script sometimes a file + $file = $_SERVER["SCRIPT_FILENAME"]; +} elseif ( isset( $_SERVER["SCRIPT_NAME"] ) ) { + # Usually unknown, document root relative rather than absolute + # Happens with some cases like /wiki/File:Image.png + if ( is_readable( $_SERVER['DOCUMENT_ROOT'] . $_SERVER["SCRIPT_NAME"] ) ) { + # Just in case this actually IS a file, set it here + $file = $_SERVER['DOCUMENT_ROOT'] . $_SERVER["SCRIPT_NAME"]; + } else { + # Otherwise let's pretend that this is supposed to go to index.php + $file = $_SERVER['DOCUMENT_ROOT'] . '/index.php'; + } +} else { + # Meh, we'll just give up + return false; +} + +# And now do handling for that $file + +if ( !is_readable( $file ) ) { + # Let the server throw the error if it doesn't exist + return false; +} +$ext = pathinfo( $file, PATHINFO_EXTENSION ); +if ( $ext == 'php' || $ext == 'php5' ) { + # Execute php files + # We use require and return true here because when you return false + # the php webserver will discard post data and things like login + # will not function in the dev environment. + require( $file ); + return true; +} +$mime = false; +$lines = explode( "\n", file_get_contents( "includes/mime.types" ) ); +foreach ( $lines as $line ) { + $exts = explode( " ", $line ); + $mime = array_shift( $exts ); + if ( in_array( $ext, $exts ) ) { + break; # this is the right value for $mime + } + $mime = false; +} +if ( !$mime ) { + $basename = basename( $file ); + if ( $basename == strtoupper( $basename ) ) { + # IF it's something like README serve it as text + $mime = "text/plain"; + } +} +if ( $mime ) { + # Use custom handling to serve files with a known mime type + # This way we can serve things like .svg files that the built-in + # PHP webserver doesn't understand. + # ;) Nicely enough we just happen to bundle a mime.types file + $f = fopen($file, 'rb'); + if ( preg_match( '^text/', $mime ) ) { + # Text should have a charset=UTF-8 (php's webserver does this too) + header("Content-Type: $mime; charset=UTF-8"); + } else { + header("Content-Type: $mime"); + } + header("Content-Length: " . filesize($file)); + // Stream that out to the browser + fpassthru($f); + return true; +} + +# Let the php server handle things on it's own otherwise +return false; diff --git a/maintenance/dev/install.sh b/maintenance/dev/install.sh new file mode 100644 index 00000000..2219894d --- /dev/null +++ b/maintenance/dev/install.sh @@ -0,0 +1,8 @@ +#!/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/installphp.sh" +"$DEV/installmw.sh" +"$DEV/start.sh" diff --git a/maintenance/dev/installmw.sh b/maintenance/dev/installmw.sh new file mode 100644 index 00000000..9ae3c593 --- /dev/null +++ b/maintenance/dev/installmw.sh @@ -0,0 +1,18 @@ +#!/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" + +set -e + +PORT=4881 + +cd "$DEV/../../"; # $IP + +mkdir -p "$DEV/data" +"$PHP" maintenance/install.php --server="http://localhost:$PORT" --scriptpath="" --dbtype=sqlite --dbpath="$DEV/data" --pass=admin "Trunk Test" "$USER" +echo "" +echo "Development wiki created with admin user $USER and password 'admin'." +echo "" diff --git a/maintenance/dev/installphp.sh b/maintenance/dev/installphp.sh new file mode 100644 index 00000000..d26ffa67 --- /dev/null +++ b/maintenance/dev/installphp.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +if [ "x$BASH_SOURCE" == "x" ]; then echo '$BASH_SOURCE not set'; exit 1; fi +DEV=$(cd -P "$(dirname "${BASH_SOURCE[0]}" )" && pwd) + +set -e # DO NOT USE PIPES unless this is rewritten + +. "$DEV/includes/php.sh" + +if [ "x$PHP" != "x" -a -x "$PHP" ]; then + echo "PHP is already installed" + exit 0 +fi + +TAR=php5.4-latest.tar.gz +PHPURL="http://snaps.php.net/$TAR" + +cd "$DEV" + +echo "Preparing to download and install a local copy of PHP 5.4, note that this can take some time to do." +echo "If you wish to avoid re-doing this for uture dev installations of MediaWiki we suggest installing php in ~/.mediawiki/php" +echo -n "Install PHP in ~/.mediawiki/php [y/N]: " +read INSTALLINHOME + +case "$INSTALLINHOME" in + [Yy] | [Yy][Ee][Ss] ) + PREFIX="$HOME/.mediawiki/php" + ;; + *) + PREFIX="$DEV/php/" + ;; +esac + +# Some debain-like systems bundle wget but not curl, some other systems +# like os x bundle curl but not wget... use whatever is available +echo -n "Downloading PHP 5.4" +if command -v wget &>/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=<class>[:<file>] Load a dump plugin class --output=<type>:<file> Begin a filtered output stream; - <type>s: file, gzip, bzip2, 7zip + <type>s: file, gzip, bzip2, 7zip --filter=<type>[:<options>] 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 @@ -<?php -/** - * Build constant slightly compact database of interwiki prefixes - * Wikimedia specific! - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - * @todo document - * @ingroup Maintenance - * @ingroup Wikimedia - */ - -require_once( dirname( __FILE__ ) . '/Site.php' ); - -require_once( dirname( __FILE__ ) . '/Maintenance.php' ); - -class DumpInterwiki extends Maintenance { - - public function __construct() { - parent::__construct(); - $this->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 <brion@pobox.com>, 2010 Alexandre Emsenhuber + * Copyright (C) 2005 Brion Vibber <brion@pobox.com> * 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=<minutes> 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=<filenamepattern> 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 <hashar at free dot fr> + * @author Antoine Musso <hashar at free dot fr> */ 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 @@ <?php +/** + * @ingroup Maintenance + */ require_once( dirname( __FILE__ ) .'/Maintenance.php' ); diff --git a/maintenance/fuzz-tester.php b/maintenance/fuzz-tester.php index 18af4de4..d87d6281 100644 --- a/maintenance/fuzz-tester.php +++ b/maintenance/fuzz-tester.php @@ -108,7 +108,6 @@ Wiki configuration for testing: $wgGroupPermissions['*']['reupload-shared'] = true; $wgGroupPermissions['*']['rollback'] = true; $wgGroupPermissions['*']['siteadmin'] = true; - $wgGroupPermissions['*']['trackback'] = true; $wgGroupPermissions['*']['unwatchedpages'] = true; $wgGroupPermissions['*']['upload'] = true; $wgGroupPermissions['*']['userrights'] = true; @@ -121,7 +120,6 @@ Wiki configuration for testing: error_reporting (E_ALL); // At a later date could be increased to E_ALL | E_STRICT $wgBlockOpenProxies = true; // Some block pages require this to be true in order to test. $wgEnableUploads = true; // enable uploads. - //$wgUseTrackbacks = true; // enable trackbacks; However this breaks the viewPageTest, so currently disabled. $wgDBerrorLog = "/root/mediawiki-db-error-log.txt"; // log DB errors, replace with suitable path. $wgShowSQLErrors = true; // Show SQL errors (instead of saying the query was hidden). $wgShowExceptionDetails = true; // want backtraces. @@ -139,11 +137,7 @@ Wiki configuration for testing: // Install & enable Special Page extensions to increase code coverage. E.g.: require_once("extensions/Cite/SpecialCite.php"); - require_once("extensions/Filepath/SpecialFilepath.php"); - require_once("extensions/Makebot/Makebot.php"); - require_once("extensions/Makesysop/SpecialMakesysop.php"); require_once("extensions/Renameuser/SpecialRenameuser.php"); - require_once("extensions/LinkSearch/LinkSearch.php"); // --------- End --------- If you want to try E_STRICT error logging, add this to the above: @@ -763,14 +757,18 @@ class wikiFuzz { static private $maxparams = 10; /** - ** Returns random number between finish and start. + * Returns random number between finish and start. + * @param $finish + * @param $start int + * @return int */ static public function randnum( $finish, $start = 0 ) { return mt_rand( $start, $finish ); } /** - ** Returns a mix of random text and random wiki syntax. + * Returns a mix of random text and random wiki syntax. + * @return string */ static private function randstring() { $thestring = ""; @@ -800,18 +798,17 @@ class wikiFuzz { } /** - ** Returns either random text, or random wiki syntax, or random data from "ints", - ** or random data from "other". + * Returns either random text, or random wiki syntax, or random data from "ints", + * or random data from "other". + * @return string */ static private function makestring() { $what = wikiFuzz::randnum( 2 ); if ( $what == 0 ) { return wikiFuzz::randstring(); - } - elseif ( $what == 1 ) { + } elseif ( $what == 1 ) { return wikiFuzz::$ints[wikiFuzz::randnum( count( wikiFuzz::$ints ) - 1 )]; - } - else { + } else { return wikiFuzz::$other[wikiFuzz::randnum( count( wikiFuzz::$other ) - 1 )]; } } @@ -819,6 +816,8 @@ class wikiFuzz { /** * Returns the matched character slash-escaped as in a C string * Helper for makeTitleSafe callback + * @param $matches + * @return atring */ static private function stringEscape( $matches ) { return sprintf( "\\x%02x", ord( $matches[1] ) ); @@ -827,6 +826,8 @@ class wikiFuzz { /** ** Strips out the stuff that Mediawiki balks at in a page's title. ** Implementation copied/pasted from cleanupTable.inc & cleanupImages.php + * @param $str string + * @return string */ static public function makeTitleSafe( $str ) { $legalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF"; @@ -837,6 +838,7 @@ class wikiFuzz { /** ** Returns a string of fuzz text. + * @return string */ static private function loop() { switch ( wikiFuzz::randnum( 3 ) ) { @@ -864,7 +866,8 @@ class wikiFuzz { } /** - ** Returns one of the three styles of random quote: ', ", and nothing. + * Returns one of the three styles of random quote: ', ", and nothing. + * @return string */ static private function getRandQuote() { switch ( wikiFuzz::randnum( 3 ) ) { @@ -876,6 +879,8 @@ class wikiFuzz { /** ** Returns fuzz text, with the parameter indicating approximately how many lines of text you want. + * @param $maxtypes int + * @return string */ static public function makeFuzz( $maxtypes = 2 ) { $page = ""; @@ -1812,32 +1817,6 @@ class thumbTest extends pageTest { } } - -/** - ** a test for trackback.php - */ -class trackbackTest extends pageTest { - function __construct() { - $this->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 */ @@ -1892,52 +1871,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). */ class specialRenameuserPageTest extends pageTest { @@ -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 @@ -<?php - -require( 'Net/Gearman/Client.php' ); -require( 'Net/Gearman/Worker.php' ); - -class MWGearmanJob extends Net_Gearman_Job_Common { - function switchWiki( $wiki, $params ) { - echo "Switching to $wiki\n"; - - # Pretend that we have completed it right now, because the new process won't do it - $this->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 @@ -<?php - -$optionsWithArgs = array( 'fake-job' ); - -require( dirname( __FILE__ ) . '/../commandLine.inc' ); -require( dirname( __FILE__ ) . '/gearman.inc' ); - -if ( !$args ) { - $args = array( 'localhost' ); -} -$client = new Net_Gearman_Client( $args ); -$batchSize = 1000; - -$dbr = wfGetDB( DB_SLAVE ); -$startId = 0; -$endId = $dbr->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 @@ -<?php - -$optionsWithArgs = array( 'fake-job', 'procs' ); -require( dirname( __FILE__ ) . '/../commandLine.inc' ); -require( dirname( __FILE__ ) . '/gearman.inc' ); - -ini_set( 'memory_limit', '150M' ); - -if ( isset( $options['procs'] ) ) { - $procs = $options['procs']; - if ( $procs < 1 || $procs > 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 <?php +define( 'MW_CONFIG_CALLBACK', 'MakeHipHop::noConfigNeeded' ); require( dirname( __FILE__ ) . '/../Maintenance.php' ); class MakeHipHop extends Maintenance { + function noConfigNeeded() {} + function execute() { global $wgHipHopBuildDirectory; diff --git a/maintenance/ibm_db2/foreignkeys.sql b/maintenance/ibm_db2/foreignkeys.sql index 81a88eb9..4f1450d9 100644 --- a/maintenance/ibm_db2/foreignkeys.sql +++ b/maintenance/ibm_db2/foreignkeys.sql @@ -93,11 +93,6 @@ ALTER TABLE oldimage ADD CONSTRAINT OLDIMAGE_NAME_FK FOREIGN KEY (oi_name) REFER ALTER TABLE watchlist ADD CONSTRAINT WATCHLIST_USER_FK FOREIGN KEY (wl_user) REFERENCES user(user_id) ON DELETE CASCADE ; --- good --- already in MySQL schema -ALTER TABLE trackbacks ADD CONSTRAINT TRACKBACKS_PAGE_FK FOREIGN KEY (tb_page) REFERENCES page(page_id) ON DELETE CASCADE -; - -- cannot contain null values -- ALTER TABLE protected_titles ADD CONSTRAINT PROTECTED_TITLES_USER_FK FOREIGN KEY (pt_user) REFERENCES user(user_id) ON DELETE SET NULL --; diff --git a/maintenance/ibm_db2/tables.sql b/maintenance/ibm_db2/tables.sql index 261a3a2b..66fc6564 100644 --- a/maintenance/ibm_db2/tables.sql +++ b/maintenance/ibm_db2/tables.sql @@ -1,16 +1,20 @@ --- DB2 +-- IBM DB2 -- SQL to create the initial tables for the MediaWiki database. -- This is read and executed by the install script; you should -- not have to run it by itself unless doing a manual install. --- This is the IBM DB2 version. --- For information about each table, please see the notes in maintenance/tables.sql + +-- Notes: +-- * DB2 will convert all table and column names to all caps internally. +-- * DB2 has a 32k limit on SQL filesize, so it may be necessary +-- to split this into two files soon. CREATE TABLE user ( -- Needs to start with 0 - user_id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0), - user_name VARCHAR(255) NOT NULL UNIQUE, + user_id BIGINT + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 0), + user_name VARCHAR(255) NOT NULL UNIQUE, user_real_name VARCHAR(255), user_password VARCHAR(1024), user_newpassword VARCHAR(1024), @@ -21,225 +25,277 @@ CREATE TABLE user ( user_email_token_expires TIMESTAMP(3), user_email_authenticated TIMESTAMP(3), -- obsolete, replace by user_properties table - user_options CLOB(64K) INLINE LENGTH 4096, + -- user_options CLOB(64K) INLINE LENGTH 4096, user_touched TIMESTAMP(3), user_registration TIMESTAMP(3), user_editcount INTEGER ); -CREATE INDEX user_email_token_idx ON user (user_email_token); ---leonsp: +CREATE INDEX user_email_token_idx + ON user (user_email_token); CREATE UNIQUE INDEX user_include_idx - ON user(user_id) - INCLUDE (user_name, user_real_name, user_password, user_newpassword, user_newpass_time, user_token, - user_email, user_email_token, user_email_token_expires, user_email_authenticated, - user_touched, user_registration, user_editcount); + ON user (user_id) + INCLUDE (user_name, user_real_name, user_password, user_newpassword, + user_newpass_time, user_token, + user_email, user_email_token, user_email_token_expires, + user_email_authenticated, + user_touched, user_registration, user_editcount); +CREATE UNIQUE INDEX user_email + ON user (user_email); + + -- Create a dummy user to satisfy fk contraints especially with revisions INSERT INTO user( -user_name, user_real_name, user_password, user_newpassword, user_newpass_time, -user_email, user_email_authenticated, user_options, user_token, user_registration, user_editcount) + user_name, user_real_name, user_password, user_newpassword, user_newpass_time, + user_email, user_email_authenticated, user_token, user_registration, user_editcount +) VALUES ( -'Anonymous','', NULL, NULL, CURRENT_TIMESTAMP, -NULL, NULL, NULL, NULL, CURRENT_timestamp, 0); + 'Anonymous', '', NULL, NULL, CURRENT_TIMESTAMP, + NULL, NULL, NULL, CURRENT_TIMESTAMP, 0 +); + CREATE TABLE user_groups ( ug_user BIGINT NOT NULL DEFAULT 0, -- REFERENCES user(user_id) ON DELETE CASCADE, - ug_group VARCHAR(255) NOT NULL + ug_group VARCHAR(255) NOT NULL ); -CREATE INDEX user_groups_unique ON user_groups (ug_user, ug_group); +CREATE INDEX user_groups_unique + ON user_groups (ug_user, ug_group); + CREATE TABLE user_newtalk ( -- registered users key - user_id BIGINT NOT NULL DEFAULT 0, + user_id BIGINT NOT NULL DEFAULT 0, -- REFERENCES user(user_id) ON DELETE CASCADE, -- anonymous users key user_ip VARCHAR(40), user_last_timestamp TIMESTAMP(3) ); -CREATE INDEX user_newtalk_id_idx ON user_newtalk (user_id); -CREATE INDEX user_newtalk_ip_idx ON user_newtalk (user_ip); ---leonsp: +CREATE INDEX user_newtalk_id_idx + ON user_newtalk (user_id); +CREATE INDEX user_newtalk_ip_idx + ON user_newtalk (user_ip); CREATE UNIQUE INDEX user_newtalk_include_idx - ON user_newtalk(user_id, user_ip) - INCLUDE (user_last_timestamp); + ON user_newtalk (user_id, user_ip) + INCLUDE (user_last_timestamp); + CREATE TABLE page ( - page_id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), - page_namespace SMALLINT NOT NULL, - page_title VARCHAR(255) NOT NULL, + page_id BIGINT + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + page_namespace SMALLINT NOT NULL, + page_title VARCHAR(255) NOT NULL, page_restrictions VARCHAR(1024), - page_counter BIGINT NOT NULL DEFAULT 0, - page_is_redirect SMALLINT NOT NULL DEFAULT 0, - page_is_new SMALLINT NOT NULL DEFAULT 0, + page_counter BIGINT NOT NULL DEFAULT 0, + page_is_redirect SMALLINT NOT NULL DEFAULT 0, + page_is_new SMALLINT NOT NULL DEFAULT 0, page_random NUMERIC(15,14) NOT NULL, page_touched TIMESTAMP(3), - page_latest BIGINT NOT NULL, -- FK? - page_len BIGINT NOT NULL -); -CREATE UNIQUE INDEX page_unique_name ON page (page_namespace, page_title); -CREATE INDEX page_random_idx ON page (page_random); -CREATE INDEX page_len_idx ON page (page_len); ---leonsp: + page_latest BIGINT NOT NULL, -- FK? + page_len BIGINT NOT NULL +); +CREATE UNIQUE INDEX page_unique_name + ON page (page_namespace, page_title); +CREATE INDEX page_random_idx + ON page (page_random); +CREATE INDEX page_len_idx + ON page (page_len); CREATE UNIQUE INDEX page_id_include - ON page (page_id) - INCLUDE (page_namespace, page_title, page_restrictions, page_counter, page_is_redirect, page_is_new, page_random, page_touched, page_latest, page_len); + ON page (page_id) + INCLUDE (page_namespace, page_title, page_restrictions, page_counter, page_is_redirect, page_is_new, page_random, page_touched, page_latest, page_len); CREATE UNIQUE INDEX page_name_include - ON page (page_namespace, page_title) - INCLUDE (page_id, page_restrictions, page_counter, page_is_redirect, page_is_new, page_random, page_touched, page_latest, page_len); + ON page (page_namespace, page_title) + INCLUDE (page_id, page_restrictions, page_counter, page_is_redirect, page_is_new, page_random, page_touched, page_latest, page_len); + CREATE TABLE revision ( - rev_id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), - rev_page BIGINT NOT NULL DEFAULT 0, + rev_id BIGINT + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + rev_page BIGINT NOT NULL DEFAULT 0, -- REFERENCES page (page_id) ON DELETE CASCADE, - rev_text_id BIGINT, -- FK - rev_comment VARCHAR(1024), - rev_user BIGINT NOT NULL DEFAULT 0, + rev_text_id BIGINT, -- FK + rev_comment VARCHAR(1024), + rev_user BIGINT NOT NULL DEFAULT 0, -- REFERENCES user(user_id) ON DELETE RESTRICT, - rev_user_text VARCHAR(255) NOT NULL, - rev_timestamp TIMESTAMP(3) NOT NULL, - rev_minor_edit SMALLINT NOT NULL DEFAULT 0, - rev_deleted SMALLINT NOT NULL DEFAULT 0, - rev_len BIGINT, - rev_parent_id BIGINT DEFAULT NULL -); -CREATE UNIQUE INDEX revision_unique ON revision (rev_page, rev_id); -CREATE INDEX rev_text_id_idx ON revision (rev_text_id); -CREATE INDEX rev_timestamp_idx ON revision (rev_timestamp); -CREATE INDEX rev_user_idx ON revision (rev_user); -CREATE INDEX rev_user_text_idx ON revision (rev_user_text); + rev_user_text VARCHAR(255) NOT NULL, + rev_timestamp TIMESTAMP(3) NOT NULL, + rev_minor_edit SMALLINT NOT NULL DEFAULT 0, + rev_deleted SMALLINT NOT NULL DEFAULT 0, + rev_len BIGINT, + rev_parent_id BIGINT DEFAULT NULL, + rev_sha1 VARCHAR(255) 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); +CREATE INDEX rev_timestamp_idx + ON revision (rev_timestamp); +CREATE INDEX rev_user_idx + ON revision (rev_user); +CREATE INDEX rev_user_text_idx + ON revision (rev_user_text); CREATE TABLE text ( -- replaces reserved word 'text' - old_id INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + old_id INTEGER + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), old_text CLOB(16M) INLINE LENGTH 4096, old_flags VARCHAR(1024) ); + CREATE TABLE page_restrictions ( - --pr_id INTEGER NOT NULL UNIQUE, --DEFAULT nextval('pr_id_val'), - --pr_id INTEGER PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), - pr_id BIGINT PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), - pr_page INTEGER NOT NULL DEFAULT 0, + pr_id BIGINT + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + pr_page INTEGER NOT NULL DEFAULT 0, --(used to be nullable) -- REFERENCES page (page_id) ON DELETE CASCADE, - pr_type VARCHAR(60) NOT NULL, - pr_level VARCHAR(60) NOT NULL, - pr_cascade SMALLINT NOT NULL, + pr_type VARCHAR(60) NOT NULL, + pr_level VARCHAR(60) NOT NULL, + pr_cascade SMALLINT NOT NULL, pr_user INTEGER, pr_expiry TIMESTAMP(3) --PRIMARY KEY (pr_page, pr_type) ); ---ALTER TABLE page_restrictions ADD CONSTRAINT page_restrictions_pk PRIMARY KEY (pr_page,pr_type); -CREATE UNIQUE INDEX pr_pagetype ON page_restrictions (pr_page,pr_type); -CREATE INDEX pr_typelevel ON page_restrictions (pr_type,pr_level); -CREATE INDEX pr_level ON page_restrictions (pr_level); -CREATE INDEX pr_cascade ON page_restrictions (pr_cascade); +--ALTER TABLE page_restrictions ADD CONSTRAINT page_restrictions_pk PRIMARY KEY (pr_page, pr_type); +CREATE UNIQUE INDEX pr_pagetype + ON page_restrictions (pr_page, pr_type); +CREATE INDEX pr_typelevel + ON page_restrictions (pr_type, pr_level); +CREATE INDEX pr_level + ON page_restrictions (pr_level); +CREATE INDEX pr_cascade + ON page_restrictions (pr_cascade); + + CREATE TABLE page_props ( - pp_page INTEGER NOT NULL DEFAULT 0, + pp_page INTEGER NOT NULL DEFAULT 0, -- REFERENCES page (page_id) ON DELETE CASCADE, - pp_propname VARCHAR(255) NOT NULL, - pp_value CLOB(64K) INLINE LENGTH 4096 NOT NULL, - PRIMARY KEY (pp_page,pp_propname) + pp_propname VARCHAR(255) NOT NULL, + pp_value CLOB(64K) INLINE LENGTH 4096 NOT NULL, + PRIMARY KEY (pp_page, pp_propname) ); ---ALTER TABLE page_props ADD CONSTRAINT page_props_pk PRIMARY KEY (pp_page,pp_propname); -CREATE INDEX page_props_propname ON page_props (pp_propname); +CREATE INDEX page_props_propname + ON page_props (pp_propname); CREATE TABLE archive ( - ar_namespace SMALLINT NOT NULL, - ar_title VARCHAR(255) NOT NULL, + ar_namespace SMALLINT NOT NULL, + ar_title VARCHAR(255) NOT NULL, ar_text CLOB(16M) INLINE LENGTH 4096, ar_comment VARCHAR(1024), ar_user BIGINT NOT NULL, -- no foreign keys in MySQL -- REFERENCES user(user_id) ON DELETE SET NULL, - ar_user_text VARCHAR(255) NOT NULL, - ar_timestamp TIMESTAMP(3) NOT NULL, - ar_minor_edit SMALLINT NOT NULL DEFAULT 0, + ar_user_text VARCHAR(255) NOT NULL, + ar_timestamp TIMESTAMP(3) NOT NULL, + ar_minor_edit SMALLINT NOT NULL DEFAULT 0, ar_flags VARCHAR(1024), ar_rev_id INTEGER, ar_text_id INTEGER, - ar_deleted SMALLINT NOT NULL DEFAULT 0, + ar_deleted SMALLINT NOT NULL DEFAULT 0, ar_len INTEGER, ar_page_id INTEGER, - ar_parent_id INTEGER + ar_parent_id INTEGER, + ar_sha1 VARCHAR(255) NOT NULL DEFAULT '' ); -CREATE INDEX archive_name_title_timestamp ON archive (ar_namespace,ar_title,ar_timestamp); -CREATE INDEX archive_user_text ON archive (ar_user_text); +CREATE INDEX archive_name_title_timestamp + ON archive (ar_namespace, ar_title, ar_timestamp); +CREATE INDEX archive_user_text + ON archive (ar_user_text); CREATE TABLE redirect ( - rd_from BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + rd_from BIGINT NOT NULL + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), --REFERENCES page(page_id) ON DELETE CASCADE, - rd_namespace SMALLINT NOT NULL DEFAULT 0, - rd_title VARCHAR(255) NOT NULL DEFAULT '', - rd_interwiki varchar(32), + rd_namespace SMALLINT NOT NULL DEFAULT 0, + rd_title VARCHAR(255) NOT NULL DEFAULT '', + rd_interwiki VARCHAR(32), rd_fragment VARCHAR(255) ); -CREATE INDEX redirect_ns_title ON redirect (rd_namespace,rd_title,rd_from); +CREATE INDEX redirect_ns_title + ON redirect (rd_namespace, rd_title, rd_from); CREATE TABLE pagelinks ( - pl_from BIGINT NOT NULL DEFAULT 0, + pl_from BIGINT NOT NULL DEFAULT 0, -- REFERENCES page(page_id) ON DELETE CASCADE, - pl_namespace SMALLINT NOT NULL, - pl_title VARCHAR(255) NOT NULL + pl_namespace SMALLINT NOT NULL, + pl_title VARCHAR(255) NOT NULL ); -CREATE UNIQUE INDEX pagelink_unique ON pagelinks (pl_from,pl_namespace,pl_title); +CREATE UNIQUE INDEX pagelink_unique + ON pagelinks (pl_from, pl_namespace, pl_title); + + CREATE TABLE templatelinks ( - tl_from BIGINT NOT NULL DEFAULT 0, + tl_from BIGINT NOT NULL DEFAULT 0, -- REFERENCES page(page_id) ON DELETE CASCADE, tl_namespace SMALLINT NOT NULL, - tl_title VARCHAR(255) NOT NULL + tl_title VARCHAR(255) NOT NULL ); -CREATE UNIQUE INDEX templatelinks_unique ON templatelinks (tl_namespace,tl_title,tl_from); -CREATE UNIQUE INDEX tl_from_idx ON templatelinks (tl_from,tl_namespace,tl_title); +CREATE UNIQUE INDEX templatelinks_unique + ON templatelinks (tl_namespace, tl_title, tl_from); +CREATE UNIQUE INDEX tl_from_idx + ON templatelinks (tl_from, tl_namespace, tl_title); + + CREATE TABLE imagelinks ( - il_from BIGINT NOT NULL DEFAULT 0, + il_from BIGINT NOT NULL DEFAULT 0, -- REFERENCES page(page_id) ON DELETE CASCADE, - il_to VARCHAR(255) NOT NULL + il_to VARCHAR(255) NOT NULL ); -CREATE UNIQUE INDEX il_from_idx ON imagelinks (il_to,il_from); -CREATE UNIQUE INDEX il_to_idx ON imagelinks (il_from,il_to); +CREATE UNIQUE INDEX il_from_idx + ON imagelinks (il_to, il_from); +CREATE UNIQUE INDEX il_to_idx + ON imagelinks (il_from, il_to); + + CREATE TABLE categorylinks ( - cl_from BIGINT NOT NULL DEFAULT 0, + cl_from BIGINT NOT NULL DEFAULT 0, -- REFERENCES page(page_id) ON DELETE CASCADE, - cl_to VARCHAR(255) NOT NULL, + cl_to VARCHAR(255) NOT NULL, -- cl_sortkey has to be at least 86 wide -- in order to be compatible with the old MySQL schema from MW 1.10 --cl_sortkey VARCHAR(86), - cl_sortkey VARCHAR(230) FOR BIT DATA NOT NULL , - cl_sortkey_prefix VARCHAR(255) FOR BIT DATA NOT NULL , - cl_timestamp TIMESTAMP(3) NOT NULL, - cl_collation VARCHAR(32) FOR BIT DATA NOT NULL , - cl_type VARCHAR(6) FOR BIT DATA NOT NULL + cl_sortkey VARCHAR(230) FOR BIT DATA NOT NULL, + cl_sortkey_prefix VARCHAR(255) FOR BIT DATA NOT NULL, + cl_timestamp TIMESTAMP(3) NOT NULL, + cl_collation VARCHAR(32) FOR BIT DATA NOT NULL, + cl_type VARCHAR(6) FOR BIT DATA NOT NULL ); -CREATE UNIQUE INDEX cl_from ON categorylinks (cl_from, cl_to); -CREATE INDEX cl_sortkey ON categorylinks (cl_to, cl_sortkey, cl_from); +CREATE UNIQUE INDEX cl_from + ON categorylinks (cl_from, cl_to); +CREATE INDEX cl_sortkey + ON categorylinks (cl_to, cl_sortkey, cl_from); CREATE TABLE externallinks ( - el_from BIGINT NOT NULL DEFAULT 0, + el_from BIGINT NOT NULL DEFAULT 0, -- REFERENCES page(page_id) ON DELETE CASCADE, - el_to VARCHAR(1024) NOT NULL, - el_index VARCHAR(1024) NOT NULL + el_to VARCHAR(1024) NOT NULL, + el_index VARCHAR(1024) NOT NULL ); -CREATE INDEX externallinks_from_to ON externallinks (el_from,el_to); -CREATE INDEX externallinks_index ON externallinks (el_index); +CREATE INDEX externallinks_from_to + ON externallinks (el_from, el_to); +CREATE INDEX externallinks_index + ON externallinks (el_index); + -- @@ -247,494 +303,636 @@ CREATE INDEX externallinks_index ON externallinks (el_index); -- CREATE TABLE external_user ( -- Foreign key to user_id - eu_local_id BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + eu_local_id BIGINT NOT NULL + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), -- Some opaque identifier provided by the external database - eu_external_id VARCHAR(255) NOT NULL + eu_external_id VARCHAR(255) NOT NULL ); CREATE UNIQUE INDEX eu_external_id_idx - ON external_user (eu_external_id) - INCLUDE (eu_local_id); + ON external_user (eu_external_id) + INCLUDE (eu_local_id); CREATE UNIQUE INDEX eu_local_id_idx - ON external_user (eu_local_id) - INCLUDE (eu_external_id); + ON external_user (eu_local_id) + INCLUDE (eu_external_id); CREATE TABLE langlinks ( - ll_from BIGINT NOT NULL DEFAULT 0, + ll_from BIGINT NOT NULL DEFAULT 0, -- REFERENCES page (page_id) ON DELETE CASCADE, ll_lang VARCHAR(20), ll_title VARCHAR(255) ); -CREATE UNIQUE INDEX langlinks_unique ON langlinks (ll_from,ll_lang); -CREATE INDEX langlinks_lang_title ON langlinks (ll_lang,ll_title); +CREATE UNIQUE INDEX langlinks_unique + ON langlinks (ll_from, ll_lang); +CREATE INDEX langlinks_lang_title + ON langlinks (ll_lang, ll_title); + CREATE TABLE site_stats ( - ss_row_id BIGINT NOT NULL UNIQUE, - ss_total_views BIGINT DEFAULT 0, - ss_total_edits BIGINT DEFAULT 0, - ss_good_articles BIGINT DEFAULT 0, - ss_total_pages INTEGER DEFAULT -1, - ss_users INTEGER DEFAULT -1, - ss_active_users INTEGER DEFAULT -1, - ss_admins INTEGER DEFAULT -1, - ss_images INTEGER DEFAULT 0 + ss_row_id BIGINT NOT NULL UNIQUE, + ss_total_views BIGINT DEFAULT 0, + ss_total_edits BIGINT DEFAULT 0, + ss_good_articles BIGINT DEFAULT 0, + ss_total_pages INTEGER DEFAULT -1, + ss_users INTEGER DEFAULT -1, + ss_active_users INTEGER DEFAULT -1, + ss_admins INTEGER DEFAULT -1, + ss_images INTEGER DEFAULT 0 ); + + CREATE TABLE hitcounter ( - hc_id BIGINT NOT NULL + hc_id BIGINT NOT NULL ); + + CREATE TABLE ipblocks ( - ipb_id INTEGER NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), - --DEFAULT nextval('ipblocks_ipb_id_val'), + ipb_id INTEGER NOT NULL + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), ipb_address VARCHAR(1024), ipb_user BIGINT NOT NULL DEFAULT 0, -- REFERENCES user(user_id) ON DELETE SET NULL, - ipb_by BIGINT NOT NULL DEFAULT 0, + ipb_by BIGINT NOT NULL DEFAULT 0, -- REFERENCES user(user_id) ON DELETE CASCADE, - ipb_by_text VARCHAR(255) NOT NULL DEFAULT '', - ipb_reason VARCHAR(1024) NOT NULL, - ipb_timestamp TIMESTAMP(3) NOT NULL, - ipb_auto SMALLINT NOT NULL DEFAULT 0, - ipb_anon_only SMALLINT NOT NULL DEFAULT 0, - ipb_create_account SMALLINT NOT NULL DEFAULT 1, - ipb_enable_autoblock SMALLINT NOT NULL DEFAULT 1, - ipb_expiry TIMESTAMP(3) NOT NULL, + ipb_by_text VARCHAR(255) NOT NULL DEFAULT '', + ipb_reason VARCHAR(1024) NOT NULL, + ipb_timestamp TIMESTAMP(3) NOT NULL, + ipb_auto SMALLINT NOT NULL DEFAULT 0, + ipb_anon_only SMALLINT NOT NULL DEFAULT 0, + ipb_create_account SMALLINT NOT NULL DEFAULT 1, + ipb_enable_autoblock SMALLINT NOT NULL DEFAULT 1, + ipb_expiry TIMESTAMP(3) NOT NULL, ipb_range_start VARCHAR(1024), ipb_range_end VARCHAR(1024), - ipb_deleted SMALLINT NOT NULL DEFAULT 0, - ipb_block_email SMALLINT NOT NULL DEFAULT 0, - ipb_allow_usertalk SMALLINT NOT NULL DEFAULT 0 + ipb_deleted SMALLINT NOT NULL DEFAULT 0, + ipb_block_email SMALLINT NOT NULL DEFAULT 0, + ipb_allow_usertalk SMALLINT NOT NULL DEFAULT 0 ); -CREATE INDEX ipb_address ON ipblocks (ipb_address); -CREATE INDEX ipb_user ON ipblocks (ipb_user); -CREATE INDEX ipb_range ON ipblocks (ipb_range_start,ipb_range_end); +CREATE INDEX ipb_address + ON ipblocks (ipb_address); +CREATE INDEX ipb_user + ON ipblocks (ipb_user); +CREATE INDEX ipb_range + ON ipblocks (ipb_range_start, ipb_range_end); CREATE TABLE image ( - img_name VARCHAR(255) NOT NULL PRIMARY KEY, - img_size BIGINT NOT NULL, - img_width INTEGER NOT NULL, - img_height INTEGER NOT NULL, - img_metadata CLOB(16M) INLINE LENGTH 4096 NOT NULL DEFAULT '', + img_name VARCHAR(255) NOT NULL + PRIMARY KEY, + img_size BIGINT NOT NULL, + img_width INTEGER NOT NULL, + img_height INTEGER NOT NULL, + img_metadata CLOB(16M) INLINE LENGTH 4096 NOT NULL DEFAULT '', img_bits SMALLINT, img_media_type VARCHAR(255), - img_major_mime VARCHAR(255) DEFAULT 'unknown', - img_minor_mime VARCHAR(32) DEFAULT 'unknown', - img_description VARCHAR(1024) NOT NULL DEFAULT '', + img_major_mime VARCHAR(255) DEFAULT 'unknown', + img_minor_mime VARCHAR(32) DEFAULT 'unknown', + img_description VARCHAR(1024) NOT NULL DEFAULT '', img_user BIGINT NOT NULL DEFAULT 0, -- REFERENCES user(user_id) ON DELETE SET NULL, - img_user_text VARCHAR(255) NOT NULL DEFAULT '', + img_user_text VARCHAR(255) NOT NULL DEFAULT '', img_timestamp TIMESTAMP(3), - img_sha1 VARCHAR(255) NOT NULL DEFAULT '' + img_sha1 VARCHAR(255) NOT NULL DEFAULT '' ); -CREATE INDEX img_size_idx ON image (img_size); -CREATE INDEX img_timestamp_idx ON image (img_timestamp); -CREATE INDEX img_sha1 ON image (img_sha1); +CREATE INDEX img_size_idx + ON image (img_size); +CREATE INDEX img_timestamp_idx + ON image (img_timestamp); +CREATE INDEX img_sha1 + ON image (img_sha1); + CREATE TABLE oldimage ( - oi_name VARCHAR(255) NOT NULL DEFAULT '', - oi_archive_name VARCHAR(255) NOT NULL, - oi_size BIGINT NOT NULL, - oi_width INTEGER NOT NULL, - oi_height INTEGER NOT NULL, - oi_bits SMALLINT NOT NULL, + oi_name VARCHAR(255) NOT NULL DEFAULT '', + oi_archive_name VARCHAR(255) NOT NULL, + oi_size BIGINT NOT NULL, + oi_width INTEGER NOT NULL, + oi_height INTEGER NOT NULL, + oi_bits SMALLINT NOT NULL, oi_description VARCHAR(1024), oi_user BIGINT NOT NULL DEFAULT 0, -- REFERENCES user(user_id) ON DELETE SET NULL, - oi_user_text VARCHAR(255) NOT NULL, - oi_timestamp TIMESTAMP(3) NOT NULL, - oi_metadata CLOB(16M) INLINE LENGTH 4096 NOT NULL DEFAULT '', - oi_media_type VARCHAR(255) , - oi_major_mime VARCHAR(255) NOT NULL DEFAULT 'unknown', - oi_minor_mime VARCHAR(255) NOT NULL DEFAULT 'unknown', - oi_deleted SMALLINT NOT NULL DEFAULT 0, - oi_sha1 VARCHAR(255) NOT NULL DEFAULT '' + oi_user_text VARCHAR(255) NOT NULL, + oi_timestamp TIMESTAMP(3) NOT NULL, + oi_metadata CLOB(16M) INLINE LENGTH 4096 NOT NULL DEFAULT '', + oi_media_type VARCHAR(255), + oi_major_mime VARCHAR(255) NOT NULL DEFAULT 'unknown', + oi_minor_mime VARCHAR(255) NOT NULL DEFAULT 'unknown', + oi_deleted SMALLINT NOT NULL DEFAULT 0, + oi_sha1 VARCHAR(255) NOT NULL DEFAULT '' --FOREIGN KEY (oi_name) REFERENCES image(img_name) ON DELETE CASCADE ); -CREATE INDEX oi_name_timestamp ON oldimage (oi_name,oi_timestamp); -CREATE INDEX oi_name_archive_name ON oldimage (oi_name,oi_archive_name); -CREATE INDEX oi_sha1 ON oldimage (oi_sha1); +CREATE INDEX oi_name_timestamp + ON oldimage (oi_name, oi_timestamp); +CREATE INDEX oi_name_archive_name + ON oldimage (oi_name, oi_archive_name); +CREATE INDEX oi_sha1 + ON oldimage (oi_sha1); CREATE TABLE filearchive ( - fa_id INTEGER NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), - --PRIMARY KEY DEFAULT nextval('filearchive_fa_id_seq'), - fa_name VARCHAR(255) NOT NULL, + fa_id INTEGER NOT NULL + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + fa_name VARCHAR(255) NOT NULL, fa_archive_name VARCHAR(255), fa_storage_group VARCHAR(255), - fa_storage_key VARCHAR(64) DEFAULT '', - fa_deleted_user BIGINT NOT NULL DEFAULT 0, + fa_storage_key VARCHAR(64) DEFAULT '', + fa_deleted_user BIGINT NOT NULL DEFAULT 0, -- REFERENCES user(user_id) ON DELETE SET NULL, - fa_deleted_timestamp TIMESTAMP(3) NOT NULL, + fa_deleted_timestamp TIMESTAMP(3) NOT NULL, fa_deleted_reason VARCHAR(255), - fa_size BIGINT NOT NULL, - fa_width INTEGER NOT NULL, - fa_height INTEGER NOT NULL, - fa_metadata CLOB(16M) INLINE LENGTH 4096 NOT NULL DEFAULT '', + fa_size BIGINT NOT NULL, + fa_width INTEGER NOT NULL, + fa_height INTEGER NOT NULL, + fa_metadata CLOB(16M) INLINE LENGTH 4096 NOT NULL DEFAULT '', fa_bits SMALLINT, fa_media_type VARCHAR(255), - fa_major_mime VARCHAR(255) DEFAULT 'unknown', - fa_minor_mime VARCHAR(255) DEFAULT 'unknown', - fa_description VARCHAR(1024) NOT NULL, + fa_major_mime VARCHAR(255) DEFAULT 'unknown', + fa_minor_mime VARCHAR(255) DEFAULT 'unknown', + fa_description VARCHAR(1024) NOT NULL, fa_user BIGINT NOT NULL DEFAULT 0, -- REFERENCES user(user_id) ON DELETE SET NULL, - fa_user_text VARCHAR(255) NOT NULL, + fa_user_text VARCHAR(255) NOT NULL, fa_timestamp TIMESTAMP(3), - fa_deleted SMALLINT NOT NULL DEFAULT 0 + fa_deleted SMALLINT NOT NULL DEFAULT 0 ); -CREATE INDEX fa_name_time ON filearchive (fa_name, fa_timestamp); -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 INDEX fa_name_time + ON filearchive (fa_name, fa_timestamp); +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 TABLE recentchanges ( - rc_id INTEGER NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), - --PRIMARY KEY DEFAULT nextval('rc_rc_id_seq'), - rc_timestamp TIMESTAMP(3) NOT NULL, - rc_cur_time TIMESTAMP(3) NOT NULL, + rc_id INTEGER NOT NULL + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + rc_timestamp TIMESTAMP(3) NOT NULL, + rc_cur_time TIMESTAMP(3) NOT NULL, rc_user BIGINT NOT NULL DEFAULT 0, -- REFERENCES user(user_id) ON DELETE SET NULL, - rc_user_text VARCHAR(255) NOT NULL, - rc_namespace SMALLINT NOT NULL, - rc_title VARCHAR(255) NOT NULL, + rc_user_text VARCHAR(255) NOT NULL, + rc_namespace SMALLINT NOT NULL, + rc_title VARCHAR(255) NOT NULL, rc_comment VARCHAR(255), - rc_minor SMALLINT NOT NULL DEFAULT 0, - rc_bot SMALLINT NOT NULL DEFAULT 0, - rc_new SMALLINT NOT NULL DEFAULT 0, + rc_minor SMALLINT NOT NULL DEFAULT 0, + rc_bot SMALLINT NOT NULL DEFAULT 0, + rc_new SMALLINT NOT NULL DEFAULT 0, rc_cur_id BIGINT NOT NULL DEFAULT 0, -- REFERENCES page(page_id) ON DELETE SET NULL, - rc_this_oldid BIGINT NOT NULL, - rc_last_oldid BIGINT NOT NULL, - rc_type SMALLINT NOT NULL DEFAULT 0, + rc_this_oldid BIGINT NOT NULL, + rc_last_oldid BIGINT NOT NULL, + rc_type SMALLINT NOT NULL DEFAULT 0, rc_moved_to_ns SMALLINT, rc_moved_to_title VARCHAR(255), - rc_patrolled SMALLINT NOT NULL DEFAULT 0, - rc_ip VARCHAR(40), -- was CIDR type + rc_patrolled SMALLINT NOT NULL DEFAULT 0, + rc_ip VARCHAR(40), -- was CIDR type rc_old_len INTEGER, rc_new_len INTEGER, - rc_deleted SMALLINT NOT NULL DEFAULT 0, - rc_logid BIGINT NOT NULL DEFAULT 0, + rc_deleted SMALLINT NOT NULL DEFAULT 0, + rc_logid BIGINT NOT NULL DEFAULT 0, rc_log_type VARCHAR(255), rc_log_action VARCHAR(255), rc_params CLOB(64K) INLINE LENGTH 4096 ); -CREATE INDEX rc_timestamp ON recentchanges (rc_timestamp); -CREATE INDEX rc_namespace_title ON recentchanges (rc_namespace, rc_title); -CREATE INDEX rc_cur_id ON recentchanges (rc_cur_id); -CREATE INDEX new_name_timestamp ON recentchanges (rc_new, rc_namespace, rc_timestamp); -CREATE INDEX rc_ip ON recentchanges (rc_ip); +CREATE INDEX rc_timestamp + ON recentchanges (rc_timestamp); +CREATE INDEX rc_namespace_title + ON recentchanges (rc_namespace, rc_title); +CREATE INDEX rc_cur_id + ON recentchanges (rc_cur_id); +CREATE INDEX new_name_timestamp + ON recentchanges (rc_new, rc_namespace, rc_timestamp); +CREATE INDEX rc_ip + ON recentchanges (rc_ip); CREATE TABLE watchlist ( - wl_user BIGINT NOT NULL DEFAULT 0, + wl_user BIGINT NOT NULL DEFAULT 0, -- REFERENCES user(user_id) ON DELETE CASCADE, - wl_namespace SMALLINT NOT NULL DEFAULT 0, - wl_title VARCHAR(255) NOT NULL, + wl_namespace SMALLINT NOT NULL DEFAULT 0, + wl_title VARCHAR(255) NOT NULL, wl_notificationtimestamp TIMESTAMP(3) ); -CREATE UNIQUE INDEX wl_user_namespace_title ON watchlist (wl_namespace, wl_title, wl_user); +CREATE UNIQUE INDEX wl_user_namespace_title + ON watchlist (wl_namespace, wl_title, wl_user); + CREATE TABLE interwiki ( - iw_prefix VARCHAR(32) NOT NULL UNIQUE, - iw_url CLOB(64K) INLINE LENGTH 4096 NOT NULL, - iw_api CLOB(64K) INLINE LENGTH 4096 NOT NULL, - iw_wikiid varchar(64) NOT NULL, - iw_local SMALLINT NOT NULL, - iw_trans SMALLINT NOT NULL DEFAULT 0 + iw_prefix VARCHAR(32) NOT NULL UNIQUE, + iw_url CLOB(64K) INLINE LENGTH 4096 NOT NULL, + iw_api CLOB(64K) INLINE LENGTH 4096 NOT NULL, + iw_wikiid VARCHAR(64) NOT NULL, + iw_local SMALLINT NOT NULL, + iw_trans SMALLINT NOT NULL DEFAULT 0 ); + CREATE TABLE querycache ( - qc_type VARCHAR(255) NOT NULL, - qc_value BIGINT NOT NULL, - qc_namespace INTEGER NOT NULL, - qc_title VARCHAR(255) NOT NULL + qc_type VARCHAR(255) NOT NULL, + qc_value BIGINT NOT NULL, + qc_namespace INTEGER NOT NULL, + qc_title VARCHAR(255) NOT NULL ); -CREATE INDEX querycache_type_value ON querycache (qc_type, qc_value); +CREATE INDEX querycache_type_value + ON querycache (qc_type, qc_value); -CREATE TABLE querycache_info ( - qci_type VARCHAR(255) UNIQUE NOT NULL, - qci_timestamp TIMESTAMP(3) +CREATE TABLE querycache_info ( + qci_type VARCHAR(255) UNIQUE NOT NULL, + qci_timestamp TIMESTAMP(3) ); + CREATE TABLE querycachetwo ( - qcc_type VARCHAR(255) NOT NULL, - qcc_value BIGINT NOT NULL DEFAULT 0, - qcc_namespace INTEGER NOT NULL DEFAULT 0, - qcc_title VARCHAR(255) NOT NULL DEFAULT '', - qcc_namespacetwo INTEGER NOT NULL DEFAULT 0, - qcc_titletwo VARCHAR(255) NOT NULL DEFAULT '' + qcc_type VARCHAR(255) NOT NULL, + qcc_value BIGINT NOT NULL DEFAULT 0, + qcc_namespace INTEGER NOT NULL DEFAULT 0, + qcc_title VARCHAR(255) NOT NULL DEFAULT '', + qcc_namespacetwo INTEGER NOT NULL DEFAULT 0, + qcc_titletwo VARCHAR(255) NOT NULL DEFAULT '' ); -CREATE INDEX querycachetwo_type_value ON querycachetwo (qcc_type, qcc_value); -CREATE INDEX querycachetwo_title ON querycachetwo (qcc_type,qcc_namespace,qcc_title); -CREATE INDEX querycachetwo_titletwo ON querycachetwo (qcc_type,qcc_namespacetwo,qcc_titletwo); +CREATE INDEX querycachetwo_type_value + ON querycachetwo (qcc_type, qcc_value); +CREATE INDEX querycachetwo_title + ON querycachetwo (qcc_type, qcc_namespace, qcc_title); +CREATE INDEX querycachetwo_titletwo + ON querycachetwo (qcc_type, qcc_namespacetwo, qcc_titletwo); + + CREATE TABLE objectcache ( - keyname VARCHAR(255) NOT NULL UNIQUE, -- was nullable - value CLOB(16M) INLINE LENGTH 4096 NOT NULL DEFAULT '', - exptime TIMESTAMP(3) NOT NULL + keyname VARCHAR(255) NOT NULL UNIQUE, -- was nullable + value CLOB(16M) INLINE LENGTH 4096 NOT NULL DEFAULT '', + exptime TIMESTAMP(3) NOT NULL ); -CREATE INDEX objectcacache_exptime ON objectcache (exptime); +CREATE INDEX objectcacache_exptime + ON objectcache (exptime); CREATE TABLE transcache ( - tc_url VARCHAR(255) NOT NULL UNIQUE, - tc_contents CLOB(64K) INLINE LENGTH 4096 NOT NULL, - tc_time TIMESTAMP(3) NOT NULL + tc_url VARCHAR(255) NOT NULL UNIQUE, + tc_contents CLOB(64K) INLINE LENGTH 4096 NOT NULL, + tc_time TIMESTAMP(3) NOT NULL ); + CREATE TABLE logging ( - log_id BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), - --PRIMARY KEY DEFAULT nextval('log_log_id_seq'), - log_type VARCHAR(32) NOT NULL, - log_action VARCHAR(32) NOT NULL, - log_timestamp TIMESTAMP(3) NOT NULL, - log_user BIGINT NOT NULL DEFAULT 0, + log_id BIGINT NOT NULL + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + log_type VARCHAR(32) NOT NULL, + log_action VARCHAR(32) NOT NULL, + log_timestamp TIMESTAMP(3) NOT NULL, + log_user BIGINT NOT NULL DEFAULT 0, -- REFERENCES user(user_id) ON DELETE SET NULL, -- Name of the user who performed this action - log_user_text VARCHAR(255) NOT NULL default '', - log_namespace SMALLINT NOT NULL, - log_title VARCHAR(255) NOT NULL, - log_page BIGINT, - log_comment VARCHAR(255), - log_params CLOB(64K) INLINE LENGTH 4096, - log_deleted SMALLINT NOT NULL DEFAULT 0 -); -CREATE INDEX logging_type_name ON logging (log_type, log_timestamp); -CREATE INDEX logging_user_time ON logging (log_timestamp, log_user); -CREATE INDEX logging_page_time ON logging (log_namespace, log_title, log_timestamp); -CREATE INDEX log_user_type_time ON logging (log_user, log_type, log_timestamp); -CREATE INDEX log_page_id_time ON logging (log_page,log_timestamp); + log_user_text VARCHAR(255) NOT NULL DEFAULT '', + log_namespace SMALLINT NOT NULL, + log_title VARCHAR(255) NOT NULL, + log_page BIGINT, + log_comment VARCHAR(255), + log_params CLOB(64K) INLINE LENGTH 4096, + log_deleted SMALLINT NOT NULL DEFAULT 0 +); +CREATE INDEX logging_type_name + ON logging (log_type, log_timestamp); +CREATE INDEX logging_user_time + ON logging (log_timestamp, log_user); +CREATE INDEX logging_page_time + ON logging (log_namespace, log_title, log_timestamp); +CREATE INDEX log_user_type_time + ON logging (log_user, log_type, log_timestamp); +CREATE INDEX log_page_id_time + ON logging (log_page, log_timestamp); +CREATE UNIQUE INDEX type_action + ON logging (log_type, log_action, log_timestamp); CREATE TABLE trackbacks ( - tb_id INTEGER NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), - --PRIMARY KEY DEFAULT nextval('trackbacks_tb_id_seq'), + tb_id INTEGER NOT NULL + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), -- foreign key also in MySQL tb_page INTEGER, -- REFERENCES page(page_id) ON DELETE CASCADE, - tb_title VARCHAR(255) NOT NULL, - tb_url CLOB(64K) INLINE LENGTH 4096 NOT NULL, + tb_title VARCHAR(255) NOT NULL, + tb_url CLOB(64K) INLINE LENGTH 4096 NOT NULL, tb_ex CLOB(64K) INLINE LENGTH 4096, tb_name VARCHAR(255) ); -CREATE INDEX trackback_page ON trackbacks (tb_page); +CREATE INDEX trackback_page + ON trackbacks (tb_page); CREATE TABLE job ( - job_id BIGINT NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), - --PRIMARY KEY DEFAULT nextval('job_job_id_seq'), - job_cmd VARCHAR(255) NOT NULL, - job_namespace SMALLINT NOT NULL, - job_title VARCHAR(255) NOT NULL, - job_params CLOB(64K) INLINE LENGTH 4096 NOT NULL + job_id BIGINT NOT NULL + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + job_cmd VARCHAR(255) NOT NULL, + job_namespace SMALLINT NOT NULL, + job_title VARCHAR(255) NOT NULL, + job_params CLOB(64K) INLINE LENGTH 4096 NOT NULL ); -CREATE INDEX job_cmd_namespace_title ON job (job_cmd, job_namespace, job_title); +CREATE INDEX job_cmd_namespace_title + ON job (job_cmd, job_namespace, job_title); + --TODO ---CREATE FUNCTION add_interwiki (TEXT,INT,SMALLINT) RETURNS INT LANGUAGE SQL AS +--CREATE FUNCTION add_interwiki (TEXT, INT, SMALLINT) RETURNS INT LANGUAGE SQL AS --$mw$ -- INSERT INTO interwiki (iw_prefix, iw_url, iw_local) VALUES ($1,$2,$3); -- SELECT 1; --$mw$; + + -- hack implementation -- should be replaced with OmniFind, Contains(), etc CREATE TABLE searchindex ( - si_page BIGINT NOT NULL, - si_title varchar(255) NOT NULL default '', - si_text clob NOT NULL + si_page BIGINT NOT NULL, + si_title VARCHAR(255) NOT NULL DEFAULT '', + si_text CLOB NOT NULL ); + + -- This table is not used unless profiling is turned on CREATE TABLE profiling ( - pf_count INTEGER NOT NULL DEFAULT 0, - pf_time NUMERIC(18,10) NOT NULL DEFAULT 0, - pf_memory NUMERIC(18,10) NOT NULL DEFAULT 0, - pf_name VARCHAR(255) NOT NULL, - pf_server VARCHAR(255) + pf_count INTEGER NOT NULL DEFAULT 0, + pf_time NUMERIC(18,10) NOT NULL DEFAULT 0, + pf_memory NUMERIC(18,10) NOT NULL DEFAULT 0, + pf_name VARCHAR(255) NOT NULL, + pf_server VARCHAR(255) ); -CREATE UNIQUE INDEX pf_name_server ON profiling (pf_name, pf_server); +CREATE UNIQUE INDEX pf_name_server + ON profiling (pf_name, pf_server); + + CREATE TABLE protected_titles ( - pt_namespace INTEGER NOT NULL, - pt_title VARCHAR(255) NOT NULL, + pt_namespace INTEGER NOT NULL, + pt_title VARCHAR(255) NOT NULL, pt_user BIGINT NOT NULL DEFAULT 0, -- REFERENCES user(user_id) ON DELETE SET NULL, pt_reason VARCHAR(1024), pt_timestamp TIMESTAMP(3) NOT NULL, - pt_expiry TIMESTAMP(3) , - pt_create_perm VARCHAR(60) NOT NULL DEFAULT '' + pt_expiry TIMESTAMP(3), + pt_create_perm VARCHAR(60) NOT NULL DEFAULT '' ); -CREATE UNIQUE INDEX protected_titles_unique ON protected_titles(pt_namespace, pt_title); +CREATE UNIQUE INDEX protected_titles_unique + ON protected_titles (pt_namespace, pt_title); CREATE TABLE updatelog ( - ul_key VARCHAR(255) NOT NULL PRIMARY KEY + ul_key VARCHAR(255) NOT NULL + PRIMARY KEY ); + CREATE TABLE category ( - cat_id INTEGER NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), - --PRIMARY KEY DEFAULT nextval('category_id_seq'), - cat_title VARCHAR(255) NOT NULL, - cat_pages INTEGER NOT NULL DEFAULT 0, - cat_subcats INTEGER NOT NULL DEFAULT 0, - cat_files INTEGER NOT NULL DEFAULT 0, - cat_hidden SMALLINT NOT NULL DEFAULT 0 + cat_id INTEGER NOT NULL + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + cat_title VARCHAR(255) NOT NULL, + cat_pages INTEGER NOT NULL DEFAULT 0, + cat_subcats INTEGER NOT NULL DEFAULT 0, + cat_files INTEGER NOT NULL DEFAULT 0, + cat_hidden SMALLINT NOT NULL DEFAULT 0 ); -CREATE UNIQUE INDEX category_title ON category(cat_title); -CREATE INDEX category_pages ON category(cat_pages); +CREATE UNIQUE INDEX category_title + ON category (cat_title); +CREATE INDEX category_pages + ON category (cat_pages); + --- added for 1.15 -- A table to track tags for revisions, logs and recent changes. CREATE TABLE change_tag ( - ct_rc_id INTEGER, - ct_log_id INTEGER, - ct_rev_id INTEGER, - ct_tag varchar(255) NOT NULL, - ct_params CLOB(64K) INLINE LENGTH 4096 -); -CREATE UNIQUE INDEX change_tag_rc_tag ON change_tag (ct_rc_id,ct_tag); -CREATE UNIQUE INDEX change_tag_log_tag ON change_tag (ct_log_id,ct_tag); -CREATE UNIQUE INDEX change_tag_rev_tag ON change_tag (ct_rev_id,ct_tag); + ct_rc_id INTEGER, + ct_log_id INTEGER, + ct_rev_id INTEGER, + ct_tag VARCHAR(255) NOT NULL, + ct_params CLOB(64K) INLINE LENGTH 4096 +); +CREATE UNIQUE INDEX change_tag_rc_tag + ON change_tag (ct_rc_id, ct_tag); +CREATE UNIQUE INDEX change_tag_log_tag + ON change_tag (ct_log_id, ct_tag); +CREATE UNIQUE INDEX change_tag_rev_tag + ON change_tag (ct_rev_id, ct_tag); -- Covering index, so we can pull all the info only out of the index. -CREATE INDEX change_tag_tag_id ON change_tag (ct_tag,ct_rc_id,ct_rev_id,ct_log_id); +CREATE INDEX change_tag_tag_id + ON change_tag (ct_tag, ct_rc_id, ct_rev_id, ct_log_id); + -- Rollup table to pull a LIST of tags simply CREATE TABLE tag_summary ( - ts_rc_id INTEGER, + ts_rc_id INTEGER, ts_log_id INTEGER, ts_rev_id INTEGER, - ts_tags CLOB(64K) INLINE LENGTH 4096 NOT NULL + ts_tags CLOB(64K) INLINE LENGTH 4096 NOT NULL ); -CREATE UNIQUE INDEX tag_summary_rc_id ON tag_summary (ts_rc_id); -CREATE UNIQUE INDEX tag_summary_log_id ON tag_summary (ts_log_id); -CREATE UNIQUE INDEX tag_summary_rev_id ON tag_summary (ts_rev_id); +CREATE UNIQUE INDEX tag_summary_rc_id + ON tag_summary (ts_rc_id); +CREATE UNIQUE INDEX tag_summary_log_id + ON tag_summary (ts_log_id); +CREATE UNIQUE INDEX tag_summary_rev_id + ON tag_summary (ts_rev_id); + CREATE TABLE valid_tag ( - vt_tag varchar(255) NOT NULL PRIMARY KEY + vt_tag VARCHAR(255) NOT NULL + PRIMARY KEY ); + + -- -- User preferences and perhaps other fun stuff. :) -- Replaces the old user.user_options blob, with a couple nice properties: -- --- 1) We only store non-default settings, so changes to the defaults +-- 1) We only store non-default settings, so changes to the DEFAULTs -- are now reflected for everybody, not just new accounts. -- 2) We can more easily do bulk lookups, statistics, or modifications of -- saved options since it's a sane table structure. -- CREATE TABLE user_properties ( -- Foreign key to user.user_id - up_user BIGINT NOT NULL, - + up_user BIGINT NOT NULL, -- Name of the option being saved. This is indexed for bulk lookup. - up_property VARCHAR(32) FOR BIT DATA NOT NULL, - + up_property VARCHAR(255) FOR BIT DATA NOT NULL, -- Property value as a string. - up_value CLOB(64K) INLINE LENGTH 4096 + up_value CLOB(64K) INLINE LENGTH 4096 ); -CREATE UNIQUE INDEX user_properties_user_property ON user_properties (up_user,up_property); -CREATE INDEX user_properties_property ON user_properties (up_property); +CREATE UNIQUE INDEX user_properties_user_property + ON user_properties (up_user, up_property); +CREATE INDEX user_properties_property + ON user_properties (up_property); CREATE TABLE log_search ( -- The type of ID (rev ID, log ID, rev TIMESTAMP(3), username) - ls_field VARCHAR(32) FOR BIT DATA NOT NULL, + ls_field VARCHAR(32) FOR BIT DATA NOT NULL, -- The value of the ID - ls_value varchar(255) NOT NULL, + ls_value VARCHAR(255) NOT NULL, -- Key to log_id - ls_log_id BIGINT NOT NULL default 0 + ls_log_id BIGINT NOT NULL DEFAULT 0 ); -CREATE UNIQUE INDEX ls_field_val ON log_search (ls_field,ls_value,ls_log_id); -CREATE INDEX ls_log_id ON log_search (ls_log_id); +CREATE UNIQUE INDEX ls_field_val + ON log_search (ls_field, ls_value, ls_log_id); +CREATE INDEX ls_log_id + ON log_search (ls_log_id); + + -- Table for storing localisation data CREATE TABLE l10n_cache ( -- Language code - lc_lang VARCHAR(32) NOT NULL, + lc_lang VARCHAR(32) NOT NULL, -- Cache key - lc_key VARCHAR(255) NOT NULL, + lc_key VARCHAR(255) NOT NULL, -- Value - lc_value CLOB(16M) INLINE LENGTH 4096 NOT NULL + lc_value CLOB(16M) INLINE LENGTH 4096 NOT NULL ); -CREATE INDEX lc_lang_key ON l10n_cache (lc_lang, lc_key); +CREATE INDEX lc_lang_key + ON l10n_cache (lc_lang, lc_key); -CREATE TABLE "MSG_RESOURCE_LINKS" -( -"MRL_RESOURCE" VARCHAR(255) FOR BIT DATA NOT NULL , -"MRL_MESSAGE" VARCHAR(255) FOR BIT DATA NOT NULL -) -; -CREATE UNIQUE INDEX "UQ61_MSG_RESOURCE_LINKS" ON "MSG_RESOURCE_LINKS" +CREATE TABLE msg_resource_links ( -"MRL_MESSAGE", -"MRL_RESOURCE" -) -ALLOW REVERSE SCANS -; + mrl_resource VARCHAR(255) FOR BIT DATA NOT NULL, + mrl_message VARCHAR(255) FOR BIT DATA NOT NULL +); +CREATE UNIQUE INDEX uq61_msg_resource_links + ON msg_resource_links (mrl_message, mrl_resource); +-- All DB2 indexes DEFAULT to allowing reverse scans -CREATE TABLE "MSG_RESOURCE" -( -"MR_RESOURCE" VARCHAR(255) FOR BIT DATA NOT NULL , -"MR_LANG" VARCHAR(32) FOR BIT DATA NOT NULL , -"MR_BLOB" BLOB NOT NULL , -"MR_TIMESTAMP" TIMESTAMP(3) NOT NULL -) -; -CREATE UNIQUE INDEX "UQ81_MSG_RESOURCE" ON "MSG_RESOURCE" + +CREATE TABLE msg_resource ( -"MR_RESOURCE" -,"MR_LANG" -) -ALLOW REVERSE SCANS -; + mr_resource VARCHAR(255) FOR BIT DATA NOT NULL, + mr_lang VARCHAR(32) FOR BIT DATA NOT NULL, + mr_blob CLOB(64K) INLINE LENGTH 4096 NOT NULL, + mr_timestamp TIMESTAMP(3) NOT NULL +); +CREATE UNIQUE INDEX uq81_msg_resource + ON msg_resource (mr_resource, mr_lang); +-- All DB2 indexes DEFAULT to allowing reverse scans + + + +CREATE TABLE module_deps ( + md_module VARCHAR(255) FOR BIT DATA NOT NULL, + md_skin VARCHAR(32) FOR BIT DATA NOT NULL, + md_deps CLOB(16M) INLINE LENGTH 4096 NOT NULL +); +CREATE UNIQUE INDEX uq96_module_deps + ON module_deps (md_module, md_skin); +-- All DB2 indexes DEFAULT to allowing reverse scans -CREATE TABLE "MODULE_DEPS" ( -"MD_MODULE" VARCHAR(255) FOR BIT DATA NOT NULL , -"MD_SKIN" VARCHAR(32) FOR BIT DATA NOT NULL , -"MD_DEPS" CLOB(16M) INLINE LENGTH 4096 NOT NULL -) -; -CREATE UNIQUE INDEX "UQ96_MODULE_DEPS" ON "MODULE_DEPS" -( -"MD_MODULE" -,"MD_SKIN" -) -ALLOW REVERSE SCANS -; -CREATE TABLE "IWLINKS" +CREATE TABLE iwlinks ( -"IWL_FROM" INT NOT NULL , -"IWL_PREFIX" VARCHAR(20) FOR BIT DATA NOT NULL , -"IWL_TITLE" VARCHAR(255) FOR BIT DATA NOT NULL -) -; + iwl_from INTEGER NOT NULL, + iwl_prefix VARCHAR(20) FOR BIT DATA NOT NULL, + iwl_title VARCHAR(255) FOR BIT DATA NOT NULL +); + + + +-- +-- Store information about newly uploaded files before they're +-- moved into the actual filestore +-- +CREATE TABLE uploadstash ( + us_id BIGINT NOT NULL + PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY (START WITH 1), + -- the user who uploaded the file. + us_user BIGINT NOT NULL, + -- file key. this is how applications actually search for the file. + -- this might go away, or become the primary key. + us_key VARCHAR(255) NOT NULL, + -- the original path + us_orig_path VARCHAR(255) NOT NULL, + -- the temporary path at which the file is actually stored + us_path VARCHAR(255) NOT NULL, + -- which type of upload the file came from (sometimes) + us_source_type VARCHAR(50), + -- the date/time on which the file was added + us_timestamp TIMESTAMP(3) NOT NULL, + us_status VARCHAR(50) NOT NULL, + -- file properties from File::getPropsFromPath. these may prove unnecessary. + -- + us_size BIGINT NOT NULL, + -- this hash comes from File::sha1Base36(), and is 31 characters + us_sha1 VARCHAR(31) NOT NULL, + us_mime VARCHAR(255), + -- Media type as defined by the MEDIATYPE_xxx constants, should duplicate definition in the image table + us_media_type VARCHAR(30) + CONSTRAINT my_constraint + CHECK ( + us_media_type in ( + 'UNKNOWN', 'BITMAP', 'DRAWING', 'AUDIO', 'VIDEO', 'MULTIMEDIA', + 'OFFICE', 'TEXT', 'EXECUTABLE', 'ARCHIVE' + ) + ) DEFAULT NULL, + -- image-specific properties + us_image_width BIGINT, + us_image_height BIGINT, + us_image_bits INTEGER +); +-- sometimes there's a delete for all of a user's stuff. +CREATE INDEX us_user + ON uploadstash (us_user); +-- pick out files by key, enforce key UNIQUEness +CREATE UNIQUE INDEX us_key + ON uploadstash (us_key); +-- the abandoned upload cleanup script needs this +CREATE INDEX us_timestamp + ON uploadstash (us_timestamp); + + + +-- Stores the groups the user has once belonged to. +-- The user may still belong these groups. Check user_groups. +CREATE TABLE user_former_groups ( + ufg_user BIGINT NOT NULL DEFAULT 0, + ufg_group VARCHAR(16) FOR BIT DATA NOT NULL +); +CREATE UNIQUE INDEX ufg_user_group + ON user_former_groups (ufg_user, ufg_group); + + + +-- Table for holding configuration changes +CREATE TABLE config ( + cf_name VARCHAR(255) NOT NULL + PRIMARY KEY, + cf_value CLOB(64K) INLINE LENGTH 4096 NOT NULL +); + diff --git a/maintenance/importDump.php b/maintenance/importDump.php index 099b7895..2ad0872f 100644 --- a/maintenance/importDump.php +++ b/maintenance/importDump.php @@ -57,12 +57,13 @@ TEXT; $this->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 @@ <?php /** - * Maintenance script to import all scripts in the MediaWiki namespace from a + * Maintenance script to import all scripts in the MediaWiki namespace from a * local site. + * @file + * @ingroup Maintenance */ + require_once( dirname( __FILE__ ) . '/Maintenance.php' ); class ImportSiteScripts extends Maintenance { @@ -16,22 +19,29 @@ class ImportSiteScripts extends Maintenance { public function execute() { global $wgUser; - $wgUser = User::newFromName( $this->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 @@ -<?php -/** - * Import data from a UseModWiki into a MediaWiki wiki - * 2003-02-09 Brion VIBBER <brion@pobox.com> - * 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 -<?xml version="1.0" encoding="UTF-8" ?> -<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.1/" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.1/ - http://www.mediawiki.org/xml/export-0.1.xsd" - version="0.1" - xml:lang="en"> -<!-- generated by importUseModWiki.php --> - -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 -</mediawiki> - -XML; - } - - private function importPageDirectory( $dir, $prefix = "" ) { - echo "\n<!-- Checking page directory " . $this->xmlCommentSafe( $dir ) . " -->\n"; - $mydir = opendir( $dir ); - while ( $entry = readdir( $mydir ) ) { - $m = array(); - if ( preg_match( '/^(.+)\.db$/', $entry, $m ) ) { - echo $this->importPage( $prefix . $m[1] ); - } else { - if ( is_dir( "$dir/$entry" ) ) { - if ( $entry != '.' && $entry != '..' ) { - $this->importPageDirectory( "$dir/$entry", "$entry/" ); - } - } else { - echo "<!-- File '" . $this->xmlCommentSafe( $entry ) . "' doesn't seem to contain an article. Skipping. -->\n"; - } - } - } - } - - 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 "<!-- skipped a bad old revision -->\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<!-- Importing page " . $this->xmlCommentSafe( $title ) . " -->\n"; - $page = $this->fetchPage( $title ); - - $newtitle = $this->xmlsafe( str_replace( '_', ' ', $this->recodeText( $title ) ) ); - - $munged = $this->mungeFormat( $page->text ); - if ( $munged != $page->text ) { - /** - * Save a *new* revision with the conversion, and put the - * previous last version into the history. - */ - $next = $this->array2object( array( - 'text' => $munged, - 'minor' => 1, - 'username' => 'Conversion script', - 'host' => '127.0.0.1', - 'ts' => time(), - 'summary' => 'link fix', - ) ); - $revisions = array( $page, $next ); - } else { - /** - * Current revision: - */ - $revisions = array( $page ); - } - $xml = <<<XML - <page> - <title>$newtitle</title> - -XML; - - # History - $revisions = array_merge( $revisions, $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 ? '<minor/>' : '' ); - list( /* $userid */ , $username ) = $this->checkUserCache( $rev->username, $rev->host ); - $username = $this->xmlsafe( $this->recodeText( $username ) ); - $timestamp = $this->xmlsafe( $this->timestamp2ISO8601( $rev->ts ) ); - $comment = $this->xmlsafe( $this->recodeText( $rev->summary ) ); - - $xml .= <<<XML - <revision> - <timestamp>$timestamp</timestamp> - <contributor><username>$username</username></contributor> - $minor - <comment>$comment</comment> - <text>$text</text> - </revision> - -XML; - } - $xml .= "</page>\n\n"; - return $xml; - } - - 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>.*?<\\/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 @@ -<?php - -/** - * A script to read a dump of the English Wikipedia from the UseModWiki period, and to - * generate an XML dump in MediaWiki format. - * - * Some relevant code was ported from UseModWiki 0.92. - * - */ - -require_once( dirname( __FILE__ ) . '/Maintenance.php' ); -require_once( dirname( __FILE__ ) .'/../includes/normal/UtfNormalUtil.php' ); - - -class ImportUseModWikipedia extends Maintenance { - var $encodeMap, $decodeMap; - - var $deepRenames = array( - 'JimboWales' => 983862286, - 'TexaS' => 983918410, - 'HistoryOfUnitedStatesTalk' => 984795423, - 'MetallicA' => 985128533, - 'PythagoreanTheorem' => 985225545, - 'TheCanonofScripture' => 985368223, - 'TaoTehChing' => 985368222, - //'TheMostRemarkableFormulaInTheWorld' => 985368221, - 'TheRecorder' => 985368220, - 'GladstoneOregon' => 985368219, - 'PacificBeach' => '?', - 'AaRiver' => '?', - ); - - var $replacements = array(); - - var $renameTextLinksOps = array( - 983846265 => array( - 'TestIgnore' => 'IgnoreTest', - ), - 983848080 => array( - 'UnitedLocomotiveWorks' => 'Atlas Shrugged/United Locomotive Works' - ), - 983856376 => array( - 'WikiPedia' => 'Wikipedia', - ), - 983896152 => array( - 'John_F_Kennedy' => 'John_F._Kennedy', - ), - 983905871 => array( - 'LarrySanger' => 'Larry_Sanger' - ), - 984697068 => array( - 'UnitedStates' => 'United States', - ), - 984792748 => array( - 'LibertarianisM' => 'Libertarianism' - ), - 985327832 => array( - 'AnarchisM' => 'Anarchism', - ), - 985290063 => array( - 'HistoryOfUnitedStatesDiscussion' => 'History_Of_United_States_Discussion' - ), - 985290091 => array( - 'BritishEmpire' => 'British Empire' - ), - /* - 985468958 => array( - 'ScienceFiction' => 'Science fiction', - ),*/ - ); - - /** - * Hack for observed substitution issues - */ - var $skipSelfSubstitution = array( - 'Pythagorean_Theorem', - 'The_Most_Remarkable_Formula_In_The_World', - 'Wine', - ); - - var $unixLineEndingsOps = array( - 987743732 => 'Wikipedia_FAQ' - ); - - var $replacementsDone = array(); - - var $moveLog = array(); - var $moveDests = array(); - var $revId; - - var $rc = array(); - var $textCache = array(); - var $blacklist = array(); - - var $FS, $FS1, $FS2, $FS3; - var $FreeLinkPattern, $UrlPattern, $LinkPattern, $InterLinkPattern; - - var $cp1252Table = array( -0x80 => 0x20ac, -0x81 => 0x0081, -0x82 => 0x201a, -0x83 => 0x0192, -0x84 => 0x201e, -0x85 => 0x2026, -0x86 => 0x2020, -0x87 => 0x2021, -0x88 => 0x02c6, -0x89 => 0x2030, -0x8a => 0x0160, -0x8b => 0x2039, -0x8c => 0x0152, -0x8d => 0x008d, -0x8e => 0x017d, -0x8f => 0x008f, -0x90 => 0x0090, -0x91 => 0x2018, -0x92 => 0x2019, -0x93 => 0x201c, -0x94 => 0x201d, -0x95 => 0x2022, -0x96 => 0x2013, -0x97 => 0x2014, -0x98 => 0x02dc, -0x99 => 0x2122, -0x9a => 0x0161, -0x9b => 0x203a, -0x9c => 0x0153, -0x9d => 0x009d, -0x9e => 0x017e, -0x9f => 0x0178); - - public function __construct() { - parent::__construct(); - $this->addOption( 'datadir', 'the value of $DataDir from wiki.cgi', true, true ); - $this->addOption( 'outfile', 'the name of the output XML file', true, true ); - $this->initLinkPatterns(); - - $this->encodeMap = $this->decodeMap = array(); - - for ($source = 0; $source <= 0xff; $source++) { - if ( isset( $this->cp1252Table[$source] ) ) { - $dest = $this->cp1252Table[$source]; - } else { - $dest = $source; - } - $sourceChar = chr( $source ); - $destChar = codepointToUtf8( $dest ); - $this->encodeMap[$sourceChar] = $destChar; - $this->decodeMap[$destChar] = $sourceChar; - } - } - - function initLinkPatterns() { - # Field separators are used in the URL-style patterns below. - $this->FS = "\xb3"; # The FS character is a superscript "3" - $this->FS1 = $this->FS . "1"; # The FS values are used to separate fields - $this->FS2 = $this->FS . "2"; # in stored hashtables and other data structures. - $this->FS3 = $this->FS . "3"; # The FS character is not allowed in user data. - - $UpperLetter = "[A-Z"; - $LowerLetter = "[a-z"; - $AnyLetter = "[A-Za-z"; - $AnyLetter .= "_0-9"; - $UpperLetter .= "]"; $LowerLetter .= "]"; $AnyLetter .= "]"; - - # Main link pattern: lowercase between uppercase, then anything - $LpA = $UpperLetter . "+" . $LowerLetter . "+" . $UpperLetter - . $AnyLetter . "*"; - # Optional subpage link pattern: uppercase, lowercase, then anything - $LpB = $UpperLetter . "+" . $LowerLetter . "+" . $AnyLetter . "*"; - - # Loose pattern: If subpage is used, subpage may be simple name - $this->LinkPattern = "((?:(?:$LpA)?\\/$LpB)|$LpA)"; - $QDelim = '(?:"")?'; # Optional quote delimiter (not in output) - $this->LinkPattern .= $QDelim; - - # Inter-site convention: sites must start with uppercase letter - # (Uppercase letter avoids confusion with URLs) - $InterSitePattern = $UpperLetter . $AnyLetter . "+"; - $this->InterLinkPattern = "((?:$InterSitePattern:[^\\]\\s\"<>{$this->FS}]+)$QDelim)"; - - $AnyLetter = "[-,. _0-9A-Za-z]"; - $this->FreeLinkPattern = "($AnyLetter+)"; - $this->FreeLinkPattern = "((?:(?:$AnyLetter+)?\\/)?$AnyLetter+)"; - $this->FreeLinkPattern .= $QDelim; - - # Url-style links are delimited by one of: - # 1. Whitespace (kept in output) - # 2. Left or right angle-bracket (< or >) (kept in output) - # 3. Right square-bracket (]) (kept in output) - # 4. A single double-quote (") (kept in output) - # 5. A $FS (field separator) character (kept in output) - # 6. A double double-quote ("") (removed from output) - - $UrlProtocols = "http|https|ftp|afs|news|nntp|mid|cid|mailto|wais|" - . "prospero|telnet|gopher"; - $UrlProtocols .= '|file'; - $this->UrlPattern = "((?:(?:$UrlProtocols):[^\\]\\s\"<>{$this->FS}]+)$QDelim)"; - $ImageExtensions = "(gif|jpg|png|bmp|jpeg)"; - $RFCPattern = "RFC\\s?(\\d+)"; - $ISBNPattern = "ISBN:?([0-9- xX]{10,})"; - } - - function execute() { - $this->articleFileName = '/tmp/importUseMod.' . mt_rand( 0, 0x7ffffff ) . '.tmp'; - $this->patchFileName = '/tmp/importUseMod.' . mt_rand( 0, 0x7ffffff ) . '.tmp'; - $this->dataDir = $this->getOption( 'datadir' ); - $this->outFile = fopen( $this->getOption( 'outfile' ), 'w' ); - if ( !$this->outFile ) { - echo "Unable to open output file\n"; - return 1; - } - $this->writeXmlHeader(); - $this->readRclog(); - $this->writeMoveLog(); - $this->writeRevisions(); - $this->reconcileCurrentRevs(); - $this->writeXmlFooter(); - unlink( $this->articleFileName ); - unlink( $this->patchFileName ); - return 0; - } - - function writeXmlHeader() { - fwrite( $this->outFile, <<<EOT -<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.3/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.3/ http://www.mediawiki.org/xml/export-0.3.xsd" version="0.3" xml:lang="en"> - <siteinfo> - <sitename>Wikipedia</sitename> - <base>http://www.wikipedia.com/</base> - <generator>MediaWiki 1.18alpha importUseModWikipedia.php</generator> - <case>case-sensitive</case> - <namespaces> - <namespace key="0" /> - </namespaces> - </siteinfo> - -EOT - ); - } - - function writeXmlFooter() { - fwrite( $this->outFile, "</mediawiki>\n" ); - } - - function readRclog() { - $rcFile = fopen( "{$this->dataDir}/rclog", 'r' ); - while ( $line = fgets( $rcFile ) ) { - $bits = explode( $this->FS3, $line ); - if ( count( $bits ) !== 7 ) { - echo "Error reading rclog\n"; - return; - } - $params = array( - 'timestamp' => $bits[0], - 'rctitle' => $bits[1], - 'summary' => $bits[2], - 'minor' => $bits[3], - 'host' => $bits[4], - 'kind' => $bits[5], - 'extra' => array() - ); - $extraList = explode( $this->FS2, $bits[6] ); - - for ( $i = 0; $i < count( $extraList ); $i += 2 ) { - $params['extra'][$extraList[$i]] = $extraList[$i + 1]; - } - $this->rc[$params['timestamp']][] = $params; - } - } - - function writeMoveLog() { - $this->moveLog = array(); - $deepRenames = $this->deepRenames; - echo "Calculating move log...\n"; - $this->processDiffFile( array( $this, 'moveLogCallback' ) ); - - // We have the timestamp intervals, now make a guess at the actual timestamp - foreach ( $this->moveLog as $newTitle => $params ) { - // Is there a time specified? - $drTime = false; - if ( isset( $deepRenames[$params['old']] ) ) { - $drTime = $deepRenames[$params['old']]; - if ( $drTime !== '?' ) { - if ( ( !isset( $params['endTime'] ) || $drTime < $params['endTime'] ) - && $drTime > $params['startTime'] ) - { - $this->moveLog[$newTitle]['timestamp'] = $drTime; - $this->moveLog[$newTitle]['deep'] = true; - - echo "{$params['old']} -> $newTitle at $drTime\n"; - unset( $deepRenames[$params['old']] ); - continue; - } else { - echo "WARNING: deep rename time invalid: {$params['old']}\n"; - unset( $deepRenames[$params['old']] ); - } - } - } - - // Guess that it is one second after the last edit to the page before it was moved - $this->moveLog[$newTitle]['timestamp'] = $params['startTime'] + 1; - if ( $drTime === '?' ) { - $this->moveLog[$newTitle]['deep'] = true; - unset( $deepRenames[$params['old']] ); - } - if ( isset( $params['endTime'] ) ) { - $this->printLatin1( "{$params['old']} -> $newTitle between " . - "{$params['startTime']} and {$params['endTime']}\n" ); - } else { - $this->printLatin1( "{$params['old']} -> $newTitle after " . - "{$params['startTime']}\n" ); - } - } - - // Write the move log to the XML file - $id = 1; - foreach ( $this->moveLog as $newTitle => $params ) { - $out = "<logitem>\n" . - $this->element( 'id', $id++ ) . - $this->element( 'timestamp', wfTimestamp( TS_ISO_8601, $params['timestamp'] ) ) . - "<contributor>\n" . - $this->element( 'username', 'UseModWiki admin' ) . - "</contributor>" . - $this->element( 'type', 'move' ) . - $this->element( 'action', 'move' ) . - $this->element( 'logtitle', $params['old'] ) . - "<params xml:space=\"preserve\">" . - htmlspecialchars( $this->encode( "{$newTitle}\n1" ) ) . - "</params>\n" . - "</logitem>\n"; - fwrite( $this->outFile, $out ); - } - - // Check for remaining deep rename entries - if ( $deepRenames ) { - echo "WARNING: the following entries in \$this->deepRenames are " . - "invalid, since no such move exists:\n" . - implode( "\n", array_keys( $deepRenames ) ) . - "\n\n"; - } - - } - - function element( $name, $value ) { - return "<$name>" . htmlspecialchars( $this->encode( $value ) ) . "</$name>\n"; - } - - function moveLogCallback( $entry ) { - $rctitle = $entry['rctitle']; - $title = $entry['title']; - $this->moveDests[$rctitle] = $title; - - if ( $rctitle === $title ) { - if ( isset( $this->moveLog[$rctitle] ) - && !isset( $this->moveLog[$rctitle]['endTime'] ) ) - { - // This is the latest time that the page could have been moved - $this->moveLog[$rctitle]['endTime'] = $entry['timestamp']; - } - } else { - if ( !isset( $this->moveLog[$rctitle] ) ) { - // Initialise the move log entry - $this->moveLog[$rctitle] = array( - 'old' => $title - ); - } - // Update the earliest time the page could have been moved - $this->moveLog[$rctitle]['startTime'] = $entry['timestamp']; - } - } - - function writeRevisions() { - $this->numGoodRevs = 0; - $this->revId = 1; - $this->processDiffFile( array( $this, 'revisionCallback' ) ); - echo "\n\nImported {$this->numGoodRevs} out of {$this->numRevs}\n"; - } - - function revisionCallback( $params ) { - $title = $params['rctitle']; - $editTime = $params['timestamp']; - - if ( isset( $this->blacklist[$title] ) ) { - return; - } - $this->doPendingOps( $editTime ); - - $origText = $this->getText( $title ); - $text = $this->patch( $origText, $params['diff'] ); - if ( $text === false ) { - echo "$editTime $title attempting resolution...\n"; - $linkSubstitutes = $this->resolveFailedDiff( $origText, $params['diff'] ); - if ( !$linkSubstitutes ) { - $this->printLatin1( "$editTime $title DIFF FAILED\n" ); - $this->blacklist[$title] = true; - return; - } - $this->printLatin1( "$editTime $title requires substitutions:\n" ); - $time = $editTime - 1; - foreach ( $linkSubstitutes as $old => $new ) { - $this->printLatin1( "SUBSTITUTE $old -> $new\n" ); - $this->renameTextLinks( $old, $new, $time-- ); - } - $origText = $this->getText( $title ); - $text = $this->patch( $origText, $params['diff'] ); - if ( $text === false ) { - $this->printLatin1( "$editTime $title STILL FAILS!\n" ); - $this->blacklist[$title] = true; - return; - } - - echo "\n"; - } - - $params['text'] = $text; - $this->saveRevision( $params ); - $this->numGoodRevs++; - #$this->printLatin1( "$editTime $title\n" ); - } - - function doPendingOps( $editTime ) { - foreach ( $this->moveLog as $newTitle => $entry ) { - if ( $entry['timestamp'] <= $editTime ) { - unset( $this->moveLog[$newTitle] ); - if ( isset( $entry['deep'] ) ) { - $this->renameTextLinks( $entry['old'], $newTitle, $entry['timestamp'] ); - } - } - } - - foreach ( $this->renameTextLinksOps as $renameTime => $replacements ) { - if ( $editTime >= $renameTime ) { - foreach ( $replacements as $old => $new ) { - $this->printLatin1( "SUBSTITUTE $old -> $new\n" ); - $this->renameTextLinks( $old, $new, $renameTime ); - } - unset( $this->renameTextLinksOps[$renameTime] ); - } - } - - foreach ( $this->unixLineEndingsOps as $fixTime => $title ) { - if ( $editTime >= $fixTime ) { - $this->printLatin1( "$fixTime $title FIXING LINE ENDINGS\n" ); - $text = $this->getText( $title ); - $text = str_replace( "\r", '', $text ); - $this->saveRevision( array( - 'rctitle' => $title, - 'timestamp' => $fixTime, - 'extra' => array( 'name' => 'UseModWiki admin' ), - 'text' => $text, - 'summary' => 'Fixing line endings', - ) ); - unset( $this->unixLineEndingsOps[$fixTime] ); - } - } - } - - function patch( $source, $diff ) { - file_put_contents( $this->articleFileName, $source ); - file_put_contents( $this->patchFileName, $diff ); - $error = wfShellExec( - wfEscapeShellArg( - 'patch', - '-n', - '-r', '-', - '--no-backup-if-mismatch', - '--binary', - $this->articleFileName, - $this->patchFileName - ) . ' 2>&1', - $status - ); - $text = file_get_contents( $this->articleFileName ); - if ( $status || $text === false ) { - return false; - } else { - return $text; - } - } - - function resolveFailedDiff( $origText, $diff ) { - $context = array(); - $diffLines = explode( "\n", $diff ); - for ( $i = 0; $i < count( $diffLines ); $i++ ) { - $diffLine = $diffLines[$i]; - if ( !preg_match( '/^(\d+)(?:,\d+)?[acd]\d+(?:,\d+)?$/', $diffLine, $m ) ) { - continue; - } - - $sourceIndex = intval( $m[1] ); - $i++; - while ( $i < count( $diffLines ) && substr( $diffLines[$i], 0, 1 ) === '<' ) { - $context[$sourceIndex - 1] = substr( $diffLines[$i], 2 ); - $sourceIndex++; - $i++; - } - $i--; - } - - $changedLinks = array(); - $origLines = explode( "\n", $origText ); - foreach ( $context as $i => $contextLine ) { - $origLine = isset( $origLines[$i] ) ? $origLines[$i] : ''; - if ( $contextLine === $origLine ) { - continue; - } - $newChanges = $this->resolveTextChange( $origLine, $contextLine ); - if ( is_array( $newChanges ) ) { - $changedLinks += $newChanges; - } else { - echo "Resolution failure on line " . ( $i + 1 ) . "\n"; - $this->printLatin1( $newChanges ); - } - } - - return $changedLinks; - } - - function resolveTextChange( $source, $dest ) { - $changedLinks = array(); - $sourceLinks = $this->getLinkList( $source ); - $destLinks = $this->getLinkList( $dest ); - $newLinks = array_diff( $destLinks, $sourceLinks ); - $removedLinks = array_diff( $sourceLinks, $destLinks ); - - // Match up the removed links with the new links - foreach ( $newLinks as $newLink ) { - $minDistance = 100000000; - $bestRemovedLink = false; - foreach ( $removedLinks as $removedLink ) { - $editDistance = levenshtein( $newLink, $removedLink ); - if ( $editDistance < $minDistance ) { - $minDistance = $editDistance; - $bestRemovedLink = $removedLink; - } - } - if ( $bestRemovedLink !== false ) { - $changedLinks[$bestRemovedLink] = $newLink; - $newLinks = array_diff( $newLinks, array( $newLink ) ); - $removedLinks = array_diff( $removedLinks, array( $bestRemovedLink ) ); - } - } - - $proposal = $source; - foreach ( $changedLinks as $removedLink => $newLink ) { - $proposal = $this->substituteTextLinks( $removedLink, $newLink, $proposal ); - } - if ( $proposal !== $dest ) { - // Resolution failed - $msg = "Source line: $source\n" . - "Source links: " . implode( ', ', $sourceLinks ) . "\n" . - "Context line: $dest\n" . - "Context links: " . implode( ', ', $destLinks ) . "\n" . - "Proposal: $proposal\n"; - return $msg; - } - return $changedLinks; - } - - function processDiffFile( $callback ) { - $diffFile = fopen( "{$this->dataDir}/diff_log", 'r' ); - - $delimiter = "------\n"; - file_put_contents( $this->articleFileName, "Describe the new page here.\n" ); - - $line = fgets( $diffFile ); - $lineNum = 1; - if ( $line !== $delimiter ) { - echo "Invalid diff file\n"; - return false; - } - $lastReportLine = 0; - $this->numRevs = 0; - - while ( true ) { - $line = fgets( $diffFile ); - $lineNum++; - if ( $line === false ) { - break; - } - if ( $lineNum > $lastReportLine + 1000 ) { - $lastReportLine = $lineNum; - fwrite( STDERR, "$lineNum \r" ); - fflush( STDERR ); - } - $line = trim( $line ); - if ( !preg_match( '/^([^|]+)\|(\d+)$/', $line, $matches ) ) { - echo "Invalid header on line $lineNum\n"; - return true; - } - list( , $title, $editTime ) = $matches; - - $diff = ''; - $diffStartLine = $lineNum; - while ( true ) { - $line = fgets( $diffFile ); - $lineNum++; - if ( $line === $delimiter ) { - break; - } - if ( $line === false ) { - break 2; - } - $diff .= $line; - } - - $this->numRevs++; - - if ( !isset( $this->rc[$editTime] ) ) { - $this->printLatin1( "$editTime $title DELETED, skipping\n" ); - continue; - } - - if ( count( $this->rc[$editTime] ) == 1 ) { - $params = $this->rc[$editTime][0]; - } else { - $params = false; - $candidates = ''; - foreach ( $this->rc[$editTime] as $rc ) { - if ( $rc['rctitle'] === $title ) { - $params = $rc; - break; - } - if ( $candidates === '' ) { - $candidates = $rc['rctitle']; - } else { - $candidates .= ', ' . $rc['rctitle']; - } - } - if ( !$params ) { - $this->printLatin1( "$editTime $title ERROR cannot resolve rclog\n" ); - $this->printLatin1( "$editTime $title CANDIDATES: $candidates\n" ); - continue; - } - } - $params['diff'] = $diff; - $params['title'] = $title; - $params['diffStartLine'] = $diffStartLine; - call_user_func( $callback, $params ); - } - echo "\n"; - - if ( !feof( $diffFile ) ) { - echo "Stopped at line $lineNum\n"; - } - return true; - } - - function reconcileCurrentRevs() { - foreach ( $this->textCache as $title => $text ) { - $fileName = "{$this->dataDir}/page/"; - if ( preg_match( '/^[A-Z]/', $title, $m ) ) { - $fileName .= $m[0]; - } else { - $fileName .= 'other'; - } - $fileName .= "/$title.db"; - - if ( !file_exists( $fileName ) ) { - $this->printLatin1( "ERROR: Cannot find page file for {$title}\n" ); - continue; - } - - $fileContents = file_get_contents( $fileName ); - $page = $this->unserializeUseMod( $fileContents, $this->FS1 ); - $section = $this->unserializeUseMod( $page['text_default'], $this->FS2 ); - $data = $this->unserializeUseMod( $section['data'], $this->FS3 ); - $pageText = $data['text']; - if ( $text !== $pageText ) { - $substs = $this->resolveTextChange( $text, $pageText ); - if ( is_array( $substs ) ) { - foreach ( $substs as $source => $dest ) { - if ( isset( $this->moveLog[$dest] ) ) { - $this->printLatin1( "ERROR: need deep rename: $source\n" ); - } else { - $this->printLatin1( "ERROR: need substitute: $source -> $dest\n" ); - } - } - } else { - $this->printLatin1( "ERROR: unresolved diff in $title:\n" ); - wfSuppressWarnings(); - $diff = xdiff_string_diff( $text, $pageText ) . ''; - wfRestoreWarnings(); - $this->printLatin1( "$diff\n" ); - } - } - } - } - - function makeTitle( $titleText ) { - return Title::newFromText( $this->encode( $titleText ) ); - } - - function getText( $titleText ) { - if ( !isset( $this->textCache[$titleText] ) ) { - return "Describe the new page here.\n"; - } else { - return $this->textCache[$titleText]; - } - } - - function saveRevision( $params ) { - $this->textCache[$params['rctitle']] = $params['text']; - - $out = "<page>\n" . - $this->element( 'title', $params['rctitle'] ) . - "<revision>\n" . - $this->element( 'id', $this->revId ++ ) . - $this->element( 'timestamp', wfTimestamp( TS_ISO_8601, $params['timestamp'] ) ) . - "<contributor>\n"; - if ( isset( $params['extra']['name'] ) ) { - $out .= $this->element( 'username', $params['extra']['name'] ); - } - if ( isset( $params['extra']['id'] ) ) { - $out .= $this->element( 'id', $params['extra']['id'] ); - } - if ( isset( $params['host'] ) ) { - $out .= $this->element( 'ip', $params['host'] ); - } - $out .= - "</contributor>\n" . - $this->element( 'comment', $params['summary'] ) . - "<text xml:space=\"preserve\">" . - htmlspecialchars( $this->encode( $params['text'] ) ) . - "</text>\n" . - "</revision>\n" . - "</page>\n"; - fwrite( $this->outFile, $out ); - } - - function renameTextLinks( $old, $new, $timestamp ) { - $newWithUnderscores = $new; - $old = str_replace( '_', ' ', $old ); - $new = str_replace( '_', ' ', $new ); - - foreach ( $this->textCache as $title => $oldText ) { - if ( $newWithUnderscores === $title - && in_array( $title, $this->skipSelfSubstitution ) ) - { - // Hack to make Pythagorean_Theorem etc. work - continue; - } - - $newText = $this->substituteTextLinks( $old, $new, $oldText ); - if ( $oldText !== $newText ) { - $this->saveRevision( array( - 'rctitle' => $title, - 'timestamp' => $timestamp, - 'text' => $newText, - 'extra' => array( 'name' => 'Page move link fixup script' ), - 'summary' => '', - 'minor' => true - ) ); - } - } - } - - function substituteTextLinks( $old, $new, $text ) { - $this->saveUrl = array(); - $this->old = $old; - $this->new = $new; - - $text = str_replace( $this->FS, '', $text ); # Remove separators (paranoia) - $text = preg_replace_callback( '/(<pre>(.*?)<\/pre>)/is', - array( $this, 'storeRaw' ), $text ); - $text = preg_replace_callback( '/(<code>(.*?)<\/code>)/is', - array( $this, 'storeRaw' ), $text ); - $text = preg_replace_callback( '/(<nowiki>(.*?)<\/nowiki>)/s', - array( $this, 'storeRaw' ), $text ); - - $text = preg_replace_callback( "/\[\[{$this->FreeLinkPattern}\|([^\]]+)\]\]/", - array( $this, 'subFreeLink' ), $text ); - $text = preg_replace_callback( "/\[\[{$this->FreeLinkPattern}\]\]/", - array( $this, 'subFreeLink' ), $text ); - $text = preg_replace_callback( "/(\[{$this->UrlPattern}\s+([^\]]+?)\])/", - array( $this, 'storeRaw' ), $text ); - $text = preg_replace_callback( "/(\[{$this->InterLinkPattern}\s+([^\]]+?)\])/", - array( $this, 'storeRaw' ), $text ); - $text = preg_replace_callback( "/(\[?{$this->UrlPattern}\]?)/", - array( $this, 'storeRaw' ), $text ); - $text = preg_replace_callback( "/(\[?{$this->InterLinkPattern}\]?)/", - array( $this, 'storeRaw' ), $text ); - $text = preg_replace_callback( "/{$this->LinkPattern}/", - array( $this, 'subWikiLink' ), $text ); - - $text = preg_replace_callback( "/{$this->FS}(\d+){$this->FS}/", - array( $this, 'restoreRaw' ), $text ); # Restore saved text - return $text; - } - - function getLinkList( $text ) { - $this->saveUrl = array(); - $this->linkList = array(); - - $text = str_replace( $this->FS, '', $text ); # Remove separators (paranoia) - $text = preg_replace_callback( '/(<pre>(.*?)<\/pre>)/is', - array( $this, 'storeRaw' ), $text ); - $text = preg_replace_callback( '/(<code>(.*?)<\/code>)/is', - array( $this, 'storeRaw' ), $text ); - $text = preg_replace_callback( '/(<nowiki>(.*?)<\/nowiki>)/s', - array( $this, 'storeRaw' ), $text ); - - $text = preg_replace_callback( "/\[\[{$this->FreeLinkPattern}\|([^\]]+)\]\]/", - array( $this, 'storeLink' ), $text ); - $text = preg_replace_callback( "/\[\[{$this->FreeLinkPattern}\]\]/", - array( $this, 'storeLink' ), $text ); - $text = preg_replace_callback( "/(\[{$this->UrlPattern}\s+([^\]]+?)\])/", - array( $this, 'storeRaw' ), $text ); - $text = preg_replace_callback( "/(\[{$this->InterLinkPattern}\s+([^\]]+?)\])/", - array( $this, 'storeRaw' ), $text ); - $text = preg_replace_callback( "/(\[?{$this->UrlPattern}\]?)/", - array( $this, 'storeRaw' ), $text ); - $text = preg_replace_callback( "/(\[?{$this->InterLinkPattern}\]?)/", - array( $this, 'storeRaw' ), $text ); - $text = preg_replace_callback( "/{$this->LinkPattern}/", - array( $this, 'storeLink' ), $text ); - - return $this->linkList; - } - - function storeRaw( $m ) { - $this->saveUrl[] = $m[1]; - return $this->FS . (count( $this->saveUrl ) - 1) . $this->FS; - } - - function subFreeLink( $m ) { - $link = $m[1]; - if ( isset( $m[2] ) ) { - $name = $m[2]; - } else { - $name = ''; - } - $oldlink = $link; - $link = preg_replace( '/^\s+/', '', $link ); - $link = preg_replace( '/\s+$/', '', $link ); - if ( $link == $this->old ) { - $link = $this->new; - } else { - $link = $oldlink; # Preserve spaces if no match - } - $link = "[[$link"; - if ( $name !== "" ) { - $link .= "|$name"; - } - $link .= "]]"; - return $this->storeRaw( array( 1 => $link ) ); - } - - function subWikiLink( $m ) { - $link = $m[1]; - if ( $link == $this->old ) { - $link = $this->new; - if ( !preg_match( "/^{$this->LinkPattern}$/", $this->new ) ) { - $link = "[[$link]]"; - } - } - return $this->storeRaw( array( 1 => $link ) ); - } - - function restoreRaw( $m ) { - return $this->saveUrl[$m[1]]; - } - - function storeLink( $m ) { - $this->linkList[] = $m[1]; - return $this->storeRaw( $m ); - } - - function encode( $s ) { - return strtr( $s, $this->encodeMap ); - } - - function decode( $s ) { - return strtr( $s, $this->decodeMap ); - } - - function printLatin1( $s ) { - echo $this->encode( $s ); - } - - function unserializeUseMod( $s, $sep ) { - $parts = explode( $sep, $s ); - $result = array(); - for ( $i = 0; $i < count( $parts ); $i += 2 ) { - $result[$parts[$i]] = $parts[$i+1]; - } - return $result; - } -} - -$maintClass = 'ImportUseModWikipedia'; -require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/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 <avarab@gmail.com> - * @author Ashar Voultoiz <hashar at free dot fr> + * @author Antoine Musso <hashar at free dot fr> */ /** 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 @@ -<?php -/** - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - * @ingroup MaintenanceLanguage - */ - -define( 'MEDIAWIKI', 1 ); -define( 'NOT_REALLY_MEDIAWIKI', 1 ); - -class Language { } -foreach ( glob( 'Language*.php' ) as $file ) { - if ( $file != 'Language.php' ) { - require_once( $file ); - } -} - -$removedFunctions = array( 'date', 'time', 'timeanddate', 'formatMonth', 'formatDay', - 'getMonthName', 'getMonthNameGen', 'getMonthAbbreviation', 'getWeekdayName', - 'userAdjust', 'dateFormat', 'timeSeparator', 'timeDateSeparator', 'timeBeforeDate', - 'monthByLatinNumber', 'getSpecialMonthName', - - 'commafy' -); - -$numRemoved = 0; -$total = 0; -$classes = get_declared_classes(); -ksort( $classes ); -foreach ( $classes as $class ) { - if ( !preg_match( '/^Language/', $class ) || $class == 'Language' || $class == 'LanguageConverter' ) { - continue; - } - - print "$class\n"; - $methods = get_class_methods( $class ); - print_r( $methods ); - - if ( !count( array_diff( $methods, $removedFunctions ) ) ) { - print "removed\n"; - $numRemoved++; - } - $total++; - print "\n"; -} - -print "$numRemoved will be removed out of $total\n"; - - diff --git a/maintenance/language/generateCollationData.php b/maintenance/language/generateCollationData.php index 2c3ffedc..e5ce5c87 100644 --- a/maintenance/language/generateCollationData.php +++ b/maintenance/language/generateCollationData.php @@ -1,4 +1,8 @@ <?php +/** + * @ingroup Maintenance + * @file + */ require_once( dirname( __FILE__ ) .'/../Maintenance.php' ); @@ -12,7 +16,7 @@ class GenerateCollationData extends Maintenance { /** The primary weights, indexed by codepoint */ var $weights; - /** + /** * A hashtable keyed by codepoint, where presence indicates that a character * has a decomposition mapping. This makes it non-preferred for group header * selection. @@ -30,7 +34,7 @@ class GenerateCollationData extends Maintenance { public function __construct() { parent::__construct(); $this->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 <avarab@gmail.com> - * @author Ashar Voultoiz <hashar at free dot fr> + * @author Antoine Musso <hashar at free dot fr> * * 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 @@ +<?php +/** + * @file + * @ingroup LockManager Maintenance + */ + +/** + * This code should not require MediaWiki setup or PHP files. + */ +if ( php_sapi_name() !== 'cli' ) { + die( "This is not a valid entry point.\n" ); +} +error_reporting( E_ALL ); + +// Run the server... +set_time_limit( 0 ); +LockServerDaemon::init( + getopt( '', array( + 'address:', 'port:', 'authKey:', + 'lockTimeout::', 'maxClients::', 'maxBacklog::', 'maxLocks::', + ) ) +)->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 <address> --port <port> --authkey <key> " . + "[--lockTimeout <seconds>] " . + "[--maxLocks <integer>] [--maxClients <integer>] [--maxBacklog <integer>]" + ); + } + } + 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 ); // <session, key, command, type, values> + 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 <hashar at free dot fr> + * @author Antoine Musso <hashar at free dot fr> * @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 <<<END +Usage: php mwdocgen.php [<command>] [<options>] + +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 <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 <dir> 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 @@ -<?php -/** - * Wikimedia specific - * - * This script generates SQL used to update MySQL users on a hardcoded - * list of hosts. It takes care of setting the wikiuser for every - * database as well as setting up wikiadmin. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @todo document - * @file - * @ingroup Maintenance - * @ingroup Wikimedia - */ - -/** */ -$wikiuser_pass = `wikiuser_pass`; -$wikiadmin_pass = `wikiadmin_pass`; -$nagios_pass = `nagios_sql_pass`; - -$hosts = array( - 'localhost', - '10.0.%', - '66.230.200.%', - '208.80.152.%', -); - -$databases = array( - '%wik%', - 'centralauth', -); - -print "/*!40100 set old_passwords=1 */;\n"; -print "/*!40100 set global old_passwords=1 */;\n"; - -foreach ( $hosts as $host ) { - print "--\n-- $host\n--\n"; - print "\n-- wikiuser\n\n"; - print "GRANT REPLICATION CLIENT,PROCESS ON *.* TO 'wikiuser'@'$host' IDENTIFIED BY '$wikiuser_pass';\n"; - print "GRANT ALL PRIVILEGES ON `boardvote%`.* TO 'wikiuser'@'$host' IDENTIFIED BY '$wikiuser_pass';\n"; - foreach ( $databases as $db ) { - print "GRANT SELECT, INSERT, UPDATE, DELETE ON `$db`.* TO 'wikiuser'@'$host' IDENTIFIED BY '$wikiuser_pass';\n"; - } - - print "\n-- wikiadmin\n\n"; - print "GRANT PROCESS, REPLICATION CLIENT ON *.* TO 'wikiadmin'@'$host' IDENTIFIED BY '$wikiadmin_pass';\n"; - print "GRANT ALL PRIVILEGES ON `boardvote%`.* TO wikiadmin@'$host' IDENTIFIED BY '$wikiadmin_pass';\n"; - foreach ( $databases as $db ) { - print "GRANT ALL PRIVILEGES ON `$db`.* TO wikiadmin@'$host' IDENTIFIED BY '$wikiadmin_pass';\n"; - } - print "\n-- nagios\n\n"; - print "GRANT REPLICATION CLIENT ON *.* TO 'nagios'@'$host' IDENTIFIED BY '$nagios_pass';\n"; - - print "\n"; -} - diff --git a/maintenance/parse.php b/maintenance/parse.php new file mode 100644 index 00000000..876f28e9 --- /dev/null +++ b/maintenance/parse.php @@ -0,0 +1,110 @@ +<?php +/** + * CLI script to easily parse some wikitext. + * Wikitext can be given by stdin or using a file. The wikitext will be parsed + * using 'CLIParser' as a title. This can be overriden with --title option. + * + * Example1: + * @code + * $ php parse.php --title foo + * ''[[foo]]''^D + * <p><i><strong class="selflink">foo</strong></i> + * </p> + * @endcode + * + * Example2: + * @code + * $ echo "'''bold'''" > /tmp/foo.txt + * $ php parse.php /tmp/foo.txt + * <p><b>bold</b> + * </p>$ + * @endcode + * + * Example3: + * @code + * $ cat /tmp/foo | php parse.php + * <p><b>bold</b> + * </p>$ + * @endcode + * + * @ingroup Maintenance + * @author Antoine Musso <hashar at free dot fr> + * @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 <Your: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/populateSha1.php b/maintenance/populateImageSha1.php index 1ab9109d..4cd2747c 100644 --- a/maintenance/populateSha1.php +++ b/maintenance/populateImageSha1.php @@ -22,7 +22,7 @@ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); -class PopulateSha1 extends Maintenance { +class PopulateImageSha1 extends LoggedUpdateMaintenance { public function __construct() { parent::__construct(); $this->mDescription = "Populate the img_sha1 field"; @@ -31,26 +31,46 @@ class PopulateSha1 extends Maintenance { $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->selectRow( + $res = $dbw->select( 'image', array( 'img_name' ), - array( 'img_name' => $dbw->addQuotes( $file ) ), + array( 'img_name' => $file ), __METHOD__ ); if ( !$res ) { $this->error( "No such file: $file", true ); - return; + return false; } + $this->output( "Populating img_sha1 field for specified files\n" ); } else { - $res = $dbw->select( 'image', array( 'img_name' ), array( 'img_sha1' => '' ), __METHOD__ ); + $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' ) { @@ -66,7 +86,7 @@ class PopulateSha1 extends Maintenance { $numRows = $res->numRows(); $i = 0; foreach ( $res as $row ) { - if ( $i % 100 == 0 ) { + if ( $i % $this->mBatchSize == 0 ) { $this->output( sprintf( "Done %d of %d, %5.3f%% \r", $i, $numRows, $i / $numRows * 100 ) ); wfWaitForSlaves(); } @@ -74,7 +94,7 @@ class PopulateSha1 extends Maintenance { if ( !$file ) { continue; } - $sha1 = File::sha1Base36( $file->getPath() ); + $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 ); @@ -92,8 +112,10 @@ class PopulateSha1 extends Maintenance { } $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 = "PopulateSha1"; +$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 @@ <?php -/* +/** * Makes the required database updates for rev_parent_id * to be of any use. It can be used for some simple tracking * and to find new page edits by users. @@ -24,28 +24,32 @@ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); -class PopulateParentId extends Maintenance { +class PopulateParentId extends LoggedUpdateMaintenance { public function __construct() { parent::__construct(); $this->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 @@ <?php -/* +/** * Populates the rev_len field for old revisions created before MW 1.10. * * This program is free software; you can redistribute it and/or modify @@ -22,29 +22,35 @@ require_once( dirname( __FILE__ ) . '/Maintenance.php' ); -class PopulateRevisionLength extends Maintenance { +class PopulateRevisionLength extends LoggedUpdateMaintenance { public function __construct() { parent::__construct(); - $this->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 @@ +<?php +/** + * Fills the rev_sha1 and ar_sha1 columns of revision + * and archive tables for revisions created before MW 1.19. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @ingroup Maintenance + */ + +require_once( dirname( __FILE__ ) . '/Maintenance.php' ); + +class PopulateRevisionSha1 extends LoggedUpdateMaintenance { + public function __construct() { + parent::__construct(); + $this->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/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 @@ +<?php +/** + * Prune file cache for pages, objects, resources, ect... + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @ingroup Maintenance + */ + +require_once( dirname( __FILE__ ) . '/Maintenance.php' ); + +class PruneFileCache extends Maintenance { + + protected $minSurviveTimestamp; + + public function __construct() { + parent::__construct(); + $this->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 @@ +<?php +/** + * Scans the deletion log and purges affected files within a timeframe. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @ingroup Maintenance + */ + +require_once( dirname( __FILE__ ) . '/Maintenance.php' ); + +class PurgeDeletedFiles extends Maintenance { + public function __construct() { + parent::__construct(); + $this->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 @@ <?php +/** + * @ingroup Maintenance + * @file + */ require( dirname( __FILE__ ) . '/Maintenance.php' ); class PurgeParserCache extends Maintenance { + var $lastProgress; + function __construct() { parent::__construct(); $this->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 @@ <?php +/** + * @ingroup Maintenance Memcached + * @file + */ require_once( dirname( __FILE__ ) . '/commandLine.inc' ); diff --git a/maintenance/reassignEdits.php b/maintenance/reassignEdits.php index bb34e51c..3830fe38 100644 --- a/maintenance/reassignEdits.php +++ b/maintenance/reassignEdits.php @@ -139,8 +139,8 @@ class ReassignEdits extends Maintenance { * i.e. user => 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 <brion at pobox.com> - * @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 @@ -<?php -/** - * Rebuild interwiki table using the file on meta and the language list - * Wikimedia specific! - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - * @todo document - * @ingroup Maintenance - * @ingroup Wikimedia - */ - -require_once( dirname( __FILE__ ) . '/Site.php' ); - -require_once( dirname( __FILE__ ) . '/Maintenance.php' ); - -class RebuildInterwiki extends Maintenance { - public function __construct() { - parent::__construct(); - $this->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 @@ -<?php -/** - * Why yes, this *is* another special-purpose Wikimedia maintenance script! - * Should be fixed up and generalized. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - * @ingroup Maintenance - * @ingroup Wikimedia - */ - -require_once( dirname( __FILE__ ) . '/Maintenance.php' ); - -class RenameWiki extends Maintenance { - public function __construct() { - parent::__construct(); - $this->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 <hashar at free dot fr> + * @author Antoine Musso <hashar at free dot fr> * Based on initStats.php by: * @author Brion Vibber * @author Rob Church <robchur@gmail.com> @@ -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 @@ +<?php + +/** + * @ingroup Testing + * + * Set of classes to help with test output and such. Right now pretty specific + * to the parser tests but could be more useful one day :) + * + * @todo Fixme: Make this more generic + */ + +class AnsiTermColorer { + function __construct() { + } + + /** + * Return ANSI terminal escape code for changing text attribs/color + * + * @param $color String: semicolon-separated list of attribute/color codes + * @return String + */ + public function color( $color ) { + global $wgCommandLineDarkBg; + + $light = $wgCommandLineDarkBg ? "1;" : "0;"; + + return "\x1b[{$light}{$color}m"; + } + + /** + * Return ANSI terminal escape code for restoring default text attributes + * + * @return String + */ + public function reset() { + return $this->color( 0 ); + } +} + +/* A colour-less terminal */ +class DummyTermColorer { + public function color( $color ) { + return ''; + } + + public function reset() { + return ''; + } +} + 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 <hashar at free dot fr> + * @author Antoine Musso <hashar at free dot fr> */ // 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); - |