Merge "FauxRequest: don’t override getValues()"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 23 Sep 2019 12:30:46 +0000 (12:30 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 23 Sep 2019 12:30:46 +0000 (12:30 +0000)
810 files changed:
.phan/config.php
.travis.yml
CODE_OF_CONDUCT.md
INSTALL
RELEASE-NOTES-1.34
autoload.php
composer.json
docs/hooks.txt
includes/AjaxResponse.php
includes/Autopromote.php
includes/DefaultSettings.php
includes/DevelopmentSettings.php
includes/EditPage.php
includes/FeedUtils.php
includes/GlobalFunctions.php
includes/Linker.php
includes/MergeHistory.php
includes/MovePage.php
includes/OutputPage.php
includes/PHPVersionCheck.php
includes/Permissions/PermissionManager.php
includes/ProtectionForm.php
includes/Rest/BasicAccess/MWBasicAuthorizer.php
includes/Rest/BasicAccess/MWBasicRequestAuthorizer.php
includes/Rest/EntryPoint.php
includes/Rest/LocalizedHttpException.php
includes/Rest/ResponseFactory.php
includes/Rest/Router.php
includes/Rest/Validator/ParamValidatorCallbacks.php
includes/Rest/Validator/Validator.php
includes/Revision/RevisionRecord.php
includes/Revision/RevisionStore.php
includes/Revision/RevisionStoreFactory.php
includes/Revision/RevisionStoreRecord.php
includes/Revision/SlotRoleHandler.php
includes/ServiceWiring.php
includes/Setup.php
includes/Status.php
includes/Storage/DerivedPageDataUpdater.php
includes/Storage/PageEditStash.php
includes/Storage/SqlBlobStore.php
includes/StreamFile.php
includes/Title.php
includes/WebRequest.php
includes/Xml.php
includes/actions/Action.php
includes/actions/HistoryAction.php
includes/actions/InfoAction.php
includes/actions/McrUndoAction.php
includes/actions/RawAction.php
includes/actions/RollbackAction.php
includes/actions/SpecialPageAction.php
includes/api/ApiBase.php
includes/api/ApiComparePages.php
includes/api/ApiEditPage.php
includes/api/ApiErrorFormatter.php
includes/api/ApiFormatXmlRsd.php
includes/api/ApiHelpParamValueMessage.php
includes/api/ApiImageRotate.php
includes/api/ApiParse.php
includes/api/ApiQueryAllUsers.php
includes/api/ApiQueryBacklinks.php
includes/api/ApiQueryBase.php
includes/api/ApiQueryBlockInfoTrait.php [new file with mode: 0644]
includes/api/ApiQueryDeletedrevs.php
includes/api/ApiQueryImageInfo.php
includes/api/ApiQueryLogEvents.php
includes/api/ApiQueryQueryPage.php
includes/api/ApiQuerySiteinfo.php
includes/api/ApiQueryUsers.php
includes/api/ApiResetPassword.php
includes/api/ApiSetNotificationTimestamp.php
includes/api/ApiTag.php
includes/api/ApiUpload.php
includes/api/i18n/ar.json
includes/api/i18n/fr.json
includes/api/i18n/gl.json
includes/api/i18n/he.json
includes/api/i18n/it.json
includes/api/i18n/ko.json
includes/api/i18n/lt.json
includes/api/i18n/mk.json
includes/api/i18n/pl.json
includes/api/i18n/pt-br.json
includes/api/i18n/pt.json
includes/api/i18n/qqq.json
includes/api/i18n/ru.json
includes/api/i18n/zh-hant.json
includes/block/BlockManager.php
includes/cache/localisation/LocalisationCache.php
includes/changes/CategoryMembershipChange.php
includes/changes/ChangesFeed.php
includes/changes/ChangesList.php
includes/changes/EnhancedChangesList.php
includes/changes/OldChangesList.php
includes/changes/RCCacheEntryFactory.php
includes/changetags/ChangeTagsLogItem.php
includes/clientpool/SquidPurgeClient.php
includes/collation/CustomUppercaseCollation.php
includes/content/ContentHandler.php
includes/debug/logger/ConsoleLogger.php
includes/deferred/CdnCacheUpdate.php
includes/deferred/HTMLCacheUpdate.php
includes/deferred/LinksUpdate.php
includes/deferred/SearchUpdate.php
includes/diff/DifferenceEngine.php
includes/exception/MWExceptionHandler.php
includes/export/Dump7ZipOutput.php
includes/export/DumpFileOutput.php
includes/export/DumpFilter.php
includes/export/DumpMultiWriter.php
includes/export/DumpOutput.php
includes/export/DumpPipeOutput.php
includes/export/WikiExporter.php
includes/filerepo/FileRepo.php
includes/filerepo/file/ArchivedFile.php
includes/filerepo/file/File.php
includes/filerepo/file/LocalFile.php
includes/filerepo/file/LocalFileDeleteBatch.php
includes/filerepo/file/LocalFileMoveBatch.php
includes/gallery/ImageGalleryBase.php
includes/historyblob/ConcatenatedGzipHistoryBlob.php
includes/historyblob/HistoryBlobCurStub.php
includes/historyblob/HistoryBlobStub.php
includes/htmlform/HTMLForm.php
includes/htmlform/HTMLFormField.php
includes/htmlform/fields/HTMLSelectAndOtherField.php
includes/htmlform/fields/HTMLTitleTextField.php
includes/htmlform/fields/HTMLUserTextField.php
includes/http/HttpRequestFactory.php
includes/http/MWCallbackStream.php
includes/import/ImportStreamSource.php
includes/import/ImportStringSource.php
includes/import/WikiImporter.php
includes/import/WikiRevision.php
includes/installer/InstallDocFormatter.php
includes/installer/LocalSettingsGenerator.php
includes/installer/MysqlUpdater.php
includes/installer/PostgresInstaller.php
includes/installer/i18n/be-tarask.json
includes/installer/i18n/de.json
includes/installer/i18n/diq.json
includes/installer/i18n/io.json
includes/installer/i18n/nap.json
includes/installer/i18n/pt.json
includes/installer/i18n/ru.json
includes/libs/MemoizedCallable.php
includes/libs/StringUtils.php
includes/libs/composer/ComposerInstalled.php
includes/libs/composer/ComposerJson.php
includes/libs/composer/ComposerLock.php
includes/libs/filebackend/FSFileBackend.php
includes/libs/filebackend/FileBackendStore.php
includes/libs/filebackend/SwiftFileBackend.php
includes/libs/filebackend/fileophandle/SwiftFileOpHandle.php
includes/libs/lockmanager/QuorumLockManager.php
includes/libs/mime/MimeAnalyzer.php
includes/libs/mime/XmlTypeCheck.php
includes/libs/objectcache/MultiWriteBagOStuff.php
includes/libs/objectcache/ReplicatedBagOStuff.php
includes/libs/objectcache/wancache/WANObjectCache.php
includes/libs/rdbms/lbfactory/LBFactoryMulti.php
includes/logging/BlockLogFormatter.php
includes/logging/ContentModelLogFormatter.php
includes/logging/DeleteLogFormatter.php
includes/logging/LogEventsList.php
includes/logging/LogFormatter.php
includes/logging/LogPager.php
includes/logging/MergeLogFormatter.php
includes/logging/MoveLogFormatter.php
includes/logging/ProtectLogFormatter.php
includes/mail/EmailNotification.php
includes/mail/UserMailer.php
includes/media/DjVuHandler.php
includes/media/DjVuImage.php
includes/media/JpegMetadataExtractor.php
includes/objectcache/ObjectCache.php
includes/objectcache/SqlBagOStuff.php
includes/page/Article.php
includes/page/ImageHistoryList.php
includes/page/ImagePage.php
includes/page/MovePageFactory.php
includes/page/PageArchive.php
includes/page/WikiFilePage.php
includes/page/WikiPage.php
includes/parser/CoreParserFunctions.php
includes/parser/PPDPart_Hash.php
includes/parser/Parser.php
includes/password/PasswordPolicyChecks.php
includes/preferences/DefaultPreferencesFactory.php
includes/profiler/SectionProfiler.php
includes/resourceloader/DerivativeResourceLoaderContext.php
includes/resourceloader/MessageBlobStore.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderCircularDependencyError.php
includes/resourceloader/ResourceLoaderClientHtml.php
includes/resourceloader/ResourceLoaderContext.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/resourceloader/ResourceLoaderFilePath.php
includes/resourceloader/ResourceLoaderForeignApiModule.php
includes/resourceloader/ResourceLoaderImage.php
includes/resourceloader/ResourceLoaderImageModule.php
includes/resourceloader/ResourceLoaderLanguageDataModule.php
includes/resourceloader/ResourceLoaderLessVarFileModule.php
includes/resourceloader/ResourceLoaderModule.php
includes/resourceloader/ResourceLoaderOOUIFileModule.php
includes/resourceloader/ResourceLoaderOOUIIconPackModule.php
includes/resourceloader/ResourceLoaderOOUIModule.php
includes/resourceloader/ResourceLoaderSiteModule.php
includes/resourceloader/ResourceLoaderSiteStylesModule.php
includes/resourceloader/ResourceLoaderSkinModule.php
includes/resourceloader/ResourceLoaderStartUpModule.php
includes/resourceloader/ResourceLoaderUserDefaultsModule.php
includes/resourceloader/ResourceLoaderUserModule.php
includes/resourceloader/ResourceLoaderUserOptionsModule.php
includes/resourceloader/ResourceLoaderUserStylesModule.php
includes/resourceloader/ResourceLoaderUserTokensModule.php
includes/resourceloader/ResourceLoaderWikiModule.php
includes/revisiondelete/RevDelArchivedFileItem.php
includes/revisiondelete/RevDelFileItem.php
includes/revisiondelete/RevDelList.php
includes/revisiondelete/RevDelLogItem.php
includes/revisiondelete/RevDelLogList.php
includes/revisiondelete/RevDelRevisionItem.php
includes/revisiondelete/RevDelRevisionList.php
includes/revisiondelete/RevisionDeleteUser.php
includes/revisiondelete/RevisionDeleter.php
includes/revisionlist/RevisionItem.php
includes/site/MediaWikiSite.php
includes/site/Site.php
includes/site/SiteImporter.php
includes/skins/BaseTemplate.php
includes/skins/SkinTemplate.php
includes/specialpage/ChangesListSpecialPage.php
includes/specialpage/LoginSignupSpecialPage.php
includes/specialpage/SpecialPage.php
includes/specials/SpecialApiSandbox.php
includes/specials/SpecialAutoblockList.php
includes/specials/SpecialBlock.php
includes/specials/SpecialBlockList.php
includes/specials/SpecialBrokenRedirects.php
includes/specials/SpecialChangeContentModel.php
includes/specials/SpecialChangeEmail.php
includes/specials/SpecialConfirmEmail.php
includes/specials/SpecialContributions.php
includes/specials/SpecialCreateAccount.php
includes/specials/SpecialDoubleRedirects.php
includes/specials/SpecialEditTags.php
includes/specials/SpecialEmailUser.php
includes/specials/SpecialExpandTemplates.php
includes/specials/SpecialExport.php
includes/specials/SpecialImport.php
includes/specials/SpecialListFiles.php
includes/specials/SpecialLog.php
includes/specials/SpecialMergeHistory.php
includes/specials/SpecialMovepage.php
includes/specials/SpecialMyLanguage.php
includes/specials/SpecialPagesWithProp.php
includes/specials/SpecialPasswordReset.php
includes/specials/SpecialPreferences.php
includes/specials/SpecialRecentChanges.php
includes/specials/SpecialRecentChangesLinked.php
includes/specials/SpecialRevisionDelete.php
includes/specials/SpecialTags.php
includes/specials/SpecialUnblock.php
includes/specials/SpecialUndelete.php
includes/specials/SpecialUpload.php
includes/specials/SpecialUserrights.php
includes/specials/SpecialWatchlist.php
includes/specials/SpecialWhatLinksHere.php
includes/specials/forms/PreferencesFormOOUI.php
includes/specials/forms/UploadForm.php
includes/specials/pagers/ActiveUsersPager.php
includes/specials/pagers/BlockListPager.php
includes/specials/pagers/ContribsPager.php
includes/specials/pagers/DeletedContribsPager.php
includes/specials/pagers/ProtectedPagesPager.php
includes/specials/pagers/UsersPager.php
includes/title/MediaWikiTitleCodec.php
includes/upload/UploadBase.php
includes/upload/UploadFromUrl.php
includes/user/LocalIdLookup.php
includes/user/PasswordReset.php
includes/user/User.php
includes/user/UserNamePrefixSearch.php
includes/utils/AvroValidator.php
includes/watcheditem/WatchedItemQueryService.php
jsduck.json
languages/LanguageConverter.php
languages/data/Names.php
languages/data/ZhConversion.php
languages/i18n/ace.json
languages/i18n/ady-cyrl.json
languages/i18n/aeb-arab.json
languages/i18n/af.json
languages/i18n/ais.json
languages/i18n/aln.json
languages/i18n/am.json
languages/i18n/ami.json
languages/i18n/an.json
languages/i18n/ang.json
languages/i18n/anp.json
languages/i18n/ar.json
languages/i18n/arc.json
languages/i18n/arn.json
languages/i18n/arq.json
languages/i18n/ary.json
languages/i18n/arz.json
languages/i18n/as.json
languages/i18n/ase.json
languages/i18n/ast.json
languages/i18n/atj.json
languages/i18n/avk.json
languages/i18n/awa.json
languages/i18n/az.json
languages/i18n/azb.json
languages/i18n/ba.json
languages/i18n/ban.json
languages/i18n/bar.json
languages/i18n/bcc.json
languages/i18n/bcl.json
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/bg.json
languages/i18n/bgn.json
languages/i18n/bho.json
languages/i18n/bjn.json
languages/i18n/bn.json
languages/i18n/bo.json
languages/i18n/bpy.json
languages/i18n/bqi.json
languages/i18n/br.json
languages/i18n/bs.json
languages/i18n/btm.json
languages/i18n/bto.json
languages/i18n/ca.json
languages/i18n/cdo.json
languages/i18n/ce.json
languages/i18n/ceb.json
languages/i18n/ch.json
languages/i18n/ckb.json
languages/i18n/co.json
languages/i18n/cps.json
languages/i18n/crh-cyrl.json
languages/i18n/crh-latn.json
languages/i18n/cs.json
languages/i18n/csb.json
languages/i18n/cu.json
languages/i18n/cv.json
languages/i18n/cy.json
languages/i18n/da.json
languages/i18n/de-formal.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/dsb.json
languages/i18n/dtp.json
languages/i18n/dty.json
languages/i18n/ee.json
languages/i18n/egl.json
languages/i18n/el.json
languages/i18n/en-gb.json
languages/i18n/en.json
languages/i18n/eo.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/exif/cs.json
languages/i18n/exif/nb.json
languages/i18n/exif/nds-nl.json
languages/i18n/exif/sr-ec.json
languages/i18n/exif/tt-cyrl.json
languages/i18n/exif/zh-hans.json
languages/i18n/ext.json
languages/i18n/fa.json
languages/i18n/fi.json
languages/i18n/fit.json
languages/i18n/fo.json
languages/i18n/fr.json
languages/i18n/frc.json
languages/i18n/frp.json
languages/i18n/frr.json
languages/i18n/fur.json
languages/i18n/fy.json
languages/i18n/ga.json
languages/i18n/gag.json
languages/i18n/gan-hans.json
languages/i18n/gan-hant.json
languages/i18n/gcr.json
languages/i18n/gd.json
languages/i18n/gl.json
languages/i18n/glk.json
languages/i18n/gom-deva.json
languages/i18n/gom-latn.json
languages/i18n/gor.json
languages/i18n/got.json
languages/i18n/grc.json
languages/i18n/gsw.json
languages/i18n/gu.json
languages/i18n/gv.json
languages/i18n/ha.json
languages/i18n/hak.json
languages/i18n/haw.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/hif-latn.json
languages/i18n/hil.json
languages/i18n/hr.json
languages/i18n/hrx.json
languages/i18n/hsb.json
languages/i18n/ht.json
languages/i18n/hu.json
languages/i18n/hy.json
languages/i18n/hyw.json
languages/i18n/ia.json
languages/i18n/id.json
languages/i18n/ie.json
languages/i18n/ig.json
languages/i18n/ilo.json
languages/i18n/inh.json
languages/i18n/io.json
languages/i18n/is.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/jam.json
languages/i18n/jut.json
languages/i18n/jv.json
languages/i18n/ka.json
languages/i18n/kaa.json
languages/i18n/kab.json
languages/i18n/kbd-cyrl.json
languages/i18n/khw.json
languages/i18n/kiu.json
languages/i18n/kjp.json
languages/i18n/kk-arab.json
languages/i18n/kk-cyrl.json
languages/i18n/kk-latn.json
languages/i18n/km.json
languages/i18n/kn.json
languages/i18n/ko.json
languages/i18n/krc.json
languages/i18n/krl.json
languages/i18n/ksh.json
languages/i18n/ku-latn.json
languages/i18n/kum.json
languages/i18n/kw.json
languages/i18n/ky.json
languages/i18n/la.json
languages/i18n/lad.json
languages/i18n/lb.json
languages/i18n/lez.json
languages/i18n/lfn.json
languages/i18n/lg.json
languages/i18n/li.json
languages/i18n/lij.json
languages/i18n/lki.json
languages/i18n/lmo.json
languages/i18n/lo.json
languages/i18n/loz.json
languages/i18n/lrc.json
languages/i18n/lt.json
languages/i18n/ltg.json
languages/i18n/lus.json
languages/i18n/luz.json
languages/i18n/lv.json
languages/i18n/lzh.json
languages/i18n/lzz.json
languages/i18n/mai.json
languages/i18n/map-bms.json
languages/i18n/mdf.json
languages/i18n/mg.json
languages/i18n/mhr.json
languages/i18n/min.json
languages/i18n/mk.json
languages/i18n/ml.json
languages/i18n/mn.json
languages/i18n/mni.json
languages/i18n/mnw.json
languages/i18n/mr.json
languages/i18n/ms.json
languages/i18n/mt.json
languages/i18n/mwl.json
languages/i18n/my.json
languages/i18n/myv.json
languages/i18n/mzn.json
languages/i18n/nah.json
languages/i18n/nan.json
languages/i18n/nap.json
languages/i18n/nb.json
languages/i18n/nds-nl.json
languages/i18n/nds.json
languages/i18n/ne.json
languages/i18n/nl-informal.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/nqo.json
languages/i18n/nso.json
languages/i18n/nys.json
languages/i18n/oc.json
languages/i18n/olo.json
languages/i18n/or.json
languages/i18n/os.json
languages/i18n/pa.json
languages/i18n/pag.json
languages/i18n/pam.json
languages/i18n/pcd.json
languages/i18n/pdc.json
languages/i18n/pfl.json
languages/i18n/pl.json
languages/i18n/pms.json
languages/i18n/pnb.json
languages/i18n/pnt.json
languages/i18n/prg.json
languages/i18n/ps.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/qu.json
languages/i18n/qug.json
languages/i18n/rif.json
languages/i18n/rm.json
languages/i18n/ro.json
languages/i18n/roa-tara.json
languages/i18n/ru.json
languages/i18n/rue.json
languages/i18n/sa.json
languages/i18n/sah.json
languages/i18n/sat.json
languages/i18n/sc.json
languages/i18n/scn.json
languages/i18n/sco.json
languages/i18n/sd.json
languages/i18n/sdc.json
languages/i18n/sdh.json
languages/i18n/se.json
languages/i18n/sei.json
languages/i18n/ses.json
languages/i18n/sgs.json
languages/i18n/sh.json
languages/i18n/shi.json
languages/i18n/shn.json
languages/i18n/shy-latn.json
languages/i18n/si.json
languages/i18n/sk.json
languages/i18n/skr-arab.json
languages/i18n/sl.json
languages/i18n/sli.json
languages/i18n/so.json
languages/i18n/sq.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/srn.json
languages/i18n/stq.json
languages/i18n/sty.json
languages/i18n/su.json
languages/i18n/sv.json
languages/i18n/sw.json
languages/i18n/szl.json
languages/i18n/ta.json
languages/i18n/tay.json
languages/i18n/tcy.json
languages/i18n/te.json
languages/i18n/tet.json
languages/i18n/tg-cyrl.json
languages/i18n/tg-latn.json
languages/i18n/th.json
languages/i18n/tk.json
languages/i18n/tl.json
languages/i18n/tly.json
languages/i18n/to.json
languages/i18n/tpi.json
languages/i18n/tr.json
languages/i18n/tru.json
languages/i18n/trv.json
languages/i18n/ts.json
languages/i18n/tt-cyrl.json
languages/i18n/tt-latn.json
languages/i18n/tyv.json
languages/i18n/tzm.json
languages/i18n/udm.json
languages/i18n/ug-arab.json
languages/i18n/uk.json
languages/i18n/ur.json
languages/i18n/uz.json
languages/i18n/vec.json
languages/i18n/vep.json
languages/i18n/vi.json
languages/i18n/vmf.json
languages/i18n/vo.json
languages/i18n/vot.json
languages/i18n/vro.json
languages/i18n/wa.json
languages/i18n/war.json
languages/i18n/wo.json
languages/i18n/wuu.json
languages/i18n/xal.json
languages/i18n/xmf.json
languages/i18n/xsy.json
languages/i18n/yi.json
languages/i18n/yo.json
languages/i18n/yue.json
languages/i18n/zea.json
languages/i18n/zgh.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/i18n/zh-hk.json
maintenance/Doxyfile
maintenance/addChangeTag.php
maintenance/archives/upgradeLogging.php
maintenance/changePassword.php
maintenance/cleanupCaps.php
maintenance/cleanupRevActorPage.php [new file with mode: 0644]
maintenance/cleanupSpam.php
maintenance/copyFileBackend.php
maintenance/createAndPromote.php
maintenance/edit.php
maintenance/findDeprecated.php
maintenance/getText.php
maintenance/importDump.php
maintenance/importImages.php
maintenance/install.php
maintenance/language/zhtable/simp2trad.manual
maintenance/language/zhtable/simp2trad_noconvert.manual
maintenance/language/zhtable/toCN.manual
maintenance/language/zhtable/toHK.manual
maintenance/language/zhtable/toSimp.manual
maintenance/language/zhtable/toTW.manual
maintenance/language/zhtable/toTrad.manual
maintenance/language/zhtable/trad2simp.manual
maintenance/language/zhtable/tradphrases.manual
maintenance/language/zhtable/tradphrases_exclude.manual
maintenance/mediawiki.Title/generatePhpCharToUpperMappings.php
maintenance/moveBatch.php
maintenance/mwdocgen.php
maintenance/populateRevisionLength.php
maintenance/preprocessDump.php
maintenance/purgeList.php
maintenance/refreshFileHeaders.php
maintenance/refreshLinks.php
maintenance/userDupes.inc
mw-config/config.css
resources/Resources.php
resources/lib/foreign-resources.yaml
resources/lib/ooui/History.md
resources/lib/ooui/i18n/fr.json
resources/lib/ooui/i18n/pl.json
resources/lib/ooui/oojs-ui-apex.js
resources/lib/ooui/oojs-ui-core-apex.css
resources/lib/ooui/oojs-ui-core-wikimediaui.css
resources/lib/ooui/oojs-ui-core.js
resources/lib/ooui/oojs-ui-toolbars-apex.css
resources/lib/ooui/oojs-ui-toolbars-wikimediaui.css
resources/lib/ooui/oojs-ui-toolbars.js
resources/lib/ooui/oojs-ui-widgets-apex.css
resources/lib/ooui/oojs-ui-widgets-wikimediaui.css
resources/lib/ooui/oojs-ui-widgets.js
resources/lib/ooui/oojs-ui-wikimediaui.js
resources/lib/ooui/oojs-ui-windows-apex.css
resources/lib/ooui/oojs-ui-windows-wikimediaui.css
resources/lib/ooui/oojs-ui-windows.js
resources/lib/ooui/themes/apex/icons-editing-advanced.json
resources/lib/ooui/themes/apex/icons-user.json
resources/lib/ooui/themes/wikimediaui/icons-editing-advanced.json
resources/lib/ooui/themes/wikimediaui/icons-user.json
resources/lib/ooui/themes/wikimediaui/images/icons/beaker-invert.png [deleted file]
resources/lib/ooui/themes/wikimediaui/images/icons/beaker-invert.svg [deleted file]
resources/lib/ooui/themes/wikimediaui/images/icons/beaker-progressive.png [deleted file]
resources/lib/ooui/themes/wikimediaui/images/icons/beaker-progressive.svg [deleted file]
resources/lib/ooui/themes/wikimediaui/images/icons/beaker.png [deleted file]
resources/lib/ooui/themes/wikimediaui/images/icons/beaker.svg [deleted file]
resources/lib/ooui/themes/wikimediaui/images/icons/labFlask-invert.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/labFlask-invert.svg [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/labFlask-progressive.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/labFlask-progressive.svg [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/labFlask.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/labFlask.svg [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-invert.svg
resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline-progressive.svg
resources/lib/ooui/themes/wikimediaui/images/icons/userAvatarOutline.svg
resources/lib/ooui/themes/wikimediaui/images/icons/userGroup-ltr-invert.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userGroup-ltr-invert.svg [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userGroup-ltr-progressive.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userGroup-ltr-progressive.svg [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userGroup-ltr.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userGroup-ltr.svg [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userGroup-rtl-invert.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userGroup-rtl-invert.svg [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userGroup-rtl-progressive.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userGroup-rtl-progressive.svg [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userGroup-rtl.png [new file with mode: 0644]
resources/lib/ooui/themes/wikimediaui/images/icons/userGroup-rtl.svg [new file with mode: 0644]
resources/src/jquery.color/jquery.color.js [new file with mode: 0644]
resources/src/jquery.color/jquery.colorUtil.js [new file with mode: 0644]
resources/src/jquery/jquery.checkboxShiftClick.js [deleted file]
resources/src/jquery/jquery.color.js [deleted file]
resources/src/jquery/jquery.colorUtil.js [deleted file]
resources/src/jquery/jquery.makeCollapsible.js
resources/src/mediawiki.ForeignApi.core.js
resources/src/mediawiki.Title/Title.js
resources/src/mediawiki.Title/phpCharToUpper.json
resources/src/mediawiki.htmlform.checker.js
resources/src/mediawiki.legacy/shared.css
resources/src/mediawiki.page.gallery.slideshow.js
resources/src/mediawiki.page.image.pagination.js
resources/src/mediawiki.page.ready.js [deleted file]
resources/src/mediawiki.page.ready/.eslintrc.json [new file with mode: 0644]
resources/src/mediawiki.page.ready/checkboxShift.js [new file with mode: 0644]
resources/src/mediawiki.page.ready/ready.js [new file with mode: 0644]
resources/src/mediawiki.special/listFiles.less [new file with mode: 0644]
resources/src/mediawiki.ui/components/forms.less
resources/src/mediawiki.util/util.js
tests/parser/parserTests.txt
tests/phpunit/MediaWikiCoversValidator.php
tests/phpunit/MediaWikiIntegrationTestCase.php
tests/phpunit/MediaWikiTestCaseTrait.php
tests/phpunit/data/media/jpeg-xmp-nullchar.jpg [new file with mode: 0644]
tests/phpunit/includes/EditPageTest.php
tests/phpunit/includes/FauxRequestTest.php
tests/phpunit/includes/HtmlTest.php
tests/phpunit/includes/LinkerTest.php
tests/phpunit/includes/MediaWikiServicesTest.php
tests/phpunit/includes/OutputPageTest.php
tests/phpunit/includes/Rest/BasicAccess/MWBasicRequestAuthorizerTest.php
tests/phpunit/includes/Rest/EntryPointTest.php
tests/phpunit/includes/Revision/McrRevisionStoreDbTest.php
tests/phpunit/includes/Revision/RenderedRevisionTest.php
tests/phpunit/includes/Revision/RevisionStoreDbTestBase.php
tests/phpunit/includes/Revision/RevisionStoreFactoryTest.php
tests/phpunit/includes/RevisionTest.php
tests/phpunit/includes/SiteStatsTest.php
tests/phpunit/includes/Storage/DerivedPageDataUpdaterTest.php
tests/phpunit/includes/TitleMethodsTest.php
tests/phpunit/includes/TitleTest.php
tests/phpunit/includes/XmlTest.php
tests/phpunit/includes/api/ApiBlockTest.php
tests/phpunit/includes/api/ApiPageSetTest.php
tests/phpunit/includes/api/ApiQueryBlockInfoTraitTest.php [new file with mode: 0644]
tests/phpunit/includes/api/ApiQuerySiteinfoTest.php
tests/phpunit/includes/api/ApiQueryWatchlistIntegrationTest.php
tests/phpunit/includes/api/ApiQueryWatchlistRawIntegrationTest.php
tests/phpunit/includes/api/ApiWatchTest.php
tests/phpunit/includes/auth/AuthManagerTest.php
tests/phpunit/includes/block/BlockManagerTest.php
tests/phpunit/includes/block/BlockRestrictionStoreTest.php
tests/phpunit/includes/block/DatabaseBlockTest.php
tests/phpunit/includes/cache/MessageCacheTest.php
tests/phpunit/includes/changes/CategoryMembershipChangeTest.php
tests/phpunit/includes/changes/EnhancedChangesListTest.php
tests/phpunit/includes/changes/RCCacheEntryFactoryTest.php
tests/phpunit/includes/content/ContentHandlerTest.php
tests/phpunit/includes/content/UnknownContentHandlerTest.php
tests/phpunit/includes/content/UnknownContentTest.php
tests/phpunit/includes/content/WikitextContentHandlerTest.php
tests/phpunit/includes/context/RequestContextTest.php
tests/phpunit/includes/db/LBFactoryTest.php
tests/phpunit/includes/deferred/LinksUpdateTest.php
tests/phpunit/includes/deferred/SiteStatsUpdateTest.php
tests/phpunit/includes/filebackend/FileBackendTest.php
tests/phpunit/includes/filebackend/SwiftFileBackendTest.php
tests/phpunit/includes/filerepo/file/LocalFileTest.php
tests/phpunit/includes/http/GuzzleHttpRequestTest.php
tests/phpunit/includes/http/HttpTest.php
tests/phpunit/includes/jobqueue/JobQueueTest.php
tests/phpunit/includes/jobqueue/jobs/ClearUserWatchlistJobTest.php
tests/phpunit/includes/libs/ProcessCacheLRUTest.php
tests/phpunit/includes/libs/StringUtilsTest.php
tests/phpunit/includes/libs/TimingTest.php
tests/phpunit/includes/libs/objectcache/HashBagOStuffTest.php
tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseSQLTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php
tests/phpunit/includes/linker/LinkRendererTest.php
tests/phpunit/includes/media/JpegMetadataExtractorTest.php
tests/phpunit/includes/media/JpegPixelFormatTest.php
tests/phpunit/includes/media/PNGMetadataExtractorTest.php
tests/phpunit/includes/page/WikiPageDbTestBase.php
tests/phpunit/includes/preferences/DefaultPreferencesFactoryTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderTest.php
tests/phpunit/includes/search/SearchNearMatchResultSetTest.php
tests/phpunit/includes/shell/ShellTest.php
tests/phpunit/includes/site/CachingSiteStoreTest.php
tests/phpunit/includes/site/DBSiteStoreTest.php
tests/phpunit/includes/site/SiteTest.php
tests/phpunit/includes/specials/SpecialBlankPageTest.php
tests/phpunit/includes/specials/SpecialPageDataTest.php
tests/phpunit/includes/user/LocalIdLookupTest.php
tests/phpunit/includes/user/PasswordResetTest.php
tests/phpunit/includes/user/UserGroupMembershipTest.php
tests/phpunit/includes/user/UserTest.php
tests/phpunit/includes/watcheditem/WatchedItemQueryServiceUnitTest.php
tests/phpunit/includes/watcheditem/WatchedItemStoreUnitTest.php
tests/phpunit/integration/includes/db/DatabaseSqliteTest.php
tests/phpunit/languages/LanguageTest.php
tests/phpunit/maintenance/DumpTestCase.php
tests/phpunit/maintenance/MWDoxygenFilterTest.php
tests/phpunit/maintenance/fetchTextTest.php
tests/phpunit/unit/includes/Rest/Handler/HelloHandlerTest.php
tests/phpunit/unit/includes/Rest/ResponseFactoryTest.php
tests/phpunit/unit/includes/Rest/RouterTest.php
tests/phpunit/unit/includes/diff/DiffOpTest.php
tests/phpunit/unit/includes/password/PasswordFactoryTest.php
tests/phpunit/unit/includes/search/SearchSuggestionSetTest.php
tests/qunit/QUnitTestResources.php
tests/qunit/suites/resources/mediawiki.api/mediawiki.ForeignApi.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js
tests/selenium/wdio-mediawiki/Api.js
tests/selenium/wdio-mediawiki/CHANGELOG.md
tests/selenium/wdio-mediawiki/README.md
tests/selenium/wdio-mediawiki/package.json
tests/selenium/wdio.conf.js
thumb.php

index fc775fe..0fdefe0 100644 (file)
@@ -101,11 +101,6 @@ $cfg['suppress_issue_types'] = array_merge( $cfg['suppress_issue_types'], [
        //after dropping HHVM
        // approximate error count: 110
        "PhanParamTooMany", // False positives with variargs. Unsuppress after dropping HHVM
-
-       // approximate error count: 45
-       "PhanTypeMismatchArgument",
-       // approximate error count: 693
-       "PhanUndeclaredProperty",
 ] );
 
 // This helps a lot in discovering bad code, but unfortunately it will always fail for
@@ -113,6 +108,10 @@ $cfg['suppress_issue_types'] = array_merge( $cfg['suppress_issue_types'], [
 // @todo Enable when the issue above is resolved and we update our config!
 $cfg['redundant_condition_detection'] = false;
 
+// Do not use aliases in core.
+// Use the correct name, because we don't need backward compatibility
+$cfg['enable_class_alias_support'] = false;
+
 $cfg['ignore_undeclared_variables_in_global_scope'] = true;
 // @todo It'd be great if we could just make phan read these from DefaultSettings, to avoid
 // duplicating the types.
index d5607f1..8dbc5f2 100644 (file)
@@ -26,8 +26,6 @@ matrix:
   include:
     - php: 7.3
     - php: 7.2
-    - php: 7.1
-    - php: 7
 
 services:
   - mysql
index 498acf7..3d9f26a 100644 (file)
@@ -1 +1,4 @@
+Code of Conduct
+===============
+
 The development of this software is covered by a [Code of Conduct](https://www.mediawiki.org/wiki/Special:MyLanguage/Code_of_Conduct).
diff --git a/INSTALL b/INSTALL
index 07dd9c3..0359166 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -7,7 +7,7 @@ Starting with MediaWiki 1.2.0, it's possible to install and configure the wiki
 
 Required software as of MediaWiki 1.34.0:
 
-* Web server with PHP 7.0.13 or higher, plus the following extesnsions:
+* Web server with PHP 7.2.0 or higher, plus the following extesnsions:
 ** ctype
 ** dom
 ** fileinfo
index 09195e1..508ba08 100644 (file)
@@ -141,7 +141,7 @@ $wgPasswordPolicy['policies']['default']['PasswordNotInLargeBlacklist'] = false;
 * Updated mediawiki/codesniffer from 25.0.0 to 26.0.0 (dev-only).
 * Updated cssjanus/cssjanus from 1.2.1 to 1.3.0.
 * Updated wikimedia/at-ease from 1.2.0 to 2.0.0.
-* Updated wikimedia/remex-html from 2.0.1 to 2.0.3.
+* Updated wikimedia/remex-html from 2.0.1 to 2.1.0.
 * Updated monolog/monolog from 1.22.1 to 1.24.0 (dev-only).
 * Updated wikimedia/object-factory from 1.0.0 to 2.1.0.
 * Updated wikimedia/timestamp from 2.2.0 to 3.0.0.
@@ -164,6 +164,21 @@ $wgPasswordPolicy['policies']['default']['PasswordNotInLargeBlacklist'] = false;
   deprecated in 1.25, has been removed.
 * (T60993) action=query list=filearchive, list=alldeletedrevisions and
   prop=deletedrevisions no longer require the 'deletedhistory' user right.
+* In the response to queries that use 'prop=imageinfo', entries for
+  non-existing files (indicated by the 'filemissing' field) now omit the
+  following fields, since they are meaningless in this context:
+  'timestamp', 'userhidden', 'user', 'userid', 'anon', 'size', 'width',
+  'height', 'pagecount', 'duration', 'commenthidden', 'parsedcomment',
+  'comment', 'thumburl', 'thumbwidth', 'thumbheight', 'thumbmime',
+  'thumberror', 'url', 'sha1', 'metadata', 'extmetadata', 'commonmetadata',
+  'mime', 'mediadtype', 'bitdepth'.
+  Clients that process these fields should first check if 'filemissing' is
+  set. Fields that are supported even if the file is missing include:
+  'canonicaltitle', ''archivename' (deleted files only), 'descriptionurl',
+  'descriptionshorturl'.
+* The 'blockexpiry' result property in list=users and list=allusers will now be
+  returned in the same format used by the rest of the API: ISO 8601 for
+  expiring blocks, and "infinite" for non-expiring blocks.
 
 === Action API internal changes in 1.34 ===
 * The exception thrown in ApiModuleManager::getModule has been changed
@@ -171,7 +186,7 @@ $wgPasswordPolicy['policies']['default']['PasswordNotInLargeBlacklist'] = false;
   ApiModuleManager::getModule now also throws InvalidArgumentExceptions when
   ObjectFactory is presented with an invalid spec or incorrectly constructed
   objects.
-* 
+* Added ApiQueryBlockInfoTrait.
 
 === Languages updated in 1.34 ===
 MediaWiki supports over 350 languages. Many localisations are updated regularly.
@@ -302,6 +317,11 @@ because of Phabricator reports.
   Use the mediawiki.String module instead.
 * mw.language.specialCharacters, deprecated in 1.33, has been removed.
   Use require( 'mediawiki.language.specialCharacters' ) instead.
+* The jquery.colorUtil module was removed. Use jquery.color instead.
+* The jquery.checkboxShiftClick module was removed. The functionality
+  is provided by mediawiki.page.ready instead (T232688).
+* The 'jquery.accessKeyLabel' module has been removed. This jQuery
+  plugin now ships as part of the 'mediawiki.util' module bundle.
 * EditPage::submit(), deprecated in 1.29, has been removed. Use $this->edit()
   directly.
 * HTMLForm::getErrors(), deprecated in 1.28, has been removed. Use
@@ -428,6 +448,9 @@ because of Phabricator reports.
   * Revision::selectTextFields()
   * Revision::selectPageFields()
   * Revision::selectUserFields()
+* User::setNewpassword(), deprecated in 1.27 has been removed.
+* The ObjectCache::getMainWANInstance and ObjectCache::getMainStashInstance
+  functions, deprecated since 1.28, have been removed.
 
 === Deprecations in 1.34 ===
 * The MWNamespace class is deprecated. Use NamespaceInfo.
@@ -483,8 +506,6 @@ because of Phabricator reports.
 * ResourceLoaderContext::getConfig and ResourceLoaderContext::getLogger have
   been deprecated. Inside ResourceLoaderModule subclasses, use the local methods
   instead. Elsewhere, use the methods from the ResourceLoader class.
-* The 'jquery.accessKeyLabel' module has been deprecated. This jQuery
-  plugin is now ships as part of the 'mediawiki.util' module bundle.
 * The Profiler::setTemplated and Profiler::getTemplated methods have been
   deprecated. Use Profiler::setAllowOutput and Profiler::getAllowOutput
   instead.
@@ -563,13 +584,21 @@ because of Phabricator reports.
 * The GetBlockedStatus hook is deprecated. Use GetUserBlock instead, to add or
   remove a block.
 * $wgContentHandlerUseDB is deprecated and should always be true.
+* StreamFile::send404Message() and StreamFile::parseRange() are now deprecated.
+  Use HTTPFileStreamer::send404Message() and HTTPFileStreamer::parseRange()
+  respectively instead.
+* Global variable $wgSysopEmailBans is deprecated; to allow sysops to ban
+  users from sending emails, use
+  $wgGroupPermissions['sysop']['blockemail'] = true;
+* ApiQueryBase::showHiddenUsersAddBlockInfo() is deprecated. Use
+  ApiQueryBlockInfoTrait instead.
 
 === Other changes in 1.34 ===
 * …
 
 == Compatibility ==
-MediaWiki 1.34 requires PHP 7.0.13 or later. Although HHVM 3.18.5 or later is
-supported, it is generally advised to use PHP 7.0.13 or later for long term
+MediaWiki 1.34 requires PHP 7.2.0 or later. Although HHVM 3.18.5 or later is
+supported, it is generally advised to use PHP 7.2.0 or later for long term
 support. It also requires the following PHP extensions:
 
 * ctype
index f95e001..7ff29ce 100644 (file)
@@ -89,6 +89,7 @@ $wgAutoloadLocalClasses = [
        'ApiQueryBacklinks' => __DIR__ . '/includes/api/ApiQueryBacklinks.php',
        'ApiQueryBacklinksprop' => __DIR__ . '/includes/api/ApiQueryBacklinksprop.php',
        'ApiQueryBase' => __DIR__ . '/includes/api/ApiQueryBase.php',
+       'ApiQueryBlockInfoTrait' => __DIR__ . '/includes/api/ApiQueryBlockInfoTrait.php',
        'ApiQueryBlocks' => __DIR__ . '/includes/api/ApiQueryBlocks.php',
        'ApiQueryCategories' => __DIR__ . '/includes/api/ApiQueryCategories.php',
        'ApiQueryCategoryInfo' => __DIR__ . '/includes/api/ApiQueryCategoryInfo.php',
@@ -272,6 +273,7 @@ $wgAutoloadLocalClasses = [
        'CleanupInvalidDbKeys' => __DIR__ . '/maintenance/cleanupInvalidDbKeys.php',
        'CleanupPreferences' => __DIR__ . '/maintenance/cleanupPreferences.php',
        'CleanupRemovedModules' => __DIR__ . '/maintenance/cleanupRemovedModules.php',
+       'CleanupRevActorPage' => __DIR__ . '/maintenance/cleanupRevActorPage.php',
        'CleanupSpam' => __DIR__ . '/maintenance/cleanupSpam.php',
        'CleanupUploadStash' => __DIR__ . '/maintenance/cleanupUploadStash.php',
        'CleanupUsersWithNoId' => __DIR__ . '/maintenance/cleanupUsersWithNoId.php',
index c1f9037..1f2776c 100644 (file)
@@ -28,7 +28,7 @@
                "ext-xml": "*",
                "guzzlehttp/guzzle": "6.3.3",
                "liuggio/statsd-php-client": "1.0.18",
-               "oojs/oojs-ui": "0.34.0",
+               "oojs/oojs-ui": "0.34.1",
                "pear/mail": "1.4.1",
                "pear/mail_mime": "1.10.2",
                "pear/net_smtp": "1.8.1",
@@ -49,7 +49,7 @@
                "wikimedia/php-session-serializer": "1.0.7",
                "wikimedia/purtle": "1.0.7",
                "wikimedia/relpath": "2.1.1",
-               "wikimedia/remex-html": "2.0.3",
+               "wikimedia/remex-html": "2.1.0",
                "wikimedia/running-stat": "1.2.1",
                "wikimedia/scoped-callback": "3.0.0",
                "wikimedia/utfnormal": "2.0.0",
index 43bfd8d..55ba06e 100644 (file)
@@ -2921,7 +2921,7 @@ result augmentors.
 Note that lists should be in the format name => object and the names in both
   lists should be distinct.
 
-'SecondaryDataUpdates': DEPRECATED! Use RevisionDataUpdates or override
+'SecondaryDataUpdates': DEPRECATED since 1.32! Use RevisionDataUpdates or override
 ContentHandler::getSecondaryDataUpdates instead.
 Allows modification of the list of DataUpdates to perform when page content is modified.
 $title: Title of the page that is being edited.
@@ -3969,7 +3969,7 @@ dumps. One, and only one hook should set this, and return false.
 &$opts: Options to use for the query
 &$join: Join conditions
 
-'WikiPageDeletionUpdates': DEPRECATED! Use PageDeletionDataUpdates or
+'WikiPageDeletionUpdates': DEPRECATED since 1.32! Use PageDeletionDataUpdates or
 override ContentHandler::getDeletionDataUpdates instead.
 Manipulates the list of DeferrableUpdates to be applied when a page is deleted.
 $page: the WikiPage
index 0664652..faff021 100644 (file)
@@ -55,7 +55,7 @@ class AjaxResponse {
 
        /**
         * HTTP response code
-        * @var string $mResponseCode
+        * @var int|string $mResponseCode
         */
        private $mResponseCode;
 
@@ -114,7 +114,7 @@ class AjaxResponse {
 
        /**
         * Set the HTTP response code
-        * @param string $code
+        * @param int|string $code
         */
        function setResponseCode( $code ) {
                $this->mResponseCode = $code;
index 2156787..f8f3c24 100644 (file)
@@ -187,10 +187,10 @@ class Autopromote {
                                }
                                return $user->getEditCount() >= $reqEditCount;
                        case APCOND_AGE:
-                               $age = time() - wfTimestampOrNull( TS_UNIX, $user->getRegistration() );
+                               $age = time() - (int)wfTimestampOrNull( TS_UNIX, $user->getRegistration() );
                                return $age >= $cond[1];
                        case APCOND_AGE_FROM_EDIT:
-                               $age = time() - wfTimestampOrNull( TS_UNIX, $user->getFirstEditTimestamp() );
+                               $age = time() - (int)wfTimestampOrNull( TS_UNIX, $user->getFirstEditTimestamp() );
                                return $age >= $cond[1];
                        case APCOND_INGROUPS:
                                $groups = array_slice( $cond, 1 );
index 47fd073..fd1affc 100644 (file)
@@ -2733,16 +2733,17 @@ $wgExtensionInfoMTime = false;
  * although they are sometimes still referred to as Squid settings for
  * historical reasons.
  *
- * Achieving a high hit ratio with an HTTP proxy requires special
- * configuration. See https://www.mediawiki.org/wiki/Manual:Squid_caching for
- * more details.
+ * Achieving a high hit ratio with an HTTP proxy requires special configuration.
+ * See https://www.mediawiki.org/wiki/Manual:Performance_tuning#Page_view_caching
+ * for more details.
  *
  * @{
  */
 
 /**
  * Enable/disable CDN.
- * See https://www.mediawiki.org/wiki/Manual:Squid_caching
+ *
+ * See https://www.mediawiki.org/wiki/Manual:Performance_tuning#Page_view_caching
  *
  * @since 1.34 Renamed from $wgUseSquid.
  */
@@ -4444,7 +4445,8 @@ $wgCentralIdLookupProvider = 'local';
  * The checks supported by core are:
  *     - MinimalPasswordLength - Minimum length a user can set.
  *     - MinimumPasswordLengthToLogin - Passwords shorter than this will
- *             not be allowed to login, regardless if it is correct.
+ *             not be allowed to login, or offered a chance to reset their password
+ *             as part of the login workflow, regardless if it is correct.
  *     - MaximalPasswordLength - maximum length password a user is allowed
  *             to attempt. Prevents DoS attacks with pbkdf2.
  *     - PasswordCannotMatchUsername - Password cannot match the username.
@@ -4990,6 +4992,8 @@ $wgBlockAllowsUTEdit = true;
 
 /**
  * Allow sysops to ban users from accessing Emailuser
+ * @deprecated since 1.34; `$wgGroupPermissions['sysop']['blockemail'] = true;`
+ * should be used instead
  */
 $wgSysopEmailBans = true;
 
index d93caa7..668de39 100644 (file)
@@ -27,7 +27,7 @@ ini_set( 'display_errors', 1 );
 
 global $wgDevelopmentWarnings, $wgShowExceptionDetails, $wgShowHostnames,
        $wgDebugRawPage, $wgCommandLineMode, $wgDebugLogFile,
-       $wgDBerrorLog, $wgDebugLogGroups;
+       $wgDBerrorLog, $wgDebugLogGroups, $wgLocalisationCacheConf;
 
 // Use of wfWarn() should cause tests to fail
 $wgDevelopmentWarnings = true;
@@ -74,3 +74,6 @@ $wgSQLMode = 'TRADITIONAL';
 
 // Disable legacy javascript globals in CI and for devs (T72470)
 $wgLegacyJavaScriptGlobals = false;
+
+// Localisation Cache to StaticArray (T218207)
+$wgLocalisationCacheConf['store'] = 'array';
index c346b75..fe00149 100644 (file)
@@ -25,7 +25,7 @@ use MediaWiki\EditPage\TextboxBuilder;
 use MediaWiki\EditPage\TextConflictHelper;
 use MediaWiki\Logger\LoggerFactory;
 use MediaWiki\MediaWikiServices;
-use MediaWiki\Storage\RevisionRecord;
+use MediaWiki\Revision\RevisionRecord;
 use Wikimedia\ScopedCallback;
 
 /**
@@ -1156,7 +1156,7 @@ class EditPage {
                        $out->showErrorPage(
                                'modeleditnotsupported-title',
                                'modeleditnotsupported-text',
-                               $modelName
+                               [ $modelName ]
                        );
                        return false;
                }
@@ -1188,7 +1188,7 @@ class EditPage {
        /**
         * @param Content|null $def_content The default value to return
         *
-        * @return Content|null Content on success, $def_content for invalid sections
+        * @return Content|false|null Content on success, $def_content for invalid sections
         *
         * @since 1.21
         */
@@ -1683,7 +1683,9 @@ class EditPage {
                        case self::AS_CANNOT_USE_CUSTOM_MODEL:
                        case self::AS_PARSE_ERROR:
                        case self::AS_UNICODE_NOT_SUPPORTED:
-                               $out->wrapWikiTextAsInterface( 'error', $status->getWikiText() );
+                               $out->wrapWikiTextAsInterface( 'error',
+                                       $status->getWikiText( false, false, $this->context->getLanguage() )
+                               );
                                return true;
 
                        case self::AS_SUCCESS_NEW_ARTICLE:
@@ -1757,7 +1759,8 @@ class EditPage {
                                // is if an extension hook aborted from inside ArticleSave.
                                // Render the status object into $this->hookError
                                // FIXME this sucks, we should just use the Status object throughout
-                               $this->hookError = '<div class="error">' . "\n" . $status->getWikiText() .
+                               $this->hookError = '<div class="error">' . "\n" .
+                                       $status->getWikiText( false, false, $this->context->getLanguage() ) .
                                        '</div>';
                                return true;
                }
@@ -2742,7 +2745,7 @@ ERROR;
         * content.
         *
         * @param Content|null|bool|string $content
-        * @return string The editable text form of the content.
+        * @return string|false|null The editable text form of the content.
         *
         * @throws MWException If $content is not an instance of TextContent and
         *   $this->allowNonTextContent is not true.
@@ -4045,11 +4048,11 @@ ERROR;
 
                if ( $this->isConflict ) {
                        $conflict = Html::rawElement(
-                               'h2', [ 'id' => 'mw-previewconflict' ],
+                               'div', [ 'id' => 'mw-previewconflict', 'class' => 'warningbox' ],
                                $this->context->msg( 'previewconflict' )->escaped()
                        );
                } else {
-                       $conflict = '<hr />';
+                       $conflict = '';
                }
 
                $previewhead = Html::rawElement(
@@ -4058,7 +4061,9 @@ ERROR;
                                'h2', [ 'id' => 'mw-previewheader' ],
                                $this->context->msg( 'preview' )->escaped()
                        ) .
-                       $out->parseAsInterface( $note ) . $conflict
+                       Html::rawElement( 'div', [ 'class' => 'warningbox' ],
+                               $out->parseAsInterface( $note )
+                       ) . $conflict
                );
 
                $pageViewLang = $this->mTitle->getPageViewLanguage();
index 8efae4f..bfd1e2a 100644 (file)
@@ -21,7 +21,7 @@
  * @ingroup Feed
  */
 
-use MediaWiki\Storage\RevisionRecord;
+use MediaWiki\Revision\RevisionRecord;
 
 /**
  * Helper functions for feeds
index 38daab5..125b917 100644 (file)
@@ -518,7 +518,7 @@ function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
                }
        }
 
-       $defaultProtoWithoutSlashes = substr( $defaultProto, 0, -2 );
+       $defaultProtoWithoutSlashes = $defaultProto !== null ? substr( $defaultProto, 0, -2 ) : '';
 
        if ( substr( $url, 0, 2 ) == '//' ) {
                $url = $defaultProtoWithoutSlashes . $url;
@@ -1816,10 +1816,11 @@ function mimeTypeMatch( $type, $avail ) {
  *
  * @param array $cprefs Client's acceptable type list
  * @param array $sprefs Server's offered types
- * @return string
+ * @return string|null
  *
  * @todo FIXME: Doesn't handle params like 'text/plain; charset=UTF-8'
  * XXX: generalize to negotiate other stuff
+ * @todo The function appears unused. Is it worth to keep?
  */
 function wfNegotiateType( $cprefs, $sprefs ) {
        $combine = [];
@@ -2030,7 +2031,7 @@ function wfRecursiveRemoveDir( $dir ) {
  */
 function wfPercent( $nr, $acc = 2, $round = true ) {
        $ret = sprintf( "%.${acc}f", $nr );
-       return $round ? round( $ret, $acc ) . '%' : "$ret%";
+       return $round ? round( (float)$ret, $acc ) . '%' : "$ret%";
 }
 
 /**
@@ -2793,7 +2794,7 @@ function wfMemoryLimit( $newLimit ) {
 function wfTransactionalTimeLimit() {
        global $wgTransactionalTimeLimit;
 
-       $timeLimit = ini_get( 'max_execution_time' );
+       $timeLimit = (int)ini_get( 'max_execution_time' );
        // Note that CLI scripts use 0
        if ( $timeLimit > 0 && $wgTransactionalTimeLimit > $timeLimit ) {
                set_time_limit( $wgTransactionalTimeLimit );
index 1a5058d..864019d 100644 (file)
@@ -21,7 +21,7 @@
  */
 use MediaWiki\Linker\LinkTarget;
 use MediaWiki\MediaWikiServices;
-use MediaWiki\Storage\RevisionRecord;
+use MediaWiki\Revision\RevisionRecord;
 
 /**
  * Some internal bits split of from Skin.php. These functions are used
@@ -891,7 +891,7 @@ class Linker {
         * Make user link (or user contributions for unregistered users)
         * @param int $userId User id in database.
         * @param string $userName User name in database.
-        * @param string $altUserName Text to display instead of the user name (optional)
+        * @param string|false $altUserName Text to display instead of the user name (optional)
         * @return string HTML fragment
         * @since 1.16.3. $altUserName was added in 1.19.
         */
@@ -1912,7 +1912,7 @@ class Linker {
         * @since 1.16.3. $context added in 1.20. $editCount added in 1.21
         * @param Revision $rev
         * @param IContextSource|null $context Context to use or null for the main context.
-        * @param int $editCount Number of edits that would be reverted
+        * @param int|false $editCount Number of edits that would be reverted
         * @return string HTML fragment
         */
        public static function buildRollbackLink( $rev, IContextSource $context = null,
index 4045a54..1a950c5 100644 (file)
@@ -273,6 +273,18 @@ class MergeHistory {
                        return $status;
                }
 
+               // Update denormalized revactor_page too
+               $this->dbw->update(
+                       'revision_actor_temp',
+                       [ 'revactor_page' => $this->dest->getArticleID() ],
+                       [
+                               'revactor_page' => $this->source->getArticleID(),
+                               // Slightly hacky, but should work given the values assigned in this class
+                               str_replace( 'rev_timestamp', 'revactor_timestamp', $this->timeWhere )
+                       ],
+                       __METHOD__
+               );
+
                // Make the source page a redirect if no revisions are left
                $haveRevisions = $this->dbw->lockForUpdate(
                        'revision',
index 634e7af..59baae7 100644 (file)
@@ -83,7 +83,7 @@ class MovePage {
         * @param ServiceOptions|null $options
         * @param ILoadBalancer|null $loadBalancer
         * @param NamespaceInfo|null $nsInfo
-        * @param WatchedItemStore|null $watchedItems
+        * @param WatchedItemStoreInterface|null $watchedItems
         * @param PermissionManager|null $permMgr
         */
        public function __construct(
index 7f005fb..acf2d25 100644 (file)
@@ -50,7 +50,7 @@ class OutputPage extends ContextSource {
        /** @var array */
        protected $mLinktags = [];
 
-       /** @var bool */
+       /** @var string|bool */
        protected $mCanonicalUrl = false;
 
        /**
@@ -332,7 +332,7 @@ class OutputPage extends ContextSource {
         * a OutputPage tied to that context.
         * @param IContextSource $context
         */
-       function __construct( IContextSource $context ) {
+       public function __construct( IContextSource $context ) {
                $this->setContext( $context );
        }
 
@@ -385,8 +385,8 @@ class OutputPage extends ContextSource {
         * @param string $name Name of the meta tag
         * @param string $val Value of the meta tag
         */
-       function addMeta( $name, $val ) {
-               array_push( $this->mMetatags, [ $name, $val ] );
+       public function addMeta( $name, $val ) {
+               $this->mMetatags[] = [ $name, $val ];
        }
 
        /**
@@ -406,8 +406,8 @@ class OutputPage extends ContextSource {
         *
         * @param array $linkarr Associative array of attributes.
         */
-       function addLink( array $linkarr ) {
-               array_push( $this->mLinktags, $linkarr );
+       public function addLink( array $linkarr ) {
+               $this->mLinktags[] = $linkarr;
        }
 
        /**
@@ -425,7 +425,7 @@ class OutputPage extends ContextSource {
         * in preference to addLink(), to avoid duplicate link tags.
         * @param string $url
         */
-       function setCanonicalUrl( $url ) {
+       public function setCanonicalUrl( $url ) {
                $this->mCanonicalUrl = $url;
        }
 
@@ -447,7 +447,7 @@ class OutputPage extends ContextSource {
         *
         * @param string $script Raw HTML
         */
-       function addScript( $script ) {
+       public function addScript( $script ) {
                $this->mScripts .= $script;
        }
 
@@ -621,7 +621,7 @@ class OutputPage extends ContextSource {
         *
         * @return array
         */
-       function getHeadItemsArray() {
+       public function getHeadItemsArray() {
                return $this->mHeadItems;
        }
 
@@ -1598,6 +1598,7 @@ class OutputPage extends ContextSource {
         * @param ParserOptions|null $options Either the ParserOption to use or null to only get the
         *   current ParserOption object. This parameter is deprecated since 1.31.
         * @return ParserOptions
+        * @suppress PhanUndeclaredProperty For isBogus
         */
        public function parserOptions( $options = null ) {
                if ( $options !== null ) {
@@ -1954,7 +1955,7 @@ class OutputPage extends ContextSource {
         * @param ParserOutput $parserOutput
         * @param array $poOptions Options to ParserOutput::getText()
         */
-       function addParserOutput( ParserOutput $parserOutput, $poOptions = [] ) {
+       public function addParserOutput( ParserOutput $parserOutput, $poOptions = [] ) {
                $this->addParserOutputMetadata( $parserOutput );
                $this->addParserOutputText( $parserOutput, $poOptions );
        }
@@ -2146,7 +2147,7 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * Get TTL in [$minTTL,$maxTTL] in pass it to lowerCdnMaxage()
+        * Get TTL in [$minTTL,$maxTTL] and pass it to lowerCdnMaxage()
         *
         * This sets and returns $minTTL if $mtime is false or null. Otherwise,
         * the TTL is higher the older the $mtime timestamp is. Essentially, the
@@ -2162,10 +2163,10 @@ class OutputPage extends ContextSource {
                $maxTTL = $maxTTL ?: $this->getConfig()->get( 'CdnMaxAge' );
 
                if ( $mtime === null || $mtime === false ) {
-                       return $minTTL; // entity does not exist
+                       return; // entity does not exist
                }
 
-               $age = MWTimestamp::time() - wfTimestamp( TS_UNIX, $mtime );
+               $age = MWTimestamp::time() - (int)wfTimestamp( TS_UNIX, $mtime );
                $adaptiveTTL = max( 0.9 * $age, $minTTL );
                $adaptiveTTL = min( $adaptiveTTL, $maxTTL );
 
@@ -2188,7 +2189,7 @@ class OutputPage extends ContextSource {
         *
         * @return array
         */
-       function getCacheVaryCookies() {
+       public function getCacheVaryCookies() {
                if ( self::$cacheVaryCookies === null ) {
                        $config = $this->getConfig();
                        self::$cacheVaryCookies = array_values( array_unique( array_merge(
@@ -2209,7 +2210,7 @@ class OutputPage extends ContextSource {
         *
         * @return bool
         */
-       function haveCacheVaryCookies() {
+       public function haveCacheVaryCookies() {
                $request = $this->getRequest();
                foreach ( $this->getCacheVaryCookies() as $cookieName ) {
                        if ( $request->getCookie( $cookieName, '', '' ) !== '' ) {
@@ -2269,7 +2270,7 @@ class OutputPage extends ContextSource {
        /**
         * Return a Link: header. Based on the values of $mLinkHeader.
         *
-        * @return string
+        * @return string|false
         */
        public function getLinkHeader() {
                if ( !$this->mLinkHeader ) {
@@ -2601,7 +2602,7 @@ class OutputPage extends ContextSource {
         * and optionally an custom HTML title (content of the "<title>" tag).
         *
         * @param string|Message $pageTitle Will be passed directly to setPageTitle()
-        * @param string|Message $htmlTitle Will be passed directly to setHTMLTitle();
+        * @param string|Message|false $htmlTitle Will be passed directly to setHTMLTitle();
         *                   optional, if not passed the "<title>" attribute will be
         *                   based on $pageTitle
         */
@@ -2822,7 +2823,7 @@ class OutputPage extends ContextSource {
        /**
         * Add a "return to" link pointing to a specified title
         *
-        * @param Title $title Title to link
+        * @param LinkTarget $title Title to link
         * @param array $query Query string parameters
         * @param string|null $text Text of the link (input is not escaped)
         * @param array $options Options array to pass to Linker
@@ -3282,7 +3283,7 @@ class OutputPage extends ContextSource {
                        $vars['wgUserId'] = $user->getId();
                        $vars['wgUserEditCount'] = $user->getEditCount();
                        $userReg = $user->getRegistration();
-                       $vars['wgUserRegistration'] = $userReg ? wfTimestamp( TS_UNIX, $userReg ) * 1000 : null;
+                       $vars['wgUserRegistration'] = $userReg ? (int)wfTimestamp( TS_UNIX, $userReg ) * 1000 : null;
                        // Get the revision ID of the oldest new message on the user's talk
                        // page. This can be used for constructing new message alerts on
                        // the client side.
index bf138c4..8d642e1 100644 (file)
@@ -77,8 +77,8 @@ class PHPVersionCheck {
        /**
         * Return the version of the installed PHP implementation.
         *
-        * @param string $impl By default, the function returns the info of the currently installed PHP
-        *  implementation. Using this parameter the caller can decide, what version info will be
+        * @param string|false $impl By default, the function returns the info of the currently installed
+        *  PHP implementation. Using this parameter the caller can decide, what version info will be
         *  returned. Valid values: HHVM, PHP
         * @return array An array of information about the PHP implementation, containing:
         *  - 'version': The version of the PHP implementation (specific to the implementation, not
@@ -108,8 +108,8 @@ class PHPVersionCheck {
                        'implementation' => 'PHP',
                        'version' => PHP_VERSION,
                        'vendor' => 'the PHP Group',
-                       'upstreamSupported' => '5.6.0',
-                       'minSupported' => '7.0.13',
+                       'upstreamSupported' => '7.1.0',
+                       'minSupported' => '7.2.0',
                        'upgradeURL' => 'https://www.php.net/downloads.php',
                );
        }
@@ -258,7 +258,7 @@ HTML;
        <head>
                <meta charset="UTF-8" />
                <title>MediaWiki {$this->mwVersion}</title>
-               <style media='screen'>
+               <style media="screen">
                        body {
                                color: #000;
                                background-color: #fff;
@@ -266,7 +266,7 @@ HTML;
                                padding: 2em;
                                text-align: center;
                        }
-                       p, img, h1, h2, ul  {
+                       p, img, h1, h2, ul {
                                text-align: left;
                                margin: 0.5em 0 1em;
                        }
@@ -279,9 +279,9 @@ HTML;
                </style>
        </head>
        <body>
-               <img src="{$encLogo}" alt='The MediaWiki logo' />
+               <img src="{$encLogo}" alt="The MediaWiki logo" />
                <h1>MediaWiki {$this->mwVersion} internal error</h1>
-               <div class='error'>
+               <div class="error">
                <p>
                        {$shortHtml}
                </p>
index 0a8e515..ef6b8ac 100644 (file)
@@ -97,7 +97,7 @@ class PermissionManager {
         */
        private $temporaryUserRights = [];
 
-       /** @var string[] Cached rights for isEveryoneAllowed */
+       /** @var bool[] Cached rights for isEveryoneAllowed, [ right => allowed ] */
        private $cachedRights = [];
 
        /**
index 8b5d995..3e639b9 100644 (file)
@@ -58,6 +58,18 @@ class ProtectionForm {
        /** @var array Map of action to the expiry time of the existing protection */
        protected $mExistingExpiry = [];
 
+       /** @var Article */
+       protected $mArticle;
+
+       /** @var Title */
+       protected $mTitle;
+
+       /** @var bool */
+       protected $disabled;
+
+       /** @var array */
+       protected $disabledAttrib;
+
        /** @var IContextSource */
        private $mContext;
 
@@ -78,7 +90,7 @@ class ProtectionForm {
                if ( wfReadOnly() ) {
                        $this->mPermErrors[] = [ 'readonlytext', wfReadOnlyReason() ];
                }
-               $this->disabled = $this->mPermErrors != [];
+               $this->disabled = $this->mPermErrors !== [];
                $this->disabledAttrib = $this->disabled
                        ? [ 'disabled' => 'disabled' ]
                        : [];
@@ -321,7 +333,9 @@ class ProtectionForm {
                );
 
                if ( !$status->isOK() ) {
-                       $this->show( $out->parseInlineAsInterface( $status->getWikiText() ) );
+                       $this->show( $out->parseInlineAsInterface(
+                               $status->getWikiText( false, false, $this->mContext->getLanguage() )
+                       ) );
                        return false;
                }
 
index 43014f1..92529b3 100644 (file)
@@ -2,24 +2,24 @@
 
 namespace MediaWiki\Rest\BasicAccess;
 
-use User;
 use MediaWiki\Permissions\PermissionManager;
 use MediaWiki\Rest\Handler;
 use MediaWiki\Rest\RequestInterface;
+use MediaWiki\User\UserIdentity;
 
 /**
- * A factory for MWBasicRequestAuthorizer which passes through a User object
+ * A factory for MWBasicRequestAuthorizer which passes through a UserIdentity.
  *
  * @internal
  */
 class MWBasicAuthorizer extends BasicAuthorizerBase {
-       /** @var User */
+       /** @var UserIdentity */
        private $user;
 
        /** @var PermissionManager */
        private $permissionManager;
 
-       public function __construct( User $user, PermissionManager $permissionManager ) {
+       public function __construct( UserIdentity $user, PermissionManager $permissionManager ) {
                $this->user = $user;
                $this->permissionManager = $permissionManager;
        }
index 8c459c6..671488a 100644 (file)
@@ -2,7 +2,7 @@
 
 namespace MediaWiki\Rest\BasicAccess;
 
-use User;
+use MediaWiki\User\UserIdentity;
 use MediaWiki\Permissions\PermissionManager;
 use MediaWiki\Rest\Handler;
 use MediaWiki\Rest\RequestInterface;
@@ -13,14 +13,14 @@ use MediaWiki\Rest\RequestInterface;
  * @internal
  */
 class MWBasicRequestAuthorizer extends BasicRequestAuthorizer {
-       /** @var User */
+       /** @var UserIdentity */
        private $user;
 
        /** @var PermissionManager */
        private $permissionManager;
 
        public function __construct( RequestInterface $request, Handler $handler,
-               User $user, PermissionManager $permissionManager
+               UserIdentity $user, PermissionManager $permissionManager
        ) {
                parent::__construct( $request, $handler );
                $this->user = $user;
index ee3441e..070451d 100644 (file)
@@ -10,6 +10,7 @@ use MediaWiki\Rest\Validator\Validator;
 use RequestContext;
 use Title;
 use WebResponse;
+use Wikimedia\Message\ITextFormatter;
 
 class EntryPoint {
        /** @var RequestInterface */
@@ -49,12 +50,18 @@ class EntryPoint {
                        'cookiePrefix' => $conf->get( 'CookiePrefix' )
                ] );
 
+               $responseFactory = new ResponseFactory( self::getTextFormatters( $services ) );
+
                // @phan-suppress-next-line PhanAccessMethodInternal
                $authorizer = new MWBasicAuthorizer( $context->getUser(),
                        $services->getPermissionManager() );
 
                // @phan-suppress-next-line PhanAccessMethodInternal
-               $restValidator = new Validator( $objectFactory, $request, RequestContext::getMain()->getUser() );
+               $restValidator = new Validator( $objectFactory,
+                       $services->getPermissionManager(),
+                       $request,
+                       RequestContext::getMain()->getUser()
+               );
 
                global $IP;
                $router = new Router(
@@ -62,7 +69,7 @@ class EntryPoint {
                        ExtensionRegistry::getInstance()->getAttribute( 'RestRoutes' ),
                        $conf->get( 'RestPath' ),
                        $services->getLocalServerObjectCache(),
-                       new ResponseFactory,
+                       $responseFactory,
                        $authorizer,
                        $objectFactory,
                        $restValidator
@@ -76,6 +83,25 @@ class EntryPoint {
                $entryPoint->execute();
        }
 
+       /**
+        * Get a TextFormatter array from MediaWikiServices
+        *
+        * @param MediaWikiServices $services
+        * @return ITextFormatter[]
+        */
+       public static function getTextFormatters( MediaWikiServices $services ) {
+               $langs = array_unique( [
+                       $services->getMainConfig()->get( 'ContLang' )->getCode(),
+                       'en'
+               ] );
+               $textFormatters = [];
+               $factory = $services->getMessageFormatterFactory();
+               foreach ( $langs as $lang ) {
+                       $textFormatters[] = $factory->getTextFormatter( $lang );
+               }
+               return $textFormatters;
+       }
+
        public function __construct( RequestContext $context, RequestInterface $request,
                WebResponse $webResponse, Router $router
        ) {
index 10d3a40..184fe16 100644 (file)
@@ -5,7 +5,14 @@ namespace MediaWiki\Rest;
 use Wikimedia\Message\MessageValue;
 
 class LocalizedHttpException extends HttpException {
-       public function __construct( MessageValue $message, $code = 500 ) {
-               parent::__construct( 'Localized exception with key ' . $message->getKey(), $code );
+       private $messageValue;
+
+       public function __construct( MessageValue $messageValue, $code = 500 ) {
+               parent::__construct( 'Localized exception with key ' . $messageValue->getKey(), $code );
+               $this->messageValue = $messageValue;
+       }
+
+       public function getMessageValue() {
+               return $this->messageValue;
        }
 }
index 5e5a198..fd0f3c7 100644 (file)
@@ -5,19 +5,31 @@ namespace MediaWiki\Rest;
 use Exception;
 use HttpStatus;
 use InvalidArgumentException;
+use LanguageCode;
 use MWExceptionHandler;
 use stdClass;
 use Throwable;
+use Wikimedia\Message\ITextFormatter;
+use Wikimedia\Message\MessageValue;
 
 /**
  * Generates standardized response objects.
  */
 class ResponseFactory {
-
        const CT_PLAIN = 'text/plain; charset=utf-8';
        const CT_HTML = 'text/html; charset=utf-8';
        const CT_JSON = 'application/json';
 
+       /** @var ITextFormatter[] */
+       private $textFormatters;
+
+       /**
+        * @param ITextFormatter[] $textFormatters
+        */
+       public function __construct( $textFormatters ) {
+               $this->textFormatters = $textFormatters;
+       }
+
        /**
         * Encode a stdClass object or array to a JSON string
         *
@@ -167,13 +179,23 @@ class ResponseFactory {
                return $response;
        }
 
+       /**
+        * Create an HTTP 4xx or 5xx response with error message localisation
+        */
+       public function createLocalizedHttpError( $errorCode, MessageValue $messageValue ) {
+               return $this->createHttpError( $errorCode, $this->formatMessage( $messageValue ) );
+       }
+
        /**
         * Turn an exception into a JSON error response.
         * @param Exception|Throwable $exception
         * @return Response
         */
        public function createFromException( $exception ) {
-               if ( $exception instanceof HttpException ) {
+               if ( $exception instanceof LocalizedHttpException ) {
+                       $response = $this->createLocalizedHttpError( $exception->getCode(),
+                               $exception->getMessageValue() );
+               } elseif ( $exception instanceof HttpException ) {
                        // FIXME can HttpException represent 2xx or 3xx responses?
                        $response = $this->createHttpError(
                                $exception->getCode(),
@@ -240,4 +262,18 @@ class ResponseFactory {
                return "<!doctype html><title>Redirect</title><a href=\"$url\">$url</a>";
        }
 
+       public function formatMessage( MessageValue $messageValue ) {
+               if ( !$this->textFormatters ) {
+                       // For unit tests
+                       return [];
+               }
+               $translations = [];
+               foreach ( $this->textFormatters as $formatter ) {
+                       $lang = LanguageCode::bcp47( $formatter->getLangCode() );
+                       $messageText = $formatter->format( $messageValue );
+                       $translations[$lang] = $messageText;
+               }
+               return [ 'messageTranslations' => $translations ];
+       }
+
 }
index a520130..6821d89 100644 (file)
@@ -4,6 +4,7 @@ namespace MediaWiki\Rest;
 
 use AppendIterator;
 use BagOStuff;
+use Wikimedia\Message\MessageValue;
 use MediaWiki\Rest\BasicAccess\BasicAuthorizerInterface;
 use MediaWiki\Rest\PathTemplateMatcher\PathMatcher;
 use MediaWiki\Rest\Validator\Validator;
@@ -226,18 +227,28 @@ class Router {
                $path = $request->getUri()->getPath();
                $relPath = $this->getRelativePath( $path );
                if ( $relPath === false ) {
-                       return $this->responseFactory->createHttpError( 404 );
+                       return $this->responseFactory->createLocalizedHttpError( 404,
+                               ( new MessageValue( 'rest-prefix-mismatch' ) )
+                                       ->plaintextParams( $path, $this->rootPath )
+                       );
                }
 
+               $requestMethod = $request->getMethod();
                $matchers = $this->getMatchers();
-               $matcher = $matchers[$request->getMethod()] ?? null;
+               $matcher = $matchers[$requestMethod] ?? null;
                $match = $matcher ? $matcher->match( $relPath ) : null;
 
+               // For a HEAD request, execute the GET handler instead if one exists.
+               // The webserver will discard the body.
+               if ( !$match && $requestMethod === 'HEAD' && isset( $matchers['GET'] ) ) {
+                       $match = $matchers['GET']->match( $relPath );
+               }
+
                if ( !$match ) {
                        // Check for 405 wrong method
                        $allowed = [];
                        foreach ( $matchers as $allowedMethod => $allowedMatcher ) {
-                               if ( $allowedMethod === $request->getMethod() ) {
+                               if ( $allowedMethod === $requestMethod ) {
                                        continue;
                                }
                                if ( $allowedMatcher->match( $relPath ) ) {
@@ -245,12 +256,20 @@ class Router {
                                }
                        }
                        if ( $allowed ) {
-                               $response = $this->responseFactory->createHttpError( 405 );
+                               $response = $this->responseFactory->createLocalizedHttpError( 405,
+                                       ( new MessageValue( 'rest-wrong-method' ) )
+                                               ->textParams( $requestMethod )
+                                               ->commaListParams( $allowed )
+                                               ->numParams( count( $allowed ) )
+                               );
                                $response->setHeader( 'Allow', $allowed );
                                return $response;
                        } else {
                                // Did not match with any other method, must be 404
-                               return $this->responseFactory->createHttpError( 404 );
+                               return $this->responseFactory->createLocalizedHttpError( 404,
+                                       ( new MessageValue( 'rest-no-match' ) )
+                                               ->plaintextParams( $relPath )
+                               );
                        }
                }
 
@@ -272,6 +291,7 @@ class Router {
 
        /**
         * Execute a fully-constructed handler
+        *
         * @param Handler $handler
         * @return ResponseInterface
         */
index 6c54a50..93de911 100644 (file)
@@ -3,21 +3,30 @@
 namespace MediaWiki\Rest\Validator;
 
 use InvalidArgumentException;
+use MediaWiki\Permissions\PermissionManager;
 use MediaWiki\Rest\RequestInterface;
+use MediaWiki\User\UserIdentity;
 use Psr\Http\Message\UploadedFileInterface;
-use User;
 use Wikimedia\ParamValidator\Callbacks;
 use Wikimedia\ParamValidator\ValidationException;
 
 class ParamValidatorCallbacks implements Callbacks {
 
+       /** @var PermissionManager */
+       private $permissionManager;
+
        /** @var RequestInterface */
        private $request;
 
-       /** @var User */
+       /** @var UserIdentity */
        private $user;
 
-       public function __construct( RequestInterface $request, User $user ) {
+       public function __construct(
+               PermissionManager $permissionManager,
+               RequestInterface $request,
+               UserIdentity $user
+       ) {
+               $this->permissionManager = $permissionManager;
                $this->request = $request;
                $this->user = $user;
        }
@@ -76,7 +85,7 @@ class ParamValidatorCallbacks implements Callbacks {
        }
 
        public function useHighLimits( array $options ) {
-               return $this->user->isAllowed( 'apihighlimits' );
+               return $this->permissionManager->userHasRight( $this->user, 'apihighlimits' );
        }
 
 }
index cee1cdb..be8d7a4 100644 (file)
@@ -2,10 +2,11 @@
 
 namespace MediaWiki\Rest\Validator;
 
+use MediaWiki\Permissions\PermissionManager;
 use MediaWiki\Rest\Handler;
 use MediaWiki\Rest\HttpException;
 use MediaWiki\Rest\RequestInterface;
-use User;
+use MediaWiki\User\UserIdentity;
 use Wikimedia\ObjectFactory;
 use Wikimedia\ParamValidator\ParamValidator;
 use Wikimedia\ParamValidator\TypeDef\BooleanDef;
@@ -62,16 +63,20 @@ class Validator {
        private $paramValidator;
 
        /**
-        * @internal
         * @param ObjectFactory $objectFactory
+        * @param PermissionManager $permissionManager
         * @param RequestInterface $request
-        * @param User $user
+        * @param UserIdentity $user
+        * @internal
         */
        public function __construct(
-               ObjectFactory $objectFactory, RequestInterface $request, User $user
+               ObjectFactory $objectFactory,
+               PermissionManager $permissionManager,
+               RequestInterface $request,
+               UserIdentity $user
        ) {
                $this->paramValidator = new ParamValidator(
-                       new ParamValidatorCallbacks( $request, $user ),
+                       new ParamValidatorCallbacks( $permissionManager, $request, $user ),
                        $objectFactory,
                        [
                                'typeDefs' => self::$typeDefs,
index ff9ac57..cf35371 100644 (file)
@@ -59,7 +59,7 @@ abstract class RevisionRecord {
        const FOR_THIS_USER = 2;
        const RAW = 3;
 
-       /** @var string Wiki ID; false means the current wiki */
+       /** @var string|false Wiki ID; false means the current wiki */
        protected $mWiki = false;
        /** @var int|null */
        protected $mId;
@@ -531,8 +531,6 @@ abstract class RevisionRecord {
                                $text = $title->getPrefixedText();
                                wfDebug( "Checking for $permissionlist on $text due to $field match on $bitfield\n" );
 
-                               $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
-
                                foreach ( $permissions as $perm ) {
                                        if ( $permissionManager->userCan( $perm, $user, $title ) ) {
                                                return true;
index 735a212..3ecef76 100644 (file)
@@ -1623,10 +1623,18 @@ class RevisionStore
         * @param object[]|IResultWrapper $slotRows
         * @param int $queryFlags
         * @param Title $title
+        * @param array|null $slotContents a map from blobAddress to slot
+        *      content blob or Content object.
         *
         * @return SlotRecord[]
         */
-       private function constructSlotRecords( $revId, $slotRows, $queryFlags, Title $title ) {
+       private function constructSlotRecords(
+               $revId,
+               $slotRows,
+               $queryFlags,
+               Title $title,
+               $slotContents = null
+       ) {
                $slots = [];
 
                foreach ( $slotRows as $row ) {
@@ -1651,8 +1659,15 @@ class RevisionStore
                                        = $this->emulateContentId( intval( $row->rev_text_id ) );
                        }
 
-                       $contentCallback = function ( SlotRecord $slot ) use ( $queryFlags ) {
-                               return $this->loadSlotContent( $slot, null, null, null, $queryFlags );
+                       $contentCallback = function ( SlotRecord $slot ) use ( $slotContents, $queryFlags ) {
+                               $blob = null;
+                               if ( isset( $slotContents[$slot->getAddress()] ) ) {
+                                       $blob = $slotContents[$slot->getAddress()];
+                                       if ( $blob instanceof Content ) {
+                                               return $blob;
+                                       }
+                               }
+                               return $this->loadSlotContent( $slot, $blob, null, null, $queryFlags );
                        };
 
                        $slots[$row->role_name] = new SlotRecord( $row, $contentCallback );
@@ -1804,8 +1819,10 @@ class RevisionStore
 
        /**
         * @param object $row A database row generated from a query based on getQueryInfo()
-        * @param null|object[] $slotRows Database rows generated from a query based on
-        *        getSlotsQueryInfo with the 'content' flag set.
+        * @param null|object[]|RevisionSlots $slots
+        *      - Database rows generated from a query based on getSlotsQueryInfo
+        *        with the 'content' flag set. Or
+        *  - RevisionSlots instance
         * @param int $queryFlags
         * @param Title|null $title
         * @param bool $fromCache if true, the returned RevisionRecord will ensure that no stale
@@ -1816,11 +1833,10 @@ class RevisionStore
         * @see RevisionFactory::newRevisionFromRow
         *
         * MCR migration note: this replaces Revision::newFromRow
-        *
         */
        public function newRevisionFromRowAndSlots(
                $row,
-               $slotRows,
+               $slots,
                $queryFlags = 0,
                Title $title = null,
                $fromCache = false
@@ -1857,7 +1873,9 @@ class RevisionStore
                // Legacy because $row may have come from self::selectFields()
                $comment = $this->commentStore->getCommentLegacy( $db, 'rev_comment', $row, true );
 
-               $slots = $this->newRevisionSlots( $row->rev_id, $row, $slotRows, $queryFlags, $title );
+               if ( !( $slots instanceof RevisionSlots ) ) {
+                       $slots = $this->newRevisionSlots( $row->rev_id, $row, $slots, $queryFlags, $title );
+               }
 
                // If this is a cached row, instantiate a cache-aware revision class to avoid stale data.
                if ( $fromCache ) {
@@ -1887,7 +1905,7 @@ class RevisionStore
         *               loaded immediately. Supports falsy or truthy value as well
         *               as an explicit list of slot role names.
         *               'content'- whether the actual content of the slots should be
-        *               preloaded. TODO: no supported yet.
+        *               preloaded.
         * @param int $queryFlags
         * @param Title|null $title
         * @return StatusValue a status with a RevisionRecord[] of successfully fetched revisions
@@ -1957,24 +1975,40 @@ class RevisionStore
                        }, $options['slots'] );
                }
 
-               // TODO: Support optional fetching of the content
-               $queryInfo = self::getSlotsQueryInfo( [ 'content' ] );
+               // We need to set the `content` flag because newRevisionFromRowAndSlots requires content
+               // metadata to be loaded.
+               $slotQueryInfo = self::getSlotsQueryInfo( [ 'content' ] );
                $db = $this->getDBConnectionRefForQueryFlags( $queryFlags );
                $slotRows = $db->select(
-                       $queryInfo['tables'],
-                       $queryInfo['fields'],
+                       $slotQueryInfo['tables'],
+                       $slotQueryInfo['fields'],
                        $slotQueryConds,
                        __METHOD__,
                        [],
-                       $queryInfo['joins']
+                       $slotQueryInfo['joins']
                );
 
                $slotRowsByRevId = [];
                foreach ( $slotRows as $slotRow ) {
                        $slotRowsByRevId[$slotRow->slot_revision_id][] = $slotRow;
                }
+
+               $slotContents = null;
+               if ( $options['content'] ?? false ) {
+                       $blobAddresses = [];
+                       foreach ( $slotRows as $slotRow ) {
+                               $blobAddresses[] = $slotRow->content_address;
+                       }
+                       $slotContentFetchStatus = $this->blobStore
+                               ->getBlobBatch( $blobAddresses, $queryFlags );
+                       foreach ( $slotContentFetchStatus->getErrors() as $error ) {
+                               $result->warning( $error['message'], ...$error['params'] );
+                       }
+                       $slotContents = $slotContentFetchStatus->getValue();
+               }
+
                $result->setResult( true, array_map( function ( $row ) use
-                       ( $slotRowsByRevId, $queryFlags, $titlesByPageId, $result ) {
+                       ( $slotRowsByRevId, $queryFlags, $titlesByPageId, $slotContents, $result ) {
                                if ( !isset( $slotRowsByRevId[$row->rev_id] ) ) {
                                        $result->warning(
                                                'internalerror',
@@ -1985,7 +2019,15 @@ class RevisionStore
                                try {
                                        return $this->newRevisionFromRowAndSlots(
                                                $row,
-                                               $slotRowsByRevId[$row->rev_id],
+                                               new RevisionSlots(
+                                                       $this->constructSlotRecords(
+                                                               $row->rev_id,
+                                                               $slotRowsByRevId[$row->rev_id],
+                                                               $queryFlags,
+                                                               $titlesByPageId[$row->rev_page],
+                                                               $slotContents
+                                                       )
+                                               ),
                                                $queryFlags,
                                                $titlesByPageId[$row->rev_page]
                                        );
index acecee1..1de4d7f 100644 (file)
@@ -27,7 +27,7 @@ namespace MediaWiki\Revision;
 
 use ActorMigration;
 use CommentStore;
-use MediaWiki\Logger\Spi as LoggerSpi;
+use Psr\Log\LoggerInterface;
 use MediaWiki\Storage\BlobStoreFactory;
 use MediaWiki\Storage\NameTableStoreFactory;
 use WANObjectCache;
@@ -54,8 +54,8 @@ class RevisionStoreFactory {
        private $dbLoadBalancerFactory;
        /** @var WANObjectCache */
        private $cache;
-       /** @var LoggerSpi */
-       private $loggerProvider;
+       /** @var LoggerInterface */
+       private $logger;
 
        /** @var CommentStore */
        private $commentStore;
@@ -84,7 +84,7 @@ class RevisionStoreFactory {
         * @param CommentStore $commentStore
         * @param ActorMigration $actorMigration
         * @param int $migrationStage
-        * @param LoggerSpi $loggerProvider
+        * @param LoggerInterface $logger
         * @param bool $contentHandlerUseDB see {@link $wgContentHandlerUseDB}. Must be the same
         *        for all wikis in the cluster. Will go away after MCR migration.
         */
@@ -97,7 +97,7 @@ class RevisionStoreFactory {
                CommentStore $commentStore,
                ActorMigration $actorMigration,
                $migrationStage,
-               LoggerSpi $loggerProvider,
+               LoggerInterface $logger,
                $contentHandlerUseDB
        ) {
                Assert::parameterType( 'integer', $migrationStage, '$migrationStage' );
@@ -109,7 +109,7 @@ class RevisionStoreFactory {
                $this->commentStore = $commentStore;
                $this->actorMigration = $actorMigration;
                $this->mcrMigrationStage = $migrationStage;
-               $this->loggerProvider = $loggerProvider;
+               $this->logger = $logger;
                $this->contentHandlerUseDB = $contentHandlerUseDB;
        }
 
@@ -118,7 +118,7 @@ class RevisionStoreFactory {
         *
         * @param bool|string $dbDomain DB domain of the relevant wiki or false for the current one
         *
-        * @return RevisionStore for the given wikiId with all necessary services and a logger
+        * @return RevisionStore for the given wikiId with all necessary services
         */
        public function getRevisionStore( $dbDomain = false ) {
                Assert::parameterType( 'string|boolean', $dbDomain, '$dbDomain' );
@@ -137,7 +137,7 @@ class RevisionStoreFactory {
                        $dbDomain
                );
 
-               $store->setLogger( $this->loggerProvider->getLogger( 'RevisionStore' ) );
+               $store->setLogger( $this->logger );
                $store->setContentHandlerUseDB( $this->contentHandlerUseDB );
 
                return $store;
index 469e494..dabf62b 100644 (file)
@@ -151,7 +151,7 @@ class RevisionStoreRecord extends RevisionRecord {
 
        /**
         * @throws RevisionAccessException if the size was unknown and could not be calculated.
-        * @return string The nominal revision size, never null. May be computed on the fly.
+        * @return int The nominal revision size, never null. May be computed on the fly.
         */
        public function getSize() {
                // If length is null, calculate and remember it (potentially SLOW!).
index 85b4c5a..7c2623b 100644 (file)
@@ -150,7 +150,7 @@ class SlotRoleHandler {
         *
         * The default implementation always returns false.
         *
-        * @return string
+        * @return bool
         */
        public function supportsArticleCount() {
                return false;
index 0b0aaf5..ab51eab 100644 (file)
  * For every service that MediaWiki core requires, an instantiator must be defined in
  * this file.
  *
+ * Note that, ideally, all information used to instantiate service objects should come
+ * from configuration. Information derived from the current request is acceptable, but
+ * only where there is no feasible alternative. It is preferred that such information
+ * (like the client IP, the acting user's identity, requested title, etc) be passed to
+ * the service object's methods as parameters. This makes the flow of information more
+ * obvious, and makes it easier to understand the behavior of services.
+ *
  * @note As of version 1.27, MediaWiki is only beginning to use dependency injection.
  * The services defined here do not yet fully represent all services used by core,
  * much of the code still relies on global state for this accessing services.
@@ -107,14 +114,12 @@ return [
        },
 
        'BlockManager' => function ( MediaWikiServices $services ) : BlockManager {
-               $context = RequestContext::getMain();
                return new BlockManager(
                        new ServiceOptions(
                                BlockManager::$constructorOptions, $services->getMainConfig()
                        ),
-                       $context->getUser(),
-                       $context->getRequest(),
-                       $services->getPermissionManager()
+                       $services->getPermissionManager(),
+                       LoggerFactory::getInstance( 'BlockManager' )
                );
        },
 
@@ -269,6 +274,8 @@ return [
                if ( defined( 'MW_NO_SESSION' ) ) {
                        return $services->getLinkRendererFactory()->create();
                } else {
+                       // Normally information from the current request would not be passed in here;
+                       // this is an exception. (See also the class documentation.)
                        return $services->getLinkRendererFactory()->createForUser(
                                RequestContext::getMain()->getUser()
                        );
@@ -545,7 +552,8 @@ return [
                        $services->getContentLanguage(),
                        AuthManager::singleton(),
                        $services->getLinkRendererFactory()->create(),
-                       $services->getNamespaceInfo()
+                       $services->getNamespaceInfo(),
+                       $services->getPermissionManager()
                );
                $factory->setLogger( LoggerFactory::getInstance( 'preferences' ) );
 
@@ -634,7 +642,7 @@ return [
                        $services->getCommentStore(),
                        $services->getActorMigration(),
                        $config->get( 'MultiContentRevisionSchemaMigrationStage' ),
-                       LoggerFactory::getProvider(),
+                       LoggerFactory::getInstance( 'RevisionStore' ),
                        $config->get( 'ContentHandlerUseDB' )
                );
 
@@ -785,7 +793,8 @@ return [
                        $services->getDBLoadBalancer(),
                        $services->getCommentStore(),
                        $services->getActorMigration(),
-                       $services->getWatchedItemStore()
+                       $services->getWatchedItemStore(),
+                       $services->getPermissionManager()
                );
        },
 
index 518531a..d450bdd 100644 (file)
@@ -440,6 +440,13 @@ if ( $wgEnableEmail ) {
        $wgUsersNotifiedOnAllChanges = [];
 }
 
+// $wgSysopEmailBans deprecated in 1.34
+if ( isset( $wgSysopEmailBans ) && $wgSysopEmailBans === false ) {
+       foreach ( $wgGroupPermissions as $group => $_ ) {
+               unset( $wgGroupPermissions[$group]['blockemail'] );
+       }
+}
+
 if ( $wgMetaNamespace === false ) {
        $wgMetaNamespace = str_replace( ' ', '_', $wgSitename );
 }
@@ -721,7 +728,7 @@ if ( is_null( $wgLocaltimezone ) ) {
 
 date_default_timezone_set( $wgLocaltimezone );
 if ( is_null( $wgLocalTZoffset ) ) {
-       $wgLocalTZoffset = date( 'Z' ) / 60;
+       $wgLocalTZoffset = (int)date( 'Z' ) / 60;
 }
 // The part after the System| is ignored, but rest of MW fills it
 // out as the local offset.
index 76b905e..932fd2a 100644 (file)
@@ -115,6 +115,7 @@ class Status extends StatusValue {
         * ]
         *
         * @return Status[]
+        * @suppress PhanUndeclaredProperty Status vs StatusValue
         */
        public function splitByErrorType() {
                list( $errorsOnlyStatus, $warningsOnlyStatus ) = parent::splitByErrorType();
index 4903cf0..b2c003a 100644 (file)
@@ -1531,7 +1531,9 @@ class DerivedPageDataUpdater implements IDBAccessObject, LoggerAwareInterface {
                if ( $this->options['changed']
                        && $title->getNamespace() == NS_USER_TALK
                        && $shortTitle != $legacyUser->getTitleKey()
-                       && !( $this->revision->isMinor() && $legacyUser->isAllowed( 'nominornewtalk' ) )
+                       && !( $this->revision->isMinor() && MediaWikiServices::getInstance()
+                                       ->getPermissionManager()
+                                       ->userHasRight( $legacyUser, 'nominornewtalk' ) )
                ) {
                        $recipient = User::newFromName( $shortTitle, false );
                        if ( !$recipient ) {
index a0ef07d..826d526 100644 (file)
@@ -231,7 +231,7 @@ class PageEditStash {
                        return false;
                }
 
-               $age = time() - wfTimestamp( TS_UNIX, $editInfo->output->getCacheTime() );
+               $age = time() - (int)wfTimestamp( TS_UNIX, $editInfo->output->getCacheTime() );
                $context['age'] = $age;
 
                $isCacheUsable = true;
@@ -450,7 +450,7 @@ class PageEditStash {
        ) {
                // If an item is renewed, mind the cache TTL determined by config and parser functions.
                // Put an upper limit on the TTL for sanity to avoid extreme template/file staleness.
-               $age = time() - wfTimestamp( TS_UNIX, $parserOutput->getCacheTime() );
+               $age = time() - (int)wfTimestamp( TS_UNIX, $parserOutput->getCacheTime() );
                $ttl = min( $parserOutput->getCacheExpiry() - $age, self::MAX_CACHE_TTL );
                // Avoid extremely stale user signature timestamps (T84843)
                if ( $parserOutput->getFlag( 'user-signature' ) ) {
index 8c011df..bcbc9e8 100644 (file)
@@ -103,10 +103,10 @@ class SqlBlobStore implements IDBAccessObject, BlobStore {
         * @param ExternalStoreAccess $extStoreAccess Access layer for external storage
         * @param WANObjectCache $cache A cache manager for caching blobs. This can be the local
         *        wiki's default instance even if $dbDomain refers to a different wiki, since
-        *        makeGlobalKey() is used to constructed a key that allows cached blobs from the
-        *        same database to be re-used between wikis. For example, enwiki and frwiki will
-        *        use the same cache keys for blobs from the wikidatawiki database, regardless of
-        *        the cache's default key space.
+        *        makeGlobalKey() is used to construct a key that allows cached blobs from the
+        *        same database to be re-used between wikis. For example, wiki A and wiki B will
+        *        use the same cache keys for blobs fetched from wiki C, regardless of the
+        *        wiki-specific default key space.
         * @param bool|string $dbDomain The ID of the target wiki database. Use false for the local wiki.
         */
        public function __construct(
@@ -449,16 +449,15 @@ class SqlBlobStore implements IDBAccessObject, BlobStore {
         * Get a cache key for a given Blob address.
         *
         * The cache key is constructed in a way that allows cached blobs from the same database
-        * to be re-used between wikis. For example, enwiki and frwiki will use the same cache keys
-        * for blobs from the wikidatawiki database.
+        * to be re-used between wikis. For example, wiki A and wiki B will use the same cache keys
+        * for blobs fetched from wiki C.
         *
         * @param string $blobAddress
         * @return string
         */
        private function getCacheKey( $blobAddress ) {
                return $this->cache->makeGlobalKey(
-                       'BlobStore',
-                       'address',
+                       'SqlBlobStore-blob',
                        $this->dbLoadBalancer->resolveDomainID( $this->dbDomain ),
                        $blobAddress
                );
index 2ad42e5..c9b2c33 100644 (file)
  */
 class StreamFile {
        // Do not send any HTTP headers unless requested by caller (e.g. body only)
+       /** @deprecated since 1.34 */
        const STREAM_HEADLESS = HTTPFileStreamer::STREAM_HEADLESS;
        // Do not try to tear down any PHP output buffers
+       /** @deprecated since 1.34 */
        const STREAM_ALLOW_OB = HTTPFileStreamer::STREAM_ALLOW_OB;
 
        /**
@@ -66,8 +68,10 @@ class StreamFile {
         * @param string $fname Full name and path of the file to stream
         * @param int $flags Bitfield of STREAM_* constants
         * @since 1.24
+        * @deprecated since 1.34, use HTTPFileStreamer::send404Message() instead
         */
        public static function send404Message( $fname, $flags = 0 ) {
+               wfDeprecated( __METHOD__, '1.34' );
                HTTPFileStreamer::send404Message( $fname, $flags );
        }
 
@@ -78,8 +82,10 @@ class StreamFile {
         * @param int $size File size
         * @return array|string Returns error string on failure (start, end, length)
         * @since 1.24
+        * @deprecated since 1.34, use HTTPFileStreamer::parseRange() instead
         */
        public static function parseRange( $range, $size ) {
+               wfDeprecated( __METHOD__, '1.34' );
                return HTTPFileStreamer::parseRange( $range, $size );
        }
 
@@ -105,7 +111,6 @@ class StreamFile {
                                case 'png':
                                        return 'image/png';
                                case 'jpg':
-                                       return 'image/jpeg';
                                case 'jpeg':
                                        return 'image/jpeg';
                        }
index 1e93c44..9843e81 100644 (file)
@@ -23,7 +23,7 @@
  */
 
 use MediaWiki\Permissions\PermissionManager;
-use MediaWiki\Storage\RevisionRecord;
+use MediaWiki\Revision\RevisionRecord;
 use Wikimedia\Assert\Assert;
 use Wikimedia\Rdbms\Database;
 use Wikimedia\Rdbms\IDatabase;
@@ -1059,7 +1059,7 @@ class Title implements LinkTarget, IDBAccessObject {
                        $this->lazyFillContentModel( $this->loadFieldFromDB( 'page_content_model', $flags ) );
                } elseif (
                        ( !$this->mContentModel || $flags & self::GAID_FOR_UPDATE ) &&
-                       $this->getArticleId( $flags )
+                       $this->getArticleID( $flags )
                ) {
                        $linkCache = MediaWikiServices::getInstance()->getLinkCache();
                        $linkCache->addLinkObj( $this ); # in case we already had an article ID
@@ -1561,14 +1561,32 @@ class Title implements LinkTarget, IDBAccessObject {
         * Get a Title object associated with the talk page of this article
         *
         * @deprecated since 1.34, use getTalkPageIfDefined() or NamespaceInfo::getTalkPage()
-        *             with NamespaceInfo::canHaveTalkPage().
+        *             with NamespaceInfo::canHaveTalkPage(). Note that the new method will
+        *             throw if asked for the talk page of a section-only link, or of an interwiki
+        *             link.
         * @return Title The object for the talk page
         * @throws MWException if $target doesn't have talk pages, e.g. because it's in NS_SPECIAL
         *         or because it's a relative link, or an interwiki link.
         */
        public function getTalkPage() {
-               return self::castFromLinkTarget(
-                       MediaWikiServices::getInstance()->getNamespaceInfo()->getTalkPage( $this ) );
+               // NOTE: The equivalent code in NamespaceInfo is less lenient about producing invalid titles.
+               //       Instead of failing on invalid titles, let's just log the issue for now.
+               //       See the discussion on T227817.
+
+               // Is this the same title?
+               $talkNS = MediaWikiServices::getInstance()->getNamespaceInfo()->getTalk( $this->mNamespace );
+               if ( $this->mNamespace == $talkNS ) {
+                       return $this;
+               }
+
+               $title = self::makeTitle( $talkNS, $this->mDbkeyform );
+
+               $this->warnIfPageCannotExist( $title, __METHOD__ );
+
+               return $title;
+               // TODO: replace the above with the code below:
+               // return self::castFromLinkTarget(
+               // MediaWikiServices::getInstance()->getNamespaceInfo()->getTalkPage( $this ) );
        }
 
        /**
@@ -1596,8 +1614,51 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return Title The object for the subject page
         */
        public function getSubjectPage() {
-               return self::castFromLinkTarget(
-                       MediaWikiServices::getInstance()->getNamespaceInfo()->getSubjectPage( $this ) );
+               // Is this the same title?
+               $subjectNS = MediaWikiServices::getInstance()->getNamespaceInfo()
+                       ->getSubject( $this->mNamespace );
+               if ( $this->mNamespace == $subjectNS ) {
+                       return $this;
+               }
+               // NOTE: The equivalent code in NamespaceInfo is less lenient about producing invalid titles.
+               //       Instead of failing on invalid titles, let's just log the issue for now.
+               //       See the discussion on T227817.
+               $title = self::makeTitle( $subjectNS, $this->mDbkeyform );
+
+               $this->warnIfPageCannotExist( $title, __METHOD__ );
+
+               return $title;
+               // TODO: replace the above with the code below:
+               // return self::castFromLinkTarget(
+               // MediaWikiServices::getInstance()->getNamespaceInfo()->getSubjectPage( $this ) );
+       }
+
+       /**
+        * @param Title $title
+        * @param string $method
+        *
+        * @return bool whether a warning was issued
+        */
+       private function warnIfPageCannotExist( Title $title, $method ) {
+               if ( $this->getText() == '' ) {
+                       wfLogWarning(
+                               $method . ': called on empty title ' . $this->getFullText() . ', returning '
+                               . $title->getFullText()
+                       );
+
+                       return true;
+               }
+
+               if ( $this->getInterwiki() !== '' ) {
+                       wfLogWarning(
+                               $method . ': called on interwiki title ' . $this->getFullText() . ', returning '
+                               . $title->getFullText()
+                       );
+
+                       return true;
+               }
+
+               return false;
        }
 
        /**
@@ -1610,8 +1671,23 @@ class Title implements LinkTarget, IDBAccessObject {
         * @return Title
         */
        public function getOtherPage() {
-               return self::castFromLinkTarget(
-                       MediaWikiServices::getInstance()->getNamespaceInfo()->getAssociatedPage( $this ) );
+               // NOTE: Depend on the methods in this class instead of their equivalent in NamespaceInfo,
+               //       until their semantics has become exactly the same.
+               //       See the discussion on T227817.
+               if ( $this->isSpecialPage() ) {
+                       throw new MWException( 'Special pages cannot have other pages' );
+               }
+               if ( $this->isTalkPage() ) {
+                       return $this->getSubjectPage();
+               } else {
+                       if ( !$this->canHaveTalkPage() ) {
+                               throw new MWException( "{$this->getPrefixedText()} does not have an other page" );
+                       }
+                       return $this->getTalkPage();
+               }
+               // TODO: replace the above with the code below:
+               // return self::castFromLinkTarget(
+               // MediaWikiServices::getInstance()->getNamespaceInfo()->getAssociatedPage( $this ) );
        }
 
        /**
@@ -1986,7 +2062,7 @@ class Title implements LinkTarget, IDBAccessObject {
         *
         * @see self::getLocalURL for the arguments.
         * @see wfExpandUrl
-        * @param string|string[] $query
+        * @param string|array $query
         * @param string|string[]|bool $query2
         * @param string|int|null $proto Protocol type to use in URL
         * @return string The URL
@@ -2047,7 +2123,7 @@ class Title implements LinkTarget, IDBAccessObject {
         *  valid to link, locally, to the current Title.
         * @see self::newFromText to produce a Title object.
         *
-        * @param string|string[] $query An optional query string,
+        * @param string|array $query An optional query string,
         *   not used for interwiki links. Can be specified as an associative array as well,
         *   e.g., [ 'action' => 'edit' ] (keys and values will be URL-escaped).
         *   Some query patterns will trigger various shorturl path replacements.
@@ -2182,7 +2258,7 @@ class Title implements LinkTarget, IDBAccessObject {
         * protocol-relative, the URL will be expanded to http://
         *
         * @see self::getLocalURL for the arguments.
-        * @param string|string[] $query
+        * @param string|array $query
         * @param string|bool $query2 Deprecated
         * @return string The URL
         */
@@ -2205,7 +2281,7 @@ class Title implements LinkTarget, IDBAccessObject {
         * NOTE: Unlike getInternalURL(), the canonical URL includes the fragment
         *
         * @see self::getLocalURL for the arguments.
-        * @param string|string[] $query
+        * @param string|array $query
         * @param string|bool $query2 Deprecated
         * @return string The URL
         * @since 1.18
@@ -4270,12 +4346,21 @@ class Title implements LinkTarget, IDBAccessObject {
         * on the number of links. Typically called on create and delete.
         */
        public function touchLinks() {
-               DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this, 'pagelinks', 'page-touch' ) );
+               $jobs = [];
+               $jobs[] = HTMLCacheUpdateJob::newForBacklinks(
+                       $this,
+                       'pagelinks',
+                       [ 'causeAction' => 'page-touch' ]
+               );
                if ( $this->mNamespace == NS_CATEGORY ) {
-                       DeferredUpdates::addUpdate(
-                               new HTMLCacheUpdate( $this, 'categorylinks', 'category-touch' )
+                       $jobs[] = HTMLCacheUpdateJob::newForBacklinks(
+                               $this,
+                               'categorylinks',
+                               [ 'causeAction' => 'category-touch' ]
                        );
                }
+
+               JobQueueGroup::singleton()->lazyPush( $jobs );
        }
 
        /**
index a48d032..c94e8d4 100644 (file)
@@ -547,7 +547,7 @@ class WebRequest {
         *
         * @param string $name
         * @param array|null $default Option default (or null)
-        * @return array Array of ints
+        * @return int[]|null
         */
        public function getIntArray( $name, $default = null ) {
                $val = $this->getArray( $name, $default );
index febf03e..b368a13 100644 (file)
@@ -266,7 +266,7 @@ class Xml {
        /**
         * Convenience function to build an HTML text input field
         * @param string $name Value of the name attribute
-        * @param int $size Value of the size attribute
+        * @param int|false $size Value of the size attribute
         * @param mixed $value Value of the value attribute
         * @param array $attribs Other attributes
         * @return string HTML
@@ -289,7 +289,7 @@ class Xml {
        /**
         * Convenience function to build an HTML password input field
         * @param string $name Value of the name attribute
-        * @param int $size Value of the size attribute
+        * @param int|false $size Value of the size attribute
         * @param mixed $value Value of the value attribute
         * @param array $attribs Other attributes
         * @return string HTML
@@ -600,7 +600,7 @@ class Xml {
         *
         * @param string|bool $legend Legend of the fieldset. If evaluates to false,
         *   legend is not added.
-        * @param string $content Pre-escaped content for the fieldset. If false,
+        * @param string|false $content Pre-escaped content for the fieldset. If false,
         *   only open fieldset is returned.
         * @param array $attribs Any attributes to fieldset-element.
         *
index f892c5e..4be2f7d 100644 (file)
@@ -22,7 +22,7 @@
 use MediaWiki\MediaWikiServices;
 
 /**
- * @defgroup Actions Action done on pages
+ * @defgroup Actions Actions
  */
 
 /**
index db874f2..6a61045 100644 (file)
@@ -433,27 +433,30 @@ class HistoryAction extends FormlessAction {
         * @return FeedItem
         */
        function feedItem( $row ) {
-               $rev = new Revision( $row, 0, $this->getTitle() );
-
+               $revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
+               $rev = $revisionStore->newRevisionFromRow( $row, 0, $this->getTitle() );
+               $prevRev = $revisionStore->getPreviousRevision( $rev );
+               $revComment = $rev->getComment() === null ? null : $rev->getComment()->text;
                $text = FeedUtils::formatDiffRow(
                        $this->getTitle(),
-                       $this->getTitle()->getPreviousRevisionID( $rev->getId() ),
+                       $prevRev ? $prevRev->getId() : false,
                        $rev->getId(),
                        $rev->getTimestamp(),
-                       $rev->getComment()
+                       $revComment
                );
-               if ( $rev->getComment() == '' ) {
+               $revUserText = $rev->getUser() ? $rev->getUser()->getName() : '';
+               if ( $revComment == '' ) {
                        $contLang = MediaWikiServices::getInstance()->getContentLanguage();
                        $title = $this->msg( 'history-feed-item-nocomment',
-                               $rev->getUserText(),
+                               $revUserText,
                                $contLang->timeanddate( $rev->getTimestamp() ),
                                $contLang->date( $rev->getTimestamp() ),
                                $contLang->time( $rev->getTimestamp() )
                        )->inContentLanguage()->text();
                } else {
-                       $title = $rev->getUserText() .
+                       $title = $revUserText .
                                $this->msg( 'colon-separator' )->inContentLanguage()->text() .
-                               FeedItem::stripComment( $rev->getComment() );
+                               FeedItem::stripComment( $revComment );
                }
 
                return new FeedItem(
@@ -461,7 +464,7 @@ class HistoryAction extends FormlessAction {
                        $text,
                        $this->getTitle()->getFullURL( 'diff=' . $rev->getId() . '&oldid=prev' ),
                        $rev->getTimestamp(),
-                       $rev->getUserText(),
+                       $revUserText,
                        $this->getTitle()->getTalkPage()->getFullURL()
                );
        }
index 207721e..0360fe4 100644 (file)
@@ -23,7 +23,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
-use MediaWiki\Storage\RevisionRecord;
+use MediaWiki\Revision\RevisionRecord;
 use Wikimedia\Rdbms\Database;
 
 /**
@@ -280,11 +280,10 @@ class InfoAction extends FormlessAction {
                // Language in which the page content is (supposed to be) written
                $pageLang = $title->getPageLanguage()->getCode();
 
-               $permissionManager = $services->getPermissionManager();
-
                $pageLangHtml = $pageLang . ' - ' .
                        Language::fetchLanguageName( $pageLang, $lang->getCode() );
                // Link to Special:PageLanguage with pre-filled page title if user has permissions
+               $permissionManager = $services->getPermissionManager();
                if ( $config->get( 'PageLanguageUseDB' )
                        && $permissionManager->userCan( 'pagelang', $user, $title )
                ) {
@@ -344,8 +343,7 @@ class InfoAction extends FormlessAction {
                ];
 
                $unwatchedPageThreshold = $config->get( 'UnwatchedPageThreshold' );
-               if (
-                       $services->getPermissionManager()->userHasRight( $user, 'unwatchedpages' ) ||
+               if ( $permissionManager->userHasRight( $user, 'unwatchedpages' ) ||
                        ( $unwatchedPageThreshold !== false &&
                                $pageCounts['watchers'] >= $unwatchedPageThreshold )
                ) {
@@ -360,7 +358,7 @@ class InfoAction extends FormlessAction {
                        ) {
                                $minToDisclose = $config->get( 'UnwatchedPageSecret' );
                                if ( $pageCounts['visitingWatchers'] > $minToDisclose ||
-                                       $services->getPermissionManager()->userHasRight( $user, 'unwatchedpages' ) ) {
+                                       $permissionManager->userHasRight( $user, 'unwatchedpages' ) ) {
                                        $pageInfo['header-basic'][] = [
                                                $this->msg( 'pageinfo-visiting-watchers' ),
                                                $lang->formatNum( $pageCounts['visitingWatchers'] )
index 41cd24e..68c0ea1 100644 (file)
@@ -289,8 +289,9 @@ class McrUndoAction extends FormAction {
                                'h2', [ 'id' => 'mw-previewheader' ],
                                $this->context->msg( 'preview' )->text()
                        ) .
-                       $out->parseAsInterface( $note ) .
-                       "<hr />"
+                       Html::rawElement( 'div', [ 'class' => 'warningbox' ],
+                               $out->parseAsInterface( $note )
+                       )
                );
 
                $pageViewLang = $this->getTitle()->getPageViewLanguage();
index 8fd4e0a..0586e09 100644 (file)
@@ -238,23 +238,31 @@ class RawAction extends FormlessAction {
         */
        public function getOldId() {
                $oldid = $this->getRequest()->getInt( 'oldid' );
+               $rl = MediaWikiServices::getInstance()->getRevisionLookup();
                switch ( $this->getRequest()->getText( 'direction' ) ) {
                        case 'next':
                                # output next revision, or nothing if there isn't one
-                               $nextid = 0;
+                               $nextRev = null;
                                if ( $oldid ) {
-                                       $nextid = $this->getTitle()->getNextRevisionID( $oldid );
+                                       $oldRev = $rl->getRevisionById( $oldid );
+                                       if ( $oldRev ) {
+                                               $nextRev = $rl->getNextRevision( $oldRev );
+                                       }
                                }
-                               $oldid = $nextid ?: -1;
+                               $oldid = $nextRev ? $nextRev->getId() : -1;
                                break;
                        case 'prev':
                                # output previous revision, or nothing if there isn't one
+                               $prevRev = null;
                                if ( !$oldid ) {
                                        # get the current revision so we can get the penultimate one
                                        $oldid = $this->page->getLatest();
                                }
-                               $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
-                               $oldid = $previd ?: -1;
+                               $oldRev = $rl->getRevisionById( $oldid );
+                               if ( $oldRev ) {
+                                       $prevRev = $rl->getPreviousRevision( $oldRev );
+                               }
+                               $oldid = $prevRev ? $prevRev->getId() : -1;
                                break;
                        case 'cur':
                                $oldid = 0;
index 519da61..1c9e63b 100644 (file)
@@ -20,7 +20,7 @@
  * @ingroup Actions
  */
 
-use MediaWiki\Storage\RevisionRecord;
+use MediaWiki\Revision\RevisionRecord;
 
 /**
  * User interface for the rollback action
index 8a231cb..56be456 100644 (file)
@@ -35,6 +35,9 @@ class SpecialPageAction extends FormlessAction {
                'editchangetags' => 'EditTags',
        ];
 
+       /**
+        * @inheritDoc
+        */
        public function getName() {
                $request = $this->getRequest();
                $actionName = $request->getVal( 'action', 'view' );
index 056d10c..7518008 100644 (file)
@@ -1585,6 +1585,7 @@ abstract class ApiBase extends ContextSource {
                                'integeroutofrange',
                                [ 'min' => $min, 'max' => $max, 'botMax' => $botMax ?: $max ]
                        );
+                       // @phan-suppress-next-line PhanTypeMismatchArgument
                        $this->warnOrDie( $msg, $enforceLimits );
                        $value = $min;
                }
@@ -1606,6 +1607,7 @@ abstract class ApiBase extends ContextSource {
                                                'integeroutofrange',
                                                [ 'min' => $min, 'max' => $max, 'botMax' => $botMax ?: $max ]
                                        );
+                                       // @phan-suppress-next-line PhanTypeMismatchArgument
                                        $this->warnOrDie( $msg, $enforceLimits );
                                        $value = $botMax;
                                }
@@ -1616,6 +1618,7 @@ abstract class ApiBase extends ContextSource {
                                        'integeroutofrange',
                                        [ 'min' => $min, 'max' => $max, 'botMax' => $botMax ?: $max ]
                                );
+                               // @phan-suppress-next-line PhanTypeMismatchArgument
                                $this->warnOrDie( $msg, $enforceLimits );
                                $value = $max;
                        }
@@ -2022,6 +2025,7 @@ abstract class ApiBase extends ContextSource {
         */
        public function dieWithException( $exception, array $options = [] ) {
                $this->dieWithError(
+                  &