From f6d65e533c62f6deb21342d4901ece24497b433e Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Thu, 4 Jun 2015 07:31:04 +0200 Subject: Update to MediaWiki 1.25.1 --- maintenance/Doxyfile | 111 +- maintenance/Maintenance.php | 73 +- maintenance/archives/patch-drop-page_counter.sql | 2 + maintenance/archives/patch-drop-ss_total_views.sql | 2 + maintenance/archives/patch-editsummary-length.sql | 11 + maintenance/archives/patch-hitcounter.sql | 9 - .../patch-user-newtalk-userid-unsigned.sql | 1 + maintenance/backupTextPass.inc | 113 +- maintenance/benchmarks/bench_HTTP_HTTPS.php | 2 +- maintenance/cdb.php | 2 + maintenance/checkComposerLockUpToDate.php | 63 + maintenance/checkLess.php | 12 +- maintenance/cleanupBlocks.php | 147 ++ maintenance/cleanupCaps.php | 11 +- maintenance/convertExtensionToRegistration.php | 226 ++ maintenance/dev/includes/router.php | 2 +- maintenance/dictionary/mediawiki.dic | 6 +- maintenance/doMaintenance.php | 36 +- maintenance/dumpIterator.php | 4 +- maintenance/dumpSisterSites.php | 62 - maintenance/dumpTextPass.php | 2 + maintenance/eval.php | 39 +- maintenance/exportSites.php | 54 + maintenance/fetchText.php | 5 +- maintenance/findHooks.php | 56 +- maintenance/findMissingFiles.php | 26 +- maintenance/fixUserRegistration.php | 64 +- maintenance/generateLocalAutoload.php | 25 + maintenance/generateSitemap.php | 23 +- maintenance/importDump.php | 2 +- maintenance/importImages.inc | 4 +- maintenance/importSiteScripts.php | 39 +- maintenance/importSites.php | 52 + maintenance/initSiteStats.php | 12 +- maintenance/interwiki.list | 150 +- maintenance/interwiki.sql | 152 +- maintenance/jsduck/CustomTags.rb | 116 - maintenance/jsduck/categories.json | 11 +- maintenance/jsduck/custom_tags.rb | 120 + maintenance/jsduck/eg-iframe.html | 71 +- maintenance/language/StatOutputs.php | 2 +- maintenance/language/checkLanguage.php | 2 +- maintenance/language/generateCollationData.php | 4 +- maintenance/language/generateNormalizerDataAr.php | 6 +- maintenance/language/generateNormalizerDataMl.php | 6 +- maintenance/language/generateUtf8Case.php | 6 +- maintenance/language/zhtable/Makefile.py | 20 +- maintenance/language/zhtable/README | 24 +- maintenance/language/zhtable/simp2trad.manual | 195 +- .../language/zhtable/simp2trad_noconvert.manual | 143 +- maintenance/language/zhtable/simpphrases.manual | 2104 +-------------- .../language/zhtable/simpphrases_exclude.manual | 11 +- maintenance/language/zhtable/symme_supp.manual | 27 + maintenance/language/zhtable/toCN.manual | 2666 +++++++++++++++++++- maintenance/language/zhtable/toHK.manual | 1418 ++++++++--- maintenance/language/zhtable/toSG.manual | 21 - maintenance/language/zhtable/toSimp.manual | 148 +- maintenance/language/zhtable/toTW.manual | 729 ++++-- maintenance/language/zhtable/toTrad.manual | 367 ++- maintenance/language/zhtable/trad2simp.manual | 733 +++++- .../language/zhtable/trad2simp_noconvert.manual | 9 + maintenance/language/zhtable/tradphrases.manual | 1845 +++++--------- .../language/zhtable/tradphrases_exclude.manual | 349 ++- maintenance/mergeMessageFileList.php | 43 +- maintenance/moveBatch.php | 8 +- maintenance/mssql/tables.sql | 16 - maintenance/mwdoc-filter.php | 29 +- maintenance/mwdocgen.php | 1 + maintenance/namespaceDupes.php | 390 ++- maintenance/oracle/tables.sql | 6 - maintenance/parse.php | 4 +- maintenance/populateBloomCache.php | 78 - maintenance/populateParentId.php | 2 +- maintenance/populateRevisionSha1.php | 4 +- maintenance/postgres/compare_schemas.pl | 1 - maintenance/postgres/tables.sql | 6 - maintenance/preprocessorFuzzTest.php | 2 +- maintenance/purgeChangedFiles.php | 1 - maintenance/purgeChangedPages.php | 1 - maintenance/rebuildLocalisationCache.php | 23 +- maintenance/rebuildSitesCache.php | 68 + maintenance/rebuildtextindex.php | 1 + maintenance/refreshLinks.php | 174 +- maintenance/removeInvalidEmails.php | 78 + maintenance/renderDump.php | 2 +- maintenance/resources/update-oojs-ui.sh | 116 +- maintenance/resources/update-oojs.sh | 4 +- maintenance/runJobs.php | 4 +- maintenance/showJobs.php | 20 +- maintenance/showSiteStats.php | 1 - maintenance/sql.php | 3 +- maintenance/sqlite.inc | 2 +- maintenance/sqlite.php | 6 +- maintenance/sqlite/archives/initial-indexes.sql | 2 - .../sqlite/archives/patch-drop-page_counter.sql | 31 + .../sqlite/archives/patch-drop-ss_admins.sql | 3 +- .../sqlite/archives/patch-drop-ss_total_views.sql | 21 + .../sqlite/archives/patch-editsummary-length.sql | 65 + maintenance/storage/checkStorage.php | 6 +- maintenance/storage/compressOld.php | 16 +- maintenance/storage/moveToExternal.php | 1 - maintenance/storage/recompressTracked.php | 6 +- maintenance/storage/resolveStubs.php | 2 - maintenance/tables.sql | 54 +- maintenance/update.php | 24 +- maintenance/updateArticleCount.php | 8 +- maintenance/updateSpecialPages.php | 12 +- maintenance/validateRegistrationFile.php | 37 + 108 files changed, 8707 insertions(+), 5428 deletions(-) create mode 100644 maintenance/archives/patch-drop-page_counter.sql create mode 100644 maintenance/archives/patch-drop-ss_total_views.sql create mode 100644 maintenance/archives/patch-editsummary-length.sql delete mode 100644 maintenance/archives/patch-hitcounter.sql create mode 100644 maintenance/archives/patch-user-newtalk-userid-unsigned.sql create mode 100644 maintenance/checkComposerLockUpToDate.php create mode 100644 maintenance/cleanupBlocks.php create mode 100644 maintenance/convertExtensionToRegistration.php delete mode 100644 maintenance/dumpSisterSites.php create mode 100644 maintenance/exportSites.php create mode 100644 maintenance/generateLocalAutoload.php create mode 100644 maintenance/importSites.php delete mode 100644 maintenance/jsduck/CustomTags.rb create mode 100644 maintenance/jsduck/custom_tags.rb create mode 100644 maintenance/language/zhtable/symme_supp.manual delete mode 100644 maintenance/language/zhtable/toSG.manual delete mode 100644 maintenance/populateBloomCache.php create mode 100644 maintenance/rebuildSitesCache.php create mode 100644 maintenance/removeInvalidEmails.php create mode 100644 maintenance/sqlite/archives/patch-drop-page_counter.sql create mode 100644 maintenance/sqlite/archives/patch-drop-ss_total_views.sql create mode 100644 maintenance/sqlite/archives/patch-editsummary-length.sql create mode 100644 maintenance/validateRegistrationFile.php (limited to 'maintenance') diff --git a/maintenance/Doxyfile b/maintenance/Doxyfile index ffc8c3b0..1f70f452 100644 --- a/maintenance/Doxyfile +++ b/maintenance/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.7.6.1 +# Doxyfile 1.8.6 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for MediaWiki. @@ -47,31 +47,39 @@ MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 -ALIASES = "type{1}= \1 :" \ - "types{2}= \1 or \2 :" \ - "types{3}= \1 , \2 , or \3 :" \ - "arrayof{2}= Array of \2" \ - "null=\type{Null}" \ - "boolean=\type{Boolean}" \ - "bool=\type{Boolean}" \ - "integer=\type{Integer}" \ - "int=\type{Integer}" \ - "string=\type{String}" \ - "str=\type{String}" \ - "mixed=\type{Mixed}" \ - "access=\par Access:\n" \ - "private=\access private" \ - "protected=\access protected" \ - "public=\access public" \ - "copyright=\note" \ - "license=\note" \ - "codeCoverageIgnore=" +ALIASES = "type{1}= \1 :" \ + "types{2}= \1 or \2 :" \ + "types{3}= \1 , \2 , or \3 :" \ + "arrayof{2}= Array of \2" \ + "null=\type{Null}" \ + "boolean=\type{Boolean}" \ + "bool=\type{Boolean}" \ + "integer=\type{Integer}" \ + "int=\type{Integer}" \ + "string=\type{String}" \ + "str=\type{String}" \ + "mixed=\type{Mixed}" \ + "access=\par Access:\n" \ + "private=\access private" \ + "protected=\access protected" \ + "public=\access public" \ + "copyright=\note" \ + "license=\note" \ + "codeCoverageIgnore=" \ + "codingStandardsIgnoreStart=" \ + "group=" \ + "covers=" \ + "dataProvider=" \ + "expectedException=" \ + "expectedExceptionMessage=" TCL_SUBST = OPTIMIZE_OUTPUT_FOR_C = NO OPTIMIZE_OUTPUT_JAVA = NO OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +AUTOLINK_SUPPORT = YES BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO SIP_SUPPORT = NO @@ -81,13 +89,13 @@ SUBGROUPING = YES INLINE_GROUPED_CLASSES = NO INLINE_SIMPLE_STRUCTS = NO TYPEDEF_HIDES_STRUCT = NO -SYMBOL_CACHE_SIZE = 0 -LOOKUP_CACHE_SIZE = 1 +LOOKUP_CACHE_SIZE = 2 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = YES +EXTRACT_PACKAGE = NO EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO @@ -100,6 +108,7 @@ INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES +SHOW_GROUPED_MEMB_INC = NO FORCE_LOCAL_INCLUDES = NO INLINE_INFO = YES SORT_MEMBER_DOCS = YES @@ -115,14 +124,13 @@ GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES -SHOW_DIRECTORIES = YES SHOW_FILES = YES SHOW_NAMESPACES = NO FILE_VERSION_FILTER = LAYOUT_FILE = CITE_BIB_FILES = #--------------------------------------------------------------------------- -# configuration options related to warning and progress messages +# Configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES @@ -132,7 +140,7 @@ WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- -# configuration options related to the input files +# Configuration options related to the input files #--------------------------------------------------------------------------- INPUT = {{INPUT}} INPUT_ENCODING = UTF-8 @@ -182,7 +190,12 @@ FILE_PATTERNS = *.c \ RECURSIVE = YES EXCLUDE = {{EXCLUDE}} EXCLUDE_SYMLINKS = YES -EXCLUDE_PATTERNS = LocalSettings.php AdminSettings.php StartProfiler.php .svn */.git/* {{EXCLUDE_PATTERNS}} +EXCLUDE_PATTERNS = LocalSettings.php \ + AdminSettings.php \ + StartProfiler.php \ + .svn \ + */.git/* \ + {{EXCLUDE_PATTERNS}} EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * @@ -192,8 +205,9 @@ INPUT_FILTER = "{{INPUT_FILTER}}" FILTER_PATTERNS = FILTER_SOURCE_FILES = NO FILTER_SOURCE_PATTERNS = +USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- -# configuration options related to source browsing +# Configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO @@ -201,16 +215,17 @@ STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES REFERENCES_LINK_SOURCE = YES +SOURCE_TOOLTIPS = YES USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index +# Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = NO COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- -# configuration options related to the HTML output +# Configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html @@ -218,13 +233,14 @@ HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = +HTML_EXTRA_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 +HTML_INDEX_NUM_ENTRIES = 100 GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" DOCSET_BUNDLE_ID = org.doxygen.Project @@ -248,20 +264,26 @@ 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 +ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES USE_MATHJAX = NO +MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = http://www.mathjax.org/mathjax MATHJAX_EXTENSIONS = +MATHJAX_CODEFILE = SEARCHENGINE = YES SERVER_BASED_SEARCH = YES +EXTERNAL_SEARCH = NO +SEARCHENGINE_URL = +SEARCHDATA_FILE = searchdata.xml +EXTERNAL_SEARCH_ID = +EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- -# configuration options related to the LaTeX output +# Configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex @@ -272,6 +294,7 @@ PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = LATEX_FOOTER = +LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO @@ -279,7 +302,7 @@ LATEX_HIDE_INDICES = NO LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain #--------------------------------------------------------------------------- -# configuration options related to the RTF output +# Configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf @@ -288,14 +311,14 @@ RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- -# configuration options related to the man page output +# Configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = {{GENERATE_MAN}} MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- -# configuration options related to the XML output +# Configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml @@ -303,11 +326,16 @@ XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- +GENERATE_DOCBOOK = NO +DOCBOOK_OUTPUT = docbook +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- -# configuration options related to the Perl module output +# Configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO @@ -326,18 +354,20 @@ PREDEFINED = EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- -# Configuration::additions related to external references +# Configuration options related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = {{OUTPUT_DIRECTORY}}/html/tagfile.xml ALLEXTERNALS = NO EXTERNAL_GROUPS = YES +EXTERNAL_PAGES = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = NO MSCGEN_PATH = +DIA_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = {{HAVE_DOT}} DOT_NUM_THREADS = 0 @@ -348,6 +378,7 @@ CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES @@ -360,10 +391,10 @@ INTERACTIVE_SVG = NO DOT_PATH = DOTFILE_DIRS = MSCFILE_DIRS = +DIAFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 1000 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = YES GENERATE_LEGEND = YES DOT_CLEANUP = YES - diff --git a/maintenance/Maintenance.php b/maintenance/Maintenance.php index 8d30df4c..923c5b41 100644 --- a/maintenance/Maintenance.php +++ b/maintenance/Maintenance.php @@ -20,12 +20,10 @@ * @defgroup Maintenance Maintenance */ -// Make sure we're on PHP5.3.2 or better -if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.2' ) < 0 ) { - // We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+ - require_once dirname( __FILE__ ) . '/../includes/PHPVersionError.php'; - wfPHPVersionError( 'cli' ); -} +// Bail on old versions of PHP, or if composer has not been run yet to install +// dependencies. Using dirname( __FILE__ ) here because __DIR__ is PHP5.3+. +require_once dirname( __FILE__ ) . '/../includes/PHPVersionCheck.php'; +wfEntryPointCheck( 'cli' ); /** * @defgroup MaintenanceArchive Maintenance archives @@ -102,7 +100,7 @@ abstract class Maintenance { private $mDependantParameters = array(); /** - * Used by getDD() / setDB() + * Used by getDB() / setDB() * @var DatabaseBase */ private $mDb = null; @@ -446,7 +444,7 @@ abstract class Maintenance { $this->addOption( 'server', "The protocol and server name to use in URLs, e.g. " . "http://en.wikipedia.org. This is sometimes necessary because " . "server name detection may fail in command line scripts.", false, true ); - $this->addOption( 'profiler', 'Set to "text" or "trace" to show profiling output', false, true ); + $this->addOption( 'profiler', 'Profiler output format (usually "text")', false, true ); # Save generic options to display them separately in help $this->mGenericParameters = $this->mParams; @@ -597,6 +595,23 @@ abstract class Maintenance { } } + /** + * Activate the profiler (assuming $wgProfiler is set) + */ + protected function activateProfiler() { + global $wgProfiler; + + $output = $this->getOption( 'profiler' ); + if ( $output && is_array( $wgProfiler ) && isset( $wgProfiler['class'] ) ) { + $class = $wgProfiler['class']; + $profiler = new $class( + array( 'sampling' => 1, 'output' => $output ) + $wgProfiler + ); + $profiler->setTemplated( true ); + Profiler::replaceStubInstance( $profiler ); + } + } + /** * Clear all params and arguments. */ @@ -679,6 +694,9 @@ abstract class Maintenance { } $options[$option] = $param; } + } elseif ( $arg == '-' ) { + # Lonely "-", often used to indicate stdin or stdout. + $args[] = $arg; } elseif ( substr( $arg, 0, 1 ) == '-' ) { # Short options $argLength = strlen( $arg ); @@ -920,26 +938,19 @@ abstract class Maintenance { LBFactory::destroyInstance(); } + // Per-script profiling; useful for debugging + $this->activateProfiler(); + $this->afterFinalSetup(); $wgShowSQLErrors = true; - // @codingStandardsIgnoreStart Allow error supppression. wfSuppressWarnings() - // is not avaiable. + // @codingStandardsIgnoreStart Allow error suppression. wfSuppressWarnings() + // is not available. @set_time_limit( 0 ); // @codingStandardsIgnoreStart $this->adjustMemoryLimit(); - - // Per-script profiling; useful for debugging - $forcedProfiler = $this->getOption( 'profiler' ); - if ( $forcedProfiler === 'text' ) { - Profiler::setInstance( new ProfilerSimpleText( array() ) ); - Profiler::instance()->setTemplated( true ); - } elseif ( $forcedProfiler === 'trace' ) { - Profiler::setInstance( new ProfilerSimpleTrace( array() ) ); - Profiler::instance()->setTemplated( true ); - } } /** @@ -1063,7 +1074,7 @@ abstract class Maintenance { * * @return DatabaseBase */ - protected function &getDB( $db, $groups = array(), $wiki = false ) { + protected function getDB( $db, $groups = array(), $wiki = false ) { if ( is_null( $this->mDb ) ) { return wfGetDB( $db, $groups, $wiki ); } else { @@ -1076,7 +1087,7 @@ abstract class Maintenance { * * @param DatabaseBase $db Database object to be used */ - public function setDB( &$db ) { + public function setDB( $db ) { $this->mDb = $db; } @@ -1084,7 +1095,7 @@ abstract class Maintenance { * Lock the search index * @param DatabaseBase &$db */ - private function lockSearchindex( &$db ) { + private function lockSearchindex( $db ) { $write = array( 'searchindex' ); $read = array( 'page', 'revision', 'text', 'interwiki', 'l10n_cache', 'user' ); $db->lockTables( $read, $write, __CLASS__ . '::' . __METHOD__ ); @@ -1094,7 +1105,7 @@ abstract class Maintenance { * Unlock the tables * @param DatabaseBase &$db */ - private function unlockSearchindex( &$db ) { + private function unlockSearchindex( $db ) { $db->unlockTables( __CLASS__ . '::' . __METHOD__ ); } @@ -1103,7 +1114,7 @@ abstract class Maintenance { * Since the lock is low-priority, queued reads will be able to complete * @param DatabaseBase &$db */ - private function relockSearchindex( &$db ) { + private function relockSearchindex( $db ) { $this->unlockSearchindex( $db ); $this->lockSearchindex( $db ); } @@ -1174,7 +1185,7 @@ abstract class Maintenance { * We default as considering stdin a tty (for nice readline methods) * but treating stout as not a tty to avoid color codes * - * @param int $fd File descriptor + * @param mixed $fd File descriptor * @return bool */ public static function posix_isatty( $fd ) { @@ -1197,7 +1208,13 @@ abstract class Maintenance { } if ( $isatty && function_exists( 'readline' ) ) { - return readline( $prompt ); + $resp = readline( $prompt ); + if ( $resp === null ) { + // Workaround for https://github.com/facebook/hhvm/issues/4776 + return false; + } else { + return $resp; + } } else { if ( $isatty ) { $st = self::readlineEmulation( $prompt ); @@ -1311,7 +1328,7 @@ abstract class LoggedUpdateMaintenance extends Maintenance { } /** - * Message to show the the update log was unable to log the completion of this update + * Message to show that the update log was unable to log the completion of this update * @return string */ protected function updatelogFailedMessage() { diff --git a/maintenance/archives/patch-drop-page_counter.sql b/maintenance/archives/patch-drop-page_counter.sql new file mode 100644 index 00000000..1d8e701b --- /dev/null +++ b/maintenance/archives/patch-drop-page_counter.sql @@ -0,0 +1,2 @@ +-- field is deprecated and no longer updated as of 1.25 +ALTER TABLE /*_*/page DROP COLUMN page_counter; diff --git a/maintenance/archives/patch-drop-ss_total_views.sql b/maintenance/archives/patch-drop-ss_total_views.sql new file mode 100644 index 00000000..00591939 --- /dev/null +++ b/maintenance/archives/patch-drop-ss_total_views.sql @@ -0,0 +1,2 @@ +-- field is deprecated and no longer updated as of 1.24 +ALTER TABLE /*_*/site_stats DROP COLUMN ss_total_views; \ No newline at end of file diff --git a/maintenance/archives/patch-editsummary-length.sql b/maintenance/archives/patch-editsummary-length.sql new file mode 100644 index 00000000..c8ac1adf --- /dev/null +++ b/maintenance/archives/patch-editsummary-length.sql @@ -0,0 +1,11 @@ +ALTER TABLE /*_*/revision MODIFY rev_comment varbinary(767) NOT NULL; +ALTER TABLE /*_*/archive MODIFY ar_comment varbinary(767) NOT NULL; +ALTER TABLE /*_*/image MODIFY img_description varbinary(767) NOT NULL; +ALTER TABLE /*_*/oldimage MODIFY oi_description varbinary(767) NOT NULL; +ALTER TABLE /*_*/filearchive MODIFY fa_description varbinary(767); +ALTER TABLE /*_*/filearchive MODIFY fa_deleted_reason varbinary(767) default ''; +ALTER TABLE /*_*/recentchanges MODIFY rc_comment varbinary(767) NOT NULL default ''; +ALTER TABLE /*_*/logging MODIFY log_comment varbinary(767) NOT NULL default ''; +ALTER TABLE /*_*/ipblocks MODIFY ipb_reason varbinary(767) NOT NULL; +ALTER TABLE /*_*/protected_titles MODIFY pt_reason varbinary(767); + diff --git a/maintenance/archives/patch-hitcounter.sql b/maintenance/archives/patch-hitcounter.sql deleted file mode 100644 index 2d698f68..00000000 --- a/maintenance/archives/patch-hitcounter.sql +++ /dev/null @@ -1,9 +0,0 @@ --- --- hitcounter table is used to buffer page hits before they are periodically --- counted and added to the cur_counter column in the cur table. --- December 2003 --- - -CREATE TABLE /*$wgDBprefix*/hitcounter ( - hc_id INTEGER UNSIGNED NOT NULL -) ENGINE=MEMORY MAX_ROWS=25000; diff --git a/maintenance/archives/patch-user-newtalk-userid-unsigned.sql b/maintenance/archives/patch-user-newtalk-userid-unsigned.sql new file mode 100644 index 00000000..a83e03b9 --- /dev/null +++ b/maintenance/archives/patch-user-newtalk-userid-unsigned.sql @@ -0,0 +1 @@ +ALTER TABLE /*_*/user_newtalk MODIFY user_id int unsigned NOT NULL default 0; diff --git a/maintenance/backupTextPass.inc b/maintenance/backupTextPass.inc index 5f776373..d83f1fcc 100644 --- a/maintenance/backupTextPass.inc +++ b/maintenance/backupTextPass.inc @@ -48,6 +48,8 @@ class TextPassDumper extends BackupDumper { protected $maxConsecutiveFailedTextRetrievals = 200; protected $failureTimeout = 5; // Seconds to sleep after db failure + protected $bufferSize = 524288; // In bytes. Maximum size to read from the stub in on go. + protected $php = "php"; protected $spawn = false; @@ -186,6 +188,10 @@ class TextPassDumper extends BackupDumper { $url = $this->processFileOpt( $val, $param ); switch ( $opt ) { + case 'buffersize': + // Lower bound for xml reading buffer size is 4 KB + $this->bufferSize = max( intval( $val ), 4 * 1024 ); + break; case 'prefetch': require_once "$IP/maintenance/backupPrefetch.inc"; $this->prefetch = new BaseDump( $url ); @@ -354,6 +360,8 @@ class TextPassDumper extends BackupDumper { $this->lastName = ""; $this->thisPage = 0; $this->thisRev = 0; + $this->thisRevModel = null; + $this->thisRevFormat = null; $parser = xml_parser_create( "UTF-8" ); xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false ); @@ -366,12 +374,11 @@ class TextPassDumper extends BackupDumper { xml_set_character_data_handler( $parser, array( &$this, 'characterData' ) ); $offset = 0; // for context extraction on error reporting - $bufferSize = 512 * 1024; do { if ( $this->checkIfTimeExceeded() ) { $this->setTimeExceeded(); } - $chunk = fread( $input, $bufferSize ); + $chunk = fread( $input, $this->bufferSize ); if ( !xml_parse( $parser, $chunk, feof( $input ) ) ) { wfDebug( "TextDumpPass::readDump encountered XML parsing error\n" ); @@ -421,8 +428,34 @@ class TextPassDumper extends BackupDumper { return true; } + /** + * Applies applicable export transformations to $text. + * + * @param string $text + * @param string $model + * @param string|null $format + * + * @return string + */ + private function exportTransform( $text, $model, $format = null ) { + try { + $handler = ContentHandler::getForModelID( $model ); + $text = $handler->exportTransform( $text, $format ); + } + catch ( MWException $ex ) { + $this->progress( + "Unable to apply export transformation for content model '$model': " . + $ex->getMessage() + ); + } + + return $text; + } + /** * Tries to get the revision text for a revision id. + * Export transformations are applied if the content model can is given or can be + * determined from the database. * * Upon errors, retries (Up to $this->maxFailures tries each call). * If still no good revision get could be found even after this retrying, "" is returned. @@ -431,11 +464,14 @@ class TextPassDumper extends BackupDumper { * is thrown. * * @param string $id The revision id to get the text for + * @param string|bool|null $model The content model used to determine applicable export transformations. + * If $model is null, it will be determined from the database. + * @param string|null $format The content format used when applying export transformations. * - * @return string The revision text for $id, or "" * @throws MWException + * @return string The revision text for $id, or "" */ - function getText( $id ) { + function getText( $id, $model = null, $format = null ) { global $wgContentHandlerUseDB; $prefetchNotTried = true; // Whether or not we already tried to get the text via prefetch. @@ -453,6 +489,24 @@ class TextPassDumper extends BackupDumper { $oldConsecutiveFailedTextRetrievals = $consecutiveFailedTextRetrievals; $consecutiveFailedTextRetrievals = 0; + if ( $model === null && $wgContentHandlerUseDB ) { + $row = $this->db->selectRow( + 'revision', + array( 'rev_content_model', 'rev_content_format' ), + array( 'rev_id' => $this->thisRev ), + __METHOD__ + ); + + if ( $row ) { + $model = $row->rev_content_model; + $format = $row->rev_content_format; + } + } + + if ( $model === null || $model === '' ) { + $model = false; + } + while ( $failures < $this->maxFailures ) { // As soon as we found a good text for the $id, we will return immediately. @@ -469,9 +523,19 @@ class TextPassDumper extends BackupDumper { $tryIsPrefetch = true; $text = $this->prefetch->prefetch( intval( $this->thisPage ), intval( $this->thisRev ) ); + if ( $text === null ) { $text = false; } + + if ( is_string( $text ) && $model !== false ) { + // Apply export transformation to text coming from an old dump. + // The purpose of this transformation is to convert up from legacy + // formats, which may still be used in the older dump that is used + // for pre-fetching. Applying the transformation again should not + // interfere with content that is already in the correct form. + $text = $this->exportTransform( $text, $model, $format ); + } } if ( $text === false ) { @@ -483,6 +547,12 @@ class TextPassDumper extends BackupDumper { $text = $this->getTextDb( $id ); } + if ( $text !== false && $model !== false ) { + // Apply export transformation to text coming from the database. + // Prefetched text should already have transformations applied. + $text = $this->exportTransform( $text, $model, $format ); + } + // No more checks for texts from DB for now. // If we received something that is not false, // We treat it as good text, regardless of whether it actually is or is not @@ -504,21 +574,8 @@ class TextPassDumper extends BackupDumper { throw new MWException( "No database available" ); } - $revLength = strlen( $text ); - if ( $wgContentHandlerUseDB ) { - $row = $this->db->selectRow( - 'revision', - array( 'rev_len', 'rev_content_model' ), - array( 'rev_id' => $revID ), - __METHOD__ - ); - if ( $row ) { - // only check the length for the wikitext content handler, - // it's a wasted (and failed) check otherwise - if ( $row->rev_content_model == CONTENT_MODEL_WIKITEXT ) { - $revLength = $row->rev_len; - } - } + if ( $model !== CONTENT_MODEL_WIKITEXT ) { + $revLength = strlen( $text ); } else { $revLength = $this->db->selectField( 'revision', 'rev_len', array( 'rev_id' => $revID ) ); } @@ -757,7 +814,14 @@ class TextPassDumper extends BackupDumper { } if ( $name == "text" && isset( $attribs['id'] ) ) { - $text = $this->getText( $attribs['id'] ); + $id = $attribs['id']; + $model = trim( $this->thisRevModel ); + $format = trim( $this->thisRevFormat ); + + $model = $model === '' ? null : $model; + $format = $format === '' ? null : $format; + + $text = $this->getText( $id, $model, $format ); $this->openElement = array( $name, array( 'xml:space' => 'preserve' ) ); if ( strlen( $text ) > 0 ) { $this->characterData( $parser, $text ); @@ -780,6 +844,8 @@ class TextPassDumper extends BackupDumper { $this->egress->writeRevision( null, $this->buffer ); $this->buffer = ""; $this->thisRev = ""; + $this->thisRevModel = null; + $this->thisRevFormat = null; } elseif ( $name == 'page' ) { if ( !$this->firstPageWritten ) { $this->firstPageWritten = trim( $this->thisPage ); @@ -834,6 +900,13 @@ class TextPassDumper extends BackupDumper { $this->thisPage .= $data; } } + elseif ( $this->lastName == "model" ) { + $this->thisRevModel .= $data; + } + elseif ( $this->lastName == "format" ) { + $this->thisRevFormat .= $data; + } + // have to skip the newline left over from closepagetag line of // end of checkpoint files. nasty hack!! if ( $this->checkpointJustWritten ) { diff --git a/maintenance/benchmarks/bench_HTTP_HTTPS.php b/maintenance/benchmarks/bench_HTTP_HTTPS.php index bb7499b7..15692348 100644 --- a/maintenance/benchmarks/bench_HTTP_HTTPS.php +++ b/maintenance/benchmarks/bench_HTTP_HTTPS.php @@ -46,7 +46,7 @@ class BenchHttpHttps extends Benchmarker { } static function doRequest( $proto ) { - Http::get( "$proto://localhost/" ); + Http::get( "$proto://localhost/", array(), __METHOD__ ); } // bench function 1 diff --git a/maintenance/cdb.php b/maintenance/cdb.php index 86c686b4..2e252adb 100644 --- a/maintenance/cdb.php +++ b/maintenance/cdb.php @@ -21,6 +21,8 @@ * @todo document * @ingroup Maintenance */ +use \Cdb\Exception as CdbException; +use \Cdb\Reader as CdbReader; /** */ require_once __DIR__ . '/commandLine.inc'; diff --git a/maintenance/checkComposerLockUpToDate.php b/maintenance/checkComposerLockUpToDate.php new file mode 100644 index 00000000..0b77578d --- /dev/null +++ b/maintenance/checkComposerLockUpToDate.php @@ -0,0 +1,63 @@ +mDescription = 'Checks whether your composer.lock file is up to date with the current composer.json'; + } + + public function execute() { + global $IP; + $lockLocation = "$IP/composer.lock"; + $jsonLocation = "$IP/composer.json"; + if ( !file_exists( $lockLocation ) ) { + // Maybe they're using mediawiki/vendor? + $lockLocation = "$IP/vendor/composer.lock"; + if ( !file_exists( $lockLocation ) ) { + $this->error( 'Could not find composer.lock file. Have you run "composer install"?', 1 ); + } + } + + $lock = new ComposerLock( $lockLocation ); + $json = new ComposerJson( $jsonLocation ); + + if ( $lock->getHash() === $json->getHash() ) { + $this->output( "Your composer.lock file is up to date with current dependencies!\n" ); + return; + } + // Out of date, lets figure out which dependencies are old + $found = false; + $installed = $lock->getInstalledDependencies(); + foreach ( $json->getRequiredDependencies() as $name => $version ) { + if ( isset( $installed[$name] ) ) { + if ( $installed[$name]['version'] !== $version ) { + $this->output( "$name: {$installed[$name]['version']} installed, $version required.\n" ); + $found = true; + } + } else { + $this->output( "$name: not installed, $version required.\n" ); + $found = true; + } + } + if ( $found ) { + $this->error( 'Error: your composer.lock file is not up to date, run "composer update" to install newer dependencies', 1 ); + } else { + // The hash is the entire composer.json file, so it can be updated without any of the dependencies changing + // We couldn't find any out-of-date dependencies, so assume everything is ok! + $this->output( "Your composer.lock file is up to date with current dependencies!\n" ); + } + + } +} + +$maintClass = 'CheckComposerLockUpToDate'; +require_once RUN_MAINTENANCE_IF_MAIN; \ No newline at end of file diff --git a/maintenance/checkLess.php b/maintenance/checkLess.php index b97e1b0b..2f533cf4 100644 --- a/maintenance/checkLess.php +++ b/maintenance/checkLess.php @@ -22,7 +22,6 @@ */ require_once __DIR__ . '/Maintenance.php'; -require_once 'PHPUnit/Autoload.php'; /** * @ingroup Maintenance @@ -43,6 +42,17 @@ class CheckLess extends Maintenance { // require it here. require_once __DIR__ . '/../tests/TestsAutoLoader.php'; + // If phpunit isn't available by autoloader try pulling it in + if ( !class_exists( 'PHPUnit_Framework_TestCase' ) ) { + require_once 'PHPUnit/Autoload.php'; + } + + // RequestContext::resetMain() will print warnings unless this + // is defined. + if ( !defined( 'MW_PHPUNIT_TEST' ) ) { + define( 'MW_PHPUNIT_TEST', true ); + } + $textUICommand = new PHPUnit_TextUI_Command(); $argv = array( "$IP/tests/phpunit/phpunit.php", diff --git a/maintenance/cleanupBlocks.php b/maintenance/cleanupBlocks.php new file mode 100644 index 00000000..1736203b --- /dev/null +++ b/maintenance/cleanupBlocks.php @@ -0,0 +1,147 @@ +mDescription = "Cleanup user blocks with user names not matching the 'user' table"; + $this->setBatchSize( 1000 ); + } + + public function execute() { + $db = wfGetDB( DB_MASTER ); + + $max = $db->selectField( 'ipblocks', 'MAX(ipb_user)' ); + + // Step 1: Clean up any duplicate user blocks + for ( $from = 1; $from <= $max; $from += $this->mBatchSize ) { + $to = min( $max, $from + $this->mBatchSize - 1 ); + $this->output( "Cleaning up duplicate ipb_user ($from-$to of $max)\n" ); + + $delete = array(); + + $res = $db->select( + 'ipblocks', + array( 'ipb_user' ), + array( + "ipb_user >= $from", + "ipb_user <= $to", + ), + __METHOD__, + array( + 'GROUP BY' => 'ipb_user', + 'HAVING' => 'COUNT(*) > 1', + ) + ); + foreach ( $res as $row ) { + $bestBlock = null; + $res2 = $db->select( + 'ipblocks', + '*', + array( + 'ipb_user' => $row->ipb_user, + ) + ); + foreach ( $res2 as $row2 ) { + $block = Block::newFromRow( $row2 ); + if ( !$bestBlock ) { + $bestBlock = $block; + continue; + } + + // Find the most-restrictive block. Can't use + // Block::chooseBlock because that's for IP blocks, not + // user blocks. + $keep = null; + if ( $keep === null && $block->getExpiry() !== $bestBlock->getExpiry() ) { + // This works for infinite blocks because 'infinity' > '20141024234513' + $keep = $block->getExpiry() > $bestBlock->getExpiry(); + } + if ( $keep === null ) { + foreach ( array( 'createaccount', 'sendemail', 'editownusertalk' ) as $action ) { + if ( $block->prevents( $action ) xor $bestBlock->prevents( $action ) ) { + $keep = $block->prevents( $action ); + break; + } + } + } + + if ( $keep ) { + $delete[] = $bestBlock->getId(); + $bestBlock = $block; + } else { + $delete[] = $block->getId(); + } + } + } + + if ( $delete ) { + $db->delete( + 'ipblocks', + array( 'ipb_id' => $delete ), + __METHOD__ + ); + } + } + + // Step 2: Update the user name in any blocks where it doesn't match + for ( $from = 1; $from <= $max; $from += $this->mBatchSize ) { + $to = min( $max, $from + $this->mBatchSize - 1 ); + $this->output( "Cleaning up mismatched user name ($from-$to of $max)\n" ); + + $res = $db->select( + array( 'ipblocks', 'user' ), + array( 'ipb_id', 'user_name' ), + array( + 'ipb_user = user_id', + "ipb_user >= $from", + "ipb_user <= $to", + 'ipb_address != user_name', + ), + __METHOD__ + ); + foreach ( $res as $row ) { + $db->update( + 'ipblocks', + array( 'ipb_address' => $row->user_name ), + array( 'ipb_id' => $row->ipb_id ), + __METHOD__ + ); + } + } + + $this->output( "Done!\n" ); + } +} + +$maintClass = "CleanupBlocks"; +require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/cleanupCaps.php b/maintenance/cleanupCaps.php index 9e88c135..6234db48 100644 --- a/maintenance/cleanupCaps.php +++ b/maintenance/cleanupCaps.php @@ -37,6 +37,9 @@ require_once __DIR__ . '/cleanupTable.inc'; * @ingroup Maintenance */ class CapsCleanup extends TableCleanup { + + private $user; + public function __construct() { parent::__construct(); $this->mDescription = "Script to cleanup capitalization"; @@ -44,13 +47,13 @@ class CapsCleanup extends TableCleanup { } public function execute() { - global $wgCapitalLinks, $wgUser; + global $wgCapitalLinks; if ( $wgCapitalLinks ) { $this->error( "\$wgCapitalLinks is on -- no need for caps links cleanup.", true ); } - $wgUser = User::newFromName( 'Conversion script' ); + $this->user = User::newFromName( 'Conversion script' ); $this->namespace = intval( $this->getOption( 'namespace', 0 ) ); $this->dryrun = $this->hasOption( 'dry-run' ); @@ -87,7 +90,9 @@ class CapsCleanup extends TableCleanup { $this->output( "\"$display\" -> \"$targetDisplay\": DRY RUN, NOT MOVED\n" ); $ok = true; } else { - $ok = $current->moveTo( $target, false, 'Converting page titles to lowercase' ); + $mp = new MovePage( $current, $target ); + $status = $mp->move( $this->user, 'Converting page titles to lowercase', true ); + $ok = $status->isOK() ? 'OK' : $status->getWikiText(); $this->output( "\"$display\" -> \"$targetDisplay\": $ok\n" ); } if ( $ok === true ) { diff --git a/maintenance/convertExtensionToRegistration.php b/maintenance/convertExtensionToRegistration.php new file mode 100644 index 00000000..acb8d3aa --- /dev/null +++ b/maintenance/convertExtensionToRegistration.php @@ -0,0 +1,226 @@ + 'handleMessagesDirs', + 'ExtensionMessagesFiles' => 'removeAbsolutePath', + 'AutoloadClasses' => 'removeAbsolutePath', + 'ExtensionCredits' => 'handleCredits', + 'ResourceModules' => 'handleResourceModules', + 'ResourceModuleSkinStyles' => 'handleResourceModules', + 'Hooks' => 'handleHooks', + 'ExtensionFunctions' => 'handleExtensionFunctions', + 'ParserTestFiles' => 'removeAbsolutePath', + ); + + /** + * Things that were formerly globals and should still be converted + * + * @var array + */ + protected $formerGlobals = array( + 'TrackingCategories', + ); + + /** + * No longer supported globals (with reason) should not be converted and emit a warning + * + * @var array + */ + protected $noLongerSupportedGlobals = array( + 'SpecialPageGroups' => 'deprecated', + ); + + /** + * Keys that should be put at the top of the generated JSON file (T86608) + * + * @var array + */ + protected $promote = array( + 'name', + 'version', + 'author', + 'url', + 'description', + 'descriptionmsg', + 'namemsg', + 'license-name', + 'type', + ); + + private $json, $dir, $hasWarning = false; + + public function __construct() { + parent::__construct(); + $this->mDescription = 'Converts extension entry points to the new JSON registration format'; + $this->addArg( 'path', 'Location to the PHP entry point you wish to convert', /* $required = */ true ); + $this->addOption( 'skin', 'Whether to write to skin.json', false, false ); + } + + protected function getAllGlobals() { + $processor = new ReflectionClass( 'ExtensionProcessor' ); + $settings = $processor->getProperty( 'globalSettings' ); + $settings->setAccessible( true ); + return $settings->getValue() + $this->formerGlobals; + } + + public function execute() { + // Extensions will do stuff like $wgResourceModules += array(...) which is a + // fatal unless an array is already set. So set an empty value. + // And use the weird $__settings name to avoid any conflicts + // with real poorly named settings. + $__settings = array_merge( $this->getAllGlobals(), array_keys( $this->custom ) ); + foreach ( $__settings as $var ) { + $var = 'wg' . $var; + $$var = array(); + } + unset( $var ); + require $this->getArg( 0 ); + // Try not to create any local variables before this line + $vars = get_defined_vars(); + unset( $vars['this'] ); + unset( $vars['__settings'] ); + $this->dir = dirname( realpath( $this->getArg( 0 ) ) ); + $this->json = array(); + $globalSettings = $this->getAllGlobals(); + foreach ( $vars as $name => $value ) { + $realName = substr( $name, 2 ); // Strip 'wg' + + // If it's an empty array that we likely set, skip it + if ( is_array( $value ) && count( $value ) === 0 && in_array( $realName, $__settings ) ) { + continue; + } + + if ( isset( $this->custom[$realName] ) ) { + call_user_func_array( array( $this, $this->custom[$realName] ), array( $realName, $value ) ); + } elseif ( in_array( $realName, $globalSettings ) ) { + $this->json[$realName] = $value; + } elseif ( array_key_exists( $realName, $this->noLongerSupportedGlobals ) ) { + $this->output( 'Warning: Skipped global "' . $name . '" (' . + $this->noLongerSupportedGlobals[$realName] . '). ' . + "Please update the entry point before convert to registration.\n" ); + $this->hasWarning = true; + } elseif ( strpos( $name, 'wg' ) === 0 ) { + // Most likely a config setting + $this->json['config'][$realName] = $value; + } + } + + // Move some keys to the top + $out = array(); + foreach ( $this->promote as $key ) { + if ( isset( $this->json[$key] ) ) { + $out[$key] = $this->json[$key]; + unset( $this->json[$key] ); + } + } + $out += $this->json; + + $type = $this->hasOption( 'skin' ) ? 'skin' : 'extension'; + $fname = "{$this->dir}/$type.json"; + $prettyJSON = FormatJson::encode( $out, "\t", FormatJson::ALL_OK ); + file_put_contents( $fname, $prettyJSON . "\n" ); + $this->output( "Wrote output to $fname.\n" ); + if ( $this->hasWarning ) { + $this->output( "Found warnings! Please resolve the warnings and rerun this script.\n" ); + } + } + + protected function handleExtensionFunctions( $realName, $value ) { + foreach ( $value as $func ) { + if ( $func instanceof Closure ) { + $this->error( "Error: Closures cannot be converted to JSON. Please move your extension function somewhere else.", 1 ); + } + } + + $this->json[$realName] = $value; + } + + protected function handleMessagesDirs( $realName, $value ) { + foreach ( $value as $key => $dirs ) { + foreach ( (array)$dirs as $dir ) { + $this->json[$realName][$key][] = $this->stripPath( $dir, $this->dir ); + } + } + } + + private function stripPath( $val, $dir ) { + if ( $val === $dir ) { + $val = ''; + } elseif ( strpos( $val, $dir ) === 0 ) { + // +1 is for the trailing / that won't be in $this->dir + $val = substr( $val, strlen( $dir ) + 1 ); + } + + return $val; + } + + protected function removeAbsolutePath( $realName, $value ) { + $out = array(); + foreach ( $value as $key => $val ) { + $out[$key] = $this->stripPath( $val, $this->dir ); + } + $this->json[$realName] = $out; + } + + protected function handleCredits( $realName, $value) { + $keys = array_keys( $value ); + $this->json['type'] = $keys[0]; + $values = array_values( $value ); + foreach ( $values[0][0] as $name => $val ) { + if ( $name !== 'path' ) { + $this->json[$name] = $val; + } + } + } + + public function handleHooks( $realName, $value ) { + foreach ( $value as $hookName => $handlers ) { + foreach ( $handlers as $func ) { + if ( $func instanceof Closure ) { + $this->error( "Error: Closures cannot be converted to JSON. Please move the handler for $hookName somewhere else.", 1 ); + } + } + } + $this->json[$realName] = $value; + } + + protected function handleResourceModules( $realName, $value ) { + $defaults = array(); + $remote = $this->hasOption( 'skin' ) ? 'remoteSkinPath' : 'remoteExtPath'; + foreach ( $value as $name => $data ) { + if ( isset( $data['localBasePath'] ) ) { + $data['localBasePath'] = $this->stripPath( $data['localBasePath'], $this->dir ); + if ( !$defaults ) { + $defaults['localBasePath'] = $data['localBasePath']; + unset( $data['localBasePath'] ); + if ( isset( $data[$remote] ) ) { + $defaults[$remote] = $data[$remote]; + unset( $data[$remote] ); + } + } else { + if ( $data['localBasePath'] === $defaults['localBasePath'] ) { + unset( $data['localBasePath'] ); + } + if ( isset( $data[$remote] ) && isset( $defaults[$remote] ) + && $data[$remote] === $defaults[$remote] + ) { + unset( $data[$remote] ); + } + } + } + + + $this->json[$realName][$name] = $data; + } + if ( $defaults ) { + $this->json['ResourceFileModulePaths'] = $defaults; + } + } +} + +$maintClass = 'ConvertExtensionToRegistration'; +require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/dev/includes/router.php b/maintenance/dev/includes/router.php index 0a65e31e..97c8954a 100644 --- a/maintenance/dev/includes/router.php +++ b/maintenance/dev/includes/router.php @@ -98,5 +98,5 @@ if ( $mime ) { return true; } -# Let the php server handle things on it's own otherwise +# Let the php server handle things on its own otherwise return false; diff --git a/maintenance/dictionary/mediawiki.dic b/maintenance/dictionary/mediawiki.dic index df8a34ca..dd27c8ca 100644 --- a/maintenance/dictionary/mediawiki.dic +++ b/maintenance/dictionary/mediawiki.dic @@ -1811,7 +1811,6 @@ historysubmit historywarning hit hitcount -hitcounter hits hlist hmac @@ -2248,7 +2247,6 @@ logextract loggedin logid login -loginend loginerror loginfo loginlanguagelinks @@ -2259,7 +2257,6 @@ loginreqlink loginreqpagetext loginreqtitle logins -loginstart logitem loglink loglist @@ -2901,7 +2898,6 @@ numberofedits numberoffiles numberofpages numberofusers -numberofviews numberofwatchingusers numedits numentries @@ -4517,7 +4513,7 @@ what whatlinkshere whatwg wheely -wheter +whether whitelist whitelisted whitelistedittext diff --git a/maintenance/doMaintenance.php b/maintenance/doMaintenance.php index 46844c9d..4b9ad9c2 100644 --- a/maintenance/doMaintenance.php +++ b/maintenance/doMaintenance.php @@ -56,8 +56,8 @@ $self = $maintenance->getName(); # Start the autoloader, so that extensions can derive classes from core files require_once "$IP/includes/AutoLoader.php"; -# Stub the profiler -require_once "$IP/includes/profiler/Profiler.php"; +# Grab profiling functions +require_once "$IP/includes/profiler/ProfilerFunctions.php"; # Start the profiler $wgProfiler = array(); @@ -68,6 +68,7 @@ if ( file_exists( "$IP/StartProfiler.php" ) ) { // Some other requires require_once "$IP/includes/Defines.php"; require_once "$IP/includes/DefaultSettings.php"; +require_once "$IP/includes/GlobalFunctions.php"; # Load composer's autoloader if present if ( is_readable( "$IP/vendor/autoload.php" ) ) { @@ -91,29 +92,26 @@ if ( $maintenance->getDbType() === Maintenance::DB_NONE ) { } } -$maintenance->setConfig( ConfigFactory::getDefaultInstance()->makeConfig( 'main' ) ); $maintenance->finalSetup(); // Some last includes require_once "$IP/includes/Setup.php"; +// Initialize main config instance +$maintenance->setConfig( ConfigFactory::getDefaultInstance()->makeConfig( 'main' ) ); + // Do the work -try { - $maintenance->execute(); +$maintenance->execute(); - // Potentially debug globals - $maintenance->globals(); +// Potentially debug globals +$maintenance->globals(); - // Perform deferred updates. - DeferredUpdates::doUpdates( 'commit' ); +// Perform deferred updates. +DeferredUpdates::doUpdates( 'commit' ); - // log profiling info - wfLogProfilingData(); +// log profiling info +wfLogProfilingData(); - // Commit and close up! - $factory = wfGetLBFactory(); - $factory->commitMasterChanges(); - $factory->shutdown(); -} catch ( MWException $mwe ) { - echo $mwe->getText(); - exit( 1 ); -} +// Commit and close up! +$factory = wfGetLBFactory(); +$factory->commitMasterChanges(); +$factory->shutdown(); diff --git a/maintenance/dumpIterator.php b/maintenance/dumpIterator.php index 4b2ff717..d8bc3a4d 100644 --- a/maintenance/dumpIterator.php +++ b/maintenance/dumpIterator.php @@ -54,7 +54,7 @@ abstract class DumpIterator extends Maintenance { $this->checkOptions(); if ( $this->hasOption( 'file' ) ) { - $revision = new WikiRevision; + $revision = new WikiRevision( $this->getConfig() ); $revision->setText( file_get_contents( $this->getOption( 'file' ) ) ); $revision->setTitle( Title::newFromText( @@ -73,7 +73,7 @@ abstract class DumpIterator extends Maintenance { $this->error( "Sorry, I don't support dump filenames yet. " . "Use - and provide it on stdin on the meantime.", true ); } - $importer = new WikiImporter( $source ); + $importer = new WikiImporter( $source, $this->getConfig() ); $importer->setRevisionCallback( array( &$this, 'handleRevision' ) ); diff --git a/maintenance/dumpSisterSites.php b/maintenance/dumpSisterSites.php deleted file mode 100644 index 784dc7a8..00000000 --- a/maintenance/dumpSisterSites.php +++ /dev/null @@ -1,62 +0,0 @@ - - * https://www.mediawiki.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - * @ingroup Maintenance - */ - -require_once __DIR__ . '/Maintenance.php'; - -/** - * Maintenance script that generates a page name dump for SisterSites usage. - * - * @ingroup Maintenance - */ -class DumpSisterSites extends Maintenance { - public function __construct() { - parent::__construct(); - $this->mDescription = "Quickie page name dump script for SisterSites usage"; - } - - public function execute() { - $dbr = wfGetDB( DB_SLAVE ); - $dbr->bufferResults( false ); - $result = $dbr->select( 'page', - array( 'page_namespace', 'page_title' ), - array( - 'page_namespace' => NS_MAIN, - 'page_is_redirect' => 0, - ), - __METHOD__ ); - - foreach ( $result as $row ) { - $title = Title::makeTitle( $row->page_namespace, $row->page_title ); - $url = $title->getFullURL(); - $text = $title->getPrefixedText(); - $this->output( "$url $text\n" ); - } - } -} - -$maintClass = "DumpSisterSites"; -require_once RUN_MAINTENANCE_IF_MAIN; diff --git a/maintenance/dumpTextPass.php b/maintenance/dumpTextPass.php index 7c176071..bde5a076 100644 --- a/maintenance/dumpTextPass.php +++ b/maintenance/dumpTextPass.php @@ -59,6 +59,8 @@ Options: --server=h Force reading from MySQL server h --current Base ETA on number of pages in database instead of all revisions --spawn Spawn a subprocess for loading text records + --buffersize= Buffer size in bytes to use for reading the stub. + (Default: 512KB, Minimum: 4KB) --help Display this help message ENDS ); diff --git a/maintenance/eval.php b/maintenance/eval.php index 51f2cace..e20c477a 100644 --- a/maintenance/eval.php +++ b/maintenance/eval.php @@ -49,23 +49,20 @@ if ( isset( $options['d'] ) ) { $lb->setServerInfo( $i, $server ); } } - if ( $d > 2 ) { - $wgDebugFunctionEntry = true; - } } -$useReadline = function_exists( 'readline_add_history' ) +$__useReadline = function_exists( 'readline_add_history' ) && Maintenance::posix_isatty( 0 /*STDIN*/ ); -if ( $useReadline ) { - $historyFile = isset( $_ENV['HOME'] ) ? +if ( $__useReadline ) { + $__historyFile = isset( $_ENV['HOME'] ) ? "{$_ENV['HOME']}/.mweval_history" : "$IP/maintenance/.mweval_history"; - readline_read_history( $historyFile ); + readline_read_history( $__historyFile ); } -$e = null; // PHP exception -while ( ( $line = Maintenance::readconsole() ) !== false ) { - if ( $e && !preg_match( '/^(exit|die);?$/', $line ) ) { +$__e = null; // PHP exception +while ( ( $__line = Maintenance::readconsole() ) !== false ) { + if ( $__e && !preg_match( '/^(exit|die);?$/', $__line ) ) { // Internal state may be corrupted or fatals may occur later due // to some object not being set. Don't drop out of eval in case // lines were being pasted in (which would then get dumped to the shell). @@ -73,23 +70,23 @@ while ( ( $line = Maintenance::readconsole() ) !== false ) { echo "Exception was thrown before; please restart eval.php\n"; continue; } - if ( $useReadline ) { - readline_add_history( $line ); - readline_write_history( $historyFile ); + if ( $__useReadline ) { + readline_add_history( $__line ); + readline_write_history( $__historyFile ); } try { - $val = eval( $line . ";" ); - } catch ( Exception $e ) { - echo "Caught exception " . get_class( $e ) . - ": {$e->getMessage()}\n" . $e->getTraceAsString() . "\n"; + $__val = eval( $__line . ";" ); + } catch ( Exception $__e ) { + echo "Caught exception " . get_class( $__e ) . + ": {$__e->getMessage()}\n" . $__e->getTraceAsString() . "\n"; continue; } - if ( wfIsHHVM() || is_null( $val ) ) { + if ( wfIsHHVM() || is_null( $__val ) ) { echo "\n"; - } elseif ( is_string( $val ) || is_numeric( $val ) ) { - echo "$val\n"; + } elseif ( is_string( $__val ) || is_numeric( $__val ) ) { + echo "$__val\n"; } else { - var_dump( $val ); + var_dump( $__val ); } } diff --git a/maintenance/exportSites.php b/maintenance/exportSites.php new file mode 100644 index 00000000..1c71dc0e --- /dev/null +++ b/maintenance/exportSites.php @@ -0,0 +1,54 @@ +mDescription = 'Exports site definitions the sites table to XML file'; + + $this->addArg( 'file', 'A file to write the XML to (see docs/sitelist.txt). Use "php://stdout" to write to stdout.', true ); + + parent::__construct(); + } + + /** + * Do the actual work. All child classes will need to implement this + */ + public function execute() { + $file = $this->getArg( 0 ); + + if ( $file === 'php://output' || $file === 'php://stdout' ) { + $this->mQuiet = true; + } + + $handle = fopen( $file, 'w' ); + + if ( !$handle ) { + $this->error( "Failed to open $file for writing.\n", 1 ); + } + + $exporter = new SiteExporter( $handle ); + + $sites = SiteSQLStore::newInstance()->getSites( 'recache' ); + $exporter->exportSites( $sites ); + + fclose( $handle ); + + $this->output( "Exported sites to " . realpath( $file ) . ".\n" ); + } + +} + +$maintClass = 'ExportSites'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/fetchText.php b/maintenance/fetchText.php index fc676b89..dd4f760f 100644 --- a/maintenance/fetchText.php +++ b/maintenance/fetchText.php @@ -32,7 +32,8 @@ require_once __DIR__ . '/Maintenance.php'; class FetchText extends Maintenance { public function __construct() { parent::__construct(); - $this->mDescription = "Fetch the revision text from an old_id"; + $this->mDescription = "Fetch the raw revision blob from an old_id."; + $this->mDescription .= "\nNOTE: Export transformations are NOT applied. This is left to backupTextPass.php"; } /** @@ -43,7 +44,7 @@ class FetchText extends Maintenance { * \n * text (may be empty) * - * note that that the text string itself is *not* followed by newline + * note that the text string itself is *not* followed by newline */ public function execute() { $db = wfGetDB( DB_SLAVE ); diff --git a/maintenance/findHooks.php b/maintenance/findHooks.php index 36760d7e..5cf45367 100644 --- a/maintenance/findHooks.php +++ b/maintenance/findHooks.php @@ -91,6 +91,7 @@ class FindHooks extends Maintenance { $IP . '/includes/jobqueue/', $IP . '/includes/json/', $IP . '/includes/logging/', + $IP . '/includes/mail/', $IP . '/includes/media/', $IP . '/includes/page/', $IP . '/includes/parser/', @@ -163,36 +164,39 @@ class FindHooks extends Maintenance { * @return array Array of documented hooks */ private function getHooksFromOnlineDoc() { - // All hooks - $allhookdata = Http::get( - 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&' - . 'cmtitle=Category:MediaWiki_hooks&cmlimit=500&format=php' + $allhooks = $this->getHooksFromOnlineDocCategory( 'MediaWiki_hooks' ); + $removed = $this->getHooksFromOnlineDocCategory( 'Removed_hooks' ); + return array_diff( $allhooks, $removed ); + } + + /** + * @param string $title + * @return array + */ + private function getHooksFromOnlineDocCategory( $title ) { + $params = array( + 'action' => 'query', + 'list' => 'categorymembers', + 'cmtitle' => "Category:$title", + 'cmlimit' => 500, + 'format' => 'json', + 'continue' => '', ); - $allhookdata = unserialize( $allhookdata ); - $allhooks = array(); - foreach ( $allhookdata['query']['categorymembers'] as $page ) { - $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches ); - if ( $found ) { - $hook = str_replace( ' ', '_', $matches[1] ); - $allhooks[] = $hook; + + $retval = array(); + while ( true ) { + $json = Http::get( wfAppendQuery( 'http://www.mediawiki.org/w/api.php', $params ), array(), __METHOD__ ); + $data = FormatJson::decode( $json, true ); + foreach ( $data['query']['categorymembers'] as $page ) { + if ( preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $m ) ) { + $retval[] = str_replace( ' ', '_', $m[1] ); + } } - } - // Removed hooks - $oldhookdata = Http::get( - 'http://www.mediawiki.org/w/api.php?action=query&list=categorymembers&' - . 'cmtitle=Category:Removed_hooks&cmlimit=500&format=php' - ); - $oldhookdata = unserialize( $oldhookdata ); - $removed = array(); - foreach ( $oldhookdata['query']['categorymembers'] as $page ) { - $found = preg_match( '/Manual\:Hooks\/([a-zA-Z0-9- :]+)/', $page['title'], $matches ); - if ( $found ) { - $hook = str_replace( ' ', '_', $matches[1] ); - $removed[] = $hook; + if ( !isset( $data['continue'] ) ) { + return $retval; } + $params = array_replace( $params, $data['continue'] ); } - - return array_diff( $allhooks, $removed ); } /** diff --git a/maintenance/findMissingFiles.php b/maintenance/findMissingFiles.php index 5f9f643a..5818ee25 100644 --- a/maintenance/findMissingFiles.php +++ b/maintenance/findMissingFiles.php @@ -26,7 +26,7 @@ class FindMissingFiles extends Maintenance { parent::__construct(); $this->mDescription = 'Find registered files with no corresponding file.'; - $this->addOption( 'start', 'Starting file name', false, true ); + $this->addOption( 'start', 'Start after this file name', false, true ); $this->addOption( 'mtimeafter', 'Only include files changed since this time', false, true ); $this->addOption( 'mtimebefore', 'Only includes files changed before this time', false, true ); $this->setBatchSize( 300 ); @@ -42,9 +42,12 @@ class FindMissingFiles extends Maintenance { $mtime1 = $dbr->timestampOrNull( $this->getOption( 'mtimeafter', null ) ); $mtime2 = $dbr->timestampOrNull( $this->getOption( 'mtimebefore', null ) ); - $joinTables = array( 'image' ); - $joinConds = array( 'image' => array( 'INNER JOIN', 'img_name = page_title' ) ); + $joinTables = array(); + $joinConds = array(); if ( $mtime1 || $mtime2 ) { + $joinTables[] = 'page'; + $joinConds['page'] = array( 'INNER JOIN', + array( 'page_title = img_name', 'page_namespace' => NS_FILE ) ); $joinTables[] = 'logging'; $on = array( 'log_page = page_id', 'log_type' => array( 'upload', 'move', 'delete' ) ); if ( $mtime1 ) { @@ -58,21 +61,22 @@ class FindMissingFiles extends Maintenance { do { $res = $dbr->select( - array_merge( array( 'page' ), $joinTables ), - array( 'img_name' => 'DISTINCT(page_title)' ), - array( 'page_namespace' => NS_FILE, - "page_title >= " . $dbr->addQuotes( $lastName ) ), + array_merge( array( 'image' ), $joinTables ), + array( 'name' => 'img_name' ), + array( "img_name > " . $dbr->addQuotes( $lastName ) ), __METHOD__, - array( 'ORDER BY' => 'page_title', 'LIMIT' => $this->mBatchSize ), + // DISTINCT causes a pointless filesort + array( 'ORDER BY' => 'name', 'GROUP BY' => 'name', + 'LIMIT' => $this->mBatchSize ), $joinConds ); // Check if any of these files are missing... $pathsByName = array(); foreach ( $res as $row ) { - $file = $repo->newFile( $row->img_name ); - $pathsByName[$row->img_name] = $file->getPath(); - $lastName = $row->img_name; + $file = $repo->newFile( $row->name ); + $pathsByName[$row->name] = $file->getPath(); + $lastName = $row->name; } $be->preloadFileStat( array( 'srcs' => $pathsByName ) ); foreach ( $pathsByName as $path ) { diff --git a/maintenance/fixUserRegistration.php b/maintenance/fixUserRegistration.php index 878593c7..40e09159 100644 --- a/maintenance/fixUserRegistration.php +++ b/maintenance/fixUserRegistration.php @@ -33,37 +33,57 @@ class FixUserRegistration extends Maintenance { public function __construct() { parent::__construct(); $this->mDescription = "Fix the user_registration field"; + $this->setBatchSize( 1000 ); } public function execute() { - $dbr = wfGetDB( DB_SLAVE ); $dbw = wfGetDB( DB_MASTER ); - // Get user IDs which need fixing - $res = $dbr->select( 'user', 'user_id', 'user_registration IS NULL', __METHOD__ ); - foreach ( $res as $row ) { - $id = $row->user_id; - // Get first edit time - $timestamp = $dbr->selectField( - 'revision', - 'MIN(rev_timestamp)', - array( 'rev_user' => $id ), - __METHOD__ + $lastId = 0; + do { + // Get user IDs which need fixing + $res = $dbw->select( + 'user', + 'user_id', + array( + 'user_id > ' . $dbw->addQuotes( $lastId ), + 'user_registration IS NULL' + ), + __METHOD__, + array( + 'LIMIT' => $this->mBatchSize, + 'ORDER BY' => 'user_id', + ) ); - // Update - if ( !empty( $timestamp ) ) { - $dbw->update( - 'user', - array( 'user_registration' => $timestamp ), - array( 'user_id' => $id ), + foreach ( $res as $row ) { + $id = $row->user_id; + $lastId = $id; + // Get first edit time + $timestamp = $dbw->selectField( + 'revision', + 'MIN(rev_timestamp)', + array( 'rev_user' => $id ), __METHOD__ ); - $this->output( "$id $timestamp\n" ); - } else { - $this->output( "$id NULL\n" ); + // Update + if ( $timestamp !== null ) { + $dbw->update( + 'user', + array( 'user_registration' => $timestamp ), + array( 'user_id' => $id ), + __METHOD__ + ); + $user = User::newFromId( $id ); + $user->invalidateCache(); + $this->output( "Set registration for #$id to $timestamp\n" ); + } else { + $this->output( "Could not find registration for #$id NULL\n" ); + } } - } - $this->output( "\n" ); + $this->output( "Waiting for slaves..." ); + wfWaitForSlaves(); + $this->output( " done.\n" ); + } while ( $res->numRows() >= $this->mBatchSize ); } } diff --git a/maintenance/generateLocalAutoload.php b/maintenance/generateLocalAutoload.php new file mode 100644 index 00000000..b8caa4d9 --- /dev/null +++ b/maintenance/generateLocalAutoload.php @@ -0,0 +1,25 @@ +readDir( $base . '/' . $dir ); +} +foreach ( glob( $base . '/*.php' ) as $file ) { + $generator->readFile( $file ); +} + +// This class is not defined, but might be added by the installer +$generator->forceClassPath( 'MyLocalSettingsGenerator', "$base/mw-config/overrides.php" ); + +// Write out the autoload +$generator->generateAutoload( 'maintenance/generateLocalAutoload.php' ); + diff --git a/maintenance/generateSitemap.php b/maintenance/generateSitemap.php index 1930a22a..12711ea3 100644 --- a/maintenance/generateSitemap.php +++ b/maintenance/generateSitemap.php @@ -181,7 +181,14 @@ class GenerateSitemap extends Maintenance { $this->setNamespacePriorities(); $this->url_limit = 50000; $this->size_limit = pow( 2, 20 ) * 10; - $this->fspath = self::init_path( $this->getOption( 'fspath', getcwd() ) ); + + # Create directory if needed + $fspath = $this->getOption( 'fspath', getcwd() ); + if ( !wfMkdirParents( $fspath, null, __METHOD__ ) ) { + $this->error( "Can not create directory $fspath.", 1 ); + } + + $this->fspath = realpath( $fspath ) . DIRECTORY_SEPARATOR; $this->urlpath = $this->getOption( 'urlpath', "" ); if ( $this->urlpath !== "" && substr( $this->urlpath, -1 ) !== '/' ) { $this->urlpath .= '/'; @@ -238,20 +245,6 @@ class GenerateSitemap extends Maintenance { } } - /** - * Create directory if it does not exist and return pathname with a trailing slash - * @param string $fspath - * @return null|string - */ - private static function init_path( $fspath ) { - # Create directory if needed - if ( $fspath && !is_dir( $fspath ) ) { - wfMkdirParents( $fspath, null, __METHOD__ ) or die( "Can not create directory $fspath.\n" ); - } - - return realpath( $fspath ) . DIRECTORY_SEPARATOR; - } - /** * Generate a one-dimensional array of existing namespaces */ diff --git a/maintenance/importDump.php b/maintenance/importDump.php index 1f75bccf..ea8c84bb 100644 --- a/maintenance/importDump.php +++ b/maintenance/importDump.php @@ -270,7 +270,7 @@ TEXT; $this->startTime = microtime( true ); $source = new ImportStreamSource( $handle ); - $importer = new WikiImporter( $source ); + $importer = new WikiImporter( $source, $this->getConfig() ); if ( $this->hasOption( 'debug' ) ) { $importer->setDebug( true ); diff --git a/maintenance/importImages.inc b/maintenance/importImages.inc index b803e3da..4b839a0f 100644 --- a/maintenance/importImages.inc +++ b/maintenance/importImages.inc @@ -117,7 +117,7 @@ function findAuxFile( $file, $auxExtension, $maxStrip = 1 ) { function getFileCommentFromSourceWiki( $wiki_host, $file ) { $url = $wiki_host . '/api.php?action=query&format=xml&titles=File:' . rawurlencode( $file ) . '&prop=imageinfo&&iiprop=comment'; - $body = Http::get( $url ); + $body = Http::get( $url, array(), __METHOD__ ); if ( preg_match( '##', $body, $matches ) == 0 ) { return false; } @@ -128,7 +128,7 @@ function getFileCommentFromSourceWiki( $wiki_host, $file ) { function getFileUserFromSourceWiki( $wiki_host, $file ) { $url = $wiki_host . '/api.php?action=query&format=xml&titles=File:' . rawurlencode( $file ) . '&prop=imageinfo&&iiprop=user'; - $body = Http::get( $url ); + $body = Http::get( $url, array(), __METHOD__ ); if ( preg_match( '##', $body, $matches ) == 0 ) { return false; } diff --git a/maintenance/importSiteScripts.php b/maintenance/importSiteScripts.php index 7705ec9c..6566a60d 100644 --- a/maintenance/importSiteScripts.php +++ b/maintenance/importSiteScripts.php @@ -59,7 +59,7 @@ class ImportSiteScripts extends Maintenance { $url = wfAppendQuery( $baseUrl, array( 'action' => 'raw', 'title' => "MediaWiki:{$page}" ) ); - $text = Http::get( $url ); + $text = Http::get( $url, array(), __METHOD__ ); $wikiPage = WikiPage::factory( $title ); $content = ContentHandler::makeContent( $text, $wikiPage->getTitle() ); @@ -70,33 +70,40 @@ class ImportSiteScripts extends Maintenance { protected function fetchScriptList() { $data = array( 'action' => 'query', - 'format' => 'php', //'json', + 'format' => 'json', 'list' => 'allpages', 'apnamespace' => '8', 'aplimit' => '500', + 'continue' => '', ); $baseUrl = $this->getArg( 0 ); $pages = array(); - do { + while ( true ) { $url = wfAppendQuery( $baseUrl, $data ); - $strResult = Http::get( $url ); - //$result = FormatJson::decode( $strResult ); // Still broken - $result = unserialize( $strResult ); + $strResult = Http::get( $url, array(), __METHOD__ ); + $result = FormatJson::decode( $strResult, true ); - if ( !empty( $result['query']['allpages'] ) ) { - foreach ( $result['query']['allpages'] as $page ) { - if ( substr( $page['title'], -3 ) === '.js' ) { - strtok( $page['title'], ':' ); - $pages[] = strtok( '' ); - } + $page = null; + foreach ( $result['query']['allpages'] as $page ) { + if ( substr( $page['title'], -3 ) === '.js' ) { + strtok( $page['title'], ':' ); + $pages[] = strtok( '' ); } } - if ( !empty( $result['query-continue'] ) ) { - $data['apfrom'] = $result['query-continue']['allpages']['apfrom']; - $this->output( "Fetching new batch from {$data['apfrom']}\n" ); + + if ( $page !== null ) { + $this->output( "Fetched list up to {$page['title']}\n" ); + } + + if ( isset( $result['continue'] ) ) { // >= 1.21 + $data = array_replace( $data, $result['continue'] ); + } elseif ( isset( $result['query-continue']['allpages'] ) ) { // <= 1.20 + $data = array_replace( $data, $result['query-continue']['allpages'] ); + } else { + break; } - } while ( isset( $result['query-continue'] ) ); + } return $pages; } diff --git a/maintenance/importSites.php b/maintenance/importSites.php new file mode 100644 index 00000000..7abb8d72 --- /dev/null +++ b/maintenance/importSites.php @@ -0,0 +1,52 @@ +mDescription = 'Imports site definitions from XML into the sites table.'; + + $this->addArg( 'file', 'An XML file containing site definitions (see docs/sitelist.txt). Use "php://stdin" to read from stdin.', true ); + + parent::__construct(); + } + + + /** + * Do the import. + */ + public function execute() { + $file = $this->getArg( 0 ); + + $importer = new SiteImporter( SiteSQLStore::newInstance() ); + $importer->setExceptionCallback( array( $this, 'reportException' ) ); + + $importer->importFromFile( $file ); + + $this->output( "Done.\n" ); + } + + /** + * Outputs a message via the output() method. + * + * @param Exception $ex + */ + public function reportException( Exception $ex ) { + $msg = $ex->getMessage(); + $this->output( "$msg\n" ); + } +} + +$maintClass = 'ImportSites'; +require_once( RUN_MAINTENANCE_IF_MAIN ); diff --git a/maintenance/initSiteStats.php b/maintenance/initSiteStats.php index 49e0e9d7..cac33ecc 100644 --- a/maintenance/initSiteStats.php +++ b/maintenance/initSiteStats.php @@ -34,11 +34,7 @@ class InitSiteStats extends Maintenance { public function __construct() { parent::__construct(); $this->mDescription = "Re-initialise the site statistics tables"; - $this->addOption( - 'update', - 'Update the existing statistics (preserves the ss_total_views field)' - ); - $this->addOption( 'noviews', "Don't update the page view counter" ); + $this->addOption( 'update', 'Update the existing statistics' ); $this->addOption( 'active', 'Also update active users count' ); $this->addOption( 'use-master', 'Count using the master database' ); } @@ -63,12 +59,6 @@ class InitSiteStats extends Maintenance { $image = $counter->files(); $this->output( "{$image}\n" ); - if ( !$this->hasOption( 'noviews' ) ) { - $this->output( "Counting total page views..." ); - $views = $counter->views(); - $this->output( "{$views}\n" ); - } - if ( $this->hasOption( 'update' ) ) { $this->output( "\nUpdating site statistics..." ); $counter->refresh(); diff --git a/maintenance/interwiki.list b/maintenance/interwiki.list index 0660e55f..91c60c1c 100644 --- a/maintenance/interwiki.list +++ b/maintenance/interwiki.list @@ -1,77 +1,77 @@ # Based more or less on the public interwiki map from MeatballWiki # Default interwiki prefixes... -acronym|http://www.acronymfinder.com/~/search/af.aspx?string=exact&Acronym=$1|0 -advogato|http://www.advogato.org/$1|0 -arxiv|http://www.arxiv.org/abs/$1|0 -c2find|http://c2.com/cgi/wiki?FindPage&value=$1|0 -cache|http://www.google.com/search?q=cache:$1|0 -commons|https://commons.wikimedia.org/wiki/$1|0 -dictionary|http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query=$1|0 -docbook|http://wiki.docbook.org/$1|0 -doi|http://dx.doi.org/$1|0 -drumcorpswiki|http://www.drumcorpswiki.com/$1|0 -dwjwiki|http://www.suberic.net/cgi-bin/dwj/wiki.cgi?$1|0 -elibre|http://enciclopedia.us.es/index.php/$1|0 -emacswiki|http://www.emacswiki.org/cgi-bin/wiki.pl?$1|0 -foldoc|http://foldoc.org/?$1|0 -foxwiki|http://fox.wikis.com/wc.dll?Wiki~$1|0 -freebsdman|http://www.FreeBSD.org/cgi/man.cgi?apropos=1&query=$1|0 -gej|http://www.esperanto.de/dej.malnova/aktivikio.pl?$1|0 -gentoo-wiki|http://gentoo-wiki.com/$1|0 -google|http://www.google.com/search?q=$1|0 -googlegroups|http://groups.google.com/groups?q=$1|0 -hammondwiki|http://www.dairiki.org/HammondWiki/$1|0 -hrwiki|http://www.hrwiki.org/wiki/$1|0 -imdb|http://www.imdb.com/find?q=$1&tt=on|0 -jargonfile|http://sunir.org/apps/meta.pl?wiki=JargonFile&redirect=$1|0 -kmwiki|http://kmwiki.wikispaces.com/$1|0 -linuxwiki|http://linuxwiki.de/$1|0 -lojban|http://www.lojban.org/tiki/tiki-index.php?page=$1|0 -lqwiki|http://wiki.linuxquestions.org/wiki/$1|0 -lugkr|http://www.lug-kr.de/wiki/$1|0 -meatball|http://www.usemod.com/cgi-bin/mb.pl?$1|0 -mediawikiwiki|https://www.mediawiki.org/wiki/$1|0 -mediazilla|https://bugzilla.wikimedia.org/$1|0 -memoryalpha|http://en.memory-alpha.org/wiki/$1|0 -metawiki|http://sunir.org/apps/meta.pl?$1|0 -metawikimedia|https://meta.wikimedia.org/wiki/$1|0 -mozillawiki|http://wiki.mozilla.org/$1|0 -mw|http://www.mediawiki.org/wiki/$1|0 -oeis|http://oeis.org/$1|0 -openwiki|http://openwiki.com/ow.asp?$1|0 -ppr|http://c2.com/cgi/wiki?$1|0 -pythoninfo|http://wiki.python.org/moin/$1|0 -rfc|http://www.rfc-editor.org/rfc/rfc$1.txt|0 -s23wiki|http://s23.org/wiki/$1|0 -seattlewireless|http://seattlewireless.net/$1|0 -senseislibrary|http://senseis.xmp.net/?$1|0 -shoutwiki|http://www.shoutwiki.com/wiki/$1|0 -sourceforge|http://sourceforge.net/$1|0 -sourcewatch|http://www.sourcewatch.org/index.php?title=$1|0 -squeak|http://wiki.squeak.org/squeak/$1|0 -tejo|http://www.tejo.org/vikio/$1|0 -tmbw|http://www.tmbw.net/wiki/$1|0 -tmnet|http://www.technomanifestos.net/?$1|0 -theopedia|http://www.theopedia.com/$1|0 -twiki|http://twiki.org/cgi-bin/view/$1|0 -uea|http://uea.org/vikio/index.php/$1|0 -uncyclopedia|http://en.uncyclopedia.co/wiki/$1|0 -unreal|http://wiki.beyondunreal.com/$1|0 -usemod|http://www.usemod.com/cgi-bin/wiki.pl?$1|0 -webseitzwiki|http://webseitz.fluxent.com/wiki/$1|0 -wiki|http://c2.com/cgi/wiki?$1|0 -wikia|http://www.wikia.com/wiki/$1|0 -wikibooks|https://en.wikibooks.org/wiki/$1|0 -wikif1|http://www.wikif1.org/$1|0 -wikihow|http://www.wikihow.com/$1|0 -wikinfo|http://wikinfo.co/English/index.php/$1|0 -wikimedia|https://wikimediafoundation.org/wiki/$1|0 -wikinews|https://en.wikinews.org/wiki/$1|0 -wikipedia|https://en.wikipedia.org/wiki/$1|0 -wikiquote|https://en.wikiquote.org/wiki/$1|0 -wikisource|https://wikisource.org/wiki/$1|0 -wikispecies|https://species.wikimedia.org/wiki/$1|0 -wikiversity|https://en.wikiversity.org/wiki/$1|0 -wikivoyage|https://en.wikivoyage.org/wiki/$1|0 -wikt|https://en.wiktionary.org/wiki/$1|0 -wiktionary|https://en.wiktionary.org/wiki/$1|0 +acronym|http://www.acronymfinder.com/~/search/af.aspx?string=exact&Acronym=$1|0| +advogato|http://www.advogato.org/$1|0| +arxiv|http://www.arxiv.org/abs/$1|0| +c2find|http://c2.com/cgi/wiki?FindPage&value=$1|0| +cache|http://www.google.com/search?q=cache:$1|0| +commons|https://commons.wikimedia.org/wiki/$1|0|https://commons.wikimedia.org/w/api.php +dictionary|http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query=$1|0| +docbook|http://wiki.docbook.org/$1|0| +doi|http://dx.doi.org/$1|0| +drumcorpswiki|http://www.drumcorpswiki.com/$1|0|http://drumcorpswiki.com/api.php +dwjwiki|http://www.suberic.net/cgi-bin/dwj/wiki.cgi?$1|0| +elibre|http://enciclopedia.us.es/index.php/$1|0|http://enciclopedia.us.es/api.php +emacswiki|http://www.emacswiki.org/cgi-bin/wiki.pl?$1|0| +foldoc|http://foldoc.org/?$1|0| +foxwiki|http://fox.wikis.com/wc.dll?Wiki~$1|0| +freebsdman|http://www.FreeBSD.org/cgi/man.cgi?apropos=1&query=$1|0| +gej|http://www.esperanto.de/dej.malnova/aktivikio.pl?$1|0| +gentoo-wiki|http://gentoo-wiki.com/$1|0| +google|http://www.google.com/search?q=$1|0| +googlegroups|http://groups.google.com/groups?q=$1|0| +hammondwiki|http://www.dairiki.org/HammondWiki/$1|0| +hrwiki|http://www.hrwiki.org/wiki/$1|0|http://www.hrwiki.org/w/api.php +imdb|http://www.imdb.com/find?q=$1&tt=on|0| +jargonfile|http://sunir.org/apps/meta.pl?wiki=JargonFile&redirect=$1|0| +kmwiki|http://kmwiki.wikispaces.com/$1|0| +linuxwiki|http://linuxwiki.de/$1|0| +lojban|http://www.lojban.org/tiki/tiki-index.php?page=$1|0| +lqwiki|http://wiki.linuxquestions.org/wiki/$1|0| +lugkr|http://www.lug-kr.de/wiki/$1|0| +meatball|http://www.usemod.com/cgi-bin/mb.pl?$1|0| +mediawikiwiki|https://www.mediawiki.org/wiki/$1|0|https://www.mediawiki.org/w/api.php +mediazilla|https://bugzilla.wikimedia.org/$1|0| +memoryalpha|http://en.memory-alpha.org/wiki/$1|0|http://en.memory-alpha.org/api.php +metawiki|http://sunir.org/apps/meta.pl?$1|0| +metawikimedia|https://meta.wikimedia.org/wiki/$1|0|https://meta.wikimedia.org/w/api.php +mozillawiki|http://wiki.mozilla.org/$1|0|https://wiki.mozilla.org/api.php +mw|https://www.mediawiki.org/wiki/$1|0|https://www.mediawiki.org/w/api.php +oeis|http://oeis.org/$1|0| +openwiki|http://openwiki.com/ow.asp?$1|0| +ppr|http://c2.com/cgi/wiki?$1|0| +pythoninfo|http://wiki.python.org/moin/$1|0| +rfc|http://www.rfc-editor.org/rfc/rfc$1.txt|0| +s23wiki|http://s23.org/wiki/$1|0|http://s23.org/w/api.php +seattlewireless|http://seattlewireless.net/$1|0| +senseislibrary|http://senseis.xmp.net/?$1|0| +shoutwiki|http://www.shoutwiki.com/wiki/$1|0|http://www.shoutwiki.com/w/api.php +sourceforge|http://sourceforge.net/$1|0| +sourcewatch|http://www.sourcewatch.org/index.php?title=$1|0|http://www.sourcewatch.org/api.php +squeak|http://wiki.squeak.org/squeak/$1|0| +tejo|http://www.tejo.org/vikio/$1|0| +tmbw|http://www.tmbw.net/wiki/$1|0|http://tmbw.net/wiki/api.php +tmnet|http://www.technomanifestos.net/?$1|0| +theopedia|http://www.theopedia.com/$1|0| +twiki|http://twiki.org/cgi-bin/view/$1|0| +uea|http://uea.org/vikio/index.php/$1|0|http://uea.org/vikio/api.php +uncyclopedia|http://en.uncyclopedia.co/wiki/$1|0|http://en.uncyclopedia.co/w/api.php +unreal|http://wiki.beyondunreal.com/$1|0|http://wiki.beyondunreal.com/w/api.php +usemod|http://www.usemod.com/cgi-bin/wiki.pl?$1|0| +webseitzwiki|http://webseitz.fluxent.com/wiki/$1|0| +wiki|http://c2.com/cgi/wiki?$1|0| +wikia|http://www.wikia.com/wiki/$1|0| +wikibooks|https://en.wikibooks.org/wiki/$1|0|https://en.wikibooks.org/w/api.php +wikif1|http://www.wikif1.org/$1|0| +wikihow|http://www.wikihow.com/$1|0|http://www.wikihow.com/api.php +wikinfo|http://wikinfo.co/English/index.php/$1|0| +wikimedia|https://wikimediafoundation.org/wiki/$1|0|https://wikimediafoundation.org/w/api.php +wikinews|https://en.wikinews.org/wiki/$1|0|https://en.wikinews.org/w/api.php +wikipedia|https://en.wikipedia.org/wiki/$1|0|https://en.wikipedia.org/w/api.php +wikiquote|https://en.wikiquote.org/wiki/$1|0|https://en.wikiquote.org/w/api.php +wikisource|https://wikisource.org/wiki/$1|0|https://wikisource.org/w/api.php +wikispecies|https://species.wikimedia.org/wiki/$1|0|https://species.wikimedia.org/w/api.php +wikiversity|https://en.wikiversity.org/wiki/$1|0|https://en.wikiversity.org/w/api.php +wikivoyage|https://en.wikivoyage.org/wiki/$1|0|https://en.wikivoyage.org/w/api.php +wikt|https://en.wiktionary.org/wiki/$1|0|https://en.wiktionary.org/w/api.php +wiktionary|https://en.wiktionary.org/wiki/$1|0|https://en.wiktionary.org/w/api.php diff --git a/maintenance/interwiki.sql b/maintenance/interwiki.sql index aad0cc3b..0628773e 100644 --- a/maintenance/interwiki.sql +++ b/maintenance/interwiki.sql @@ -1,80 +1,80 @@ -- Based more or less on the public interwiki map from MeatballWiki -- Default interwiki prefixes... -REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local) VALUES -('acronym','http://www.acronymfinder.com/~/search/af.aspx?string=exact&Acronym=$1',0), -('advogato','http://www.advogato.org/$1',0), -('arxiv','http://www.arxiv.org/abs/$1',0), -('c2find','http://c2.com/cgi/wiki?FindPage&value=$1',0), -('cache','http://www.google.com/search?q=cache:$1',0), -('commons','https://commons.wikimedia.org/wiki/$1',0), -('dictionary','http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query=$1',0), -('docbook','http://wiki.docbook.org/$1',0), -('doi','http://dx.doi.org/$1',0), -('drumcorpswiki','http://www.drumcorpswiki.com/$1',0), -('dwjwiki','http://www.suberic.net/cgi-bin/dwj/wiki.cgi?$1',0), -('elibre','http://enciclopedia.us.es/index.php/$1',0), -('emacswiki','http://www.emacswiki.org/cgi-bin/wiki.pl?$1',0), -('foldoc','http://foldoc.org/?$1',0), -('foxwiki','http://fox.wikis.com/wc.dll?Wiki~$1',0), -('freebsdman','http://www.FreeBSD.org/cgi/man.cgi?apropos=1&query=$1',0), -('gej','http://www.esperanto.de/dej.malnova/aktivikio.pl?$1',0), -('gentoo-wiki','http://gentoo-wiki.com/$1',0), -('google','http://www.google.com/search?q=$1',0), -('googlegroups','http://groups.google.com/groups?q=$1',0), -('hammondwiki','http://www.dairiki.org/HammondWiki/$1',0), -('hrwiki','http://www.hrwiki.org/wiki/$1',0), -('imdb','http://www.imdb.com/find?q=$1&tt=on',0), -('jargonfile','http://sunir.org/apps/meta.pl?wiki=JargonFile&redirect=$1',0), -('kmwiki','http://kmwiki.wikispaces.com/$1',0), -('linuxwiki','http://linuxwiki.de/$1',0), -('lojban','http://www.lojban.org/tiki/tiki-index.php?page=$1',0), -('lqwiki','http://wiki.linuxquestions.org/wiki/$1',0), -('lugkr','http://www.lug-kr.de/wiki/$1',0), -('meatball','http://www.usemod.com/cgi-bin/mb.pl?$1',0), -('mediawikiwiki','https://www.mediawiki.org/wiki/$1',0), -('mediazilla','https://bugzilla.wikimedia.org/$1',0), -('memoryalpha','http://en.memory-alpha.org/wiki/$1',0), -('metawiki','http://sunir.org/apps/meta.pl?$1',0), -('metawikimedia','https://meta.wikimedia.org/wiki/$1',0), -('mozillawiki','http://wiki.mozilla.org/$1',0), -('mw','http://www.mediawiki.org/wiki/$1',0), -('oeis','http://oeis.org/$1',0), -('openwiki','http://openwiki.com/ow.asp?$1',0), -('ppr','http://c2.com/cgi/wiki?$1',0), -('pythoninfo','http://wiki.python.org/moin/$1',0), -('rfc','http://www.rfc-editor.org/rfc/rfc$1.txt',0), -('s23wiki','http://s23.org/wiki/$1',0), -('seattlewireless','http://seattlewireless.net/$1',0), -('senseislibrary','http://senseis.xmp.net/?$1',0), -('shoutwiki','http://www.shoutwiki.com/wiki/$1',0), -('sourceforge','http://sourceforge.net/$1',0), -('sourcewatch','http://www.sourcewatch.org/index.php?title=$1',0), -('squeak','http://wiki.squeak.org/squeak/$1',0), -('tejo','http://www.tejo.org/vikio/$1',0), -('tmbw','http://www.tmbw.net/wiki/$1',0), -('tmnet','http://www.technomanifestos.net/?$1',0), -('theopedia','http://www.theopedia.com/$1',0), -('twiki','http://twiki.org/cgi-bin/view/$1',0), -('uea','http://uea.org/vikio/index.php/$1',0), -('uncyclopedia','http://en.uncyclopedia.co/wiki/$1',0), -('unreal','http://wiki.beyondunreal.com/$1',0), -('usemod','http://www.usemod.com/cgi-bin/wiki.pl?$1',0), -('webseitzwiki','http://webseitz.fluxent.com/wiki/$1',0), -('wiki','http://c2.com/cgi/wiki?$1',0), -('wikia','http://www.wikia.com/wiki/$1',0), -('wikibooks','https://en.wikibooks.org/wiki/$1',0), -('wikif1','http://www.wikif1.org/$1',0), -('wikihow','http://www.wikihow.com/$1',0), -('wikinfo','http://wikinfo.co/English/index.php/$1',0), -('wikimedia','https://wikimediafoundation.org/wiki/$1',0), -('wikinews','https://en.wikinews.org/wiki/$1',0), -('wikipedia','https://en.wikipedia.org/wiki/$1',0), -('wikiquote','https://en.wikiquote.org/wiki/$1',0), -('wikisource','https://wikisource.org/wiki/$1',0), -('wikispecies','https://species.wikimedia.org/wiki/$1',0), -('wikiversity','https://en.wikiversity.org/wiki/$1',0), -('wikivoyage','https://en.wikivoyage.org/wiki/$1',0), -('wikt','https://en.wiktionary.org/wiki/$1',0), -('wiktionary','https://en.wiktionary.org/wiki/$1',0) +REPLACE INTO /*$wgDBprefix*/interwiki (iw_prefix,iw_url,iw_local,iw_api) VALUES +('acronym','http://www.acronymfinder.com/~/search/af.aspx?string=exact&Acronym=$1',0,''), +('advogato','http://www.advogato.org/$1',0,''), +('arxiv','http://www.arxiv.org/abs/$1',0,''), +('c2find','http://c2.com/cgi/wiki?FindPage&value=$1',0,''), +('cache','http://www.google.com/search?q=cache:$1',0,''), +('commons','https://commons.wikimedia.org/wiki/$1',0,'https://commons.wikimedia.org/w/api.php'), +('dictionary','http://www.dict.org/bin/Dict?Database=*&Form=Dict1&Strategy=*&Query=$1',0,''), +('docbook','http://wiki.docbook.org/$1',0,''), +('doi','http://dx.doi.org/$1',0,''), +('drumcorpswiki','http://www.drumcorpswiki.com/$1',0,'http://drumcorpswiki.com/api.php'), +('dwjwiki','http://www.suberic.net/cgi-bin/dwj/wiki.cgi?$1',0,''), +('elibre','http://enciclopedia.us.es/index.php/$1',0,'http://enciclopedia.us.es/api.php'), +('emacswiki','http://www.emacswiki.org/cgi-bin/wiki.pl?$1',0,''), +('foldoc','http://foldoc.org/?$1',0,''), +('foxwiki','http://fox.wikis.com/wc.dll?Wiki~$1',0,''), +('freebsdman','http://www.FreeBSD.org/cgi/man.cgi?apropos=1&query=$1',0,''), +('gej','http://www.esperanto.de/dej.malnova/aktivikio.pl?$1',0,''), +('gentoo-wiki','http://gentoo-wiki.com/$1',0,''), +('google','http://www.google.com/search?q=$1',0,''), +('googlegroups','http://groups.google.com/groups?q=$1',0,''), +('hammondwiki','http://www.dairiki.org/HammondWiki/$1',0,''), +('hrwiki','http://www.hrwiki.org/wiki/$1',0,'http://www.hrwiki.org/w/api.php'), +('imdb','http://www.imdb.com/find?q=$1&tt=on',0,''), +('jargonfile','http://sunir.org/apps/meta.pl?wiki=JargonFile&redirect=$1',0,''), +('kmwiki','http://kmwiki.wikispaces.com/$1',0,''), +('linuxwiki','http://linuxwiki.de/$1',0,''), +('lojban','http://www.lojban.org/tiki/tiki-index.php?page=$1',0,''), +('lqwiki','http://wiki.linuxquestions.org/wiki/$1',0,''), +('lugkr','http://www.lug-kr.de/wiki/$1',0,''), +('meatball','http://www.usemod.com/cgi-bin/mb.pl?$1',0,''), +('mediawikiwiki','https://www.mediawiki.org/wiki/$1',0,'https://www.mediawiki.org/w/api.php'), +('mediazilla','https://bugzilla.wikimedia.org/$1',0,''), +('memoryalpha','http://en.memory-alpha.org/wiki/$1',0,'http://en.memory-alpha.org/api.php'), +('metawiki','http://sunir.org/apps/meta.pl?$1',0,''), +('metawikimedia','https://meta.wikimedia.org/wiki/$1',0,'https://meta.wikimedia.org/w/api.php'), +('mozillawiki','http://wiki.mozilla.org/$1',0,'https://wiki.mozilla.org/api.php'), +('mw','https://www.mediawiki.org/wiki/$1',0,'https://www.mediawiki.org/w/api.php'), +('oeis','http://oeis.org/$1',0,''), +('openwiki','http://openwiki.com/ow.asp?$1',0,''), +('ppr','http://c2.com/cgi/wiki?$1',0,''), +('pythoninfo','http://wiki.python.org/moin/$1',0,''), +('rfc','http://www.rfc-editor.org/rfc/rfc$1.txt',0,''), +('s23wiki','http://s23.org/wiki/$1',0,'http://s23.org/w/api.php'), +('seattlewireless','http://seattlewireless.net/$1',0,''), +('senseislibrary','http://senseis.xmp.net/?$1',0,''), +('shoutwiki','http://www.shoutwiki.com/wiki/$1',0,'http://www.shoutwiki.com/w/api.php'), +('sourceforge','http://sourceforge.net/$1',0,''), +('sourcewatch','http://www.sourcewatch.org/index.php?title=$1',0,'http://www.sourcewatch.org/api.php'), +('squeak','http://wiki.squeak.org/squeak/$1',0,''), +('tejo','http://www.tejo.org/vikio/$1',0,''), +('tmbw','http://www.tmbw.net/wiki/$1',0,'http://tmbw.net/wiki/api.php'), +('tmnet','http://www.technomanifestos.net/?$1',0,''), +('theopedia','http://www.theopedia.com/$1',0,''), +('twiki','http://twiki.org/cgi-bin/view/$1',0,''), +('uea','http://uea.org/vikio/index.php/$1',0,'http://uea.org/vikio/api.php'), +('uncyclopedia','http://en.uncyclopedia.co/wiki/$1',0,'http://en.uncyclopedia.co/w/api.php'), +('unreal','http://wiki.beyondunreal.com/$1',0,'http://wiki.beyondunreal.com/w/api.php'), +('usemod','http://www.usemod.com/cgi-bin/wiki.pl?$1',0,''), +('webseitzwiki','http://webseitz.fluxent.com/wiki/$1',0,''), +('wiki','http://c2.com/cgi/wiki?$1',0,''), +('wikia','http://www.wikia.com/wiki/$1',0,''), +('wikibooks','https://en.wikibooks.org/wiki/$1',0,'https://en.wikibooks.org/w/api.php'), +('wikif1','http://www.wikif1.org/$1',0,''), +('wikihow','http://www.wikihow.com/$1',0,'http://www.wikihow.com/api.php'), +('wikinfo','http://wikinfo.co/English/index.php/$1',0,''), +('wikimedia','https://wikimediafoundation.org/wiki/$1',0,'https://wikimediafoundation.org/w/api.php'), +('wikinews','https://en.wikinews.org/wiki/$1',0,'https://en.wikinews.org/w/api.php'), +('wikipedia','https://en.wikipedia.org/wiki/$1',0,'https://en.wikipedia.org/w/api.php'), +('wikiquote','https://en.wikiquote.org/wiki/$1',0,'https://en.wikiquote.org/w/api.php'), +('wikisource','https://wikisource.org/wiki/$1',0,'https://wikisource.org/w/api.php'), +('wikispecies','https://species.wikimedia.org/wiki/$1',0,'https://species.wikimedia.org/w/api.php'), +('wikiversity','https://en.wikiversity.org/wiki/$1',0,'https://en.wikiversity.org/w/api.php'), +('wikivoyage','https://en.wikivoyage.org/wiki/$1',0,'https://en.wikivoyage.org/w/api.php'), +('wikt','https://en.wiktionary.org/wiki/$1',0,'https://en.wiktionary.org/w/api.php'), +('wiktionary','https://en.wiktionary.org/wiki/$1',0,'https://en.wiktionary.org/w/api.php') ; diff --git a/maintenance/jsduck/CustomTags.rb b/maintenance/jsduck/CustomTags.rb deleted file mode 100644 index 2aff9881..00000000 --- a/maintenance/jsduck/CustomTags.rb +++ /dev/null @@ -1,116 +0,0 @@ -# Custom tags for JSDuck 5.x -# See also: -# - https://github.com/senchalabs/jsduck/wiki/Tags -# - https://github.com/senchalabs/jsduck/wiki/Custom-tags -# - https://github.com/senchalabs/jsduck/wiki/Custom-tags/7f5c32e568eab9edc8e3365e935bcb836cb11f1d -require 'jsduck/tag/tag' - -class CommonTag < JsDuck::Tag::Tag - def initialize - @html_position = POS_DOC + 0.1 - @repeatable = true - end - - def parse_doc(scanner, position) - if @multiline - return { :tagname => @tagname, :doc => :multiline } - else - text = scanner.match(/.*$/) - return { :tagname => @tagname, :doc => text } - end - end - - def process_doc(context, tags, position) - context[@tagname] = tags - end - - def format(context, formatter) - context[@tagname].each do |tag| - tag[:doc] = formatter.format(tag[:doc]) - end - end -end - -class SourceTag < CommonTag - def initialize - @tagname = :source - @pattern = "source" - super - end - - def to_html(context) - context[@tagname].map do |source| - <<-EOHTML -

