Merge "Fix and make some types in PHPDoc and JSDoc tags more specific"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sat, 5 Aug 2017 22:07:59 +0000 (22:07 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sat, 5 Aug 2017 22:08:00 +0000 (22:08 +0000)
412 files changed:
.gitignore
.mailmap
.travis.yml
CREDITS
Gruntfile.js
RELEASE-NOTES-1.30
autoload.php
composer.json
docs/database.txt
docs/hooks.txt
includes/Block.php
includes/DefaultSettings.php
includes/EditPage.php
includes/Feed.php
includes/FileDeleteForm.php
includes/GlobalFunctions.php
includes/HistoryBlob.php
includes/LinkFilter.php
includes/Linker.php
includes/ListToggle.php
includes/MWNamespace.php
includes/MagicWord.php
includes/MediaWiki.php
includes/MimeMagic.php
includes/Preferences.php
includes/Revision.php
includes/Sanitizer.php
includes/ServiceWiring.php
includes/Setup.php
includes/SiteStats.php
includes/Title.php
includes/WikiMap.php
includes/Xml.php
includes/actions/Action.php
includes/actions/InfoAction.php
includes/api/ApiBase.php
includes/api/ApiEmailUser.php
includes/api/ApiMain.php
includes/api/ApiPageSet.php
includes/api/ApiQueryImageInfo.php
includes/api/ApiQueryInfo.php
includes/api/ApiQueryLinks.php
includes/api/ApiResult.php
includes/api/ApiUsageException.php
includes/api/i18n/he.json
includes/api/i18n/ko.json
includes/api/i18n/zh-hant.json
includes/auth/AuthenticationResponse.php
includes/cache/MessageCache.php
includes/changes/ChangesFeed.php
includes/changes/ChangesList.php
includes/changes/EnhancedChangesList.php
includes/changes/RecentChange.php
includes/changetags/ChangeTags.php
includes/collation/IcuCollation.php
includes/collation/NumericUppercaseCollation.php
includes/config/EtcdConfig.php
includes/content/ContentHandler.php
includes/context/DerivativeContext.php
includes/db/CloneDatabase.php
includes/debug/MWDebug.php
includes/deferred/LinksUpdate.php
includes/deferred/SearchUpdate.php
includes/deferred/WANCacheReapUpdate.php
includes/diff/DifferenceEngine.php
includes/exception/HttpError.php
includes/export/WikiExporter.php
includes/filerepo/ForeignAPIRepo.php
includes/htmlform/HTMLForm.php
includes/htmlform/HTMLFormField.php
includes/htmlform/OOUIHTMLForm.php
includes/htmlform/fields/HTMLCheckField.php
includes/htmlform/fields/HTMLFormFieldCloner.php
includes/htmlform/fields/HTMLMultiSelectField.php
includes/htmlform/fields/HTMLRadioField.php
includes/htmlform/fields/HTMLTextAreaField.php
includes/htmlform/fields/HTMLTextField.php
includes/htmlform/fields/HTMLUsersMultiselectField.php
includes/http/Http.php
includes/import/ImportStreamSource.php
includes/installer/DatabaseUpdater.php
includes/installer/MssqlUpdater.php
includes/installer/MysqlUpdater.php
includes/installer/PostgresUpdater.php
includes/installer/SqliteUpdater.php
includes/installer/WebInstaller.php
includes/installer/i18n/csb.json
includes/installer/i18n/ms.json
includes/installer/i18n/pt.json
includes/installer/i18n/roa-tara.json
includes/jobqueue/JobQueue.php
includes/jobqueue/JobQueueFederated.php
includes/jobqueue/JobQueueSecondTestQueue.php [new file with mode: 0644]
includes/libs/CSSMin.php
includes/libs/IP.php
includes/libs/StringUtils.php
includes/libs/filebackend/FileBackendStore.php
includes/libs/mime/MimeAnalyzer.php
includes/libs/objectcache/APCBagOStuff.php
includes/libs/objectcache/APCUBagOStuff.php
includes/libs/objectcache/MemcachedPeclBagOStuff.php
includes/libs/objectcache/MultiWriteBagOStuff.php
includes/libs/rdbms/database/Database.php
includes/libs/rdbms/database/DatabaseSqlite.php
includes/logging/BlockLogFormatter.php
includes/logging/LogPage.php
includes/logging/LogPager.php
includes/mail/EmailNotification.php
includes/mail/UserMailer.php
includes/media/DjVuImage.php
includes/media/Exif.php
includes/media/SVGMetadataExtractor.php
includes/page/Article.php
includes/page/CategoryPage.php
includes/page/ImagePage.php
includes/page/WikiPage.php
includes/pager/IndexPager.php
includes/parser/Parser.php
includes/parser/ParserOptions.php
includes/parser/ParserOutput.php
includes/profiler/output/ProfilerOutput.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderClientHtml.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/resourceloader/ResourceLoaderMediaWikiUtilModule.php [new file with mode: 0644]
includes/resourceloader/ResourceLoaderModule.php
includes/search/SearchDatabase.php
includes/skins/BaseTemplate.php
includes/skins/Skin.php
includes/specialpage/LoginSignupSpecialPage.php
includes/specialpage/SpecialPage.php
includes/specials/SpecialActiveusers.php
includes/specials/SpecialAllMessages.php
includes/specials/SpecialAllPages.php
includes/specials/SpecialBrokenRedirects.php
includes/specials/SpecialContributions.php
includes/specials/SpecialDoubleRedirects.php
includes/specials/SpecialImport.php
includes/specials/SpecialListgrants.php
includes/specials/SpecialListgrouprights.php
includes/specials/SpecialListredirects.php
includes/specials/SpecialListusers.php
includes/specials/SpecialPagesWithProp.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialShortpages.php
includes/specials/SpecialSpecialpages.php
includes/specials/SpecialUncategorizedcategories.php
includes/specials/SpecialUndelete.php
includes/specials/SpecialUpload.php
includes/specials/SpecialVersion.php
includes/specials/SpecialWatchlist.php
includes/specials/pagers/AllMessagesTablePager.php
includes/specials/pagers/ContribsPager.php
includes/specials/pagers/UsersPager.php
includes/tidy/Balancer.php
includes/tidy/RemexCompatMunger.php
includes/user/BotPassword.php
includes/user/User.php
includes/widget/search/BasicSearchResultSetWidget.php
jsduck.json
languages/ConverterRule.php
languages/Language.php
languages/data/Names.php
languages/i18n/af.json
languages/i18n/ar.json
languages/i18n/as.json
languages/i18n/ast.json
languages/i18n/azb.json
languages/i18n/ba.json
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/bg.json
languages/i18n/bho.json
languages/i18n/bn.json
languages/i18n/bs.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/ckb.json
languages/i18n/cs.json
languages/i18n/csb.json
languages/i18n/cy.json
languages/i18n/da.json
languages/i18n/de.json
languages/i18n/din.json
languages/i18n/el.json
languages/i18n/en.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/frr.json
languages/i18n/gl.json
languages/i18n/gor.json
languages/i18n/gsw.json
languages/i18n/gu.json
languages/i18n/ha.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/hif-latn.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/hy.json
languages/i18n/ia.json
languages/i18n/id.json
languages/i18n/inh.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/jam.json
languages/i18n/jv.json
languages/i18n/kab.json
languages/i18n/ko.json
languages/i18n/lb.json
languages/i18n/li.json
languages/i18n/lv.json
languages/i18n/mk.json
languages/i18n/ms.json
languages/i18n/mwl.json
languages/i18n/nap.json
languages/i18n/nb.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/pl.json
languages/i18n/pnb.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/rif.json
languages/i18n/rm.json
languages/i18n/ro.json
languages/i18n/roa-tara.json
languages/i18n/ru.json
languages/i18n/sd.json
languages/i18n/shi.json
languages/i18n/skr-arab.json [new file with mode: 0644]
languages/i18n/sl.json
languages/i18n/sq.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/sv.json
languages/i18n/te.json
languages/i18n/tet.json
languages/i18n/tg-cyrl.json
languages/i18n/tr.json
languages/i18n/tt-cyrl.json
languages/i18n/uk.json
languages/i18n/ur.json
languages/i18n/vi.json
languages/i18n/yi.json
languages/i18n/yue.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/messages/MessagesSkr.php [new file with mode: 0644]
languages/messages/MessagesSkr_arab.php [new file with mode: 0644]
maintenance/Maintenance.php
maintenance/addRFCandPMIDInterwiki.php
maintenance/benchmarks/benchmarkLruHash.php [new file with mode: 0644]
maintenance/generateSitemap.php
maintenance/jsduck/categories.json
maintenance/language/checkLanguage.inc
maintenance/mssql/archives/patch-add-3d.sql [new file with mode: 0644]
maintenance/mssql/tables.sql
maintenance/populatePPSortKey.php [new file with mode: 0644]
maintenance/postgres/archives/patch-add-3d.sql [new file with mode: 0644]
maintenance/postgres/tables.sql
maintenance/rebuildrecentchanges.php
maintenance/refreshLinks.php
maintenance/sqlite.inc
maintenance/sqlite/archives/patch-add-3d.sql [new file with mode: 0644]
maintenance/updateCredits.php
maintenance/userOptions.inc
phpcs.xml
profileinfo.php
resources/Resources.php
resources/lib/oojs-ui/i18n/as.json
resources/lib/oojs-ui/i18n/bs.json
resources/lib/oojs-ui/i18n/pt-br.json
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-core-apex.css
resources/lib/oojs-ui/oojs-ui-core-wikimediaui.css
resources/lib/oojs-ui/oojs-ui-core.js
resources/lib/oojs-ui/oojs-ui-core.js.map
resources/lib/oojs-ui/oojs-ui-toolbars-apex.css
resources/lib/oojs-ui/oojs-ui-toolbars-wikimediaui.css
resources/lib/oojs-ui/oojs-ui-toolbars.js
resources/lib/oojs-ui/oojs-ui-widgets-apex.css
resources/lib/oojs-ui/oojs-ui-widgets-wikimediaui.css
resources/lib/oojs-ui/oojs-ui-widgets.js
resources/lib/oojs-ui/oojs-ui-widgets.js.map
resources/lib/oojs-ui/oojs-ui-wikimediaui.js
resources/lib/oojs-ui/oojs-ui-windows-apex.css
resources/lib/oojs-ui/oojs-ui-windows-wikimediaui.css
resources/lib/oojs-ui/oojs-ui-windows.js
resources/lib/oojs-ui/themes/apex/icons-media.json
resources/lib/oojs-ui/themes/apex/images/icons/play-ltr.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/play-ltr.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/play-rtl.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/play-rtl.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/wikimediaui/images/icons/play-ltr-invert.png
resources/lib/oojs-ui/themes/wikimediaui/images/icons/play-ltr-invert.svg
resources/lib/oojs-ui/themes/wikimediaui/images/icons/play-ltr-progressive.png
resources/lib/oojs-ui/themes/wikimediaui/images/icons/play-ltr-progressive.svg
resources/lib/oojs-ui/themes/wikimediaui/images/icons/play-ltr.png
resources/lib/oojs-ui/themes/wikimediaui/images/icons/play-ltr.svg
resources/lib/oojs-ui/themes/wikimediaui/images/icons/play-rtl-invert.png
resources/lib/oojs-ui/themes/wikimediaui/images/icons/play-rtl-invert.svg
resources/lib/oojs-ui/themes/wikimediaui/images/icons/play-rtl-progressive.png
resources/lib/oojs-ui/themes/wikimediaui/images/icons/play-rtl-progressive.svg
resources/lib/oojs-ui/themes/wikimediaui/images/icons/play-rtl.png
resources/lib/oojs-ui/themes/wikimediaui/images/icons/play-rtl.svg
resources/lib/oojs-ui/themes/wikimediaui/images/icons/stop-invert.png
resources/lib/oojs-ui/themes/wikimediaui/images/icons/stop-invert.svg
resources/lib/oojs-ui/themes/wikimediaui/images/icons/stop-progressive.png
resources/lib/oojs-ui/themes/wikimediaui/images/icons/stop-progressive.svg
resources/lib/oojs-ui/themes/wikimediaui/images/icons/stop.png
resources/lib/oojs-ui/themes/wikimediaui/images/icons/stop.svg
resources/lib/qunitjs/qunit.css
resources/lib/qunitjs/qunit.js
resources/src/mediawiki.action/mediawiki.action.edit.js
resources/src/mediawiki.action/mediawiki.action.edit.preview.js
resources/src/mediawiki.action/mediawiki.action.edit.styles.css
resources/src/mediawiki.legacy/commonPrint.css
resources/src/mediawiki.less/mediawiki.mixins.animation.less
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ChangesListViewModel.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FiltersViewModel.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueryItemModel.js
resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js
resources/src/mediawiki.rcfilters/mw.rcfilters.HighlightColors.js
resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js
resources/src/mediawiki.rcfilters/mw.rcfilters.init.js
resources/src/mediawiki.rcfilters/mw.rcfilters.js
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.mixins.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ChangesLimitPopupWidget.less [new file with mode: 0644]
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.ChangesListWrapperWidget.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterTagMultiselectWidget.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.FilterWrapperWidget.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.LiveUpdateButtonWidget.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.SaveFiltersPopupButtonWidget.less
resources/src/mediawiki.rcfilters/styles/mw.rcfilters.ui.less
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitButtonWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesLimitPopupWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ChangesListWrapperWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.DateButtonWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterItemHighlightButton.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuHeaderWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterMenuSectionOptionWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterWrapperWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FormWrapperWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.LiveUpdateButtonWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.SaveFiltersPopupButtonWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.SavedLinksListItemWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.SavedLinksListWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.ValuePickerWidget.js
resources/src/mediawiki.skinning/content.parsoid.less
resources/src/mediawiki.special/mediawiki.special.block.js
resources/src/mediawiki.special/mediawiki.special.changeslist.legend.js
resources/src/mediawiki.special/mediawiki.special.preferences.styles.css
resources/src/mediawiki.special/mediawiki.special.search.styles.css
resources/src/mediawiki.widgets.visibleByteLimit/mediawiki.widgets.visibleByteLimit.js
resources/src/mediawiki.widgets/mw.widgets.DateInputWidget.js
resources/src/mediawiki/htmlform/selectorother.js
resources/src/mediawiki/mediawiki.checkboxtoggle.js
resources/src/mediawiki/mediawiki.hlist-allskins.less [new file with mode: 0644]
resources/src/mediawiki/mediawiki.hlist.css
resources/src/mediawiki/mediawiki.util.js
resources/src/mediawiki/page/gallery.css
resources/src/mediawiki/page/gallery.print.css
tests/common/TestsAutoLoader.php
tests/parser/ParserTestRunner.php
tests/parser/parserTests.txt
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/ResourceLoaderTestCase.php
tests/phpunit/includes/DeprecatedGlobalTest.php
tests/phpunit/includes/GitInfoTest.php
tests/phpunit/includes/PreferencesTest.php
tests/phpunit/includes/RevisionStorageTest.php
tests/phpunit/includes/SanitizerTest.php
tests/phpunit/includes/SiteStatsTest.php [new file with mode: 0644]
tests/phpunit/includes/TitleTest.php
tests/phpunit/includes/WatchedItemQueryServiceUnitTest.php
tests/phpunit/includes/WatchedItemStoreUnitTest.php
tests/phpunit/includes/api/ApiErrorFormatterTest.php
tests/phpunit/includes/api/ApiMainTest.php
tests/phpunit/includes/changes/EnhancedChangesListTest.php
tests/phpunit/includes/changes/OldChangesListTest.php
tests/phpunit/includes/changetags/ChangeTagsTest.php [new file with mode: 0644]
tests/phpunit/includes/config/ConfigFactoryTest.php
tests/phpunit/includes/config/EtcdConfigTest.php
tests/phpunit/includes/content/WikitextContentTest.php
tests/phpunit/includes/db/LBFactoryTest.php
tests/phpunit/includes/libs/CSSMinTest.php
tests/phpunit/includes/libs/objectcache/MultiWriteBagOStuffTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseDomainTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php
tests/phpunit/includes/page/WikiPageTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderClientHtmlTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderFileModuleTest.php
tests/phpunit/includes/site/TestSites.php
tests/phpunit/includes/specialpage/ChangesListSpecialPageTest.php
tests/phpunit/includes/specials/SpecialRecentchangesTest.php
tests/phpunit/includes/specials/SpecialShortpagesTest.php [new file with mode: 0644]
tests/phpunit/suite.xml
tests/qunit/data/testrunner.js
tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js
tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js

index a82ae21..3e97aab 100644 (file)
@@ -48,7 +48,6 @@ node_modules/
 # Composer
 /vendor
 /composer.lock
-/composer.json
 /composer.local.json
 
 # MediaWiki UI documentation
index 2134fc5..5a76fb9 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -63,6 +63,7 @@ aude <aude.wiki@gmail.com>
 Audrey Tang <audreyt@audreyt.org>
 Audrey Tang <audreyt@audreyt.org> <au@localhost>
 ayush_garg <ayush.ce13@iitp.ac.in>
+Bae Junehyeon <devunt@gmail.com>
 Bahodir Mansurov <bmansurov@wikimedia.org>
 Bartosz Dziewoński <matma.rex@gmail.com>
 Bartosz Dziewoński <matma.rex@gmail.com> <bdziewonski@wikimedia.org>
@@ -227,7 +228,6 @@ Jon Robson <jrobson@wikimedia.org>
 Jon Robson <jrobson@wikimedia.org> <jdlrobson@gmail.com>
 Juliusz Gonera <jgonera@gmail.com>
 Juliusz Gonera <jgonera@gmail.com> <jgonera@wikimedia.org>
-JuneHyeon Bae <devunt@gmail.com>
 Jure Kajzer <freak@drajv.si>
 Jure Kajzer <freak@drajv.si> <freakolowsky@users.mediawiki.org>
 Justin Du <justin.d128@gmail.com>
index 5e2c7a0..cde7193 100644 (file)
@@ -7,11 +7,12 @@
 # complement that setup by testing MediaWiki on travis
 #
 language: php
-# Use the slower sudo-enabled VMs instead of fast containers:
-# - Package 'djvulibre-bin' is not yet whitelisted for trusty containers.
-#   https://github.com/travis-ci/apt-package-whitelist/issues/4036
-sudo: required
-# Use Trusty instead of Travis default (precise)
+
+
+# Use fast containers instead of the slower sudo-enabled VMs:
+sudo: false
+# Use Ubuntu 14 Trusty (not Ubuntu 12 Precise)
+# <https://docs.travis-ci.com/user/reference/trusty/>
 # - Required in order to use HHVM 3.6 or higher.
 # - Required for non-buggy xml library for XmlTypeCheck/UploadBaseTest (T75176).
 dist: trusty
@@ -25,10 +26,11 @@ matrix:
       php: 5.5
     - env: dbtype=postgres dbuser=travis
       php: 5.5
+    # https://docs.travis-ci.com/user/languages/php#HHVM-versions
     - env: dbtype=mysql dbuser=root
-      # https://docs.travis-ci.com/user/languages/php#HHVM-versions
-      # https://github.com/travis-ci/travis-ci/issues/7368
       php: hhvm-3.12
+    - env: dbtype=mysql dbuser=root
+      php: hhvm-3.18
     - env: dbtype=mysql dbuser=root
       php: 7
 
diff --git a/CREDITS b/CREDITS
index 14c454e..c38c3fc 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -78,6 +78,7 @@ The following list can be found parsed under Special:Version/Credits -->
 * awu42
 * ayush_garg
 * Azliq7
+* Bae Junehyeon
 * Bagariavivek
 * Bahodir Mansurov
 * balloonguy
@@ -312,7 +313,6 @@ The following list can be found parsed under Special:Version/Credits -->
 * Julian Ostrow
 * Juliano F. Ravasi
 * Juliusz Gonera
-* JuneHyeon Bae
 * Jure Kajzer
 * Justin Du
 * Kai Nissen
index 811d2c0..dbbfcb8 100644 (file)
@@ -44,8 +44,6 @@ module.exports = function ( grunt ) {
                                '!extensions/**/*.js',
                                '!skins/**/*.js',
                                // Skip functions aren't even parseable
-                               '!resources/src/dom-level2-skip.js',
-                               '!resources/src/es5-skip.js',
                                '!resources/src/mediawiki.hidpi-skip.js'
                        ]
                },
index 51f9764..452cb35 100644 (file)
@@ -26,6 +26,13 @@ section).
   array. This allows dependency injection to be used for ResourceLoader modules.
 * $wgExceptionHooks has been removed.
 * (T45547) $wgUsePigLatinVariant added (off by default).
+* (T152540) MediaWiki now supports a section ID escaping style that allows to display
+  non-Latin characters verbatim on many modern browsers. This is controlled by the
+  new configuration setting, $wgFragmentMode.
+* $wgExperimentalHtmlIds is now deprecated and will be removed in a future version,
+  use $wgFragmentMode to migrate off it to a modern alternative.
+* $wgExternalInterwikiFragmentMode was introduced to control how fragments in
+  sinterwikis going outside of current wiki farm are encoded.
 
 === New features in 1.30 ===
 * (T37247) Output from Parser::parse() will now be wrapped in a div with
@@ -46,6 +53,7 @@ section).
 === Languages updated in 1.30 ===
 
 * Support for kbp (Kabɩyɛ / Kabiyè) was added.
+* Support for skr (Saraiki, سرائیکی) was added.
 
 === External library changes in 1.30 ===
 
@@ -142,6 +150,14 @@ changes to languages because of Phabricator reports.
   MediaWikiServices instead. Access to the underlying BagOStuff is possible
   through the new ParserCache::getCacheStorage() method.
 * .mw-ui-constructive CSS class (deprecated in 1.27) was removed.
+* Sanitizer::escapeId() was deprecated, use escapeIdForAttribute(),
+  escapeIdForLink() or escapeIdForExternalInterwiki() instead.
+* Title::escapeFragmentForURL() was deprecated, use one of the aforementioned
+  Sanitizer functions or, if possible, Title::getFragmentForURL().
+* Second parameter to Sanitizer::escapeIdReferenceList() ($options) now does
+  nothing and is deprecated.
+* mw.util.escapeId() was deprecated, use escapeIdForAttribute() or
+  escapeIdForLink().
 
 == Compatibility ==
 MediaWiki 1.30 requires PHP 5.5.9 or later. There is experimental support for