Source

- #{source[:doc]} - EOHTML - end.join - end -end - -class SeeTag < CommonTag - def initialize - @tagname = :see - @pattern = "see" - super - end - - def format(context, formatter) - position = context[:files][0] - context[@tagname].each do |tag| - tag[:doc] = '
  • ' + render_long_see(tag[:doc], formatter, position) + '
  • ' - end - end - - def to_html(context) - <<-EOHTML -

    Related

    -
      - #{ context[@tagname].map {|tag| tag[:doc] }.join("\n") } -
    - EOHTML - end - - def render_long_see(tag, formatter, position) - if tag =~ /\A([^\s]+)( .*)?\Z/m - name = $1 - doc = $2 ? ': ' + $2 : '' - return formatter.format("{@link #{name}} #{doc}") - else - JsDuck::Logger.warn(nil, 'Unexpected @see argument: "'+tag+'"', position) - return tag - end - end -end - -class ContextTag < CommonTag - def initialize - @tagname = :context - @pattern = 'context' - super - end - - def format(context, formatter) - position = context[:files][0] - context[@tagname].each do |tag| - tag[:doc] = render_long_context(tag[:doc], formatter, position) - end - end - - def to_html(context) - <<-EOHTML -

    Context

    - #{ context[@tagname].last[:doc] } - EOHTML - end - - def render_long_context(tag, formatter, position) - if tag =~ /\A([^\s]+)/m - name = $1 - return formatter.format("`context` : {@link #{name}}") - else - JsDuck::Logger.warn(nil, 'Unexpected @context argument: "'+tag+'"', position) - return tag - end - end -end diff --git a/maintenance/jsduck/categories.json b/maintenance/jsduck/categories.json index d6163bde..eab2b632 100644 --- a/maintenance/jsduck/categories.json +++ b/maintenance/jsduck/categories.json @@ -13,7 +13,9 @@ "mw.html", "mw.html.Cdata", "mw.html.Raw", - "mw.hook" + "mw.hook", + "mw.template", + "mw.errorLogger" ] }, { @@ -21,6 +23,7 @@ "classes": [ "mw.Title", "mw.Uri", + "mw.messagePoster.*", "mw.notification", "mw.Notification_", "mw.user", @@ -54,7 +57,8 @@ { "name": "Interfaces", "classes": [ - "mw.Feedback" + "mw.Feedback", + "mw.Feedback.Dialog" ] }, { @@ -69,8 +73,7 @@ "mw.log", "mw.inspect", "mw.inspect.reports", - "mw.Debug", - "mw.Debug.profile" + "mw.Debug" ] } ] diff --git a/maintenance/jsduck/custom_tags.rb b/maintenance/jsduck/custom_tags.rb new file mode 100644 index 00000000..39589a06 --- /dev/null +++ b/maintenance/jsduck/custom_tags.rb @@ -0,0 +1,120 @@ +# Custom tags for JSDuck 5.x +# See also: +# - https://github.com/senchalabs/jsduck/wiki/Tags +# - https://github.com/senchalabs/jsduck/wiki/Custom-tags +# - https://github.com/senchalabs/jsduck/wiki/Custom-tags/7f5c32e568eab9edc8e3365e935bcb836cb11f1d +require 'jsduck/tag/tag' + +class CommonTag < JsDuck::Tag::Tag + def initialize + @html_position = POS_DOC + 0.1 + @repeatable = true + end + + def parse_doc(scanner, _position) + if @multiline + return { tagname: @tagname, doc: :multiline } + else + text = scanner.match(/.*$/) + return { tagname: @tagname, doc: text } + end + end + + def process_doc(context, tags, _position) + context[@tagname] = tags + end + + def format(context, formatter) + context[@tagname].each do |tag| + tag[:doc] = formatter.format(tag[:doc]) + end + end +end + +class SourceTag < CommonTag + def initialize + @tagname = :source + @pattern = 'source' + super + end + + def to_html(context) + context[@tagname].map do |source| + <<-EOHTML +

    Source

    + #{source[:doc]} + EOHTML + end.join + end +end + +class SeeTag < CommonTag + def initialize + @tagname = :see + @pattern = 'see' + super + end + + def format(context, formatter) + position = context[:files][0] + context[@tagname].each do |tag| + tag[:doc] = '
  • ' + render_long_see(tag[:doc], formatter, position) + '
  • ' + end + end + + def to_html(context) + <<-EOHTML +

    Related

    +
      + #{ context[@tagname].map { |tag| tag[:doc] }.join("\n") } +
    + EOHTML + end + + def render_long_see(tag, formatter, position) + match = /\A([^\s]+)( .*)?\Z/m.match(tag) + + if match + name = match[1] + doc = match[2] ? ': ' + match[2] : '' + return formatter.format("{@link #{name}} #{doc}") + else + JsDuck::Logger.warn(nil, 'Unexpected @see argument: "' + tag + '"', position) + return tag + end + end +end + +class ContextTag < CommonTag + def initialize + @tagname = :context + @pattern = 'context' + super + end + + def format(context, formatter) + position = context[:files][0] + context[@tagname].each do |tag| + tag[:doc] = render_long_context(tag[:doc], formatter, position) + end + end + + def to_html(context) + <<-EOHTML +

    Context

    + #{ context[@tagname].last[:doc] } + EOHTML + end + + def render_long_context(tag, formatter, position) + match = /\A([^\s]+)/m.match(tag) + + if match + name = match[1] + return formatter.format("`context` : {@link #{name}}") + else + JsDuck::Logger.warn(nil, 'Unexpected @context argument: "' + tag + '"', position) + return tag + end + end +end diff --git a/maintenance/jsduck/eg-iframe.html b/maintenance/jsduck/eg-iframe.html index 7dc4afa8..fca839d9 100644 --- a/maintenance/jsduck/eg-iframe.html +++ b/maintenance/jsduck/eg-iframe.html @@ -3,14 +3,46 @@ MediaWiki Code Example - + + - - + + + +