index a6128a4..d44a305 100644 (file)
@@ -193,6 +193,7 @@ $wgAutoloadLocalClasses = [
        'BenchmarkDeleteTruncate' => __DIR__ . '/maintenance/benchmarks/bench_delete_truncate.php',
        'BenchmarkHooks' => __DIR__ . '/maintenance/benchmarks/benchmarkHooks.php',
        'BenchmarkJSMinPlus' => __DIR__ . '/maintenance/benchmarks/benchmarkJSMinPlus.php',
+       'BenchmarkLruHash' => __DIR__ . '/maintenance/benchmarks/benchmarkLruHash.php',
        'BenchmarkParse' => __DIR__ . '/maintenance/benchmarks/benchmarkParse.php',
        'BenchmarkPurge' => __DIR__ . '/maintenance/benchmarks/benchmarkPurge.php',
        'BenchmarkTidy' => __DIR__ . '/maintenance/benchmarks/benchmarkTidy.php',
@@ -679,6 +680,7 @@ $wgAutoloadLocalClasses = [
        'JobQueueMemory' => __DIR__ . '/includes/jobqueue/JobQueueMemory.php',
        'JobQueueReadOnlyError' => __DIR__ . '/includes/jobqueue/JobQueue.php',
        'JobQueueRedis' => __DIR__ . '/includes/jobqueue/JobQueueRedis.php',
+       'JobQueueSecondTestQueue' => __DIR__ . '/includes/jobqueue/JobQueueSecondTestQueue.php',
        'JobRunner' => __DIR__ . '/includes/jobqueue/JobRunner.php',
        'JobSpecification' => __DIR__ . '/includes/jobqueue/JobSpecification.php',
        'JpegHandler' => __DIR__ . '/includes/media/Jpeg.php',
@@ -1118,6 +1120,7 @@ $wgAutoloadLocalClasses = [
        'PopulateInterwiki' => __DIR__ . '/maintenance/populateInterwiki.php',
        'PopulateLogSearch' => __DIR__ . '/maintenance/populateLogSearch.php',
        'PopulateLogUsertext' => __DIR__ . '/maintenance/populateLogUsertext.php',
+       'PopulatePPSortKey' => __DIR__ . '/maintenance/populatePPSortKey.php',
        'PopulateParentId' => __DIR__ . '/maintenance/populateParentId.php',
        'PopulateRecentChangesSource' => __DIR__ . '/maintenance/populateRecentChangesSource.php',
        'PopulateRevisionLength' => __DIR__ . '/maintenance/populateRevisionLength.php',
@@ -1228,6 +1231,7 @@ $wgAutoloadLocalClasses = [
        'ResourceLoaderJqueryMsgModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderJqueryMsgModule.php',
        'ResourceLoaderLanguageDataModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderLanguageDataModule.php',
        'ResourceLoaderLanguageNamesModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderLanguageNamesModule.php',
+       'ResourceLoaderMediaWikiUtilModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderMediaWikiUtilModule.php',
        'ResourceLoaderModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderModule.php',
        'ResourceLoaderOOUIFileModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderOOUIFileModule.php',
        'ResourceLoaderOOUIImageModule' => __DIR__ . '/includes/resourceloader/ResourceLoaderOOUIImageModule.php',
index 83fcda0..bc48360 100644 (file)
@@ -25,7 +25,7 @@
                "ext-xml": "*",
                "liuggio/statsd-php-client": "1.0.18",
                "mediawiki/at-ease": "1.1.0",
-               "oojs/oojs-ui": "0.22.3",
+               "oojs/oojs-ui": "0.22.4",
                "oyejorge/less.php": "1.7.0.14",
                "php": ">=5.5.9",
                "psr/log": "1.0.2",
@@ -37,7 +37,7 @@
                "wikimedia/html-formatter": "1.0.1",
                "wikimedia/ip-set": "1.1.0",
                "wikimedia/php-session-serializer": "1.0.4",
-               "wikimedia/relpath": "1.0.3",
+               "wikimedia/relpath": "2.0.0",
                "wikimedia/remex-html": "1.0.1",
                "wikimedia/running-stat": "1.1.0",
                "wikimedia/scoped-callback": "1.0.0",
@@ -53,7 +53,7 @@
                "jakub-onderka/php-parallel-lint": "0.9.2",
                "jetbrains/phpstorm-stubs": "dev-master#1b9906084d6635456fcf3f3a01f0d7d5b99a578a",
                "justinrainbow/json-schema": "~5.2",
-               "mediawiki/mediawiki-codesniffer": "0.8.1",
+               "mediawiki/mediawiki-codesniffer": "0.10.1",
                "monolog/monolog": "~1.22.1",
                "nikic/php-parser": "2.1.0",
                "nmred/kafka-php": "0.1.5",
index 44ec764..dbc9204 100644 (file)
@@ -17,7 +17,7 @@ description of the tables and their contents, please see:
 
 To make a read query, something like this usually suffices:
 
-$dbr = wfGetDB( DB_SLAVE );
+$dbr = wfGetDB( DB_REPLICA );
 $res = $dbr->select( /* ...see docs... */ );
 foreach ( $res as $row ) {
        ...
index 3ff3365..8912b82 100644 (file)
@@ -2764,9 +2764,10 @@ configuration variables to JavaScript. Things that depend on the current page
 or request state must be added through MakeGlobalVariablesScript instead.
 &$vars: array( variable name => value )
 
-'ResourceLoaderGetLessVars': Called in ResourceLoader::getLessVars after
-variables from $wgResourceLoaderLESSVars are added. Can be used to add
-context-based variables.
+'ResourceLoaderGetLessVars': DEPRECATED! Called in ResourceLoader::getLessVars
+to add global LESS variables. Loaded after $wgResourceLoaderLESSVars is added.
+Global LESS variables are deprecated. Use ResourceLoaderModule::getLessVars()
+instead to expose variables only in modules that need them.
 &$lessVars: array of variables already added
 
 'ResourceLoaderJqueryMsgModuleMagicWords': Called in
index 2c935df..2ca56a5 100644 (file)
@@ -485,7 +485,7 @@ class Block {
 
                # Periodic purge via commit hooks
                if ( mt_rand( 0, 9 ) == 0 ) {
-                       Block::purgeExpired();
+                       self::purgeExpired();
                }
 
                $row = $this->getDatabaseArray();
@@ -778,12 +778,12 @@ class Block {
                # It's okay to autoblock. Go ahead and insert/update the block...
 
                # Do not add a *new* block if the IP is already blocked.
-               $ipblock = Block::newFromTarget( $autoblockIP );
+               $ipblock = self::newFromTarget( $autoblockIP );
                if ( $ipblock ) {
                        # Check if the block is an autoblock and would exceed the user block
                        # if renewed. If so, do nothing, otherwise prolong the block time...
                        if ( $ipblock->mAuto && // @todo Why not compare $ipblock->mExpiry?
-                               $this->mExpiry > Block::getAutoblockExpiry( $ipblock->mTimestamp )
+                               $this->mExpiry > self::getAutoblockExpiry( $ipblock->mTimestamp )
                        ) {
                                # Reset block timestamp to now and its expiry to
                                # $wgAutoblockExpiry in the future
@@ -810,11 +810,11 @@ class Block {
 
                if ( $this->mExpiry == 'infinity' ) {
                        # Original block was indefinite, start an autoblock now
-                       $autoblock->mExpiry = Block::getAutoblockExpiry( $timestamp );
+                       $autoblock->mExpiry = self::getAutoblockExpiry( $timestamp );
                } else {
                        # If the user is already blocked with an expiry date, we don't
                        # want to pile on top of that.
-                       $autoblock->mExpiry = min( $this->mExpiry, Block::getAutoblockExpiry( $timestamp ) );
+                       $autoblock->mExpiry = min( $this->mExpiry, self::getAutoblockExpiry( $timestamp ) );
                }
 
                # Insert the block...
@@ -870,7 +870,7 @@ class Block {
        public function updateTimestamp() {
                if ( $this->mAuto ) {
                        $this->mTimestamp = wfTimestamp();
-                       $this->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp );
+                       $this->mExpiry = self::getAutoblockExpiry( $this->mTimestamp );
 
                        $dbw = wfGetDB( DB_MASTER );
                        $dbw->update( 'ipblocks',
@@ -958,6 +958,7 @@ class Block {
 
        /**
         * Get the system block type, if any
+        * @since 1.29
         * @return string|null
         */
        public function getSystemBlockType() {
@@ -1111,8 +1112,8 @@ class Block {
         */
        public static function newFromTarget( $specificTarget, $vagueTarget = null, $fromMaster = false ) {
                list( $target, $type ) = self::parseTarget( $specificTarget );
-               if ( $type == Block::TYPE_ID || $type == Block::TYPE_AUTO ) {
-                       return Block::newFromID( $target );
+               if ( $type == self::TYPE_ID || $type == self::TYPE_AUTO ) {
+                       return self::newFromID( $target );
 
                } elseif ( $target === null && $vagueTarget == '' ) {
                        # We're not going to find anything useful here
@@ -1122,7 +1123,7 @@ class Block {
 
                } elseif ( in_array(
                        $type,
-                       [ Block::TYPE_USER, Block::TYPE_IP, Block::TYPE_RANGE, null ] )
+                       [ self::TYPE_USER, self::TYPE_IP, self::TYPE_RANGE, null ] )
                ) {
                        $block = new Block();
                        $block->fromMaster( $fromMaster );
@@ -1189,7 +1190,7 @@ class Block {
                }
                $selectFields = array_merge(
                        [ 'ipb_range_start', 'ipb_range_end' ],
-                       Block::selectFields()
+                       self::selectFields()
                );
                $rows = $db->select( 'ipblocks',
                        $selectFields,
@@ -1350,12 +1351,12 @@ class Block {
                        # off validation checking (which would exclude IP addresses)
                        return [
                                User::newFromName( IP::sanitizeIP( $target ), false ),
-                               Block::TYPE_IP
+                               self::TYPE_IP
                        ];
 
                } elseif ( IP::isValidBlock( $target ) ) {
                        # Can't create a User from an IP range
-                       return [ IP::sanitizeRange( $target ), Block::TYPE_RANGE ];
+                       return [ IP::sanitizeRange( $target ), self::TYPE_RANGE ];
                }
 
                # Consider the possibility that this is not a username at all
@@ -1370,11 +1371,11 @@ class Block {
                        # Note that since numbers are valid usernames, a $target of "12345" will be
                        # considered a User.  If you want to pass a block ID, prepend a hash "#12345",
                        # since hash characters are not valid in usernames or titles generally.
-                       return [ $userObj, Block::TYPE_USER ];
+                       return [ $userObj, self::TYPE_USER ];
 
                } elseif ( preg_match( '/^#\d+$/', $target ) ) {
                        # Autoblock reference in the form "#12345"
-                       return [ substr( $target, 1 ), Block::TYPE_AUTO ];
+                       return [ substr( $target, 1 ), self::TYPE_AUTO ];
 
                } else {
                        # WTF?
@@ -1450,6 +1451,8 @@ class Block {
         * Set the 'BlockID' cookie to this block's ID and expiry time. The cookie's expiry will be
         * the same as the block's, to a maximum of 24 hours.
         *
+        * @since 1.29
+        *
         * @param WebResponse $response The response on which to set the cookie.
         */
        public function setCookie( WebResponse $response ) {
@@ -1472,6 +1475,8 @@ class Block {
        /**
         * Unset the 'BlockID' cookie.
         *
+        * @since 1.29
+        *
         * @param WebResponse $response The response on which to unset the cookie.
         */
        public static function clearCookie( WebResponse $response ) {
@@ -1482,6 +1487,9 @@ class Block {
         * Get the BlockID cookie's value for this block. This is usually the block ID concatenated
         * with an HMAC in order to avoid spoofing (T152951), but if wgSecretKey is not set will just
         * be the block ID.
+        *
+        * @since 1.29
+        *
         * @return string The block ID, probably concatenated with "!" and the HMAC.
         */
        public function getCookieValue() {
@@ -1500,7 +1508,11 @@ class Block {
        /**
         * Get the stored ID from the 'BlockID' cookie. The cookie's value is usually a combination of
         * the ID and a HMAC (see Block::setCookie), but will sometimes only be the ID.
+        *
+        * @since 1.29
+        *
         * @param string $cookieValue The string in which to find the ID.
+        *
         * @return integer|null The block ID, or null if the HMAC is present and invalid.
         */
        public static function getIdFromCookieValue( $cookieValue ) {
index f35715e..a28aa5b 100644 (file)
@@ -1304,7 +1304,7 @@ $wgMimeInfoFile = 'includes/mime.info';
  * Sets an external MIME detector program. The command must print only
  * the MIME type to standard output.
  * The name of the file to process will be appended to the command given here.
- * If not set or NULL, PHP's fileinfo extension will be used if available.
+ * If not set or NULL, PHP's mime_content_type function will be used.
  *
  * @par Example:
  * @code
@@ -3372,16 +3372,56 @@ $wgApiFrameOptions = 'DENY';
 $wgDisableOutputCompression = false;
 
 /**
- * Should we allow a broader set of characters in id attributes, per HTML5?  If
- * not, use only HTML 4-compatible IDs.  This option is for testing -- when the
- * functionality is ready, it will be on by default with no option.
+ * Abandoned experiment with HTML5-style ID escaping. Normalized IDs a bit
+ * too aggressively, breaking preexisting content (particularly Cite).
+ * See T29733, T29694, T29474.
  *
- * Currently this appears to work fine in all browsers, but it's disabled by
- * default because it normalizes id's a bit too aggressively, breaking preexisting
- * content (particularly Cite).  See T29733, T29694, T29474.
+ * @deprecated since 1.30, use $wgFragmentMode
  */
 $wgExperimentalHtmlIds = false;
 
+/**
+ * How should section IDs be encoded?
+ * This array can contain 1 or 2 elements, each of them can be one of:
+ * - 'html5'  is modern HTML5 style encoding with minimal escaping. Allows to
+ *            display Unicode characters in many browsers' address bars.
+ * - 'legacy' is old MediaWiki-style encoding, e.g. 啤酒 turns into .E5.95.A4.E9.85.92
+ * - 'html5-legacy' corresponds to DEPRECATED $wgExperimentalHtmlIds mode. DO NOT use
+ *            it for anything but migration off that mode (see below).
+ *
+ * The first element of this array specifies the primary mode of escaping IDs. This
+ * is what users will see when they e.g. follow an [[#internal link]] to a section of
+ * a page.
+ *
+ * The optional second element defines a fallback mode, useful for migrations.
+ * If present, it will direct MediaWiki to add empty <span>s to every section with its
+ * id attribute set to fallback encoded title so that links using the previous encoding
+ * would still work.
+ *
+ * Example: you want to migrate your wiki from 'legacy' to 'html5'
+ *
+ * On the first step, set this variable to [ 'legacy', 'html5' ]. After a while, when
+ * all caches (parser, HTTP, etc.) contain only pages generated with this setting,
+ * flip the value to [ 'html5', 'legacy' ]. This will result in all internal links being
+ * generated in the new encoding while old links (both external and cached internal) will
+ * still work. After a long time, you might want to ditch backwards compatibility and
+ * set it to [ 'html5' ]. After all, pages get edited, breaking incoming links no matter which
+ * fragment mode is used.
+ *
+ * @since 1.30
+ */
+$wgFragmentMode = [ 'legacy' ];
+
+/**
+ * Which ID escaping mode should be used for external interwiki links? See documentation
+ * for $wgFragmentMode above for details of each mode. Because you can't control external sites,
+ * this setting should probably always be 'legacy', unless every wiki you link to has converted
+ * to 'html5'.
+ *
+ * @since 1.30
+ */
+$wgExternalInterwikiFragmentMode = 'legacy';
+
 /**
  * Abstract list of footer icons for skins in place of old copyrightico and poweredbyico code
  * You can add new icons to the built in copyright or poweredby, or you can create
@@ -3754,20 +3794,18 @@ $wgResourceLoaderValidateStaticJS = false;
  * at the beginning of all your .less files, with all the consequences.
  * In particular, string values must be escaped and quoted.
  *
- * Changes to LESS variables do not trigger cache invalidation.
- *
- * If the LESS variables need to be dynamic, you can use the
- * ResourceLoaderGetLessVars hook (since 1.25).
+ * Changes to this configuration do NOT trigger cache invalidation.
  *
  * @par Example:
  * @code
  *   $wgResourceLoaderLESSVars = [
- *     'baseFontSize'  => '1em',
- *     'smallFontSize' => '0.75em',
- *     'WikimediaBlue' => '#006699',
+ *     'exampleFontSize'  => '1em',
+ *     'exampleBlue' => '#eee',
  *   ];
  * @endcode
  * @since 1.22
+ * @deprecated since 1.30 Use ResourceLoaderModule::getLessVars() instead to
+ *  add variables to individual modules that need them.
  */
 $wgResourceLoaderLESSVars = [
        /**
@@ -4091,6 +4129,14 @@ $wgTrackingCategories = [];
  */
 $wgContentNamespaces = [ NS_MAIN ];
 
+/**
+ * Optional array of namespaces which should be blacklisted from Special:ShortPages
+ * Only pages inside $wgContentNamespaces but not $wgShortPagesNamespaceBlacklist will
+ * be shown on that page.
+ * @since 1.30
+ */
+$wgShortPagesNamespaceBlacklist = [];
+
 /**
  * Array of namespaces, in addition to the talk namespaces, where signatures
  * (~~~~) are likely to be used. This determines whether to display the
@@ -6771,11 +6817,6 @@ $wgRCWatchCategoryMembership = false;
  */
 $wgUseRCPatrol = true;
 
-/**
- * Whether to allow users to save their RecentChanges filters
- */
-$wgStructuredChangeFiltersEnableSaving = true;
-
 /**
  * Whether to show the new experimental views (like namespaces, tags, and users) in
  * RecentChanges filters
@@ -8238,10 +8279,15 @@ $wgHTTPProxy = false;
  * Local virtual hosts.
  *
  * This lists domains that are configured as virtual hosts on the same machine.
- * If a request is to be made to a domain listed here, or any subdomain thereof,
- * then no proxy will be used.
- * Command-line scripts are not affected by this setting and will always use
- * proxy if it is configured.
+ *
+ * This affects the following:
+ * - MWHttpRequest: If a request is to be made to a domain listed here, or any
+ *   subdomain thereof, then no proxy will be used.
+ *   Command-line scripts are not affected by this setting and will always use
+ *   the proxy if it is configured.
+ * - ChronologyProtector: Decide to shutdown LBFactory asynchronously instead
+ *   synchronously if the current response redirects to a local virtual host.
+ *
  * @since 1.25
  */
 $wgLocalVirtualHosts = [];
index 973327b..9d83fbd 100644 (file)
@@ -1698,7 +1698,7 @@ class EditPage {
                global $wgParser;
 
                if ( $this->sectiontitle !== '' ) {
-                       $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle );
+                       $sectionanchor = $this->guessSectionName( $this->sectiontitle );
                        // If no edit summary was specified, create one automatically from the section
                        // title and have it link to the new section. Otherwise, respect the summary as
                        // passed.
@@ -1708,7 +1708,7 @@ class EditPage {
                                        ->rawParams( $cleanSectionTitle )->inContentLanguage()->text();
                        }
                } elseif ( $this->summary !== '' ) {
-                       $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $this->summary );
+                       $sectionanchor = $this->guessSectionName( $this->summary );
                        # This is a new section, so create a link to the new section
                        # in the revision summary.
                        $cleanSummary = $wgParser->stripSectionName( $this->summary );
@@ -1743,7 +1743,7 @@ class EditPage {
         * time.
         */
        public function internalAttemptSave( &$result, $bot = false ) {
-               global $wgUser, $wgRequest, $wgParser, $wgMaxArticleSize;
+               global $wgUser, $wgRequest, $wgMaxArticleSize;
                global $wgContentHandlerUseDB;
 
                $status = Status::newGood();
@@ -2117,7 +2117,7 @@ class EditPage {
                                # We can't deal with anchors, includes, html etc in the header for now,
                                # headline would need to be parsed to improve this.
                                if ( $hasmatch && strlen( $matches[2] ) > 0 ) {
-                                       $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $matches[2] );
+                                       $sectionanchor = $this->guessSectionName( $matches[2] );
                                }
                        }
                        $result['sectionanchor'] = $sectionanchor;
@@ -2783,7 +2783,7 @@ class EditPage {
                $wgOut->addHTML( $this->editFormTextBeforeContent );
 
                if ( !$this->isCssJsSubpage && $showToolbar && $wgUser->getOption( 'showtoolbar' ) ) {
-                       $wgOut->addHTML( EditPage::getEditToolbar( $this->mTitle ) );
+                       $wgOut->addHTML( self::getEditToolbar( $this->mTitle ) );
                }
 
                if ( $this->blankArticle ) {
@@ -3076,7 +3076,7 @@ class EditPage {
                        'tabindex' => 1,
                        'size' => 60,
                        'spellcheck' => 'true',
-               ] + Linker::tooltipAndAccesskeyAttribs( 'summary' );
+               ];
        }
 
        /**
@@ -3097,6 +3097,7 @@ class EditPage {
                $inputAttrs = null, $spanLabelAttrs = null
        ) {
                $inputAttrs = $this->getSummaryInputAttributes( $inputAttrs );
+               $inputAttrs += Linker::tooltipAndAccesskeyAttribs( 'summary' );
 
                $spanLabelAttrs = ( is_array( $spanLabelAttrs ) ? $spanLabelAttrs : [] ) + [
                        'class' => $this->missingSummary ? 'mw-summarymissed' : 'mw-summary',
@@ -3132,6 +3133,10 @@ class EditPage {
                $inputAttrs = OOUI\Element::configFromHtmlAttributes(
                        $this->getSummaryInputAttributes( $inputAttrs )
                );
+               $inputAttrs += [
+                       'title' => Linker::titleAttrib( 'summary' ),
+                       'accessKey' => Linker::accesskey( 'summary' ),
+               ];
 
                // For compatibility with old scripts and extensions, we want the legacy 'id' on the `<input>`
                $inputAttrs['inputId'] = $inputAttrs['id'];
@@ -3492,6 +3497,10 @@ HTML
                }
        }
 
+       /**
+        * Inserts optional text shown below edit and upload forms. Can be used to offer special characters not present on
+        * most keyboards for copying/pasting.
+        */
        protected function showEditTools() {
                global $wgOut;
                $wgOut->addHTML( '<div class="mw-editTools">' .
@@ -4273,7 +4282,7 @@ HTML
                        $accesskey = null;
                        if ( isset( $options['tooltip'] ) ) {
                                $accesskey = $this->context->msg( "accesskey-{$options['tooltip']}" )->text();
-                               $title = Linker::titleAttrib( $options['tooltip'], 'withaccess' );
+                               $title = Linker::titleAttrib( $options['tooltip'] );
                        }
                        if ( isset( $options['title-message'] ) ) {
                                $title = $this->context->msg( $options['title-message'] )->text();
@@ -4351,8 +4360,7 @@ HTML
                $attribs = [
                        'name' => 'wpSave',
                        'tabindex' => ++$tabindex,
-               ] + Linker::tooltipAndAccesskeyAttribs( 'save' );
-
+               ];
                if ( $this->oouiEnabled ) {
                        $saveConfig = OOUI\Element::configFromHtmlAttributes( $attribs );
                        $buttons['save'] = new OOUI\ButtonInputWidget( [
@@ -4364,11 +4372,13 @@ HTML
                                'label' => $buttonLabel,
                                'infusable' => true,
                                'type' => 'submit',
+                               'title' => Linker::titleAttrib( 'save' ),
+                               'accessKey' => Linker::accesskey( 'save' ),
                        ] + $saveConfig );
                } else {
                        $buttons['save'] = Html::submitButton(
                                $buttonLabel,
-                               $attribs + [ 'id' => 'wpSave' ],
+                               $attribs + Linker::tooltipAndAccesskeyAttribs( 'save' ) + [ 'id' => 'wpSave' ],
                                [ 'mw-ui-progressive' ]
                        );
                }
@@ -4376,7 +4386,7 @@ HTML
                $attribs = [
                        'name' => 'wpPreview',
                        'tabindex' => ++$tabindex,
-               ] + Linker::tooltipAndAccesskeyAttribs( 'preview' );
+               ];
                if ( $this->oouiEnabled ) {
                        $previewConfig = OOUI\Element::configFromHtmlAttributes( $attribs );
                        $buttons['preview'] = new OOUI\ButtonInputWidget( [
@@ -4386,18 +4396,20 @@ HTML
                                'useInputTag' => true,
                                'label' => $this->context->msg( 'showpreview' )->text(),
                                'infusable' => true,
-                               'type' => 'submit'
+                               'type' => 'submit',
+                               'title' => Linker::titleAttrib( 'preview' ),
+                               'accessKey' => Linker::accesskey( 'preview' ),
                        ] + $previewConfig );
                } else {
                        $buttons['preview'] = Html::submitButton(
                                $this->context->msg( 'showpreview' )->text(),
-                               $attribs + [ 'id' => 'wpPreview' ]
+                               $attribs + Linker::tooltipAndAccesskeyAttribs( 'preview' ) + [ 'id' => 'wpPreview' ]
                        );
                }
                $attribs = [
                        'name' => 'wpDiff',
                        'tabindex' => ++$tabindex,
-               ] + Linker::tooltipAndAccesskeyAttribs( 'diff' );
+               ];
                if ( $this->oouiEnabled ) {
                        $diffConfig = OOUI\Element::configFromHtmlAttributes( $attribs );
                        $buttons['diff'] = new OOUI\ButtonInputWidget( [
@@ -4408,11 +4420,13 @@ HTML
                                'label' => $this->context->msg( 'showdiff' )->text(),
                                'infusable' => true,
                                'type' => 'submit',
+                               'title' => Linker::titleAttrib( 'diff' ),
+                               'accessKey' => Linker::accesskey( 'diff' ),
                        ] + $diffConfig );
                } else {
                        $buttons['diff'] = Html::submitButton(
                                $this->context->msg( 'showdiff' )->text(),
-                               $attribs + [ 'id' => 'wpDiff' ]
+                               $attribs + Linker::tooltipAndAccesskeyAttribs( 'diff' ) + [ 'id' => 'wpDiff' ]
                        );
                }
 
@@ -4781,4 +4795,27 @@ HTML
                }
                return $wikitext;
        }
+
+       /**
+        * Turns section name wikitext into anchors for use in HTTP redirects. Various
+        * versions of Microsoft browsers misinterpret fragment encoding of Location: headers
+        * resulting in mojibake in address bar. Redirect them to legacy section IDs,
+        * if possible. All the other browsers get HTML5 if the wiki is configured for it, to
+        * spread the new style links more efficiently.
+        *
+        * @param string $text
+        * @return string
+        */
+       private function guessSectionName( $text ) {
+               global $wgParser;
+
+               // Detect Microsoft browsers
+               $userAgent = $this->context->getRequest()->getHeader( 'User-Agent' );
+               if ( $userAgent && preg_match( '/MSIE|Edge/', $userAgent ) ) {
+                       // ...and redirect them to legacy encoding, if available
+                       return $wgParser->guessLegacySectionNameFromWikiText( $text );
+               }
+               // Meanwhile, real browsers get real anchors
+               return $wgParser->guessSectionNameFromWikiText( $text );
+       }
 }
index 189fd9f..f76a634 100644 (file)
@@ -54,8 +54,6 @@ class FeedItem {
        public $rssIsPermalink = false;
 
        /**
-        * Constructor
-        *
         * @param string|Title $title Item's title
         * @param string $description
         * @param string $url URL uniquely designating the item.
index e7b4a1f..8a1cd35 100644 (file)
@@ -47,8 +47,6 @@ class FileDeleteForm {
        private $oldimage = '';
 
        /**
-        * Constructor
-        *
         * @param File $file File object we're deleting
         */
        public function __construct( $file ) {
index 92cb8d8..70784ba 100644 (file)
@@ -3617,6 +3617,7 @@ function wfCanIPUseHTTPS( $ip ) {
  * @since 1.25
  */
 function wfIsInfinity( $str ) {
+       // These are hardcoded elsewhere in MediaWiki (e.g. mediawiki.special.block.js).
        $infinityValues = [ 'infinite', 'indefinite', 'infinity', 'never' ];
        return in_array( $str, $infinityValues );
 }
index 56cf815..51bd7a9 100644 (file)
@@ -76,9 +76,6 @@ class ConcatenatedGzipHistoryBlob implements HistoryBlob {
        public $mMaxSize = 10000000;
        public $mMaxCount = 100;
 
-       /**
-        * Constructor
-        */
        public function __construct() {
                if ( !function_exists( 'gzdeflate' ) ) {
                        throw new MWException( "Need zlib support to read or write this "
index 2f50558..790e2be 100644 (file)
@@ -50,7 +50,7 @@ class LinkFilter {
 
                $text = $content->getNativeData();
 
-               $regex = LinkFilter::makeRegex( $filterEntry );
+               $regex = self::makeRegex( $filterEntry );
                return preg_match( $regex, $text );
        }
 
index f2e4ac4..2ca851c 100644 (file)
@@ -1328,7 +1328,7 @@ class Linker {
                Title $title, $text, $wikiId = null, $options = []
        ) {
                if ( $wikiId !== null && !$title->isExternal() ) {
-                       $link = Linker::makeExternalLink(
+                       $link = self::makeExternalLink(
                                WikiMap::getForeignURL(
                                        $wikiId,
                                        $title->getNamespace() === 0
@@ -1341,7 +1341,7 @@ class Linker {
                                /* escape = */ false // Already escaped
                        );
                } else {
-                       $link = Linker::link( $title, $text, [], [], $options );
+                       $link = self::link( $title, $text, [], [], $options );
                }
 
                return $link;
@@ -1608,22 +1608,24 @@ class Linker {
         *   a space and ending with '>'
         *   This *must* be at least '>' for no attribs
         * @param string $anchor The anchor to give the headline (the bit after the #)
-        * @param string $html Html for the text of the header
+        * @param string $html HTML for the text of the header
         * @param string $link HTML to add for the section edit link
-        * @param bool|string $legacyAnchor A second, optional anchor to give for
+        * @param string|bool $fallbackAnchor A second, optional anchor to give for
         *   backward compatibility (false to omit)
         *
         * @return string HTML headline
         */
        public static function makeHeadline( $level, $attribs, $anchor, $html,
-               $link, $legacyAnchor = false
+               $link, $fallbackAnchor = false
        ) {
+               $anchorEscaped = htmlspecialchars( $anchor );
                $ret = "<h$level$attribs"
-                       . "<span class=\"mw-headline\" id=\"$anchor\">$html</span>"
+                       . "<span class=\"mw-headline\" id=\"$anchorEscaped\">$html</span>"
                        . $link
                        . "</h$level>";
-               if ( $legacyAnchor !== false ) {
-                       $ret = "<div id=\"$legacyAnchor\"></div>$ret";
+               if ( $fallbackAnchor !== false && $fallbackAnchor !== $anchor ) {
+                       $fallbackAnchor = htmlspecialchars( $fallbackAnchor );
+                       $ret = "<div id=\"$fallbackAnchor\"></div>$ret";
                }
                return $ret;
        }
@@ -2021,7 +2023,7 @@ class Linker {
                }
 
                if ( !$rev->userCan( Revision::DELETED_RESTRICTED, $user ) ) {
-                       return Linker::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops
+                       return self::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops
                } else {
                        if ( $rev->getId() ) {
                                // RevDelete links using revision ID are stable across
@@ -2040,7 +2042,7 @@ class Linker {
                                        'ids' => $rev->getTimestamp()
                                ];
                        }
-                       return Linker::revDeleteLink( $query,
+                       return self::revDeleteLink( $query,
                                $rev->isDeleted( Revision::DELETED_RESTRICTED ), $canHide );
                }
        }
index 2c87b8b..7a5fd9a 100644 (file)
@@ -42,7 +42,7 @@ class ListToggle {
        private function checkboxLink( $checkboxType ) {
                return Html::element(
                        // CSS classes: mw-checkbox-all, mw-checkbox-none, mw-checkbox-invert
-                       'a', [ 'href' => '#', 'class' => 'mw-checkbox-' . $checkboxType ],
+                       'a', [ 'class' => 'mw-checkbox-' . $checkboxType, 'role' => 'button', 'tabindex' => 0 ],
                        $this->output->msg( 'checkbox-' . $checkboxType )->text()
                );
        }
index 89cb616..97dba26 100644 (file)
@@ -370,7 +370,7 @@ class MWNamespace {
         */
        public static function getSubjectNamespaces() {
                return array_filter(
-                       MWNamespace::getValidNamespaces(),
+                       self::getValidNamespaces(),
                        'MWNamespace::isSubject'
                );
        }
@@ -383,7 +383,7 @@ class MWNamespace {
         */
        public static function getTalkNamespaces() {
                return array_filter(
-                       MWNamespace::getValidNamespaces(),
+                       self::getValidNamespaces(),
                        'MWNamespace::isTalk'
                );
        }
index ee95918..1703179 100644 (file)
@@ -664,7 +664,7 @@ class MagicWord {
                $search = [];
                $replace = [];
                foreach ( $magicarr as $id => $replacement ) {
-                       $mw = MagicWord::get( $id );
+                       $mw = self::get( $id );
                        $search[] = $mw->getRegex();
                        $replace[] = $replacement;
                }
index 4df4d76..4e47184 100644 (file)
@@ -609,6 +609,7 @@ class MediaWiki {
                        $lbFactory->hasOrMadeRecentMasterChanges( INF )
                ) ? self::getUrlDomainDistance( $output->getRedirect(), $context ) : false;
 
+               $allowHeaders = !( $output->isDisabled() || headers_sent() );
                if ( $urlDomainDistance === 'local' || $urlDomainDistance === 'remote' ) {
                        // OutputPage::output() will be fast; $postCommitWork will not be useful for
                        // masking the latency of syncing DB positions accross all datacenters synchronously.
@@ -616,7 +617,7 @@ class MediaWiki {
                        $flags = $lbFactory::SHUTDOWN_CHRONPROT_ASYNC;
                        $cpPosTime = microtime( true );
                        // Client's next request should see 1+ positions with this DBMasterPos::asOf() time
-                       if ( $urlDomainDistance === 'local' ) {
+                       if ( $urlDomainDistance === 'local' && $allowHeaders ) {
                                // Client will stay on this domain, so set an unobtrusive cookie
                                $expires = time() + ChronologyProtector::POSITION_TTL;
                                $options = [ 'prefix' => '' ];
@@ -633,7 +634,7 @@ class MediaWiki {
                        // OutputPage::output() is fairly slow; run it in $postCommitWork to mask
                        // the latency of syncing DB positions accross all datacenters synchronously
                        $flags = $lbFactory::SHUTDOWN_CHRONPROT_SYNC;
-                       if ( $lbFactory->hasOrMadeRecentMasterChanges( INF ) ) {
+                       if ( $lbFactory->hasOrMadeRecentMasterChanges( INF ) && $allowHeaders ) {
                                $cpPosTime = microtime( true );
                                // Set a cookie in case the DB position store cannot sync accross datacenters.
                                // This will at least cover the common case of the user staying on the domain.
index 8670729..a2a44bb 100644 (file)
@@ -35,7 +35,7 @@ class MimeMagic extends MimeAnalyzer {
                $instance = MediaWikiServices::getInstance()->getMimeAnalyzer();
                Assert::postcondition(
                        $instance instanceof MimeMagic,
-                       __METHOD__ . ' should return an instance of ' . MimeMagic::class
+                       __METHOD__ . ' should return an instance of ' . self::class
                );
                return $instance;
        }
index 008963b..15ed2d4 100644 (file)
@@ -901,6 +901,8 @@ class Preferences {
                ];
                $defaultPreferences['rclimit'] = [
                        'type' => 'int',
+                       'min' => 0,
+                       'max' => 1000,
                        'label-message' => 'recentchangescount',
                        'help-message' => 'prefs-help-recentchangescount',
                        'section' => 'rc/displayrc',
@@ -918,6 +920,9 @@ class Preferences {
                $defaultPreferences['rcfilters-saved-queries'] = [
                        'type' => 'api',
                ];
+               $defaultPreferences['rcfilters-rclimit'] = [
+                       'type' => 'api',
+               ];
 
                if ( $config->get( 'RCWatchCategoryMembership' ) ) {
                        $defaultPreferences['hidecategorization'] = [
@@ -1316,7 +1321,7 @@ class Preferences {
                $formClass = 'PreferencesForm',
                array $remove = []
        ) {
-               $formDescriptor = Preferences::getPreferences( $user, $context );
+               $formDescriptor = self::getPreferences( $user, $context );
                if ( count( $remove ) ) {
                        $removeKeys = array_flip( $remove );
                        $formDescriptor = array_diff_key( $formDescriptor, $removeKeys );
index c3782ba..537b7c1 100644 (file)
@@ -559,8 +559,6 @@ class Revision implements IDBAccessObject {
        }
 
        /**
-        * Constructor
-        *
         * @param object|array $row Either a database row or an array
         * @throws MWException
         * @access private
@@ -1004,7 +1002,7 @@ class Revision implements IDBAccessObject {
 
                return RecentChange::newFromConds(
                        [
-                               'rc_user_text' => $this->getUserText( Revision::RAW ),
+                               'rc_user_text' => $this->getUserText( self::RAW ),
                                'rc_timestamp' => $dbr->timestamp( $this->getTimestamp() ),
                                'rc_this_oldid' => $this->getId()
                        ],
@@ -1468,7 +1466,7 @@ class Revision implements IDBAccessObject {
                                ? $this->getPreviousRevisionId( $dbw )
                                : $this->mParentId,
                        'rev_sha1'       => $this->mSha1 === null
-                               ? Revision::base36Sha1( $this->mText )
+                               ? self::base36Sha1( $this->mText )
                                : $this->mSha1,
                ];
 
@@ -1557,7 +1555,7 @@ class Revision implements IDBAccessObject {
                        }
                }
 
-               $content = $this->getContent( Revision::RAW );
+               $content = $this->getContent( self::RAW );
                $prefixedDBkey = $title->getPrefixedDBkey();
                $revId = $this->mId;
 
index b08bc69..907da16 100644 (file)
@@ -56,6 +56,21 @@ class Sanitizer {
        const EVIL_URI_PATTERN = '!(^|\s|\*/\s*)(javascript|vbscript)([^\w]|$)!i';
        const XMLNS_ATTRIBUTE_PATTERN = "/^xmlns:[:A-Z_a-z-.0-9]+$/";
 
+       /**
+        * Tells escapeUrlForHtml() to encode the ID using the wiki's primary encoding.
+        *
+        * @since 1.30
+        */
+       const ID_PRIMARY = 0;
+
+       /**
+        * Tells escapeUrlForHtml() to encode the ID using the fallback encoding, or return false
+        * if no fallback is configured.
+        *
+        * @since 1.30
+        */
+       const ID_FALLBACK = 1;
+
        /**
         * List of all named character entities defined in HTML 4.01
         * https://www.w3.org/TR/html4/sgml/entities.html
@@ -465,7 +480,7 @@ class Sanitizer {
                extract( self::getRecognizedTagData( $extratags, $removetags ) );
 
                # Remove HTML comments
-               $text = Sanitizer::removeHTMLcomments( $text );
+               $text = self::removeHTMLcomments( $text );
                $bits = explode( '<', $text );
                $text = str_replace( '>', '&gt;', array_shift( $bits ) );
                if ( !MWTidy::isEnabled() ) {
@@ -583,12 +598,12 @@ class Sanitizer {
                                                        call_user_func_array( $processCallback, [ &$params, $args ] );
                                                }
 
-                                               if ( !Sanitizer::validateTag( $params, $t ) ) {
+                                               if ( !self::validateTag( $params, $t ) ) {
                                                        $badtag = true;
                                                }
 
                                                # Strip non-approved attributes from the tag
-                                               $newparams = Sanitizer::fixTagAttributes( $params, $t );
+                                               $newparams = self::fixTagAttributes( $params, $t );
                                        }
                                        if ( !$badtag ) {
                                                $rest = str_replace( '>', '&gt;', $rest );
@@ -629,11 +644,11 @@ class Sanitizer {
                                                                call_user_func_array( $warnCallback, [ 'deprecated-self-close-category' ] );
                                                        }
                                                }
-                                               if ( !Sanitizer::validateTag( $params, $t ) ) {
+                                               if ( !self::validateTag( $params, $t ) ) {
                                                        $badtag = true;
                                                }
 
-                                               $newparams = Sanitizer::fixTagAttributes( $params, $t );
+                                               $newparams = self::fixTagAttributes( $params, $t );
                                                if ( !$badtag ) {
                                                        if ( $brace === '/>' && !isset( $htmlsingleonly[$t] ) ) {
                                                                # Interpret self-closing tags as empty tags even when
@@ -710,7 +725,7 @@ class Sanitizer {
         * @return bool
         */
        static function validateTag( $params, $element ) {
-               $params = Sanitizer::decodeTagAttributes( $params );
+               $params = self::decodeTagAttributes( $params );
 
                if ( $element == 'meta' || $element == 'link' ) {
                        if ( !isset( $params['itemprop'] ) ) {
@@ -746,8 +761,8 @@ class Sanitizer {
         * @todo Check for unique id attribute :P
         */
        static function validateTagAttributes( $attribs, $element ) {
-               return Sanitizer::validateAttributes( $attribs,
-                       Sanitizer::attributeWhitelist( $element ) );
+               return self::validateAttributes( $attribs,
+                       self::attributeWhitelist( $element ) );
        }
 
        /**
@@ -795,12 +810,12 @@ class Sanitizer {
                        # Strip javascript "expression" from stylesheets.
                        # https://msdn.microsoft.com/en-us/library/ms537634.aspx
                        if ( $attribute == 'style' ) {
-                               $value = Sanitizer::checkCss( $value );
+                               $value = self::checkCss( $value );
                        }
 
                        # Escape HTML id attributes
                        if ( $attribute === 'id' ) {
-                               $value = Sanitizer::escapeId( $value, 'noninitial' );
+                               $value = self::escapeIdForAttribute( $value, Sanitizer::ID_PRIMARY );
                        }
 
                        # Escape HTML id reference lists
@@ -809,7 +824,7 @@ class Sanitizer {
                                || $attribute === 'aria-labelledby'
                                || $attribute === 'aria-owns'
                        ) {
-                               $value = Sanitizer::escapeIdReferenceList( $value, 'noninitial' );
+                               $value = self::escapeIdReferenceList( $value, 'noninitial' );
                        }
 
                        // RDFa and microdata properties allow URLs, URIs and/or CURIs.
@@ -907,7 +922,7 @@ class Sanitizer {
         */
        public static function normalizeCss( $value ) {
                // Decode character references like &#123;
-               $value = Sanitizer::decodeCharReferences( $value );
+               $value = self::decodeCharReferences( $value );
 
                // Decode escape sequences and line continuation
                // See the grammar in the CSS 2 spec, appendix D.
@@ -1087,14 +1102,14 @@ class Sanitizer {
                        return '';
                }
 
-               $decoded = Sanitizer::decodeTagAttributes( $text );
-               $stripped = Sanitizer::validateTagAttributes( $decoded, $element );
+               $decoded = self::decodeTagAttributes( $text );
+               $stripped = self::validateTagAttributes( $decoded, $element );
 
                if ( $sorted ) {
                        ksort( $stripped );
                }
 
-               return Sanitizer::safeEncodeTagAttributes( $stripped );
+               return self::safeEncodeTagAttributes( $stripped );
        }
 
        /**
@@ -1124,7 +1139,7 @@ class Sanitizer {
         * @return string HTML-encoded text fragment
         */
        static function safeEncodeAttribute( $text ) {
-               $encValue = Sanitizer::encodeAttribute( $text );
+               $encValue = self::encodeAttribute( $text );
 
                # Templates and links may be expanded in later parsing,
                # creating invalid or dangerous output. Suppress this.
@@ -1164,6 +1179,8 @@ class Sanitizer {
         * ambiguous if it's part of something that looks like a percent escape
         * (which don't work reliably in fragments cross-browser).
         *
+        * @deprecated since 1.30, use one of this class' escapeIdFor*() functions
+        *
         * @see https://www.w3.org/TR/html401/types.html#type-name Valid characters
         *   in the id and name attributes
         * @see https://www.w3.org/TR/html401/struct/links.html#h-12.2.3 Anchors with
@@ -1186,7 +1203,7 @@ class Sanitizer {
                global $wgExperimentalHtmlIds;
                $options = (array)$options;
 
-               $id = Sanitizer::decodeCharReferences( $id );
+               $id = self::decodeCharReferences( $id );
 
                if ( $wgExperimentalHtmlIds && !in_array( 'legacy', $options ) ) {
                        $id = preg_replace( '/[ \t\n\r\f_\'"&#%]+/', '_', $id );
@@ -1215,21 +1232,146 @@ class Sanitizer {
                return $id;
        }
 
+       /**
+        * Given a section name or other user-generated or otherwise unsafe string, escapes it to be
+        * a valid HTML id attribute.
+        *
+        * WARNING: unlike escapeId(), the output of this function is not guaranteed to be HTML safe,
+        * be sure to use proper escaping.
+        *
+        * @param string $id String to escape
+        * @param int $mode One of ID_* constants, specifying whether the primary or fallback encoding
+        *     should be used.
+        * @return string|bool Escaped ID or false if fallback encoding is requested but it's not
+        *     configured.
+        *
+        * @since 1.30
+        */
+       public static function escapeIdForAttribute( $id, $mode = self::ID_PRIMARY ) {
+               global $wgFragmentMode;
+
+               if ( !isset( $wgFragmentMode[$mode] ) ) {
+                       if ( $mode === self::ID_PRIMARY ) {
+                               throw new UnexpectedValueException( '$wgFragmentMode is configured with no primary mode' );
+                       }
+                       return false;
+               }
+
+               $internalMode = $wgFragmentMode[$mode];
+
+               return self::escapeIdInternal( $id, $internalMode );
+       }
+
+       /**
+        * Given a section name or other user-generated or otherwise unsafe string, escapes it to be
+        * a valid URL fragment.
+        *
+        * WARNING: unlike escapeId(), the output of this function is not guaranteed to be HTML safe,
+        * be sure to use proper escaping.
+        *
+        * @param string $id String to escape
+        * @return string Escaped ID
+        *
+        * @since 1.30
+        */
+       public static function escapeIdForLink( $id ) {
+               global $wgFragmentMode;
+
+               if ( !isset( $wgFragmentMode[self::ID_PRIMARY] ) ) {
+                       throw new UnexpectedValueException( '$wgFragmentMode is configured with no primary mode' );
+               }
+
+               $mode = $wgFragmentMode[self::ID_PRIMARY];
+
+               $id = self::escapeIdInternal( $id, $mode );
+               $id = self::urlEscapeId( $id, $mode );
+
+               return $id;
+       }
+
+       /**
+        * Given a section name or other user-generated or otherwise unsafe string, escapes it to be
+        * a valid URL fragment for external interwikis.
+        *
+        * @param string $id String to escape
+        * @return string Escaped ID
+        *
+        * @since 1.30
+        */
+       public static function escapeIdForExternalInterwiki( $id ) {
+               global $wgExternalInterwikiFragmentMode;
+
+               $id = self::escapeIdInternal( $id, $wgExternalInterwikiFragmentMode );
+               $id = self::urlEscapeId( $id, $wgExternalInterwikiFragmentMode );
+
+               return $id;
+       }
+
+       /**
+        * Helper for escapeIdFor*() functions. URL-escapes the ID if needed.
+        *
+        * @param string $id String to escape
+        * @param string $mode One of modes from $wgFragmentMode
+        * @return string
+        */
+       private static function urlEscapeId( $id, $mode ) {
+               if ( $mode === 'html5' ) {
+                       $id = urlencode( $id );
+                       $id = str_replace( '%3A', ':', $id );
+               }
+
+               return $id;
+       }
+
+       /**
+        * Helper for escapeIdFor*() functions. Performs most of the actual escaping.
+        *
+        * @param string $id String to escape
+        * @param string $mode One of modes from $wgFragmentMode
+        * @return string
+        */
+       private static function escapeIdInternal( $id, $mode ) {
+               $id = Sanitizer::decodeCharReferences( $id );
+
+               switch ( $mode ) {
+                       case 'html5':
+                               $id = str_replace( ' ', '_', $id );
+                               break;
+                       case 'legacy':
+                               // This corresponds to 'noninitial' mode of the old escapeId()
+                               static $replace = [
+                                       '%3A' => ':',
+                                       '%' => '.'
+                               ];
+
+                               $id = urlencode( str_replace( ' ', '_', $id ) );
+                               $id = strtr( $id, $replace );
+                               break;
+                       case 'html5-legacy':
+                               $id = preg_replace( '/[ \t\n\r\f_\'"&#%]+/', '_', $id );
+                               $id = trim( $id, '_' );
+                               if ( $id === '' ) {
+                                       // Must have been all whitespace to start with.
+                                       $id = '_';
+                               }
+                               break;
+                       default:
+                               throw new InvalidArgumentException( "Invalid mode '$mode' passed to '" . __METHOD__ );
+               }
+
+               return $id;
+       }
+
        /**
         * Given a string containing a space delimited list of ids, escape each id
         * to match ids escaped by the escapeId() function.
         *
+        * @todo wfDeprecated() uses of $options in 1.31, remove completely in 1.32
+        *
         * @since 1.27
         *
         * @param string $referenceString Space delimited list of ids
-        * @param string|array $options String or array of strings (default is array()):
-        *   'noninitial': This is a non-initial fragment of an id, not a full id,
-        *       so don't pay attention if the first character isn't valid at the
-        *       beginning of an id.  Only matters if $wgExperimentalHtmlIds is
-        *       false.
-        *   'legacy': Behave the way the old HTML 4-based ID escaping worked even
-        *       if $wgExperimentalHtmlIds is used, so we can generate extra
-        *       anchors and links won't break.
+        * @param string|array $options Deprecated and does nothing.
         * @return string
         */
        static function escapeIdReferenceList( $referenceString, $options = [] ) {
@@ -1238,7 +1380,7 @@ class Sanitizer {
 
                # Escape each token as an id
                foreach ( $references as &$ref ) {
-                       $ref = Sanitizer::escapeId( $ref, $options );
+                       $ref = self::escapeIdForAttribute( $ref );
                }
 
                # Merge the array back to a space delimited list string
@@ -1275,7 +1417,7 @@ class Sanitizer {
         * @return string Escaped input
         */
        static function escapeHtmlAllowEntities( $html ) {
-               $html = Sanitizer::decodeCharReferences( $html );
+               $html = self::decodeCharReferences( $html );
                # It seems wise to escape ' as well as ", as a matter of course.  Can't
                # hurt. Use ENT_SUBSTITUTE so that incorrectly truncated multibyte characters
                # don't cause the entire string to disappear.
@@ -1317,14 +1459,14 @@ class Sanitizer {
 
                foreach ( $pairs as $set ) {
                        $attribute = strtolower( $set[1] );
-                       $value = Sanitizer::getTagAttributeCallback( $set );
+                       $value = self::getTagAttributeCallback( $set );
 
                        // Normalize whitespace
                        $value = preg_replace( '/[\t\r\n ]+/', ' ', $value );
                        $value = trim( $value );
 
                        // Decode character references
-                       $attribs[$attribute] = Sanitizer::decodeCharReferences( $value );
+                       $attribs[$attribute] = self::decodeCharReferences( $value );
                }
                return $attribs;
        }
@@ -1340,7 +1482,7 @@ class Sanitizer {
                $attribs = [];
                foreach ( $assoc_array as $attribute => $value ) {
                        $encAttribute = htmlspecialchars( $attribute );
-                       $encValue = Sanitizer::safeEncodeAttribute( $value );
+                       $encValue = self::safeEncodeAttribute( $value );
 
                        $attribs[] = "$encAttribute=\"$encValue\"";
                }
@@ -1427,11 +1569,11 @@ class Sanitizer {
        static function normalizeCharReferencesCallback( $matches ) {
                $ret = null;
                if ( $matches[1] != '' ) {
-                       $ret = Sanitizer::normalizeEntity( $matches[1] );
+                       $ret = self::normalizeEntity( $matches[1] );
                } elseif ( $matches[2] != '' ) {
-                       $ret = Sanitizer::decCharReference( $matches[2] );
+                       $ret = self::decCharReference( $matches[2] );
                } elseif ( $matches[3] != '' ) {
-                       $ret = Sanitizer::hexCharReference( $matches[3] );
+                       $ret = self::hexCharReference( $matches[3] );
                }
                if ( is_null( $ret ) ) {
                        return htmlspecialchars( $matches[0] );
@@ -1468,7 +1610,7 @@ class Sanitizer {
         */
        static function decCharReference( $codepoint ) {
                $point = intval( $codepoint );
-               if ( Sanitizer::validateCodepoint( $point ) ) {
+               if ( self::validateCodepoint( $point ) ) {
                        return sprintf( '&#%d;', $point );
                } else {
                        return null;
@@ -1481,7 +1623,7 @@ class Sanitizer {
         */
        static function hexCharReference( $codepoint ) {
                $point = hexdec( $codepoint );
-               if ( Sanitizer::validateCodepoint( $point ) ) {
+               if ( self::validateCodepoint( $point ) ) {
                        return sprintf( '&#x%x;', $point );
                } else {
                        return null;
@@ -1550,11 +1692,11 @@ class Sanitizer {
         */
        static function decodeCharReferencesCallback( $matches ) {
                if ( $matches[1] != '' ) {
-                       return Sanitizer::decodeEntity( $matches[1] );
+                       return self::decodeEntity( $matches[1] );
                } elseif ( $matches[2] != '' ) {
-                       return Sanitizer::decodeChar( intval( $matches[2] ) );
+                       return self::decodeChar( intval( $matches[2] ) );
                } elseif ( $matches[3] != '' ) {
-                       return Sanitizer::decodeChar( hexdec( $matches[3] ) );
+                       return self::decodeChar( hexdec( $matches[3] ) );
                }
                # Last case should be an ampersand by itself
                return $matches[0];
@@ -1568,7 +1710,7 @@ class Sanitizer {
         * @private
         */
        static function decodeChar( $codepoint ) {
-               if ( Sanitizer::validateCodepoint( $codepoint ) ) {
+               if ( self::validateCodepoint( $codepoint ) ) {
                        return UtfNormal\Utils::codepointToUtf8( $codepoint );
                } else {
                        return UtfNormal\Constants::UTF8_REPLACEMENT;
@@ -1601,7 +1743,7 @@ class Sanitizer {
         * @return array
         */
        static function attributeWhitelist( $element ) {
-               $list = Sanitizer::setupAttributeWhitelist();
+               $list = self::setupAttributeWhitelist();
                return isset( $list[$element] )
                        ? $list[$element]
                        : [];
@@ -1876,7 +2018,7 @@ class Sanitizer {
        static function cleanUrl( $url ) {
                # Normalize any HTML entities in input. They will be
                # re-escaped by makeExternalLink().
-               $url = Sanitizer::decodeCharReferences( $url );
+               $url = self::decodeCharReferences( $url );
 
                # Escape any control characters introduced by the above step
                $url = preg_replace_callback( '/[\][<>"\\x00-\\x20\\x7F\|]/',
index e1244e7..d048007 100644 (file)
@@ -287,7 +287,7 @@ return [
                return ObjectFactory::constructClassInstance( $conf['class'], [ $conf ] );
        },
 
-       'ParserCache' => function( MediaWikiServices $services ) {
+       'ParserCache' => function ( MediaWikiServices $services ) {
                $config = $services->getMainConfig();
                $cache = ObjectCache::getInstance( $config->get( 'ParserCacheType' ) );
                wfDebugLog( 'caches', 'parser: ' . get_class( $cache ) );
@@ -298,7 +298,7 @@ return [
                );
        },
 
-       'LinkCache' => function( MediaWikiServices $services ) {
+       'LinkCache' => function ( MediaWikiServices $services ) {
                return new LinkCache(
                        $services->getTitleFormatter(),
                        $services->getMainWANObjectCache()
index ac00fab..68e3d96 100644 (file)
@@ -282,6 +282,11 @@ foreach ( $wgForeignFileRepos as &$repo ) {
 }
 unset( $repo ); // no global pollution; destroy reference
 
+// Convert this deprecated setting to modern system
+if ( $wgExperimentalHtmlIds ) {
+       $wgFragmentMode = [ 'html5-legacy', 'legacy' ];
+}
+
 $rcMaxAgeDays = $wgRCMaxAge / ( 3600 * 24 );
 if ( $wgRCFilterByAge ) {
        // Trim down $wgRCLinkDays so that it only lists links which are valid
@@ -687,7 +692,7 @@ $messageMemc = wfGetMessageCacheStorage();
 /**
  * @deprecated since 1.30
  */
-$parserMemc = new DeprecatedGlobal( 'parserMemc', function() {
+$parserMemc = new DeprecatedGlobal( 'parserMemc', function () {
        return MediaWikiServices::getInstance()->getParserCache()->getCacheStorage();
 }, '1.30' );
 
index 6ce1aed..6a2d0e2 100644 (file)
@@ -34,9 +34,6 @@ class SiteStats {
        /** @var bool */
        private static $loaded = false;
 
-       /** @var int */
-       private static $jobs;
-
        /** @var int[] */
        private static $pageCount = [];
 
@@ -213,24 +210,24 @@ class SiteStats {
        }
 
        /**
+        * Total number of jobs in the job queue.
         * @return int
         */
        static function jobs() {
-               if ( !isset( self::$jobs ) ) {
-                       try{
-                               self::$jobs = array_sum( JobQueueGroup::singleton()->getQueueSizes() );
-                       } catch ( JobQueueError $e ) {
-                               self::$jobs = 0;
-                       }
-                       /**
-                        * Zero rows still do single row read for row that doesn't exist,
-                        * but people are annoyed by that
-                        */
-                       if ( self::$jobs == 1 ) {
-                               self::$jobs = 0;
-                       }
-               }
-               return self::$jobs;
+               $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
+               return $cache->getWithSetCallback(
+                       $cache->makeKey( 'SiteStats', 'jobscount' ),
+                       $cache::TTL_MINUTE,
+                       function ( $oldValue, &$ttl, array &$setOpts ) {
+                               try{
+                                       $jobs = array_sum( JobQueueGroup::singleton()->getQueueSizes() );
+                               } catch ( JobQueueError $e ) {
+                                       $jobs = 0;
+                               }
+                               return $jobs;
+                       },
+                       [ 'pcTTL' => $cache::TTL_PROC_LONG ]
+               );
        }
 
        /**
@@ -296,7 +293,6 @@ class SiteStatsInit {
        private $mUsers = null, $mFiles = null;
 
        /**
-        * Constructor
         * @param bool|IDatabase $database
         * - boolean: Whether to use the master DB
         * - IDatabase: Database connection to use
index 083a725..5decece 100644 (file)
@@ -272,7 +272,7 @@ class Title implements LinkTarget {
                }
 
                try {
-                       return Title::newFromTextThrow( strval( $text ), $defaultNamespace );
+                       return self::newFromTextThrow( strval( $text ), $defaultNamespace );
                } catch ( MalformedTitleException $ex ) {
                        return null;
                }
@@ -411,7 +411,7 @@ class Title implements LinkTarget {
                        __METHOD__
                );
                if ( $row !== false ) {
-                       $title = Title::newFromRow( $row );
+                       $title = self::newFromRow( $row );
                } else {
                        $title = null;
                }
@@ -439,7 +439,7 @@ class Title implements LinkTarget {
 
                $titles = [];
                foreach ( $res as $row ) {
-                       $titles[] = Title::newFromRow( $row );
+                       $titles[] = self::newFromRow( $row );
                }
                return $titles;
        }
@@ -541,7 +541,7 @@ class Title implements LinkTarget {
                }
 
                $t = new Title();
-               $t->mDbkeyform = Title::makeName( $ns, $title, $fragment, $interwiki, true );
+               $t->mDbkeyform = self::makeName( $ns, $title, $fragment, $interwiki, true );
 
                try {
                        $t->secureAndSplit();
@@ -557,10 +557,10 @@ class Title implements LinkTarget {
         * @return Title The new object
         */
        public static function newMainPage() {
-               $title = Title::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
+               $title = self::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
                // Don't give fatal errors if the message is broken
                if ( !$title ) {
-                       $title = Title::newFromText( 'Main Page' );
+                       $title = self::newFromText( 'Main Page' );
                }
                return $title;
        }
@@ -748,6 +748,8 @@ class Title implements LinkTarget {
        /**
         * Escape a text fragment, say from a link, for a URL
         *
+        * @deprecated since 1.30, use Sanitizer::escapeIdForLink() or escapeIdForExternalInterwiki()
+        *
         * @param string $fragment Containing a URL or link fragment (after the "#")
         * @return string Escaped string
         */
@@ -933,7 +935,7 @@ class Title implements LinkTarget {
         */
        public function getContentModel( $flags = 0 ) {
                if ( !$this->mForcedContentModel
-                       && ( !$this->mContentModel || $flags === Title::GAID_FOR_UPDATE )
+                       && ( !$this->mContentModel || $flags === self::GAID_FOR_UPDATE )
                        && $this->getArticleID( $flags )
                ) {
                        $linkCache = LinkCache::singleton();
@@ -1096,7 +1098,7 @@ class Title implements LinkTarget {
                        if ( $canonicalName ) {
                                $localName = SpecialPageFactory::getLocalNameFor( $canonicalName, $par );
                                if ( $localName != $this->mDbkeyform ) {
-                                       return Title::makeTitle( NS_SPECIAL, $localName );
+                                       return self::makeTitle( NS_SPECIAL, $localName );
                                }
                        }
                }
@@ -1195,7 +1197,7 @@ class Title implements LinkTarget {
         * @return bool
         */
        public function isMainPage() {
-               return $this->equals( Title::newMainPage() );
+               return $this->equals( self::newMainPage() );
        }
 
        /**
@@ -1313,7 +1315,24 @@ class Title implements LinkTarget {
         * @return Title The object for the talk page
         */
        public function getTalkPage() {
-               return Title::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() );
+               return self::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() );
+       }
+
+       /**
+        * Get a Title object associated with the talk page of this article,
+        * if such a talk page can exist.
+        *
+        * @since 1.30
+        *
+        * @return Title The object for the talk page,
+        *         or null if no associated talk page can exist, according to canHaveTalkPage().
+        */
+       public function getTalkPageIfDefined() {
+               if ( !$this->canHaveTalkPage() ) {
+                       return null;
+               }
+
+               return $this->getTalkPage();
        }
 
        /**
@@ -1328,7 +1347,7 @@ class Title implements LinkTarget {
                if ( $this->getNamespace() == $subjectNS ) {
                        return $this;
                }
-               return Title::makeTitle( $subjectNS, $this->getDBkey() );
+               return self::makeTitle( $subjectNS, $this->getDBkey() );
        }
 
        /**
@@ -1382,14 +1401,16 @@ class Title implements LinkTarget {
 
        /**
         * Get the fragment in URL form, including the "#" character if there is one
+        *
         * @return string Fragment in URL form
         */
        public function getFragmentForURL() {
                if ( !$this->hasFragment() ) {
                        return '';
-               } else {
-                       return '#' . Title::escapeFragmentForURL( $this->getFragment() );
+               } elseif ( $this->isExternal() && !$this->getTransWikiID() ) {
+                       return '#' . Sanitizer::escapeIdForExternalInterwiki( $this->getFragment() );
                }
+               return '#' . Sanitizer::escapeIdForLink( $this->getFragment() );
        }
 
        /**
@@ -1535,7 +1556,7 @@ class Title implements LinkTarget {
         * @since 1.20
         */
        public function getRootTitle() {
-               return Title::makeTitle( $this->getNamespace(), $this->getRootText() );
+               return self::makeTitle( $this->getNamespace(), $this->getRootText() );
        }
 
        /**
@@ -1575,7 +1596,7 @@ class Title implements LinkTarget {
         * @since 1.20
         */
        public function getBaseTitle() {
-               return Title::makeTitle( $this->getNamespace(), $this->getBaseText() );
+               return self::makeTitle( $this->getNamespace(), $this->getBaseText() );
        }
 
        /**
@@ -1611,7 +1632,7 @@ class Title implements LinkTarget {
         * @since 1.20
         */
        public function getSubpage( $text ) {
-               return Title::makeTitleSafe( $this->getNamespace(), $this->getText() . '/' . $text );
+               return self::makeTitleSafe( $this->getNamespace(), $this->getText() . '/' . $text );
        }
 
        /**
@@ -2847,7 +2868,7 @@ class Title implements LinkTarget {
                                        $page_id = $row->pr_page;
                                        $page_ns = $row->page_namespace;
                                        $page_title = $row->page_title;
-                                       $sources[$page_id] = Title::makeTitle( $page_ns, $page_title );
+                                       $sources[$page_id] = self::makeTitle( $page_ns, $page_title );
                                        # Add groups needed for each restriction type if its not already there
                                        # Make sure this restriction type still exists
 
@@ -3172,7 +3193,7 @@ class Title implements LinkTarget {
                if ( $limit > -1 ) {
                        $options['LIMIT'] = $limit;
                }
-               $this->mSubpages = TitleArray::newFromResult(
+               return TitleArray::newFromResult(
                        $dbr->select( 'page',
                                [ 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ],
                                $conds,
@@ -3180,7 +3201,6 @@ class Title implements LinkTarget {
                                $options
                        )
                );
-               return $this->mSubpages;
        }
 
        /**
@@ -3329,7 +3349,7 @@ class Title implements LinkTarget {
         * @return int Int or 0 if the page doesn't exist
         */
        public function getLatestRevID( $flags = 0 ) {
-               if ( !( $flags & Title::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
+               if ( !( $flags & self::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
                        return intval( $this->mLatestID );
                }
                if ( !$this->getArticleID( $flags ) ) {
@@ -3489,7 +3509,7 @@ class Title implements LinkTarget {
                if ( $res->numRows() ) {
                        $linkCache = LinkCache::singleton();
                        foreach ( $res as $row ) {
-                               $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title );
+                               $titleObj = self::makeTitle( $row->page_namespace, $row->page_title );
                                if ( $titleObj ) {
                                        $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
                                        $retVal[] = $titleObj;
@@ -3557,9 +3577,9 @@ class Title implements LinkTarget {
                $linkCache = LinkCache::singleton();
                foreach ( $res as $row ) {
                        if ( $row->page_id ) {
-                               $titleObj = Title::newFromRow( $row );
+                               $titleObj = self::newFromRow( $row );
                        } else {
-                               $titleObj = Title::makeTitle( $row->$blNamespace, $row->$blTitle );
+                               $titleObj = self::makeTitle( $row->$blNamespace, $row->$blTitle );
                                $linkCache->addBadLinkObj( $titleObj );
                        }
                        $retVal[] = $titleObj;
@@ -3615,7 +3635,7 @@ class Title implements LinkTarget {
 
                $retVal = [];
                foreach ( $res as $row ) {
-                       $retVal[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
+                       $retVal[] = self::makeTitle( $row->pl_namespace, $row->pl_title );
                }
                return $retVal;
        }
@@ -3827,7 +3847,7 @@ class Title implements LinkTarget {
                        }
                        # T16385: we need makeTitleSafe because the new page names may
                        # be longer than 255 characters.
-                       $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
+                       $newSubpage = self::makeTitleSafe( $newNs, $newPageName );
 
                        $success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect, $changeTags );
                        if ( $success === true ) {
@@ -3989,7 +4009,7 @@ class Title implements LinkTarget {
                                        # Circular reference
                                        $stack[$parent] = [];
                                } else {
-                                       $nt = Title::newFromText( $parent );
+                                       $nt = self::newFromText( $parent );
                                        if ( $nt ) {
                                                $stack[$parent] = $nt->getParentCategoryTree( $children + [ $parent => 1 ] );
                                        }
index a03bc19..6a532e5 100644 (file)
@@ -115,7 +115,7 @@ class WikiMap {
         * @return string|int Wiki's name or $wiki_id if the wiki was not found
         */
        public static function getWikiName( $wikiID ) {
-               $wiki = WikiMap::getWiki( $wikiID );
+               $wiki = self::getWiki( $wikiID );
 
                if ( $wiki ) {
                        return $wiki->getDisplayName();
@@ -166,7 +166,7 @@ class WikiMap {
         * @return string|bool URL or false if the wiki was not found
         */
        public static function getForeignURL( $wikiID, $page, $fragmentId = null ) {
-               $wiki = WikiMap::getWiki( $wikiID );
+               $wiki = self::getWiki( $wikiID );
 
                if ( $wiki ) {
                        return $wiki->getFullUrl( $page, $fragmentId );
index d016433..16a5a9d 100644 (file)
@@ -225,7 +225,7 @@ class Xml {
                $selected = isset( $languages[$selected] ) ? $selected : $wgLanguageCode;
                $options = "\n";
                foreach ( $languages as $code => $name ) {
-                       $options .= Xml::option( "$code - $name", $code, $code == $selected ) . "\n";
+                       $options .= self::option( "$code - $name", $code, $code == $selected ) . "\n";
                }
 
                $attrs = [ 'id' => 'wpUserLanguage', 'name' => 'wpUserLanguage' ];
@@ -235,8 +235,8 @@ class Xml {
                        $msg = wfMessage( 'yourlanguage' );
                }
                return [
-                       Xml::label( $msg->text(), $attrs['id'] ),
-                       Xml::tags( 'select', $attrs, $options )
+                       self::label( $msg->text(), $attrs['id'] ),
+                       self::tags( 'select', $attrs, $options )
                ];
        }
 
@@ -400,7 +400,7 @@ class Xml {
                $value = false, $attribs = []
        ) {
                return [
-                       Xml::label( $label, $id, $attribs ),
+                       self::label( $label, $id, $attribs ),
                        self::input( $name, $size, $value, [ 'id' => $id ] + $attribs )
                ];
        }
@@ -556,11 +556,11 @@ class Xml {
                        $attribs['tabindex'] = $tabindex;
                }
 
-               return Xml::openElement( 'select', $attribs )
+               return self::openElement( 'select', $attribs )
                        . "\n"
                        . $options
                        . "\n"
-                       . Xml::closeElement( 'select' );
+                       . self::closeElement( 'select' );
        }
 
        /**
@@ -575,15 +575,15 @@ class Xml {
         * @return string
         */
        public static function fieldset( $legend = false, $content = false, $attribs = [] ) {
-               $s = Xml::openElement( 'fieldset', $attribs ) . "\n";
+               $s = self::openElement( 'fieldset', $attribs ) . "\n";
 
                if ( $legend ) {
-                       $s .= Xml::element( 'legend', null, $legend ) . "\n";
+                       $s .= self::element( 'legend', null, $legend ) . "\n";
                }
 
                if ( $content !== false ) {
                        $s .= $content . "\n";
-                       $s .= Xml::closeElement( 'fieldset' ) . "\n";
+                       $s .= self::closeElement( 'fieldset' ) . "\n";
                }
 
                return $s;
@@ -644,7 +644,7 @@ class Xml {
         */
        public static function encodeJsCall( $name, $args, $pretty = false ) {
                foreach ( $args as &$arg ) {
-                       $arg = Xml::encodeJsVar( $arg, $pretty );
+                       $arg = self::encodeJsVar( $arg, $pretty );
                        if ( $arg === false ) {
                                return false;
                        }
@@ -702,7 +702,7 @@ class Xml {
                        $text .
                        '</html>';
 
-               return Xml::isWellFormed( $html );
+               return self::isWellFormed( $html );
        }
 
        /**
@@ -736,25 +736,25 @@ class Xml {
 
                foreach ( $fields as $labelmsg => $input ) {
                        $id = "mw-$labelmsg";
-                       $form .= Xml::openElement( 'tr', [ 'id' => $id ] );
+                       $form .= self::openElement( 'tr', [ 'id' => $id ] );
 
                        // TODO use a <label> here for accessibility purposes - will need
                        // to either not use a table to build the form, or find the ID of
                        // the input somehow.
 
-                       $form .= Xml::tags( 'td', [ 'class' => 'mw-label' ], wfMessage( $labelmsg )->parse() );
-                       $form .= Xml::openElement( 'td', [ 'class' => 'mw-input' ] )
-                               . $input . Xml::closeElement( 'td' );
-                       $form .= Xml::closeElement( 'tr' );
+                       $form .= self::tags( 'td', [ 'class' => 'mw-label' ], wfMessage( $labelmsg )->parse() );
+                       $form .= self::openElement( 'td', [ 'class' => 'mw-input' ] )
+                               . $input . self::closeElement( 'td' );
+                       $form .= self::closeElement( 'tr' );
                }
 
                if ( $submitLabel ) {
-                       $form .= Xml::openElement( 'tr' );
-                       $form .= Xml::tags( 'td', [], '' );
-                       $form .= Xml::openElement( 'td', [ 'class' => 'mw-submit' ] )
-                               . Xml::submitButton( wfMessage( $submitLabel )->text(), $submitAttribs )
-                               . Xml::closeElement( 'td' );
-                       $form .= Xml::closeElement( 'tr' );
+                       $form .= self::openElement( 'tr' );
+                       $form .= self::tags( 'td', [], '' );
+                       $form .= self::openElement( 'td', [ 'class' => 'mw-submit' ] )
+                               . self::submitButton( wfMessage( $submitLabel )->text(), $submitAttribs )
+                               . self::closeElement( 'td' );
+                       $form .= self::closeElement( 'tr' );
                }
 
                $form .= "</tbody></table>";
@@ -770,10 +770,10 @@ class Xml {
         * @return string
         */
        public static function buildTable( $rows, $attribs = [], $headers = null ) {
-               $s = Xml::openElement( 'table', $attribs );
+               $s = self::openElement( 'table', $attribs );
 
                if ( is_array( $headers ) ) {
-                       $s .= Xml::openElement( 'thead', $attribs );
+                       $s .= self::openElement( 'thead', $attribs );
 
                        foreach ( $headers as $id => $header ) {
                                $attribs = [];
@@ -782,9 +782,9 @@ class Xml {
                                        $attribs['id'] = $id;
                                }
 
-                               $s .= Xml::element( 'th', $attribs, $header );
+                               $s .= self::element( 'th', $attribs, $header );
                        }
-                       $s .= Xml::closeElement( 'thead' );
+                       $s .= self::closeElement( 'thead' );
                }
 
                foreach ( $rows as $id => $row ) {
@@ -794,10 +794,10 @@ class Xml {
                                $attribs['id'] = $id;
                        }
 
-                       $s .= Xml::buildTableRow( $attribs, $row );
+                       $s .= self::buildTableRow( $attribs, $row );
                }
 
-               $s .= Xml::closeElement( 'table' );
+               $s .= self::closeElement( 'table' );
 
                return $s;
        }
@@ -809,7 +809,7 @@ class Xml {
         * @return string
         */
        public static function buildTableRow( $attribs, $cells ) {
-               $s = Xml::openElement( 'tr', $attribs );
+               $s = self::openElement( 'tr', $attribs );
 
                foreach ( $cells as $id => $cell ) {
                        $attribs = [];
@@ -818,10 +818,10 @@ class Xml {
                                $attribs['id'] = $id;
                        }
 
-                       $s .= Xml::element( 'td', $attribs, $cell );
+                       $s .= self::element( 'td', $attribs, $cell );
                }
 
-               $s .= Xml::closeElement( 'tr' );
+               $s .= self::closeElement( 'tr' );
 
                return $s;
        }
index 88382b6..e8d9a3e 100644 (file)
@@ -151,7 +151,7 @@ abstract class Action implements MessageLocalizer {
                        return 'view';
                }
 
-               $action = Action::factory( $actionName, $context->getWikiPage(), $context );
+               $action = self::factory( $actionName, $context->getWikiPage(), $context );
                if ( $action instanceof Action ) {
                        return $action->getName();
                }
@@ -388,7 +388,7 @@ abstract class Action implements MessageLocalizer {
        public function addHelpLink( $to, $overrideBaseUrl = false ) {
                global $wgContLang;
                $msg = wfMessage( $wgContLang->lc(
-                       Action::getActionName( $this->getContext() )
+                       self::getActionName( $this->getContext() )
                        ) . '-helppage' );
 
                if ( !$msg->isDisabled() ) {
index baec944..68dda37 100644 (file)
@@ -156,8 +156,8 @@ class InfoAction extends FormlessAction {
         * @return string The HTML.
         */
        protected function makeHeader( $header, $canonicalId ) {
-               $spanAttribs = [ 'class' => 'mw-headline', 'id' => Sanitizer::escapeId( $header ) ];
-               $h2Attribs = [ 'id' => Sanitizer::escapeId( $canonicalId ) ];
+               $spanAttribs = [ 'class' => 'mw-headline', 'id' => Sanitizer::escapeIdForAttribute( $header ) ];
+               $h2Attribs = [ 'id' => Sanitizer::escapeIdForAttribute( $canonicalId ) ];
 
                return Html::rawElement( 'h2', $h2Attribs, Html::element( 'span', $spanAttribs, $header ) );
        }
index 44a2280..500f432 100644 (file)
@@ -548,7 +548,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has this method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module.' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module.' );
                }
 
                return $this->getMain()->lacksSameOriginSecurity();
@@ -620,7 +620,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has getResult() method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module. ' );
                }
 
                return $this->getMain()->getResult();
@@ -634,7 +634,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has getErrorFormatter() method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module. ' );
                }
 
                return $this->getMain()->getErrorFormatter();
@@ -660,7 +660,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has getContinuationManager() method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module. ' );
                }
 
                return $this->getMain()->getContinuationManager();
@@ -674,7 +674,7 @@ abstract class ApiBase extends ContextSource {
                // Main module has setContinuationManager() method overridden
                // Safety - avoid infinite loop:
                if ( $this->isMain() ) {
-                       ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' );
+                       self::dieDebug( __METHOD__, 'base method was called on main module. ' );
                }
 
                $this->getMain()->setContinuationManager( $manager );
@@ -1059,7 +1059,7 @@ abstract class ApiBase extends ContextSource {
                if ( $type == 'boolean' ) {
                        if ( isset( $default ) && $default !== false ) {
                                // Having a default value of anything other than 'false' is not allowed
-                               ApiBase::dieDebug(
+                               self::dieDebug(
                                        __METHOD__,
                                        "Boolean param $encParamName's default is set to '$default'. " .
                                                'Boolean parameters must default to false.'
@@ -1070,13 +1070,13 @@ abstract class ApiBase extends ContextSource {
                } elseif ( $type == 'upload' ) {
                        if ( isset( $default ) ) {
                                // Having a default value is not allowed
-                               ApiBase::dieDebug(
+                               self::dieDebug(
                                        __METHOD__,
                                        "File upload param $encParamName's default is set to " .
                                                "'$default'. File upload parameters may not have a default." );
                        }
                        if ( $multi ) {
-                               ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
+                               self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
                        }
                        $value = $this->getMain()->getUpload( $encParamName );
                        if ( !$value->exists() ) {
@@ -1138,7 +1138,7 @@ abstract class ApiBase extends ContextSource {
 
                $allSpecifier = ( is_string( $allowAll ) ? $allowAll : self::ALL_DEFAULT_STRING );
                if ( $allowAll && $multi && is_array( $type ) && in_array( $allSpecifier, $type, true ) ) {
-                       ApiBase::dieDebug(
+                       self::dieDebug(
                                __METHOD__,
                                "For param $encParamName, PARAM_ALL collides with a possible value" );
                }
@@ -1194,13 +1194,13 @@ abstract class ApiBase extends ContextSource {
                                                if ( !isset( $paramSettings[self::PARAM_MAX] )
                                                        || !isset( $paramSettings[self::PARAM_MAX2] )
                                                ) {
-                                                       ApiBase::dieDebug(
+                                                       self::dieDebug(
                                                                __METHOD__,
                                                                "MAX1 or MAX2 are not defined for the limit $encParamName"
                                                        );
                                                }
                                                if ( $multi ) {
-                                                       ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
+                                                       self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
                                                }
                                                $min = isset( $paramSettings[self::PARAM_MIN] ) ? $paramSettings[self::PARAM_MIN] : 0;
                                                if ( $value == 'max' ) {
@@ -1221,7 +1221,7 @@ abstract class ApiBase extends ContextSource {
                                                break;
                                        case 'boolean':
                                                if ( $multi ) {
-                                                       ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
+                                                       self::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" );
                                                }
                                                break;
                                        case 'timestamp':
@@ -1255,7 +1255,7 @@ abstract class ApiBase extends ContextSource {
                                                }
                                                break;
                                        default:
-                                               ApiBase::dieDebug( __METHOD__, "Param $encParamName's type is unknown - $type" );
+                                               self::dieDebug( __METHOD__, "Param $encParamName's type is unknown - $type" );
                                }
                        }
 
@@ -2077,19 +2077,19 @@ abstract class ApiBase extends ContextSource {
         * @return Message
         */
        public function getFinalSummary() {
-               $msg = ApiBase::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
+               $msg = self::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
                        $this->getModulePrefix(),
                        $this->getModuleName(),
                        $this->getModulePath(),
                ] );
                if ( !$msg->exists() ) {
                        wfDeprecated( 'API help "description" messages', '1.30' );
-                       $msg = ApiBase::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
+                       $msg = self::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
                                $this->getModulePrefix(),
                                $this->getModuleName(),
                                $this->getModulePath(),
                        ] );
-                       $msg = ApiBase::makeMessage( 'rawmessage', $this->getContext(), [
+                       $msg = self::makeMessage( 'rawmessage', $this->getContext(), [
                                preg_replace( '/\n.*/s', '', $msg->text() )
                        ] );
                }
@@ -2116,12 +2116,12 @@ abstract class ApiBase extends ContextSource {
                        $desc = (string)$desc;
                }
 
-               $summary = ApiBase::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
+               $summary = self::makeMessage( $this->getSummaryMessage(), $this->getContext(), [
                        $this->getModulePrefix(),
                        $this->getModuleName(),
                        $this->getModulePath(),
                ] );
-               $extendedDescription = ApiBase::makeMessage(
+               $extendedDescription = self::makeMessage(
                        $this->getExtendedDescription(), $this->getContext(), [
                                $this->getModulePrefix(),
                                $this->getModuleName(),
@@ -2133,7 +2133,7 @@ abstract class ApiBase extends ContextSource {
                        $msgs = [ $summary, $extendedDescription ];
                } else {
                        wfDeprecated( 'API help "description" messages', '1.30' );
-                       $description = ApiBase::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
+                       $description = self::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [
                                $this->getModulePrefix(),
                                $this->getModuleName(),
                                $this->getModulePath(),
@@ -2165,10 +2165,10 @@ abstract class ApiBase extends ContextSource {
 
                if ( $this->needsToken() ) {
                        $params['token'] = [
-                               ApiBase::PARAM_TYPE => 'string',
-                               ApiBase::PARAM_REQUIRED => true,
-                               ApiBase::PARAM_SENSITIVE => true,
-                               ApiBase::PARAM_HELP_MSG => [
+                               self::PARAM_TYPE => 'string',
+                               self::PARAM_REQUIRED => true,
+                               self::PARAM_SENSITIVE => true,
+                               self::PARAM_HELP_MSG => [
                                        'api-help-param-token',
                                        $this->needsToken(),
                                ],
@@ -2205,7 +2205,7 @@ abstract class ApiBase extends ContextSource {
                }
                $desc = self::escapeWikiText( $desc );
 
-               $params = $this->getFinalParams( ApiBase::GET_VALUES_FOR_HELP );
+               $params = $this->getFinalParams( self::GET_VALUES_FOR_HELP );
                $msgs = [];
                foreach ( $params as $param => $settings ) {
                        if ( !is_array( $settings ) ) {
@@ -2224,15 +2224,15 @@ abstract class ApiBase extends ContextSource {
                                $d = implode( ' ', $d );
                        }
 
-                       if ( isset( $settings[ApiBase::PARAM_HELP_MSG] ) ) {
-                               $msg = $settings[ApiBase::PARAM_HELP_MSG];
+                       if ( isset( $settings[self::PARAM_HELP_MSG] ) ) {
+                               $msg = $settings[self::PARAM_HELP_MSG];
                        } else {
                                $msg = $this->msg( "apihelp-{$path}-param-{$param}" );
                                if ( !$msg->exists() ) {
                                        $msg = $this->msg( 'api-help-fallback-parameter', $d );
                                }
                        }
-                       $msg = ApiBase::makeMessage( $msg, $this->getContext(),
+                       $msg = self::makeMessage( $msg, $this->getContext(),
                                [ $prefix, $param, $name, $path ] );
                        if ( !$msg ) {
                                self::dieDebug( __METHOD__,
@@ -2240,11 +2240,11 @@ abstract class ApiBase extends ContextSource {
                        }
                        $msgs[$param] = [ $msg ];
 
-                       if ( isset( $settings[ApiBase::PARAM_TYPE] ) &&
-                               $settings[ApiBase::PARAM_TYPE] === 'submodule'
+                       if ( isset( $settings[self::PARAM_TYPE] ) &&
+                               $settings[self::PARAM_TYPE] === 'submodule'
                        ) {
-                               if ( isset( $settings[ApiBase::PARAM_SUBMODULE_MAP] ) ) {
-                                       $map = $settings[ApiBase::PARAM_SUBMODULE_MAP];
+                               if ( isset( $settings[self::PARAM_SUBMODULE_MAP] ) ) {
+                                       $map = $settings[self::PARAM_SUBMODULE_MAP];
                                } else {
                                        $prefix = $this->isMain() ? '' : ( $this->getModulePath() . '+' );
                                        $map = [];
@@ -2282,29 +2282,29 @@ abstract class ApiBase extends ContextSource {
                                        $arr[] = $m->setContext( $this->getContext() );
                                }
                                $msgs[$param] = array_merge( $msgs[$param], $submodules, $deprecatedSubmodules );
-                       } elseif ( isset( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) {
-                               if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) {
+                       } elseif ( isset( $settings[self::PARAM_HELP_MSG_PER_VALUE] ) ) {
+                               if ( !is_array( $settings[self::PARAM_HELP_MSG_PER_VALUE] ) ) {
                                        self::dieDebug( __METHOD__,
                                                'ApiBase::PARAM_HELP_MSG_PER_VALUE is not valid' );
                                }
-                               if ( !is_array( $settings[ApiBase::PARAM_TYPE] ) ) {
+                               if ( !is_array( $settings[self::PARAM_TYPE] ) ) {
                                        self::dieDebug( __METHOD__,
                                                'ApiBase::PARAM_HELP_MSG_PER_VALUE may only be used when ' .
                                                'ApiBase::PARAM_TYPE is an array' );
                                }
 
-                               $valueMsgs = $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE];
-                               $deprecatedValues = isset( $settings[ApiBase::PARAM_DEPRECATED_VALUES] )
-                                       ? $settings[ApiBase::PARAM_DEPRECATED_VALUES]
+                               $valueMsgs = $settings[self::PARAM_HELP_MSG_PER_VALUE];
+                               $deprecatedValues = isset( $settings[self::PARAM_DEPRECATED_VALUES] )
+                                       ? $settings[self::PARAM_DEPRECATED_VALUES]
                                        : [];
 
-                               foreach ( $settings[ApiBase::PARAM_TYPE] as $value ) {
+                               foreach ( $settings[self::PARAM_TYPE] as $value ) {
                                        if ( isset( $valueMsgs[$value] ) ) {
                                                $msg = $valueMsgs[$value];
                                        } else {
                                                $msg = "apihelp-{$path}-paramvalue-{$param}-{$value}";
                                        }
-                                       $m = ApiBase::makeMessage( $msg, $this->getContext(),
+                                       $m = self::makeMessage( $msg, $this->getContext(),
                                                [ $prefix, $param, $name, $path, $value ] );
                                        if ( $m ) {
                                                $m = new ApiHelpParamValueMessage(
@@ -2321,13 +2321,13 @@ abstract class ApiBase extends ContextSource {
                                }
                        }
 
-                       if ( isset( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) {
-                               if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) {
+                       if ( isset( $settings[self::PARAM_HELP_MSG_APPEND] ) ) {
+                               if ( !is_array( $settings[self::PARAM_HELP_MSG_APPEND] ) ) {
                                        self::dieDebug( __METHOD__,
                                                'Value for ApiBase::PARAM_HELP_MSG_APPEND is not an array' );
                                }
-                               foreach ( $settings[ApiBase::PARAM_HELP_MSG_APPEND] as $m ) {
-                                       $m = ApiBase::makeMessage( $m, $this->getContext(),
+                               foreach ( $settings[self::PARAM_HELP_MSG_APPEND] as $m ) {
+                                       $m = self::makeMessage( $m, $this->getContext(),
                                                [ $prefix, $param, $name, $path ] );
                                        if ( $m ) {
                                                $msgs[$param][] = $m;
@@ -2614,6 +2614,7 @@ abstract class ApiBase extends ContextSource {
         * @param string $warning Warning message
         */
        public function setWarning( $warning ) {
+               wfDeprecated( __METHOD__, '1.29' );
                $msg = new ApiRawMessage( $warning, 'warning' );
                $this->getErrorFormatter()->addWarning( $this->getModulePath(), $msg );
        }
@@ -2632,6 +2633,7 @@ abstract class ApiBase extends ContextSource {
         * @throws ApiUsageException always
         */
        public function dieUsage( $description, $errorCode, $httpRespCode = 0, $extradata = null ) {
+               wfDeprecated( __METHOD__, '1.29' );
                $this->dieWithError(
                        new RawMessage( '$1', [ $description ] ),
                        $errorCode,
@@ -2651,6 +2653,7 @@ abstract class ApiBase extends ContextSource {
         * @throws MWException
         */
        public function getErrorFromStatus( $status, &$extraData = null ) {
+               wfDeprecated( __METHOD__, '1.29' );
                if ( $status->isGood() ) {
                        throw new MWException( 'Successful status passed to ApiBase::dieStatus' );
                }
@@ -2860,6 +2863,7 @@ abstract class ApiBase extends ContextSource {
         * @return array [ 'code' => code, 'info' => info ]
         */
        public function parseMsg( $error ) {
+               wfDeprecated( __METHOD__, '1.29' );
                // Check whether someone passed the whole array, instead of one element as
                // documented. This breaks if it's actually an array of fallback keys, but
                // that's long-standing misbehavior introduced in r87627 to incorrectly
@@ -2889,6 +2893,7 @@ abstract class ApiBase extends ContextSource {
         * @throws ApiUsageException always
         */
        public function dieUsageMsg( $error ) {
+               wfDeprecated( __METHOD__, '1.29' );
                $this->dieWithError( $this->parseMsgInternal( $error ) );
        }
 
@@ -2901,6 +2906,7 @@ abstract class ApiBase extends ContextSource {
         * @since 1.21
         */
        public function dieUsageMsgOrDebug( $error ) {
+               wfDeprecated( __METHOD__, '1.29' );
                $this->dieWithErrorOrDebug( $this->parseMsgInternal( $error ) );
        }
 
index 72c7c35..4b4b76b 100644 (file)
@@ -71,7 +71,7 @@ class ApiEmailUser extends ApiBase {
                }
 
                $result = array_filter( [
-                       'result' => $retval->isGood() ? 'Success' : $retval->isOk() ? 'Warnings' : 'Failure',
+                       'result' => $retval->isGood() ? 'Success' : ( $retval->isOk() ? 'Warnings' : 'Failure' ),
                        'warnings' => $this->getErrorFormatter()->arrayFromStatus( $retval, 'warning' ),
                        'errors' => $this->getErrorFormatter()->arrayFromStatus( $retval, 'error' ),
                ] );
index 52f79ee..6468235 100644 (file)
@@ -1041,7 +1041,7 @@ class ApiMain extends ApiBase {
                        // None of the rest have any messages for non-error types
                } elseif ( $e instanceof UsageException ) {
                        // User entered incorrect parameters - generate error response
-                       $data = $e->getMessageArray();
+                       $data = MediaWiki\quietCall( [ $e, 'getMessageArray' ] );
                        $code = $data['code'];
                        $info = $data['info'];
                        unset( $data['code'], $data['info'] );
@@ -1829,7 +1829,7 @@ class ApiMain extends ApiBase {
                                ApiBase::PARAM_TYPE => 'submodule',
                        ],
                        'format' => [
-                               ApiBase::PARAM_DFLT => ApiMain::API_DEFAULT_FORMAT,
+                               ApiBase::PARAM_DFLT => self::API_DEFAULT_FORMAT,
                                ApiBase::PARAM_TYPE => 'submodule',
                        ],
                        'maxlag' => [
@@ -1931,14 +1931,15 @@ class ApiMain extends ApiBase {
 
                        $header = $this->msg( 'api-help-datatypes-header' )->parse();
 
-                       // Add an additional span with sanitized ID
-                       if ( !$this->getConfig()->get( 'ExperimentalHtmlIds' ) ) {
-                               $header = Html::element( 'span', [ 'id' => Sanitizer::escapeId( 'main/datatypes' ) ] ) .
-                                       $header;
-                       }
-                       $help['datatypes'] .= Html::rawElement( 'h' . min( 6, $level ),
-                               [ 'id' => 'main/datatypes', 'class' => 'apihelp-header' ],
-                               $header
+                       $id = Sanitizer::escapeIdForAttribute( 'main/datatypes', Sanitizer::ID_PRIMARY );
+                       $idFallback = Sanitizer::escapeIdForAttribute( 'main/datatypes', Sanitizer::ID_FALLBACK );
+
+                       $help['datatypes'] .= Linker::makeHeadline( min( 6, $level ),
+                               ' class="apihelp-header"',
+                               $id,
+                               $header,
+                               '',
+                               $idFallback
                        );
                        $help['datatypes'] .= $this->msg( 'api-help-datatypes' )->parseAsBlock();
                        if ( !isset( $tocData['main/datatypes'] ) ) {
@@ -1953,15 +1954,15 @@ class ApiMain extends ApiBase {
                                ];
                        }
 
-                       // Add an additional span with sanitized ID
-                       if ( !$this->getConfig()->get( 'ExperimentalHtmlIds' ) ) {
-                               $header = Html::element( 'span', [ 'id' => Sanitizer::escapeId( 'main/credits' ) ] ) .
-                                       $header;
-                       }
                        $header = $this->msg( 'api-credits-header' )->parse();
-                       $help['credits'] .= Html::rawElement( 'h' . min( 6, $level ),
-                               [ 'id' => 'main/credits', 'class' => 'apihelp-header' ],
-                               $header
+                       $id = Sanitizer::escapeIdForAttribute( 'main/credits', Sanitizer::ID_PRIMARY );
+                       $idFallback = Sanitizer::escapeIdForAttribute( 'main/credits', Sanitizer::ID_FALLBACK );
+                       $help['credits'] .= Linker::makeHeadline( min( 6, $level ),
+                               ' class="apihelp-header"',
+                               $id,
+                               $header,
+                               '',
+                               $idFallback
                        );
                        $help['credits'] .= $this->msg( 'api-credits' )->useDatabase( false )->parseAsBlock();
                        if ( !isset( $tocData['main/credits'] ) ) {
index 599b3de..cfac761 100644 (file)
@@ -121,7 +121,7 @@ class ApiPageSet extends ApiBase {
        public function __construct( ApiBase $dbSource, $flags = 0, $defaultNamespace = NS_MAIN ) {
                parent::__construct( $dbSource->getMain(), $dbSource->getModuleName() );
                $this->mDbSource = $dbSource;
-               $this->mAllowGenerator = ( $flags & ApiPageSet::DISABLE_GENERATORS ) == 0;
+               $this->mAllowGenerator = ( $flags & self::DISABLE_GENERATORS ) == 0;
                $this->mDefaultNamespace = $defaultNamespace;
 
                $this->mParams = $this->extractRequestParams();
@@ -166,7 +166,7 @@ class ApiPageSet extends ApiBase {
                        }
                        // Create a temporary pageset to store generator's output,
                        // add any additional fields generator may need, and execute pageset to populate titles/pageids
-                       $tmpPageSet = new ApiPageSet( $dbSource, ApiPageSet::DISABLE_GENERATORS );
+                       $tmpPageSet = new ApiPageSet( $dbSource, self::DISABLE_GENERATORS );
                        $generator->setGeneratorMode( $tmpPageSet );
                        $this->mCacheMode = $generator->getCacheMode( $generator->extractRequestParams() );
 
index b2664df..bfd5b17 100644 (file)
@@ -677,7 +677,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
                                ApiBase::PARAM_DFLT => -1,
                                ApiBase::PARAM_HELP_MSG => [
                                        'apihelp-query+imageinfo-param-urlwidth',
-                                       ApiQueryImageInfo::TRANSFORM_LIMIT,
+                                       self::TRANSFORM_LIMIT,
                                ],
                        ],
                        'urlheight' => [
index 6b8f98c..ecdebd4 100644 (file)
@@ -128,7 +128,7 @@ class ApiQueryInfo extends ApiQueryBase {
         * @deprecated since 1.24
         */
        public static function resetTokenCache() {
-               ApiQueryInfo::$cachedTokens = [];
+               self::$cachedTokens = [];
        }
 
        /**
@@ -144,11 +144,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['edit'] ) ) {
-                       ApiQueryInfo::$cachedTokens['edit'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['edit'] ) ) {
+                       self::$cachedTokens['edit'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['edit'];
+               return self::$cachedTokens['edit'];
        }
 
        /**
@@ -161,11 +161,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['delete'] ) ) {
-                       ApiQueryInfo::$cachedTokens['delete'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['delete'] ) ) {
+                       self::$cachedTokens['delete'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['delete'];
+               return self::$cachedTokens['delete'];
        }
 
        /**
@@ -178,11 +178,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['protect'] ) ) {
-                       ApiQueryInfo::$cachedTokens['protect'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['protect'] ) ) {
+                       self::$cachedTokens['protect'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['protect'];
+               return self::$cachedTokens['protect'];
        }
 
        /**
@@ -195,11 +195,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['move'] ) ) {
-                       ApiQueryInfo::$cachedTokens['move'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['move'] ) ) {
+                       self::$cachedTokens['move'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['move'];
+               return self::$cachedTokens['move'];
        }
 
        /**
@@ -212,11 +212,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['block'] ) ) {
-                       ApiQueryInfo::$cachedTokens['block'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['block'] ) ) {
+                       self::$cachedTokens['block'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['block'];
+               return self::$cachedTokens['block'];
        }
 
        /**
@@ -237,11 +237,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['email'] ) ) {
-                       ApiQueryInfo::$cachedTokens['email'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['email'] ) ) {
+                       self::$cachedTokens['email'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['email'];
+               return self::$cachedTokens['email'];
        }
 
        /**
@@ -254,11 +254,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['import'] ) ) {
-                       ApiQueryInfo::$cachedTokens['import'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['import'] ) ) {
+                       self::$cachedTokens['import'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['import'];
+               return self::$cachedTokens['import'];
        }
 
        /**
@@ -271,11 +271,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['watch'] ) ) {
-                       ApiQueryInfo::$cachedTokens['watch'] = $wgUser->getEditToken( 'watch' );
+               if ( !isset( self::$cachedTokens['watch'] ) ) {
+                       self::$cachedTokens['watch'] = $wgUser->getEditToken( 'watch' );
                }
 
-               return ApiQueryInfo::$cachedTokens['watch'];
+               return self::$cachedTokens['watch'];
        }
 
        /**
@@ -288,11 +288,11 @@ class ApiQueryInfo extends ApiQueryBase {
                }
 
                // The token is always the same, let's exploit that
-               if ( !isset( ApiQueryInfo::$cachedTokens['options'] ) ) {
-                       ApiQueryInfo::$cachedTokens['options'] = $wgUser->getEditToken();
+               if ( !isset( self::$cachedTokens['options'] ) ) {
+                       self::$cachedTokens['options'] = $wgUser->getEditToken();
                }
 
-               return ApiQueryInfo::$cachedTokens['options'];
+               return self::$cachedTokens['options'];
        }
 
        public function execute() {
index 29c0b74..3639c06 100644 (file)
@@ -34,7 +34,7 @@ class ApiQueryLinks extends ApiQueryGeneratorBase {
        const LINKS = 'links';
        const TEMPLATES = 'templates';
 
-       private $table, $prefix, $helpUrl;
+       private $table, $prefix, $titlesParam, $helpUrl;
 
        public function __construct( ApiQuery $query, $moduleName ) {
                switch ( $moduleName ) {
index 6734740..468d878 100644 (file)
@@ -287,12 +287,12 @@ class ApiResult implements ApiSerializable {
         * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
         */
        public static function setValue( array &$arr, $name, $value, $flags = 0 ) {
-               if ( ( $flags & ApiResult::NO_VALIDATE ) !== ApiResult::NO_VALIDATE ) {
+               if ( ( $flags & self::NO_VALIDATE ) !== self::NO_VALIDATE ) {
                        $value = self::validateValue( $value );
                }
 
                if ( $name === null ) {
-                       if ( $flags & ApiResult::ADD_ON_TOP ) {
+                       if ( $flags & self::ADD_ON_TOP ) {
                                array_unshift( $arr, $value );
                        } else {
                                array_push( $arr, $value );
@@ -301,8 +301,8 @@ class ApiResult implements ApiSerializable {
                }
 
                $exists = isset( $arr[$name] );
-               if ( !$exists || ( $flags & ApiResult::OVERRIDE ) ) {
-                       if ( !$exists && ( $flags & ApiResult::ADD_ON_TOP ) ) {
+               if ( !$exists || ( $flags & self::OVERRIDE ) ) {
+                       if ( !$exists && ( $flags & self::ADD_ON_TOP ) ) {
                                $arr = [ $name => $value ] + $arr;
                        } else {
                                $arr[$name] = $value;
@@ -403,13 +403,13 @@ class ApiResult implements ApiSerializable {
         * @since 1.21 int $flags replaced boolean $override
         */
        public function addValue( $path, $name, $value, $flags = 0 ) {
-               $arr = &$this->path( $path, ( $flags & ApiResult::ADD_ON_TOP ) ? 'prepend' : 'append' );
+               $arr = &$this->path( $path, ( $flags & self::ADD_ON_TOP ) ? 'prepend' : 'append' );
 
-               if ( $this->checkingSize && !( $flags & ApiResult::NO_SIZE_CHECK ) ) {
+               if ( $this->checkingSize && !( $flags & self::NO_SIZE_CHECK ) ) {
                        // self::size needs the validated value. Then flag
                        // to not re-validate later.
                        $value = self::validateValue( $value );
-                       $flags |= ApiResult::NO_VALIDATE;
+                       $flags |= self::NO_VALIDATE;
 
                        $newsize = $this->size + self::size( $value );
                        if ( $this->maxSize !== false && $newsize > $this->maxSize ) {
@@ -459,7 +459,7 @@ class ApiResult implements ApiSerializable {
                        $name = array_pop( $path );
                }
                $ret = self::unsetValue( $this->path( $path, 'dummy' ), $name );
-               if ( $this->checkingSize && !( $flags & ApiResult::NO_SIZE_CHECK ) ) {
+               if ( $this->checkingSize && !( $flags & self::NO_SIZE_CHECK ) ) {
                        $newsize = $this->size - self::size( $ret );
                        $this->size = max( $newsize, 0 );
                }
@@ -511,7 +511,7 @@ class ApiResult implements ApiSerializable {
        public function addParsedLimit( $moduleName, $limit ) {
                // Add value, allowing overwriting
                $this->addValue( 'limits', $moduleName, $limit,
-                       ApiResult::OVERRIDE | ApiResult::NO_SIZE_CHECK );
+                       self::OVERRIDE | self::NO_SIZE_CHECK );
        }
 
        /**@}*/
@@ -551,7 +551,7 @@ class ApiResult implements ApiSerializable {
         * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
         */
        public function addContentField( $path, $name, $flags = 0 ) {
-               $arr = &$this->path( $path, ( $flags & ApiResult::ADD_ON_TOP ) ? 'prepend' : 'append' );
+               $arr = &$this->path( $path, ( $flags & self::ADD_ON_TOP ) ? 'prepend' : 'append' );
                self::setContentField( $arr, $name, $flags );
        }
 
@@ -1156,7 +1156,7 @@ class ApiResult implements ApiSerializable {
                $bools = [];
                foreach ( $vars as $k => $v ) {
                        if ( is_array( $v ) || is_object( $v ) ) {
-                               $vars[$k] = ApiResult::addMetadataToResultVars( (array)$v, is_object( $v ) );
+                               $vars[$k] = self::addMetadataToResultVars( (array)$v, is_object( $v ) );
                        } elseif ( is_bool( $v ) ) {
                                // Better here to use real bools even in BC formats
                                $bools[] = $k;
@@ -1176,22 +1176,22 @@ class ApiResult implements ApiSerializable {
                        // Get the list of keys we actually care about. Unfortunately, we can't support
                        // certain keys that conflict with ApiResult metadata.
                        $keys = array_diff( array_keys( $vars ), [
-                               ApiResult::META_TYPE, ApiResult::META_PRESERVE_KEYS, ApiResult::META_KVP_KEY_NAME,
-                               ApiResult::META_INDEXED_TAG_NAME, ApiResult::META_BC_BOOLS
+                               self::META_TYPE, self::META_PRESERVE_KEYS, self::META_KVP_KEY_NAME,
+                               self::META_INDEXED_TAG_NAME, self::META_BC_BOOLS
                        ] );
 
                        return [
-                               ApiResult::META_TYPE => 'kvp',
-                               ApiResult::META_KVP_KEY_NAME => 'key',
-                               ApiResult::META_PRESERVE_KEYS => $keys,
-                               ApiResult::META_BC_BOOLS => $bools,
-                               ApiResult::META_INDEXED_TAG_NAME => 'var',
+                               self::META_TYPE => 'kvp',
+                               self::META_KVP_KEY_NAME => 'key',
+                               self::META_PRESERVE_KEYS => $keys,
+                               self::META_BC_BOOLS => $bools,
+                               self::META_INDEXED_TAG_NAME => 'var',
                        ] + $vars;
                } else {
                        return [
-                               ApiResult::META_TYPE => 'array',
-                               ApiResult::META_BC_BOOLS => $bools,
-                               ApiResult::META_INDEXED_TAG_NAME => 'value',
+                               self::META_TYPE => 'array',
+                               self::META_BC_BOOLS => $bools,
+                               self::META_INDEXED_TAG_NAME => 'value',
                        ] + $vars;
                }
        }
index 9dc1f92..fb49e2d 100644 (file)
@@ -45,6 +45,10 @@ class UsageException extends MWException {
                $this->mCodestr = $codestr;
                $this->mExtraData = $extradata;
 
+               if ( !$this instanceof ApiUsageException ) {
+                       wfDeprecated( __METHOD__, '1.29' );
+               }
+
                // This should never happen, so throw an exception about it that will
                // hopefully get logged with a backtrace (T138585)
                if ( !is_string( $codestr ) || $codestr === '' ) {
@@ -58,6 +62,7 @@ class UsageException extends MWException {
         * @return string
         */
        public function getCodeString() {
+               wfDeprecated( __METHOD__, '1.29' );
                return $this->mCodestr;
        }
 
@@ -65,6 +70,7 @@ class UsageException extends MWException {
         * @return array
         */
        public function getMessageArray() {
+               wfDeprecated( __METHOD__, '1.29' );
                $result = [
                        'code' => $this->mCodestr,
                        'info' => $this->getMessage()
@@ -183,6 +189,7 @@ class ApiUsageException extends UsageException implements ILocalizedException {
         * @inheritdoc
         */
        public function getCodeString() {
+               wfDeprecated( __METHOD__, '1.29' );
                return $this->getApiMessage()->getApiCode();
        }
 
@@ -192,6 +199,7 @@ class ApiUsageException extends UsageException implements ILocalizedException {
         * @inheritdoc
         */
        public function getMessageArray() {
+               wfDeprecated( __METHOD__, '1.29' );
                $enMsg = clone $this->getApiMessage();
                $enMsg->inLanguage( 'en' )->useDatabase( false );
 
index 6fea718..8918620 100644 (file)
        "apihelp-query+langlinks-param-lang": "להחזיר רק קישורי שפה עם קוד השפה הזה.",
        "apihelp-query+langlinks-param-title": "קישור לחיפוש. חובה להשתמש עם <var>$1lang</var>.",
        "apihelp-query+langlinks-param-dir": "באיזה כיוון לרשום.",
-       "apihelp-query+langlinks-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×©שמות שפות מתורגמות.",
+       "apihelp-query+langlinks-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×\91ש×\91×\99×\9c שמות שפות מתורגמות.",
        "apihelp-query+langlinks-example-simple": "קבלת קישורים בין־לשוניים מהדף <kbd>Main Page</kbd>.",
        "apihelp-query+links-summary": "החזרת כל הקישורים מהדפים שצוינו.",
        "apihelp-query+links-param-namespace": "להציג קישורים רק במרחבי השם האלה.",
        "apihelp-query+siteinfo-param-filteriw": "החזרה רק של עיולים מקומיים או רק של עיולים לא מקומיים ממפת הבינוויקי.",
        "apihelp-query+siteinfo-param-showalldb": "רשימת כל שרתי מסד הנתונים, לא רק אלה שהכי מתעכבים.",
        "apihelp-query+siteinfo-param-numberingroup": "רשימת מספרי משתמשים בקבוצות משתמשים.",
-       "apihelp-query+siteinfo-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×©שמות שפות מתורגמות (מאמץ טוב ביותר) ושמות עיצובים.",
+       "apihelp-query+siteinfo-param-inlanguagecode": "ק×\95×\93 ×©×¤×\94 ×\91ש×\91×\99×\9c שמות שפות מתורגמות (מאמץ טוב ביותר) ושמות עיצובים.",
        "apihelp-query+siteinfo-example-simple": "איזור מידע על האתר.",
        "apihelp-query+siteinfo-example-interwiki": "אחזור תחיליות בינוויקי מקומיות.",
        "apihelp-query+siteinfo-example-replag": "בדיקת שיהוי השכפול הנוכחי.",
index 36b580e..bff361e 100644 (file)
        "apihelp-move-param-watchlist": "현재 사용자의 주시목록에서 문서를 무조건적으로 추가하거나 제거하거나, 환경 설정을 사용하거나 주시를 변경하지 않습니다.",
        "apihelp-move-param-ignorewarnings": "모든 경고 무시하기",
        "apihelp-move-example-move": "<kbd>기존 제목</kbd>에서 <kbd>대상 제목</kbd>으로 넘겨주기를 만들지 않고 이동하기.",
-       "apihelp-opensearch-summary": "OpenSearch 프로토콜을 이용하여 위키 검색하기",
+       "apihelp-opensearch-summary": "OpenSearch 프로토콜을 이용하여 위키를 검색합니다.",
        "apihelp-opensearch-param-search": "문자열 검색",
        "apihelp-opensearch-param-limit": "반환할 결과의 최대 수",
        "apihelp-opensearch-param-namespace": "검색할 이름공간.",
        "apihelp-setpagelanguage-param-lang": "문서를 변경할 언어의 언어 코드입니다. 문서를 위키의 기본 콘텐츠 언어로 재설정하려면 <kbd>default</kbd>를 사용하십시오.",
        "apihelp-setpagelanguage-param-reason": "변경 이유.",
        "apihelp-setpagelanguage-example-language": "<kbd>Main Page</kbd>의 언어를 바스크어로 변경합니다.",
+       "apihelp-stashedit-summary": "공유된 캐시에서 편집을 준비합니다.",
        "apihelp-stashedit-param-sectiontitle": "새 문단을 위한 제목.",
        "apihelp-stashedit-param-text": "문서 내용.",
        "apihelp-stashedit-param-contentmodel": "새 콘텐츠의 콘텐츠 모델.",
        "api-help-authmanagerhelper-continue": "이 요청은 초기 <samp>UI</samp> 또는 <samp>REDIRECT</samp> 응답 이후에 계속됩니다. 이것 또는 <var>$1returnurl</var> 중 하나가 필요합니다.",
        "api-help-authmanagerhelper-additional-params": "이 모듈은 사용 가능한 인증 요청에 따라 추가 변수를 허용합니다. 사용 가능한 요청 및 사용되는 필드를 결정하려면 <kbd>amirequestsfor=$1</kbd>(또는 해당되는 경우 이 모듈의 과거 응답)과 함께 <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd>을(를) 사용하십시오.",
        "apierror-articleexists": "작성하려는 문서가 이미 만들어져 있습니다.",
+       "apierror-assertbotfailed": "사용자의 <code>bot</code> 권한 보유 표명이 실패했습니다.",
+       "apierror-assertnameduserfailed": "사용자의 \"$1\" 지정 표명이 실패했습니다.",
+       "apierror-assertuserfailed": "사용자의 로그인 표명이 실패했습니다.",
        "apierror-autoblocked": "사용자의 IP 주소는 차단된 사용자에 의해 사용되었으므로 자동으로 차단된 상태입니다.",
        "apierror-badgenerator-unknown": "알 수 없는 <kbd>generator=$1</kbd>.",
        "apierror-badip": "IP 변수가 유효하지 않습니다.",
index 1dfeb34..a311728 100644 (file)
@@ -11,7 +11,8 @@
                        "Winstonyin",
                        "Arthur2e5",
                        "烈羽",
-                       "Corainn"
+                       "Corainn",
+                       "A2093064"
                ]
        },
        "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|說明文件]]\n* [[mw:API:FAQ|常見問題]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 郵寄清單]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API公告]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R 報告錯誤及請求功能]\n</div>\n<strong>狀態資訊:</strong>本頁所展示的所有功能都應正常工作,但是API仍在開發當中,將會隨時變化。請訂閱[https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ mediawiki-api-announce 郵件清單]以便得到更新通知。\n\n<strong>錯誤的請求:</strong>當API收到錯誤的請求時,會發出以「MediaWiki-API-Error」為鍵的HTTP頭欄位,隨後頭欄位的值與錯誤碼將會被設為相同的值。詳細資訊請參閱[[mw:API:Errors_and_warnings|API: 錯誤與警告]]。\n\n<strong>測試:</strong>要簡化API請求的測試過程,請見[[Special:ApiSandbox]]。",
@@ -57,7 +58,7 @@
        "apihelp-createaccount-summary": "建立新使用者帳號。",
        "apihelp-createaccount-param-name": "使用者名稱。",
        "apihelp-createaccount-param-password": "密碼 (若有設定 <var>$1mailpassword</var> 則可略過)。",
-       "apihelp-createaccount-param-domain": "å¤\96é\83¨èª\8d証使用的網域 (選填)。",
+       "apihelp-createaccount-param-domain": "å¤\96é\83¨èª\8dè­\89使用的網域 (選填)。",
        "apihelp-createaccount-param-token": "在第一次請求時已取得的帳號建立金鑰。",
        "apihelp-createaccount-param-email": "使用者的電子郵件地址 (選填) 。",
        "apihelp-createaccount-param-realname": "使用者的真實姓名 (選填)。",
index 6684fb9..956c985 100644 (file)
@@ -133,7 +133,7 @@ class AuthenticationResponse {
         */
        public static function newPass( $username = null ) {
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::PASS;
+               $ret->status = self::PASS;
                $ret->username = $username;
                return $ret;
        }
@@ -145,7 +145,7 @@ class AuthenticationResponse {
         */
        public static function newFail( Message $msg ) {
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::FAIL;
+               $ret->status = self::FAIL;
                $ret->message = $msg;
                $ret->messageType = 'error';
                return $ret;
@@ -158,7 +158,7 @@ class AuthenticationResponse {
         */
        public static function newRestart( Message $msg ) {
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::RESTART;
+               $ret->status = self::RESTART;
                $ret->message = $msg;
                return $ret;
        }
@@ -169,7 +169,7 @@ class AuthenticationResponse {
         */
        public static function newAbstain() {
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::ABSTAIN;
+               $ret->status = self::ABSTAIN;
                return $ret;
        }
 
@@ -189,7 +189,7 @@ class AuthenticationResponse {
                }
 
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::UI;
+               $ret->status = self::UI;
                $ret->neededRequests = $reqs;
                $ret->message = $msg;
                $ret->messageType = $msgtype;
@@ -209,7 +209,7 @@ class AuthenticationResponse {
                }
 
                $ret = new AuthenticationResponse;
-               $ret->status = AuthenticationResponse::REDIRECT;
+               $ret->status = self::REDIRECT;
                $ret->neededRequests = $reqs;
                $ret->redirectTarget = $redirectTarget;
                $ret->redirectApiData = $redirectApiData;
index ad1fffb..f9f9a08 100644 (file)
@@ -816,7 +816,7 @@ class MessageCache {
                }
 
                // Normalise title-case input (with some inlining)
-               $lckey = MessageCache::normalizeKey( $key );
+               $lckey = self::normalizeKey( $key );
 
                Hooks::run( 'MessageCache::get', [ &$lckey ] );
 
index cffb59a..99dc899 100644 (file)
@@ -31,8 +31,6 @@ class ChangesFeed {
        public $format, $type, $titleMsg, $descMsg;
 
        /**
-        * Constructor
-        *
         * @param string $format Feed's format (either 'rss' or 'atom')
         * @param string $type Type of feed (for cache keys)
         */
index 5aa693d..2182c6c 100644 (file)
@@ -747,20 +747,22 @@ class ChangesList extends ContextSource {
         * @return string[] attribute name => value
         */
        protected function getDataAttributes( RecentChange $rc ) {
+               $attrs = [];
+
                $type = $rc->getAttribute( 'rc_source' );
                switch ( $type ) {
                        case RecentChange::SRC_EDIT:
                        case RecentChange::SRC_NEW:
-                               return [
-                                       'data-mw-revid' => $rc->mAttribs['rc_this_oldid'],
-                               ];
+                               $attrs[ 'data-mw-revid' ] = $rc->mAttribs['rc_this_oldid'];
+                               break;
                        case RecentChange::SRC_LOG:
-                               return [
-                                       'data-mw-logid' => $rc->mAttribs['rc_logid'],
-                                       'data-mw-logaction' => $rc->mAttribs['rc_log_type'] . '/' . $rc->mAttribs['rc_log_action'],
-                               ];
-                       default:
-                               return [];
+                               $attrs[ 'data-mw-logid' ] = $rc->mAttribs['rc_logid'];
+                               $attrs[ 'data-mw-logaction' ] = $rc->mAttribs['rc_log_type'] . '/' . $rc->mAttribs['rc_log_action'];
+                               break;
                }
+
+               $attrs[ 'data-mw-ts' ] = $rc->getAttribute( 'rc_timestamp' );
+
+               return $attrs;
        }
 }
index 30c6995..55cb9ed 100644 (file)
@@ -102,15 +102,17 @@ class EnhancedChangesList extends ChangesList {
                        $rc->mAttribs['rc_timestamp'],
                        $this->getUser()
                );
+               if ( $this->lastdate === '' ) {
+                       $this->lastdate = $date;
+               }
 
                $ret = '';
 
-               # If it's a new day, add the headline and flush the cache
-               if ( $date != $this->lastdate ) {
-                       # Process current cache
+               # If it's a new day, flush the cache and update $this->lastdate
+               if ( $date !== $this->lastdate ) {
+                       # Process current cache (uses $this->lastdate to generate a heading)
                        $ret = $this->recentChangesBlock();
                        $this->rc_cache = [];
-                       $ret .= Xml::element( 'h4', null, $date ) . "\n";
                        $this->lastdate = $date;
                }
 
@@ -763,7 +765,11 @@ class EnhancedChangesList extends ChangesList {
                        }
                }
 
-               return '<div>' . $blockOut . '</div>';
+               if ( $blockOut === '' ) {
+                       return '';
+               }
+               // $this->lastdate is kept up to date by recentChangesLine()
+               return Xml::element( 'h4', null, $this->lastdate ) . "\n<div>" . $blockOut . '</div>';
        }
 
        /**
index e8e35a3..5fad8fd 100644 (file)
@@ -130,7 +130,7 @@ class RecentChange {
                if ( is_array( $type ) ) {
                        $retval = [];
                        foreach ( $type as $t ) {
-                               $retval[] = RecentChange::parseToRCType( $t );
+                               $retval[] = self::parseToRCType( $t );
                        }
 
                        return $retval;
@@ -459,7 +459,7 @@ class RecentChange {
 
                $change = $change instanceof RecentChange
                        ? $change
-                       : RecentChange::newFromId( $change );
+                       : self::newFromId( $change );
 
                if ( !$change instanceof RecentChange ) {
                        return null;
index c9b5f96..2eb9b22 100644 (file)
@@ -643,24 +643,32 @@ class ChangeTags {
         * Handles selecting tags, and filtering.
         * Needs $tables to be set up properly, so we can figure out which join conditions to use.
         *
+        * WARNING: If $filter_tag contains more than one tag, this function will add DISTINCT,
+        * which may cause performance problems for your query unless you put the ID field of your
+        * table at the end of the ORDER BY, and set a GROUP BY equal to the ORDER BY. For example,
+        * if you had ORDER BY foo_timestamp DESC, you will now need GROUP BY foo_timestamp, foo_id
+        * ORDER BY foo_timestamp DESC, foo_id DESC.
+        *
         * @param string|array $tables Table names, see Database::select
         * @param string|array $fields Fields used in query, see Database::select
         * @param string|array $conds Conditions used in query, see Database::select
         * @param array $join_conds Join conditions, see Database::select
-        * @param array $options Options, see Database::select
-        * @param bool|string $filter_tag Tag to select on
+        * @param string|array $options Options, see Database::select
+        * @param string|array $filter_tag Tag(s) to select on
         *
         * @throws MWException When unable to determine appropriate JOIN condition for tagging
         */
        public static function modifyDisplayQuery( &$tables, &$fields, &$conds,
-                                                                               &$join_conds, &$options, $filter_tag = false ) {
-               global $wgRequest, $wgUseTagFilter;
+                                                                               &$join_conds, &$options, $filter_tag = '' ) {
+               global $wgUseTagFilter;
 
-               if ( $filter_tag === false ) {
-                       $filter_tag = $wgRequest->getVal( 'tagfilter' );
-               }
+               // Normalize to arrays
+               $tables = (array)$tables;
+               $fields = (array)$fields;
+               $conds = (array)$conds;
+               $options = (array)$options;
 
-               // Figure out which conditions can be done.
+               // Figure out which ID field to use
                if ( in_array( 'recentchanges', $tables ) ) {
                        $join_cond = 'ct_rc_id=rc_id';
                } elseif ( in_array( 'logging', $tables ) ) {
@@ -683,7 +691,13 @@ class ChangeTags {
 
                        $tables[] = 'change_tag';
                        $join_conds['change_tag'] = [ 'INNER JOIN', $join_cond ];
-                       $conds['ct_tag'] = explode( '|', $filter_tag );
+                       $conds['ct_tag'] = $filter_tag;
+                       if (
+                               is_array( $filter_tag ) && count( $filter_tag ) > 1 &&
+                               !in_array( 'DISTINCT', $options )
+                       ) {
+                               $options[] = 'DISTINCT';
+                       }
                }
        }
 
@@ -962,7 +976,7 @@ class ChangeTags {
 
                // tags cannot contain commas (used as a delimiter in tag_summary table),
                // pipe (used as a delimiter between multiple tags in
-               // modifyDisplayQuery), or slashes (would break tag description messages in
+               // SpecialRecentchanges and friends), or slashes (would break tag description messages in
                // MediaWiki namespace)
                if ( strpos( $tag, ',' ) !== false || strpos( $tag, '|' ) !== false
                        || strpos( $tag, '/' ) !== false ) {
index e2f82d4..d93362b 100644 (file)
@@ -535,7 +535,7 @@ class IcuCollation extends Collation {
         * @return string|bool
         */
        static function getUnicodeVersionForICU() {
-               $icuVersion = IcuCollation::getICUVersion();
+               $icuVersion = self::getICUVersion();
                if ( !$icuVersion ) {
                        return false;
                }
index 2d2ca47..8dd7a38 100644 (file)
@@ -40,8 +40,6 @@ class NumericUppercaseCollation extends UppercaseCollation {
        private $digitTransformLang;
 
        /**
-        * Constructor
-        *
         * @param $lang Language How to convert digits.
         *  For example, if given language "my" than ၇ is treated like 7.
         *
index c57eba7..6605c38 100644 (file)
@@ -243,7 +243,7 @@ class EtcdConfig implements Config, LoggerAwareInterface {
 
                $info = json_decode( $rbody, true );
                if ( $info === null || !isset( $info['node']['nodes'] ) ) {
-                       return [ null, $rcode, "Unexpected JSON response; missing 'nodes' list.", false ];
+                       return [ null, "Unexpected JSON response; missing 'nodes' list.", false ];
                }
 
                $config = [];
index f85b00d..8603360 100644 (file)
@@ -136,7 +136,7 @@ abstract class ContentHandler {
                        $modelId = $title->getContentModel();
                }
 
-               $handler = ContentHandler::getForModelID( $modelId );
+               $handler = self::getForModelID( $modelId );
 
                return $handler->unserializeContent( $text, $format );
        }
@@ -240,7 +240,7 @@ abstract class ContentHandler {
        public static function getForTitle( Title $title ) {
                $modelId = $title->getContentModel();
 
-               return ContentHandler::getForModelID( $modelId );
+               return self::getForModelID( $modelId );
        }
 
        /**
@@ -256,7 +256,7 @@ abstract class ContentHandler {
        public static function getForContent( Content $content ) {
                $modelId = $content->getModel();
 
-               return ContentHandler::getForModelID( $modelId );
+               return self::getForModelID( $modelId );
        }
 
        /**
@@ -293,8 +293,8 @@ abstract class ContentHandler {
        public static function getForModelID( $modelId ) {
                global $wgContentHandlers;
 
-               if ( isset( ContentHandler::$handlers[$modelId] ) ) {
-                       return ContentHandler::$handlers[$modelId];
+               if ( isset( self::$handlers[$modelId] ) ) {
+                       return self::$handlers[$modelId];
                }
 
                if ( empty( $wgContentHandlers[$modelId] ) ) {
@@ -327,9 +327,9 @@ abstract class ContentHandler {
                wfDebugLog( 'ContentHandler', 'Created handler for ' . $modelId
                        . ': ' . get_class( $handler ) );
 
-               ContentHandler::$handlers[$modelId] = $handler;
+               self::$handlers[$modelId] = $handler;
 
-               return ContentHandler::$handlers[$modelId];
+               return self::$handlers[$modelId];
        }
 
        /**
@@ -372,7 +372,7 @@ abstract class ContentHandler {
                $formats = [];
 
                foreach ( $wgContentHandlers as $model => $class ) {
-                       $handler = ContentHandler::getForModelID( $model );
+                       $handler = self::getForModelID( $model );
                        $formats = array_merge( $formats, $handler->getSupportedFormats() );
                }
 
index 0d0c149..6e3eda6 100644 (file)
@@ -75,7 +75,6 @@ class DerivativeContext extends ContextSource implements MutableContext {
        private $timing;
 
        /**
-        * Constructor
         * @param IContextSource $context Context to inherit from
         */
        public function __construct( IContextSource $context ) {
index 6d18444..3d22c03 100644 (file)
@@ -46,8 +46,6 @@ class CloneDatabase {
        private $db;
 
        /**
-        * Constructor
-        *
         * @param IMaintainableDatabase $db A database subclass
         * @param array $tablesToClone An array of tables to clone, unprefixed
         * @param string $newTablePrefix Prefix to assign to the tables
index e67a0b3..012837f 100644 (file)
@@ -425,7 +425,7 @@ class MWDebug {
                $html = '';
 
                if ( self::$enabled ) {
-                       MWDebug::log( 'MWDebug output complete' );
+                       self::log( 'MWDebug output complete' );
                        $debugInfo = self::getDebugInfo( $context );
 
                        // Cannot use OutputPage::addJsConfigVars because those are already outputted
@@ -495,7 +495,7 @@ class MWDebug {
                        }
                }
 
-               MWDebug::log( 'MWDebug output complete' );
+               self::log( 'MWDebug output complete' );
                $debugInfo = self::getDebugInfo( $context );