summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2015-06-04 07:31:04 +0200
committerPierre Schmitz <pierre@archlinux.de>2015-06-04 07:58:39 +0200
commitf6d65e533c62f6deb21342d4901ece24497b433e (patch)
treef28adf0362d14bcd448f7b65a7aaf38650f923aa /includes
parentc27b2e832fe25651ef2410fae85b41072aae7519 (diff)
Update to MediaWiki 1.25.1
Diffstat (limited to 'includes')
-rw-r--r--includes/AjaxDispatcher.php9
-rw-r--r--includes/AutoLoader.php1194
-rw-r--r--includes/Autopromote.php4
-rw-r--r--includes/Block.php66
-rw-r--r--includes/Category.php11
-rw-r--r--includes/CategoryFinder.php1
-rw-r--r--includes/CategoryViewer.php119
-rw-r--r--includes/CdbCompat.php45
-rw-r--r--includes/ChangeTags.php931
-rw-r--r--includes/Collation.php4
-rw-r--r--includes/DefaultSettings.php502
-rw-r--r--includes/Defines.php29
-rw-r--r--includes/EditPage.php469
-rw-r--r--includes/Export.php37
-rw-r--r--includes/Feed.php14
-rw-r--r--includes/FeedUtils.php7
-rw-r--r--includes/FileDeleteForm.php4
-rw-r--r--includes/GitInfo.php2
-rw-r--r--includes/GlobalFunctions.php723
-rw-r--r--includes/Hooks.php20
-rw-r--r--includes/Html.php192
-rw-r--r--includes/HtmlFormatter.php18
-rw-r--r--includes/HttpFunctions.php98
-rw-r--r--includes/Import.php377
-rw-r--r--includes/LinkFilter.php2
-rw-r--r--includes/Linker.php172
-rw-r--r--includes/MWNamespace.php4
-rw-r--r--includes/MWTimestamp.php9
-rw-r--r--includes/MagicWord.php12
-rw-r--r--includes/MediaWiki.php116
-rw-r--r--includes/MediaWikiVersionFetcher.php3
-rw-r--r--includes/Message.php79
-rw-r--r--includes/MessageBlobStore.php14
-rw-r--r--includes/MimeMagic.php14
-rw-r--r--includes/MovePage.php204
-rw-r--r--includes/NoLocalSettings.php (renamed from includes/templates/NoLocalSettings.php)70
-rw-r--r--includes/OutputHandler.php3
-rw-r--r--includes/OutputPage.php266
-rw-r--r--includes/PHPVersionCheck.php157
-rw-r--r--includes/PHPVersionError.php110
-rw-r--r--includes/Preferences.php102
-rw-r--r--includes/PrefixSearch.php161
-rw-r--r--includes/ProtectionForm.php69
-rw-r--r--includes/Revision.php125
-rw-r--r--includes/RevisionList.php27
-rw-r--r--includes/Sanitizer.php109
-rw-r--r--includes/Setup.php100
-rw-r--r--includes/SiteConfiguration.php4
-rw-r--r--includes/SiteStats.php37
-rw-r--r--includes/StatCounter.php154
-rw-r--r--includes/Status.php252
-rw-r--r--includes/StreamFile.php5
-rw-r--r--includes/StubObject.php27
-rw-r--r--includes/TemplateParser.php201
-rw-r--r--includes/Title.php526
-rw-r--r--includes/TitleArray.php2
-rw-r--r--includes/User.php636
-rw-r--r--includes/UserArray.php23
-rw-r--r--includes/UserRightsProxy.php11
-rw-r--r--includes/WatchedItem.php10
-rw-r--r--includes/WebRequest.php66
-rw-r--r--includes/WebResponse.php42
-rw-r--r--includes/WebStart.php37
-rw-r--r--includes/WikiMap.php2
-rw-r--r--includes/Xml.php12
-rw-r--r--includes/ZhConversion.php10864
-rw-r--r--includes/actions/Action.php24
-rw-r--r--includes/actions/CreditsAction.php6
-rw-r--r--includes/actions/DeleteAction.php3
-rw-r--r--includes/actions/EditAction.php2
-rw-r--r--includes/actions/FormAction.php4
-rw-r--r--includes/actions/HistoryAction.php59
-rw-r--r--includes/actions/InfoAction.php53
-rw-r--r--includes/actions/RawAction.php2
-rw-r--r--includes/actions/RevertAction.php4
-rw-r--r--includes/actions/RevisiondeleteAction.php6
-rw-r--r--includes/actions/SpecialPageAction.php79
-rw-r--r--includes/actions/UnwatchAction.php2
-rw-r--r--includes/actions/WatchAction.php10
-rw-r--r--includes/api/ApiBase.php1084
-rw-r--r--includes/api/ApiBlock.php62
-rw-r--r--includes/api/ApiCheckToken.php81
-rw-r--r--includes/api/ApiClearHasMsg.php9
-rw-r--r--includes/api/ApiComparePages.php25
-rw-r--r--includes/api/ApiContinuationManager.php238
-rw-r--r--includes/api/ApiCreateAccount.php48
-rw-r--r--includes/api/ApiDelete.php29
-rw-r--r--includes/api/ApiDisabled.php16
-rw-r--r--includes/api/ApiEditPage.php143
-rw-r--r--includes/api/ApiEmailUser.php19
-rw-r--r--includes/api/ApiErrorFormatter.php303
-rw-r--r--includes/api/ApiExpandTemplates.php85
-rw-r--r--includes/api/ApiFeedContributions.php54
-rw-r--r--includes/api/ApiFeedRecentChanges.php35
-rw-r--r--includes/api/ApiFeedWatchlist.php90
-rw-r--r--includes/api/ApiFileRevert.php22
-rw-r--r--includes/api/ApiFormatBase.php332
-rw-r--r--includes/api/ApiFormatDbg.php11
-rw-r--r--includes/api/ApiFormatDump.php11
-rw-r--r--includes/api/ApiFormatFeedWrapper.php30
-rw-r--r--includes/api/ApiFormatJson.php96
-rw-r--r--includes/api/ApiFormatNone.php4
-rw-r--r--includes/api/ApiFormatPhp.php35
-rw-r--r--includes/api/ApiFormatRaw.php31
-rw-r--r--includes/api/ApiFormatTxt.php11
-rw-r--r--includes/api/ApiFormatWddx.php73
-rw-r--r--includes/api/ApiFormatXml.php302
-rw-r--r--includes/api/ApiFormatYaml.php4
-rw-r--r--includes/api/ApiHelp.php675
-rw-r--r--includes/api/ApiHelpParamValueMessage.php72
-rw-r--r--includes/api/ApiImageRotate.php42
-rw-r--r--includes/api/ApiImport.php35
-rw-r--r--includes/api/ApiLogin.php35
-rw-r--r--includes/api/ApiLogout.php19
-rw-r--r--includes/api/ApiMain.php541
-rw-r--r--includes/api/ApiManageTags.php107
-rw-r--r--includes/api/ApiMessage.php191
-rw-r--r--includes/api/ApiMove.php97
-rw-r--r--includes/api/ApiOpenSearch.php349
-rw-r--r--includes/api/ApiOptions.php37
-rw-r--r--includes/api/ApiPageSet.php337
-rw-r--r--includes/api/ApiParamInfo.php455
-rw-r--r--includes/api/ApiParse.php294
-rw-r--r--includes/api/ApiPatrol.php21
-rw-r--r--includes/api/ApiProtect.php46
-rw-r--r--includes/api/ApiPurge.php39
-rw-r--r--includes/api/ApiQuery.php117
-rw-r--r--includes/api/ApiQueryAllCategories.php42
-rw-r--r--includes/api/ApiQueryAllDeletedRevisions.php427
-rw-r--r--includes/api/ApiQueryAllImages.php99
-rw-r--r--includes/api/ApiQueryAllLinks.php95
-rw-r--r--includes/api/ApiQueryAllMessages.php43
-rw-r--r--includes/api/ApiQueryAllPages.php79
-rw-r--r--includes/api/ApiQueryAllUsers.php273
-rw-r--r--includes/api/ApiQueryBacklinks.php483
-rw-r--r--includes/api/ApiQueryBacklinksprop.php114
-rw-r--r--includes/api/ApiQueryBase.php42
-rw-r--r--includes/api/ApiQueryBlocks.php114
-rw-r--r--includes/api/ApiQueryCategories.php42
-rw-r--r--includes/api/ApiQueryCategoryInfo.php26
-rw-r--r--includes/api/ApiQueryCategoryMembers.php89
-rw-r--r--includes/api/ApiQueryContributors.php37
-rw-r--r--includes/api/ApiQueryDeletedRevisions.php304
-rw-r--r--includes/api/ApiQueryDeletedrevs.php158
-rw-r--r--includes/api/ApiQueryDisabled.php14
-rw-r--r--includes/api/ApiQueryDuplicateFiles.php33
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php60
-rw-r--r--includes/api/ApiQueryExternalLinks.php31
-rw-r--r--includes/api/ApiQueryFileRepoInfo.php27
-rw-r--r--includes/api/ApiQueryFilearchive.php69
-rw-r--r--includes/api/ApiQueryIWBacklinks.php39
-rw-r--r--includes/api/ApiQueryIWLinks.php51
-rw-r--r--includes/api/ApiQueryImageInfo.php121
-rw-r--r--includes/api/ApiQueryImages.php28
-rw-r--r--includes/api/ApiQueryInfo.php122
-rw-r--r--includes/api/ApiQueryLangBacklinks.php40
-rw-r--r--includes/api/ApiQueryLangLinks.php55
-rw-r--r--includes/api/ApiQueryLinks.php43
-rw-r--r--includes/api/ApiQueryLogEvents.php184
-rw-r--r--includes/api/ApiQueryORM.php11
-rw-r--r--includes/api/ApiQueryPagePropNames.php22
-rw-r--r--includes/api/ApiQueryPageProps.php22
-rw-r--r--includes/api/ApiQueryPagesWithProp.php39
-rw-r--r--includes/api/ApiQueryPrefixSearch.php40
-rw-r--r--includes/api/ApiQueryProtectedTitles.php40
-rw-r--r--includes/api/ApiQueryQueryPage.php28
-rw-r--r--includes/api/ApiQueryRandom.php27
-rw-r--r--includes/api/ApiQueryRecentChanges.php122
-rw-r--r--includes/api/ApiQueryRevisions.php616
-rw-r--r--includes/api/ApiQueryRevisionsBase.php477
-rw-r--r--includes/api/ApiQuerySearch.php160
-rw-r--r--includes/api/ApiQuerySiteinfo.php260
-rw-r--r--includes/api/ApiQueryStashImageInfo.php52
-rw-r--r--includes/api/ApiQueryTags.php169
-rw-r--r--includes/api/ApiQueryTokens.php30
-rw-r--r--includes/api/ApiQueryUserContributions.php92
-rw-r--r--includes/api/ApiQueryUserInfo.php89
-rw-r--r--includes/api/ApiQueryUsers.php69
-rw-r--r--includes/api/ApiQueryWatchlist.php122
-rw-r--r--includes/api/ApiQueryWatchlistRaw.php36
-rw-r--r--includes/api/ApiResult.php1553
-rw-r--r--includes/api/ApiRevisionDelete.php36
-rw-r--r--includes/api/ApiRollback.php36
-rw-r--r--includes/api/ApiRsd.php44
-rw-r--r--includes/api/ApiSerializable.php47
-rw-r--r--includes/api/ApiSetNotificationTimestamp.php67
-rw-r--r--includes/api/ApiStashEdit.php412
-rw-r--r--includes/api/ApiTag.php177
-rw-r--r--includes/api/ApiTokens.php39
-rw-r--r--includes/api/ApiUnblock.php24
-rw-r--r--includes/api/ApiUndelete.php36
-rw-r--r--includes/api/ApiUpload.php138
-rw-r--r--includes/api/ApiUserrights.php56
-rw-r--r--includes/api/ApiWatch.php58
-rw-r--r--includes/api/i18n/ar.json28
-rw-r--r--includes/api/i18n/av.json8
-rw-r--r--includes/api/i18n/awa.json11
-rw-r--r--includes/api/i18n/be-tarask.json55
-rw-r--r--includes/api/i18n/bn.json8
-rw-r--r--includes/api/i18n/bs.json16
-rw-r--r--includes/api/i18n/ca.json43
-rw-r--r--includes/api/i18n/ce.json12
-rw-r--r--includes/api/i18n/cs.json223
-rw-r--r--includes/api/i18n/cv.json8
-rw-r--r--includes/api/i18n/de.json433
-rw-r--r--includes/api/i18n/el.json12
-rw-r--r--includes/api/i18n/en-gb.json156
-rw-r--r--includes/api/i18n/en.json1169
-rw-r--r--includes/api/i18n/es.json206
-rw-r--r--includes/api/i18n/eu.json57
-rw-r--r--includes/api/i18n/fa.json240
-rw-r--r--includes/api/i18n/fi.json10
-rw-r--r--includes/api/i18n/fr.json1054
-rw-r--r--includes/api/i18n/frc.json19
-rw-r--r--includes/api/i18n/fy.json15
-rw-r--r--includes/api/i18n/gl.json1030
-rw-r--r--includes/api/i18n/he.json180
-rw-r--r--includes/api/i18n/hsb.json8
-rw-r--r--includes/api/i18n/hu.json17
-rw-r--r--includes/api/i18n/ia.json28
-rw-r--r--includes/api/i18n/it.json31
-rw-r--r--includes/api/i18n/ja.json213
-rw-r--r--includes/api/i18n/jam.json8
-rw-r--r--includes/api/i18n/ko.json36
-rw-r--r--includes/api/i18n/ksh.json311
-rw-r--r--includes/api/i18n/ku-latn.json9
-rw-r--r--includes/api/i18n/lb.json94
-rw-r--r--includes/api/i18n/ln.json8
-rw-r--r--includes/api/i18n/lv.json8
-rw-r--r--includes/api/i18n/lzh.json8
-rw-r--r--includes/api/i18n/mg.json11
-rw-r--r--includes/api/i18n/mk.json398
-rw-r--r--includes/api/i18n/ms.json61
-rw-r--r--includes/api/i18n/nap.json10
-rw-r--r--includes/api/i18n/nb.json24
-rw-r--r--includes/api/i18n/nds.json8
-rw-r--r--includes/api/i18n/nl.json61
-rw-r--r--includes/api/i18n/oc.json17
-rw-r--r--includes/api/i18n/pa.json8
-rw-r--r--includes/api/i18n/pam.json31
-rw-r--r--includes/api/i18n/pl.json117
-rw-r--r--includes/api/i18n/ps.json29
-rw-r--r--includes/api/i18n/pt-br.json14
-rw-r--r--includes/api/i18n/pt.json104
-rw-r--r--includes/api/i18n/qqq.json1069
-rw-r--r--includes/api/i18n/roa-tara.json11
-rw-r--r--includes/api/i18n/ru.json55
-rw-r--r--includes/api/i18n/si.json70
-rw-r--r--includes/api/i18n/sr-ec.json24
-rw-r--r--includes/api/i18n/sr-el.json11
-rw-r--r--includes/api/i18n/sv.json372
-rw-r--r--includes/api/i18n/te.json8
-rw-r--r--includes/api/i18n/tl.json35
-rw-r--r--includes/api/i18n/tr.json40
-rw-r--r--includes/api/i18n/uk.json30
-rw-r--r--includes/api/i18n/vi.json156
-rw-r--r--includes/api/i18n/zh-hans.json782
-rw-r--r--includes/api/i18n/zh-hant.json244
-rw-r--r--includes/cache/BacklinkCache.php60
-rw-r--r--includes/cache/CacheDependency.php18
-rw-r--r--includes/cache/HTMLFileCache.php2
-rw-r--r--includes/cache/LinkBatch.php4
-rw-r--r--includes/cache/LinkCache.php17
-rw-r--r--includes/cache/LocalisationCache.php71
-rw-r--r--includes/cache/MessageCache.php36
-rw-r--r--includes/cache/ResourceFileCache.php9
-rw-r--r--includes/cache/UserCache.php2
-rw-r--r--includes/cache/bloom/BloomCache.php323
-rw-r--r--includes/cache/bloom/BloomCacheRedis.php370
-rw-r--r--includes/cache/bloom/BloomFilters.php79
-rw-r--r--includes/changes/ChangesFeed.php4
-rw-r--r--includes/changes/ChangesList.php6
-rw-r--r--includes/changes/EnhancedChangesList.php213
-rw-r--r--includes/changes/OldChangesList.php9
-rw-r--r--includes/changes/RecentChange.php39
-rw-r--r--includes/changetags/ChangeTagsList.php77
-rw-r--r--includes/changetags/ChangeTagsLogItem.php100
-rw-r--r--includes/changetags/ChangeTagsLogList.php89
-rw-r--r--includes/changetags/ChangeTagsRevisionItem.php58
-rw-r--r--includes/changetags/ChangeTagsRevisionList.php99
-rw-r--r--includes/config/ConfigException.php2
-rw-r--r--includes/config/GlobalVarConfig.php21
-rw-r--r--includes/content/AbstractContent.php27
-rw-r--r--includes/content/CodeContentHandler.php1
-rw-r--r--includes/content/Content.php7
-rw-r--r--includes/content/ContentHandler.php23
-rw-r--r--includes/content/JavaScriptContentHandler.php8
-rw-r--r--includes/content/JsonContent.php192
-rw-r--r--includes/content/JsonContentHandler.php24
-rw-r--r--includes/content/TextContent.php1
-rw-r--r--includes/content/WikitextContent.php7
-rw-r--r--includes/context/ContextSource.php17
-rw-r--r--includes/context/DerivativeContext.php18
-rw-r--r--includes/context/IContextSource.php38
-rw-r--r--includes/context/RequestContext.php88
-rw-r--r--includes/db/Database.php856
-rw-r--r--includes/db/DatabaseError.php6
-rw-r--r--includes/db/DatabaseMssql.php86
-rw-r--r--includes/db/DatabaseMysql.php7
-rw-r--r--includes/db/DatabaseMysqlBase.php125
-rw-r--r--includes/db/DatabaseMysqli.php14
-rw-r--r--includes/db/DatabaseOracle.php18
-rw-r--r--includes/db/DatabasePostgres.php23
-rw-r--r--includes/db/DatabaseSqlite.php163
-rw-r--r--includes/db/DatabaseUtility.php27
-rw-r--r--includes/db/IORMTable.php18
-rw-r--r--includes/db/LBFactory.php78
-rw-r--r--includes/db/LBFactoryMulti.php58
-rw-r--r--includes/db/LBFactorySingle.php20
-rw-r--r--includes/db/LoadBalancer.php390
-rw-r--r--includes/db/LoadMonitor.php4
-rw-r--r--includes/db/ORMTable.php50
-rw-r--r--includes/debug/MWDebug.php33
-rw-r--r--includes/debug/logger/LegacyLogger.php380
-rw-r--r--includes/debug/logger/LegacySpi.php59
-rw-r--r--includes/debug/logger/LoggerFactory.php121
-rw-r--r--includes/debug/logger/MonologSpi.php251
-rw-r--r--includes/debug/logger/NullSpi.php64
-rw-r--r--includes/debug/logger/Spi.php47
-rw-r--r--includes/debug/logger/monolog/LegacyFormatter.php48
-rw-r--r--includes/debug/logger/monolog/LegacyHandler.php243
-rw-r--r--includes/debug/logger/monolog/SyslogHandler.php96
-rw-r--r--includes/debug/logger/monolog/WikiProcessor.php47
-rw-r--r--includes/deferred/DeferredUpdates.php6
-rw-r--r--includes/deferred/HTMLCacheUpdate.php5
-rw-r--r--includes/deferred/LinksUpdate.php33
-rw-r--r--includes/deferred/SearchUpdate.php7
-rw-r--r--includes/deferred/SiteStatsUpdate.php9
-rw-r--r--includes/deferred/SqlDataUpdate.php85
-rw-r--r--includes/deferred/SquidUpdate.php43
-rw-r--r--includes/deferred/ViewCountUpdate.php119
-rw-r--r--includes/diff/DairikiDiff.php18
-rw-r--r--includes/diff/DiffFormatter.php4
-rw-r--r--includes/diff/DifferenceEngine.php61
-rw-r--r--includes/diff/TableDiffFormatter.php4
-rw-r--r--includes/exception/HttpError.php42
-rw-r--r--includes/exception/MWException.php23
-rw-r--r--includes/exception/MWExceptionHandler.php201
-rw-r--r--includes/exception/TimestampException.php (renamed from includes/TimestampException.php)0
-rw-r--r--includes/exception/UserNotLoggedIn.php10
-rw-r--r--includes/externalstore/ExternalStore.php2
-rw-r--r--includes/externalstore/ExternalStoreHttp.php2
-rw-r--r--includes/filebackend/FSFile.php7
-rw-r--r--includes/filebackend/FSFileBackend.php8
-rw-r--r--includes/filebackend/FileBackend.php4
-rw-r--r--includes/filebackend/FileBackendMultiWrite.php6
-rw-r--r--includes/filebackend/FileBackendStore.php87
-rw-r--r--includes/filebackend/FileOpBatch.php1
-rw-r--r--includes/filebackend/SwiftFileBackend.php81
-rw-r--r--includes/filebackend/TempFSFile.php3
-rw-r--r--includes/filebackend/filejournal/FileJournal.php4
-rw-r--r--includes/filebackend/lockmanager/DBLockManager.php4
-rw-r--r--includes/filebackend/lockmanager/LockManager.php10
-rw-r--r--includes/filebackend/lockmanager/LockManagerGroup.php16
-rw-r--r--includes/filebackend/lockmanager/MemcLockManager.php6
-rw-r--r--includes/filebackend/lockmanager/RedisLockManager.php2
-rw-r--r--includes/filerepo/FileRepo.php64
-rw-r--r--includes/filerepo/FileRepoStatus.php36
-rw-r--r--includes/filerepo/ForeignAPIRepo.php2
-rw-r--r--includes/filerepo/RepoGroup.php6
-rw-r--r--includes/filerepo/file/ArchivedFile.php12
-rw-r--r--includes/filerepo/file/File.php51
-rw-r--r--includes/filerepo/file/LocalFile.php194
-rw-r--r--includes/filerepo/file/OldLocalFile.php14
-rw-r--r--includes/filerepo/file/UnregisteredLocalFile.php12
-rw-r--r--includes/gallery/ImageGalleryBase.php20
-rw-r--r--includes/gallery/TraditionalImageGallery.php4
-rw-r--r--includes/htmlform/HTMLCheckField.php46
-rw-r--r--includes/htmlform/HTMLCheckMatrix.php14
-rw-r--r--includes/htmlform/HTMLFloatField.php2
-rw-r--r--includes/htmlform/HTMLForm.php191
-rw-r--r--includes/htmlform/HTMLFormField.php50
-rw-r--r--includes/htmlform/HTMLFormFieldCloner.php42
-rw-r--r--includes/htmlform/HTMLHiddenField.php18
-rw-r--r--includes/htmlform/HTMLIntField.php5
-rw-r--r--includes/htmlform/HTMLMultiSelectField.php8
-rw-r--r--includes/htmlform/HTMLRadioField.php2
-rw-r--r--includes/htmlform/HTMLSelectAndOtherField.php11
-rw-r--r--includes/htmlform/HTMLSelectNamespace.php18
-rw-r--r--includes/htmlform/HTMLTagFilter.php31
-rw-r--r--includes/htmlform/HTMLTextField.php7
-rw-r--r--includes/htmlform/VFormHTMLForm.php141
-rw-r--r--includes/installer/DatabaseInstaller.php15
-rw-r--r--includes/installer/DatabaseUpdater.php60
-rw-r--r--includes/installer/InstallDocFormatter.php34
-rw-r--r--includes/installer/Installer.php87
-rw-r--r--includes/installer/LocalSettingsGenerator.php39
-rw-r--r--includes/installer/MssqlInstaller.php4
-rw-r--r--includes/installer/MssqlUpdater.php5
-rw-r--r--includes/installer/MysqlInstaller.php9
-rw-r--r--includes/installer/MysqlUpdater.php45
-rw-r--r--includes/installer/OracleUpdater.php6
-rw-r--r--includes/installer/PostgresInstaller.php16
-rw-r--r--includes/installer/PostgresUpdater.php6
-rw-r--r--includes/installer/SqliteInstaller.php72
-rw-r--r--includes/installer/SqliteUpdater.php13
-rw-r--r--includes/installer/WebInstaller.php27
-rw-r--r--includes/installer/WebInstallerOutput.php37
-rw-r--r--includes/installer/WebInstallerPage.php21
-rw-r--r--includes/installer/i18n/ar.json8
-rw-r--r--includes/installer/i18n/ast.json9
-rw-r--r--includes/installer/i18n/av.json5
-rw-r--r--includes/installer/i18n/az.json2
-rw-r--r--includes/installer/i18n/ba.json21
-rw-r--r--includes/installer/i18n/be-tarask.json2
-rw-r--r--includes/installer/i18n/bg.json11
-rw-r--r--includes/installer/i18n/bgn.json26
-rw-r--r--includes/installer/i18n/bn.json13
-rw-r--r--includes/installer/i18n/bs.json33
-rw-r--r--includes/installer/i18n/ca.json49
-rw-r--r--includes/installer/i18n/ckb.json19
-rw-r--r--includes/installer/i18n/cs.json2
-rw-r--r--includes/installer/i18n/cv.json10
-rw-r--r--includes/installer/i18n/cy.json3
-rw-r--r--includes/installer/i18n/de.json24
-rw-r--r--includes/installer/i18n/diq.json2
-rw-r--r--includes/installer/i18n/el.json12
-rw-r--r--includes/installer/i18n/eml.json16
-rw-r--r--includes/installer/i18n/en-gb.json6
-rw-r--r--includes/installer/i18n/en.json9
-rw-r--r--includes/installer/i18n/es.json142
-rw-r--r--includes/installer/i18n/et.json16
-rw-r--r--includes/installer/i18n/eu.json5
-rw-r--r--includes/installer/i18n/fa.json40
-rw-r--r--includes/installer/i18n/fi.json51
-rw-r--r--includes/installer/i18n/fr.json5
-rw-r--r--includes/installer/i18n/frc.json21
-rw-r--r--includes/installer/i18n/fy.json14
-rw-r--r--includes/installer/i18n/gl.json2
-rw-r--r--includes/installer/i18n/gor.json48
-rw-r--r--includes/installer/i18n/he.json2
-rw-r--r--includes/installer/i18n/hi.json16
-rw-r--r--includes/installer/i18n/hu.json13
-rw-r--r--includes/installer/i18n/ia.json13
-rw-r--r--includes/installer/i18n/id.json6
-rw-r--r--includes/installer/i18n/it.json7
-rw-r--r--includes/installer/i18n/ja.json1
-rw-r--r--includes/installer/i18n/ko.json76
-rw-r--r--includes/installer/i18n/ksh.json31
-rw-r--r--includes/installer/i18n/ku-latn.json1
-rw-r--r--includes/installer/i18n/lb.json3
-rw-r--r--includes/installer/i18n/lzh.json1
-rw-r--r--includes/installer/i18n/mai.json26
-rw-r--r--includes/installer/i18n/mfe.json45
-rw-r--r--includes/installer/i18n/mk.json7
-rw-r--r--includes/installer/i18n/ms.json11
-rw-r--r--includes/installer/i18n/nap.json196
-rw-r--r--includes/installer/i18n/nb.json26
-rw-r--r--includes/installer/i18n/ne.json6
-rw-r--r--includes/installer/i18n/nl.json28
-rw-r--r--includes/installer/i18n/oc.json3
-rw-r--r--includes/installer/i18n/pa.json24
-rw-r--r--includes/installer/i18n/pl.json7
-rw-r--r--includes/installer/i18n/pms.json10
-rw-r--r--includes/installer/i18n/pt-br.json8
-rw-r--r--includes/installer/i18n/pt.json2
-rw-r--r--includes/installer/i18n/qqq.json3
-rw-r--r--includes/installer/i18n/ro.json22
-rw-r--r--includes/installer/i18n/roa-tara.json15
-rw-r--r--includes/installer/i18n/ru.json9
-rw-r--r--includes/installer/i18n/sco.json13
-rw-r--r--includes/installer/i18n/si.json4
-rw-r--r--includes/installer/i18n/sk.json5
-rw-r--r--includes/installer/i18n/sl.json16
-rw-r--r--includes/installer/i18n/sr-ec.json17
-rw-r--r--includes/installer/i18n/sr-el.json1
-rw-r--r--includes/installer/i18n/su.json8
-rw-r--r--includes/installer/i18n/sv.json8
-rw-r--r--includes/installer/i18n/te.json2
-rw-r--r--includes/installer/i18n/th.json90
-rw-r--r--includes/installer/i18n/tl.json2
-rw-r--r--includes/installer/i18n/tr.json7
-rw-r--r--includes/installer/i18n/uk.json2
-rw-r--r--includes/installer/i18n/vi.json137
-rw-r--r--includes/installer/i18n/yi.json5
-rw-r--r--includes/installer/i18n/zh-hans.json11
-rw-r--r--includes/installer/i18n/zh-hant.json23
-rw-r--r--includes/interwiki/Interwiki.php4
-rw-r--r--includes/jobqueue/Job.php120
-rw-r--r--includes/jobqueue/JobQueue.php83
-rw-r--r--includes/jobqueue/JobQueueDB.php6
-rw-r--r--includes/jobqueue/JobQueueFederated.php131
-rw-r--r--includes/jobqueue/JobQueueGroup.php14
-rw-r--r--includes/jobqueue/JobQueueRedis.php255
-rw-r--r--includes/jobqueue/JobRunner.php86
-rw-r--r--includes/jobqueue/JobSpecification.php36
-rw-r--r--includes/jobqueue/aggregator/JobQueueAggregator.php28
-rw-r--r--includes/jobqueue/aggregator/JobQueueAggregatorMemc.php125
-rw-r--r--includes/jobqueue/aggregator/JobQueueAggregatorRedis.php2
-rw-r--r--includes/jobqueue/jobs/AssembleUploadChunksJob.php19
-rw-r--r--includes/jobqueue/jobs/DuplicateJob.php2
-rw-r--r--includes/jobqueue/jobs/EnqueueJob.php88
-rw-r--r--includes/jobqueue/jobs/HTMLCacheUpdateJob.php46
-rw-r--r--includes/jobqueue/jobs/NullJob.php2
-rw-r--r--includes/jobqueue/jobs/PublishStashedFileJob.php22
-rw-r--r--includes/jobqueue/jobs/RecentChangesUpdateJob.php223
-rw-r--r--includes/jobqueue/jobs/RefreshLinksJob.php16
-rw-r--r--includes/jobqueue/jobs/RefreshLinksJob2.php141
-rw-r--r--includes/jobqueue/jobs/ThumbnailRenderJob.php109
-rw-r--r--includes/jobqueue/jobs/UploadFromUrlJob.php2
-rw-r--r--includes/json/FormatJson.php102
-rw-r--r--includes/libs/APACHE-LICENSE-2.0.txt202
-rw-r--r--includes/libs/ArrayUtils.php (renamed from includes/utils/ArrayUtils.php)4
-rw-r--r--includes/libs/BufferingStatsdDataFactory.php59
-rw-r--r--includes/libs/CSSJanus.php458
-rw-r--r--includes/libs/CSSMin.php151
-rw-r--r--includes/libs/Cookie.php (renamed from includes/Cookie.php)4
-rw-r--r--includes/libs/DeferredStringifier.php57
-rw-r--r--includes/libs/ExplodeIterator.php116
-rw-r--r--includes/libs/GenericArrayObject.php6
-rw-r--r--includes/libs/IPSet.php3
-rw-r--r--includes/libs/MapCacheLRU.php (renamed from includes/cache/MapCacheLRU.php)12
-rw-r--r--includes/libs/MessageSpecifier.php39
-rw-r--r--includes/libs/MultiHttpClient.php28
-rw-r--r--includes/libs/ObjectFactory.php93
-rw-r--r--includes/libs/ProcessCacheLRU.php39
-rw-r--r--includes/libs/ReplacementArray.php125
-rw-r--r--includes/libs/RunningStat.php8
-rw-r--r--includes/libs/ScopedCallback.php12
-rw-r--r--includes/libs/StatusValue.php316
-rw-r--r--includes/libs/StringUtils.php (renamed from includes/utils/StringUtils.php)301
-rw-r--r--includes/libs/UDPTransport.php102
-rw-r--r--includes/libs/Xhprof.php445
-rw-r--r--includes/libs/XmlTypeCheck.php2
-rw-r--r--includes/libs/composer/ComposerJson.php54
-rw-r--r--includes/libs/composer/ComposerLock.php38
-rw-r--r--includes/libs/jsminplus.php2
-rw-r--r--includes/libs/lessc.inc.php3796
-rw-r--r--includes/libs/normal/UtfNormal.php129
-rw-r--r--includes/libs/normal/UtfNormalDefines.php (renamed from includes/normal/UtfNormalDefines.php)119
-rw-r--r--includes/libs/normal/UtfNormalUtil.php (renamed from includes/normal/UtfNormalUtil.php)83
-rw-r--r--includes/libs/objectcache/APCBagOStuff.php (renamed from includes/objectcache/APCBagOStuff.php)41
-rw-r--r--includes/libs/objectcache/BagOStuff.php (renamed from includes/objectcache/BagOStuff.php)104
-rw-r--r--includes/libs/objectcache/EmptyBagOStuff.php (renamed from includes/objectcache/EmptyBagOStuff.php)45
-rw-r--r--includes/libs/objectcache/HashBagOStuff.php (renamed from includes/objectcache/HashBagOStuff.php)54
-rw-r--r--includes/libs/objectcache/WinCacheBagOStuff.php (renamed from includes/objectcache/WinCacheBagOStuff.php)13
-rw-r--r--includes/libs/objectcache/XCacheBagOStuff.php (renamed from includes/objectcache/XCacheBagOStuff.php)30
-rw-r--r--includes/libs/replacers/DoubleReplacer.php43
-rw-r--r--includes/libs/replacers/HashtableReplacer.php44
-rw-r--r--includes/libs/replacers/RegexlikeReplacer.php46
-rw-r--r--includes/libs/replacers/Replacer.php (renamed from includes/resourceloader/ResourceLoaderFilePageModule.php)21
-rw-r--r--includes/libs/virtualrest/ParsoidVirtualRESTService.php126
-rw-r--r--includes/libs/virtualrest/RestbaseVirtualRESTService.php177
-rw-r--r--includes/libs/virtualrest/VirtualRESTServiceClient.php31
-rw-r--r--includes/logging/BlockLogFormatter.php224
-rw-r--r--includes/logging/DeleteLogFormatter.php78
-rw-r--r--includes/logging/LogEntry.php38
-rw-r--r--includes/logging/LogEventsList.php76
-rw-r--r--includes/logging/LogFormatter.php253
-rw-r--r--includes/logging/LogPage.php156
-rw-r--r--includes/logging/LogPager.php2
-rw-r--r--includes/logging/MergeLogFormatter.php91
-rw-r--r--includes/logging/MoveLogFormatter.php29
-rw-r--r--includes/logging/PatrolLogFormatter.php20
-rw-r--r--includes/logging/RightsLogFormatter.php65
-rw-r--r--includes/logging/TagLogFormatter.php49
-rw-r--r--includes/logging/UploadLogFormatter.php49
-rw-r--r--includes/mail/EmailNotification.php10
-rw-r--r--includes/mail/MailAddress.php2
-rw-r--r--includes/mail/UserMailer.php4
-rw-r--r--includes/media/BMP.php2
-rw-r--r--includes/media/Bitmap.php14
-rw-r--r--includes/media/BitmapMetadataHandler.php2
-rw-r--r--includes/media/DjVu.php5
-rw-r--r--includes/media/DjVuImage.php16
-rw-r--r--includes/media/Exif.php2
-rw-r--r--includes/media/ExifBitmap.php5
-rw-r--r--includes/media/FormatMetadata.php98
-rw-r--r--includes/media/GIF.php5
-rw-r--r--includes/media/GIFMetadataExtractor.php2
-rw-r--r--includes/media/IPTC.php2
-rw-r--r--includes/media/ImageHandler.php6
-rw-r--r--includes/media/Jpeg.php4
-rw-r--r--includes/media/JpegMetadataExtractor.php4
-rw-r--r--includes/media/MediaHandler.php34
-rw-r--r--includes/media/MediaTransformInvalidParametersException.php26
-rw-r--r--includes/media/MediaTransformOutput.php40
-rw-r--r--includes/media/PNG.php10
-rw-r--r--includes/media/SVG.php9
-rw-r--r--includes/media/SVGMetadataExtractor.php20
-rw-r--r--includes/media/Tiff.php2
-rw-r--r--includes/media/TransformationalImageHandler.php75
-rw-r--r--includes/media/XCF.php2
-rw-r--r--includes/media/XMP.php13
-rw-r--r--includes/media/XMPInfo.php2
-rw-r--r--includes/mime.info1
-rw-r--r--includes/mime.types3
-rw-r--r--includes/normal/Makefile78
-rw-r--r--includes/normal/README59
-rw-r--r--includes/normal/RandomTest.php102
-rw-r--r--includes/normal/Utf8Test.php156
-rw-r--r--includes/normal/UtfNormal.php790
-rw-r--r--includes/normal/UtfNormalBench.php105
-rw-r--r--includes/normal/UtfNormalData.inc14
-rw-r--r--includes/normal/UtfNormalDataK.inc11
-rw-r--r--includes/normal/UtfNormalGenerate.php250
-rw-r--r--includes/normal/UtfNormalMemStress.php107
-rw-r--r--includes/normal/UtfNormalTest.php257
-rw-r--r--includes/normal/UtfNormalTest2.php275
-rw-r--r--includes/objectcache/MemcachedBagOStuff.php19
-rw-r--r--includes/objectcache/MemcachedClient.php20
-rw-r--r--includes/objectcache/MemcachedPeclBagOStuff.php25
-rw-r--r--includes/objectcache/MemcachedPhpBagOStuff.php18
-rw-r--r--includes/objectcache/MultiWriteBagOStuff.php30
-rw-r--r--includes/objectcache/ObjectCache.php11
-rw-r--r--includes/objectcache/ObjectCacheSessionHandler.php38
-rw-r--r--includes/objectcache/RedisBagOStuff.php24
-rw-r--r--includes/objectcache/SqlBagOStuff.php75
-rw-r--r--includes/page/Article.php350
-rw-r--r--includes/page/CategoryPage.php6
-rw-r--r--includes/page/ImagePage.php100
-rw-r--r--includes/page/WikiPage.php476
-rw-r--r--includes/pager/IndexPager.php5
-rw-r--r--includes/parser/CacheTime.php26
-rw-r--r--includes/parser/CoreParserFunctions.php92
-rw-r--r--includes/parser/CoreTagHooks.php27
-rw-r--r--includes/parser/DateFormatter.php15
-rw-r--r--includes/parser/LinkHolderArray.php25
-rw-r--r--includes/parser/MWTidy.php76
-rw-r--r--includes/parser/Parser.php686
-rw-r--r--includes/parser/ParserCache.php7
-rw-r--r--includes/parser/ParserOptions.php97
-rw-r--r--includes/parser/ParserOutput.php141
-rw-r--r--includes/parser/Preprocessor_DOM.php30
-rw-r--r--includes/parser/Preprocessor_Hash.php20
-rw-r--r--includes/parser/StripState.php2
-rw-r--r--includes/password/PasswordFactory.php15
-rw-r--r--includes/poolcounter/PoolCounter.php57
-rw-r--r--includes/poolcounter/PoolCounterRedis.php17
-rw-r--r--includes/poolcounter/PoolWorkArticleView.php7
-rw-r--r--includes/profiler/ProfileSection.php43
-rw-r--r--includes/profiler/Profiler.php499
-rw-r--r--includes/profiler/ProfilerFunctions.php56
-rw-r--r--includes/profiler/ProfilerMwprof.php256
-rw-r--r--includes/profiler/ProfilerSectionOnly.php104
-rw-r--r--includes/profiler/ProfilerSimpleText.php82
-rw-r--r--includes/profiler/ProfilerSimpleTrace.php85
-rw-r--r--includes/profiler/ProfilerSimpleUDP.php75
-rw-r--r--includes/profiler/ProfilerStandard.php559
-rw-r--r--includes/profiler/ProfilerStub.php25
-rw-r--r--includes/profiler/ProfilerXhprof.php194
-rw-r--r--includes/profiler/SectionProfiler.php530
-rw-r--r--includes/profiler/TransactionProfiler.php274
-rw-r--r--includes/profiler/output/ProfilerOutput.php57
-rw-r--r--includes/profiler/output/ProfilerOutputDb.php (renamed from includes/profiler/ProfilerSimpleDB.php)87
-rw-r--r--includes/profiler/output/ProfilerOutputDump.php51
-rw-r--r--includes/profiler/output/ProfilerOutputStats.php57
-rw-r--r--includes/profiler/output/ProfilerOutputText.php77
-rw-r--r--includes/profiler/output/ProfilerOutputUdp.php96
-rw-r--r--includes/rcfeed/IRCColourfulRCFeedFormatter.php2
-rw-r--r--includes/rcfeed/UDPRCFeedEngine.php3
-rw-r--r--includes/registration/ExtensionProcessor.php299
-rw-r--r--includes/registration/ExtensionRegistry.php254
-rw-r--r--includes/registration/Processor.php27
-rw-r--r--includes/resourceloader/DerivativeResourceLoaderContext.php1
-rw-r--r--includes/resourceloader/ResourceLoader.php323
-rw-r--r--includes/resourceloader/ResourceLoaderContext.php91
-rw-r--r--includes/resourceloader/ResourceLoaderEditToolbarModule.php1
-rw-r--r--includes/resourceloader/ResourceLoaderFileModule.php109
-rw-r--r--includes/resourceloader/ResourceLoaderImage.php388
-rw-r--r--includes/resourceloader/ResourceLoaderImageModule.php327
-rw-r--r--includes/resourceloader/ResourceLoaderLanguageDataModule.php12
-rw-r--r--includes/resourceloader/ResourceLoaderLanguageNamesModule.php14
-rw-r--r--includes/resourceloader/ResourceLoaderModule.php87
-rw-r--r--includes/resourceloader/ResourceLoaderSiteModule.php13
-rw-r--r--includes/resourceloader/ResourceLoaderSkinModule.php92
-rw-r--r--includes/resourceloader/ResourceLoaderSpecialCharacterDataModule.php107
-rw-r--r--includes/resourceloader/ResourceLoaderStartUpModule.php118
-rw-r--r--includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php14
-rw-r--r--includes/resourceloader/ResourceLoaderUserDefaultsModule.php62
-rw-r--r--includes/resourceloader/ResourceLoaderUserGroupsModule.php26
-rw-r--r--includes/resourceloader/ResourceLoaderUserModule.php36
-rw-r--r--includes/resourceloader/ResourceLoaderUserOptionsModule.php15
-rw-r--r--includes/resourceloader/ResourceLoaderUserTokensModule.php13
-rw-r--r--includes/resourceloader/ResourceLoaderWikiModule.php90
-rw-r--r--includes/revisiondelete/RevDelArchiveList.php2
-rw-r--r--includes/revisiondelete/RevDelArchivedFileList.php2
-rw-r--r--includes/revisiondelete/RevDelFileList.php2
-rw-r--r--includes/revisiondelete/RevDelList.php28
-rw-r--r--includes/revisiondelete/RevDelLogItem.php10
-rw-r--r--includes/revisiondelete/RevDelLogList.php8
-rw-r--r--includes/revisiondelete/RevDelRevisionList.php4
-rw-r--r--includes/revisiondelete/RevisionDeleteUser.php4
-rw-r--r--includes/revisiondelete/RevisionDeleter.php3
-rw-r--r--includes/search/SearchEngine.php64
-rw-r--r--includes/search/SearchHighlighter.php26
-rw-r--r--includes/search/SearchMySQL.php4
-rw-r--r--includes/search/SearchPostgres.php2
-rw-r--r--includes/search/SearchResult.php9
-rw-r--r--includes/site/CachingSiteStore.php195
-rw-r--r--includes/site/DBSiteStore.php345
-rw-r--r--includes/site/FileBasedSiteLookup.php139
-rw-r--r--includes/site/HashSiteStore.php123
-rw-r--r--includes/site/MediaWikiSite.php4
-rw-r--r--includes/site/SiteExporter.php114
-rw-r--r--includes/site/SiteImporter.php263
-rw-r--r--includes/site/SiteLookup.php (renamed from includes/resourceloader/ResourceLoaderNoscriptModule.php)44
-rw-r--r--includes/site/SiteSQLStore.php459
-rw-r--r--includes/site/SiteStore.php29
-rw-r--r--includes/site/SitesCacheFileBuilder.php113
-rw-r--r--includes/skins/BaseTemplate.php644
-rw-r--r--includes/skins/MediaWikiI18N.php52
-rw-r--r--includes/skins/QuickTemplate.php194
-rw-r--r--includes/skins/Skin.php176
-rw-r--r--includes/skins/SkinApi.php71
-rw-r--r--includes/skins/SkinApiTemplate.php63
-rw-r--r--includes/skins/SkinFactory.php110
-rw-r--r--includes/skins/SkinFallbackTemplate.php29
-rw-r--r--includes/skins/SkinTemplate.php908
-rw-r--r--includes/specialpage/ChangesListSpecialPage.php18
-rw-r--r--includes/specialpage/FormSpecialPage.php29
-rw-r--r--includes/specialpage/ImageQueryPage.php4
-rw-r--r--includes/specialpage/PageQueryPage.php2
-rw-r--r--includes/specialpage/QueryPage.php17
-rw-r--r--includes/specialpage/RedirectSpecialPage.php2
-rw-r--r--includes/specialpage/SpecialPage.php55
-rw-r--r--includes/specialpage/SpecialPageFactory.php40
-rw-r--r--includes/specialpage/WantedQueryPage.php4
-rw-r--r--includes/specials/SpecialActiveusers.php174
-rw-r--r--includes/specials/SpecialAllMessages.php14
-rw-r--r--includes/specials/SpecialAllPages.php5
-rw-r--r--includes/specials/SpecialApiHelp.php93
-rw-r--r--includes/specials/SpecialBlock.php54
-rw-r--r--includes/specials/SpecialBlockList.php17
-rw-r--r--includes/specials/SpecialBooksources.php23
-rw-r--r--includes/specials/SpecialCategories.php6
-rw-r--r--includes/specials/SpecialChangeEmail.php34
-rw-r--r--includes/specials/SpecialChangePassword.php14
-rw-r--r--includes/specials/SpecialConfirmemail.php3
-rw-r--r--includes/specials/SpecialContributions.php59
-rw-r--r--includes/specials/SpecialDeletedContributions.php113
-rw-r--r--includes/specials/SpecialDiff.php1
-rw-r--r--includes/specials/SpecialEditTags.php460
-rw-r--r--includes/specials/SpecialEditWatchlist.php42
-rw-r--r--includes/specials/SpecialEmailuser.php12
-rw-r--r--includes/specials/SpecialExpandTemplates.php5
-rw-r--r--includes/specials/SpecialExport.php9
-rw-r--r--includes/specials/SpecialFileDuplicateSearch.php52
-rw-r--r--includes/specials/SpecialFilepath.php1
-rw-r--r--includes/specials/SpecialImport.php43
-rw-r--r--includes/specials/SpecialJavaScriptTest.php33
-rw-r--r--includes/specials/SpecialLinkSearch.php52
-rw-r--r--includes/specials/SpecialListDuplicatedFiles.php2
-rw-r--r--includes/specials/SpecialListfiles.php12
-rw-r--r--includes/specials/SpecialListgrouprights.php78
-rw-r--r--includes/specials/SpecialListredirects.php2
-rw-r--r--includes/specials/SpecialListusers.php71
-rw-r--r--includes/specials/SpecialLog.php115
-rw-r--r--includes/specials/SpecialLonelypages.php2
-rw-r--r--includes/specials/SpecialMediaStatistics.php22
-rw-r--r--includes/specials/SpecialMergeHistory.php27
-rw-r--r--includes/specials/SpecialMostcategories.php2
-rw-r--r--includes/specials/SpecialMostimages.php2
-rw-r--r--includes/specials/SpecialMostinterwikis.php2
-rw-r--r--includes/specials/SpecialMostlinked.php2
-rw-r--r--includes/specials/SpecialMostlinkedcategories.php2
-rw-r--r--includes/specials/SpecialMostlinkedtemplates.php2
-rw-r--r--includes/specials/SpecialMovepage.php226
-rw-r--r--includes/specials/SpecialMyLanguage.php5
-rw-r--r--includes/specials/SpecialNewimages.php14
-rw-r--r--includes/specials/SpecialNewpages.php135
-rw-r--r--includes/specials/SpecialPageLanguage.php8
-rw-r--r--includes/specials/SpecialPagesWithProp.php49
-rw-r--r--includes/specials/SpecialPasswordReset.php15
-rw-r--r--includes/specials/SpecialPopularpages.php89
-rw-r--r--includes/specials/SpecialPreferences.php2
-rw-r--r--includes/specials/SpecialPrefixindex.php5
-rw-r--r--includes/specials/SpecialProtectedpages.php17
-rw-r--r--includes/specials/SpecialProtectedtitles.php14
-rw-r--r--includes/specials/SpecialRandomInCategory.php10
-rw-r--r--includes/specials/SpecialRandompage.php2
-rw-r--r--includes/specials/SpecialRecentchanges.php23
-rw-r--r--includes/specials/SpecialRedirect.php15
-rw-r--r--includes/specials/SpecialResetTokens.php2
-rw-r--r--includes/specials/SpecialRevisiondelete.php34
-rw-r--r--includes/specials/SpecialRunJobs.php11
-rw-r--r--includes/specials/SpecialSearch.php131
-rw-r--r--includes/specials/SpecialShortpages.php2
-rw-r--r--includes/specials/SpecialSpecialpages.php1
-rw-r--r--includes/specials/SpecialStatistics.php100
-rw-r--r--includes/specials/SpecialTags.php336
-rw-r--r--includes/specials/SpecialTrackingCategories.php142
-rw-r--r--includes/specials/SpecialUnblock.php11
-rw-r--r--includes/specials/SpecialUndelete.php32
-rw-r--r--includes/specials/SpecialUpload.php99
-rw-r--r--includes/specials/SpecialUploadStash.php14
-rw-r--r--includes/specials/SpecialUserlogin.php103
-rw-r--r--includes/specials/SpecialUserlogout.php2
-rw-r--r--includes/specials/SpecialUserrights.php65
-rw-r--r--includes/specials/SpecialVersion.php151
-rw-r--r--includes/specials/SpecialWantedcategories.php7
-rw-r--r--includes/specials/SpecialWantedfiles.php4
-rw-r--r--includes/specials/SpecialWantedpages.php7
-rw-r--r--includes/specials/SpecialWatchlist.php60
-rw-r--r--includes/specials/SpecialWhatlinkshere.php67
-rw-r--r--includes/templates/NoLocalSettings.mustache39
-rw-r--r--includes/templates/Usercreate.php25
-rw-r--r--includes/templates/Userlogin.php75
-rw-r--r--includes/title/ForeignTitle.php117
-rw-r--r--includes/title/ForeignTitleFactory.php36
-rw-r--r--includes/title/ImportTitleFactory.php36
-rw-r--r--includes/title/MalformedTitleException.php1
-rw-r--r--includes/title/MediaWikiPageLinkRenderer.php1
-rw-r--r--includes/title/MediaWikiTitleCodec.php34
-rw-r--r--includes/title/NaiveForeignTitleFactory.php71
-rw-r--r--includes/title/NaiveImportTitleFactory.php65
-rw-r--r--includes/title/NamespaceAwareForeignTitleFactory.php134
-rw-r--r--includes/title/NamespaceImportTitleFactory.php52
-rw-r--r--includes/title/PageLinkRenderer.php1
-rw-r--r--includes/title/SubpageImportTitleFactory.php55
-rw-r--r--includes/title/TitleFormatter.php1
-rw-r--r--includes/title/TitleParser.php1
-rw-r--r--includes/title/TitleValue.php1
-rw-r--r--includes/upload/UploadBase.php161
-rw-r--r--includes/upload/UploadFromChunks.php20
-rw-r--r--includes/upload/UploadFromUrl.php26
-rw-r--r--includes/upload/UploadStash.php4
-rw-r--r--includes/utils/AutoloadGenerator.php296
-rw-r--r--includes/utils/Cdb.php163
-rw-r--r--includes/utils/CdbDBA.php75
-rw-r--r--includes/utils/CdbPHP.php494
-rw-r--r--includes/utils/IP.php23
-rw-r--r--includes/utils/MWCryptHKDF.php11
-rw-r--r--includes/utils/MWCryptRand.php11
-rw-r--r--includes/utils/MWFunction.php37
-rw-r--r--includes/utils/UIDGenerator.php6
-rw-r--r--includes/utils/ZipDirectoryReader.php1
827 files changed, 52489 insertions, 33391 deletions
diff --git a/includes/AjaxDispatcher.php b/includes/AjaxDispatcher.php
index 9bc92be9..b14114d7 100644
--- a/includes/AjaxDispatcher.php
+++ b/includes/AjaxDispatcher.php
@@ -56,8 +56,6 @@ class AjaxDispatcher {
* Load up our object with user supplied data
*/
function __construct( Config $config ) {
- wfProfileIn( __METHOD__ );
-
$this->config = $config;
$this->mode = "";
@@ -88,13 +86,11 @@ class AjaxDispatcher {
}
break;
default:
- wfProfileOut( __METHOD__ );
return;
# Or we could throw an exception:
# throw new MWException( __METHOD__ . ' called without any data (mode empty).' );
}
- wfProfileOut( __METHOD__ );
}
/**
@@ -110,11 +106,8 @@ class AjaxDispatcher {
return;
}
- wfProfileIn( __METHOD__ );
-
if ( !in_array( $this->func_name, $this->config->get( 'AjaxExportList' ) ) ) {
wfDebug( __METHOD__ . ' Bad Request for unknown function ' . $this->func_name . "\n" );
-
wfHttpError(
400,
'Bad Request',
@@ -127,7 +120,6 @@ class AjaxDispatcher {
'You are not allowed to view pages.' );
} else {
wfDebug( __METHOD__ . ' dispatching ' . $this->func_name . "\n" );
-
try {
$result = call_user_func_array( $this->func_name, $this->args );
@@ -162,6 +154,5 @@ class AjaxDispatcher {
}
}
- wfProfileOut( __METHOD__ );
}
}
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index 6b0daa14..6344c276 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -25,1199 +25,7 @@
* Extension classes are specified with $wgAutoloadClasses
* This array is a global instead of a static member of AutoLoader to work around a bug in APC
*/
-global $wgAutoloadLocalClasses;
-
-$wgAutoloadLocalClasses = array(
- # Includes
- 'AjaxDispatcher' => 'includes/AjaxDispatcher.php',
- 'AjaxResponse' => 'includes/AjaxResponse.php',
- 'AtomFeed' => 'includes/Feed.php',
- 'AuthPlugin' => 'includes/AuthPlugin.php',
- 'AuthPluginUser' => 'includes/AuthPlugin.php',
- 'Autopromote' => 'includes/Autopromote.php',
- 'Block' => 'includes/Block.php',
- 'BloomCache' => 'includes/cache/bloom/BloomCache.php',
- 'BloomCacheRedis' => 'includes/cache/bloom/BloomCacheRedis.php',
- 'BloomFilterTitleHasLogs' => 'includes/cache/bloom/BloomFilters.php',
- 'CacheHelper' => 'includes/CacheHelper.php',
- 'Category' => 'includes/Category.php',
- 'CategoryFinder' => 'includes/CategoryFinder.php',
- 'CategoryViewer' => 'includes/CategoryViewer.php',
- 'ChangeTags' => 'includes/ChangeTags.php',
- 'ChannelFeed' => 'includes/Feed.php',
- 'Collation' => 'includes/Collation.php',
- 'CollationCkb' => 'includes/Collation.php',
- 'CollationEt' => 'includes/Collation.php',
- 'ConcatenatedGzipHistoryBlob' => 'includes/HistoryBlob.php',
- 'Cookie' => 'includes/Cookie.php',
- 'CookieJar' => 'includes/Cookie.php',
- 'CurlHttpRequest' => 'includes/HttpFunctions.php',
- 'DeprecatedGlobal' => 'includes/DeprecatedGlobal.php',
- 'DerivativeRequest' => 'includes/WebRequest.php',
- 'DiffHistoryBlob' => 'includes/HistoryBlob.php',
- 'DummyLinker' => 'includes/Linker.php',
- 'Dump7ZipOutput' => 'includes/Export.php',
- 'DumpBZip2Output' => 'includes/Export.php',
- 'DumpFileOutput' => 'includes/Export.php',
- 'DumpFilter' => 'includes/Export.php',
- 'DumpGZipOutput' => 'includes/Export.php',
- 'DumpLatestFilter' => 'includes/Export.php',
- 'DumpMultiWriter' => 'includes/Export.php',
- 'DumpNamespaceFilter' => 'includes/Export.php',
- 'DumpNotalkFilter' => 'includes/Export.php',
- 'DumpOutput' => 'includes/Export.php',
- 'DumpPipeOutput' => 'includes/Export.php',
- 'EditPage' => 'includes/EditPage.php',
- 'EmptyBloomCache' => 'includes/cache/bloom/BloomCache.php',
- 'Fallback' => 'includes/Fallback.php',
- 'FauxRequest' => 'includes/WebRequest.php',
- 'FauxResponse' => 'includes/WebResponse.php',
- 'FeedItem' => 'includes/Feed.php',
- 'FeedUtils' => 'includes/FeedUtils.php',
- 'FileDeleteForm' => 'includes/FileDeleteForm.php',
- 'ForkController' => 'includes/ForkController.php',
- 'FormOptions' => 'includes/FormOptions.php',
- 'GitInfo' => 'includes/GitInfo.php',
- 'HistoryBlob' => 'includes/HistoryBlob.php',
- 'HistoryBlobCurStub' => 'includes/HistoryBlob.php',
- 'HistoryBlobStub' => 'includes/HistoryBlob.php',
- 'Hooks' => 'includes/Hooks.php',
- 'Html' => 'includes/Html.php',
- 'HtmlFormatter' => 'includes/HtmlFormatter.php',
- 'HTMLApiField' => 'includes/htmlform/HTMLApiField.php',
- 'HTMLAutoCompleteSelectField' => 'includes/htmlform/HTMLAutoCompleteSelectField.php',
- 'HTMLButtonField' => 'includes/htmlform/HTMLButtonField.php',
- 'HTMLCheckField' => 'includes/htmlform/HTMLCheckField.php',
- 'HTMLCheckMatrix' => 'includes/htmlform/HTMLCheckMatrix.php',
- 'HTMLFormFieldCloner' => 'includes/htmlform/HTMLFormFieldCloner.php',
- 'HTMLEditTools' => 'includes/htmlform/HTMLEditTools.php',
- 'HTMLFloatField' => 'includes/htmlform/HTMLFloatField.php',
- 'HTMLForm' => 'includes/htmlform/HTMLForm.php',
- 'HTMLFormField' => 'includes/htmlform/HTMLFormField.php',
- 'HTMLFormFieldRequiredOptionsException' =>
- 'includes/htmlform/HTMLFormFieldRequiredOptionsException.php',
- 'HTMLHiddenField' => 'includes/htmlform/HTMLHiddenField.php',
- 'HTMLInfoField' => 'includes/htmlform/HTMLInfoField.php',
- 'HTMLIntField' => 'includes/htmlform/HTMLIntField.php',
- 'HTMLNestedFilterable' => 'includes/htmlform/HTMLNestedFilterable.php',
- 'HTMLMultiSelectField' => 'includes/htmlform/HTMLMultiSelectField.php',
- 'HTMLRadioField' => 'includes/htmlform/HTMLRadioField.php',
- 'HTMLSelectAndOtherField' => 'includes/htmlform/HTMLSelectAndOtherField.php',
- 'HTMLSelectField' => 'includes/htmlform/HTMLSelectField.php',
- 'HTMLSelectLimitField' => 'includes/htmlform/HTMLSelectLimitField.php',
- 'HTMLSelectOrOtherField' => 'includes/htmlform/HTMLSelectOrOtherField.php',
- 'HTMLSubmitField' => 'includes/htmlform/HTMLSubmitField.php',
- 'HTMLTextAreaField' => 'includes/htmlform/HTMLTextAreaField.php',
- 'HTMLTextField' => 'includes/htmlform/HTMLTextField.php',
- 'Http' => 'includes/HttpFunctions.php',
- 'IcuCollation' => 'includes/Collation.php',
- 'IdentityCollation' => 'includes/Collation.php',
- 'ImportStreamSource' => 'includes/Import.php',
- 'ImportStringSource' => 'includes/Import.php',
- 'Interwiki' => 'includes/interwiki/Interwiki.php',
- 'License' => 'includes/Licenses.php',
- 'Licenses' => 'includes/Licenses.php',
- 'Linker' => 'includes/Linker.php',
- 'LinkFilter' => 'includes/LinkFilter.php',
- 'MagicWord' => 'includes/MagicWord.php',
- 'MagicWordArray' => 'includes/MagicWord.php',
- 'MediaWiki' => 'includes/MediaWiki.php',
- 'MediaWikiVersionFetcher' => 'includes/MediaWikiVersionFetcher.php',
- 'Message' => 'includes/Message.php',
- 'MessageBlobStore' => 'includes/MessageBlobStore.php',
- 'MimeMagic' => 'includes/MimeMagic.php',
- 'MovePage' => 'includes/MovePage.php',
- 'MWHookException' => 'includes/Hooks.php',
- 'MWHttpRequest' => 'includes/HttpFunctions.php',
- 'MWNamespace' => 'includes/MWNamespace.php',
- 'OutputPage' => 'includes/OutputPage.php',
- 'PathRouter' => 'includes/PathRouter.php',
- 'PathRouterPatternReplacer' => 'includes/PathRouter.php',
- 'PhpHttpRequest' => 'includes/HttpFunctions.php',
- 'PoolCounter' => 'includes/poolcounter/PoolCounter.php',
- 'PoolCounter_Stub' => 'includes/poolcounter/PoolCounter.php',
- 'PoolCounterRedis' => 'includes/poolcounter/PoolCounterRedis.php',
- 'PoolCounterWork' => 'includes/poolcounter/PoolCounterWork.php',
- 'PoolCounterWorkViaCallback' => 'includes/poolcounter/PoolCounterWorkViaCallback.php',
- 'PoolWorkArticleView' => 'includes/poolcounter/PoolWorkArticleView.php',
- 'Preferences' => 'includes/Preferences.php',
- 'PreferencesForm' => 'includes/Preferences.php',
- 'PrefixSearch' => 'includes/PrefixSearch.php',
- 'ProtectionForm' => 'includes/ProtectionForm.php',
- 'RawMessage' => 'includes/Message.php',
- 'RevisionItem' => 'includes/RevisionList.php',
- 'RevisionItemBase' => 'includes/RevisionList.php',
- 'RevisionListBase' => 'includes/RevisionList.php',
- 'Revision' => 'includes/Revision.php',
- 'RevisionList' => 'includes/RevisionList.php',
- 'RSSFeed' => 'includes/Feed.php',
- 'Sanitizer' => 'includes/Sanitizer.php',
- 'SiteConfiguration' => 'includes/SiteConfiguration.php',
- 'SiteStats' => 'includes/SiteStats.php',
- 'SiteStatsInit' => 'includes/SiteStats.php',
- 'SquidPurgeClient' => 'includes/SquidPurgeClient.php',
- 'SquidPurgeClientPool' => 'includes/SquidPurgeClient.php',
- 'StatCounter' => 'includes/StatCounter.php',
- 'Status' => 'includes/Status.php',
- 'StreamFile' => 'includes/StreamFile.php',
- 'StringPrefixSearch' => 'includes/PrefixSearch.php',
- 'StubObject' => 'includes/StubObject.php',
- 'StubUserLang' => 'includes/StubObject.php',
- 'MWTimestamp' => 'includes/MWTimestamp.php',
- 'TimestampException' => 'includes/TimestampException.php',
- 'Title' => 'includes/Title.php',
- 'TitleArray' => 'includes/TitleArray.php',
- 'TitleArrayFromResult' => 'includes/TitleArrayFromResult.php',
- 'TitlePrefixSearch' => 'includes/PrefixSearch.php',
- 'UploadSourceAdapter' => 'includes/Import.php',
- 'UppercaseCollation' => 'includes/Collation.php',
- 'User' => 'includes/User.php',
- 'UserArray' => 'includes/UserArray.php',
- 'UserArrayFromResult' => 'includes/UserArrayFromResult.php',
- 'UserRightsProxy' => 'includes/UserRightsProxy.php',
- 'WatchedItem' => 'includes/WatchedItem.php',
- 'WebRequest' => 'includes/WebRequest.php',
- 'WebRequestUpload' => 'includes/WebRequest.php',
- 'WebResponse' => 'includes/WebResponse.php',
- 'WikiExporter' => 'includes/Export.php',
- 'WikiImporter' => 'includes/Import.php',
- 'WikiRevision' => 'includes/Import.php',
- 'WikiMap' => 'includes/WikiMap.php',
- 'WikiReference' => 'includes/WikiMap.php',
- 'Xml' => 'includes/Xml.php',
- 'XmlDumpWriter' => 'includes/Export.php',
- 'XmlJsCode' => 'includes/Xml.php',
- 'XmlSelect' => 'includes/Xml.php',
-
- # includes/actions
- 'Action' => 'includes/actions/Action.php',
- 'CachedAction' => 'includes/actions/CachedAction.php',
- 'CreditsAction' => 'includes/actions/CreditsAction.php',
- 'DeleteAction' => 'includes/actions/DeleteAction.php',
- 'EditAction' => 'includes/actions/EditAction.php',
- 'FormlessAction' => 'includes/actions/FormlessAction.php',
- 'FormAction' => 'includes/actions/FormAction.php',
- 'HistoryAction' => 'includes/actions/HistoryAction.php',
- 'HistoryPager' => 'includes/actions/HistoryAction.php',
- 'InfoAction' => 'includes/actions/InfoAction.php',
- 'MarkpatrolledAction' => 'includes/actions/MarkpatrolledAction.php',
- 'ProtectAction' => 'includes/actions/ProtectAction.php',
- 'PurgeAction' => 'includes/actions/PurgeAction.php',
- 'RawAction' => 'includes/actions/RawAction.php',
- 'RenderAction' => 'includes/actions/RenderAction.php',
- 'RevertAction' => 'includes/actions/RevertAction.php',
- 'RevisiondeleteAction' => 'includes/actions/RevisiondeleteAction.php',
- 'RollbackAction' => 'includes/actions/RollbackAction.php',
- 'SubmitAction' => 'includes/actions/SubmitAction.php',
- 'UnprotectAction' => 'includes/actions/UnprotectAction.php',
- 'UnwatchAction' => 'includes/actions/UnwatchAction.php',
- 'ViewAction' => 'includes/actions/ViewAction.php',
- 'WatchAction' => 'includes/actions/WatchAction.php',
-
- # includes/api
- 'ApiBase' => 'includes/api/ApiBase.php',
- 'ApiBlock' => 'includes/api/ApiBlock.php',
- 'ApiClearHasMsg' => 'includes/api/ApiClearHasMsg.php',
- 'ApiComparePages' => 'includes/api/ApiComparePages.php',
- 'ApiCreateAccount' => 'includes/api/ApiCreateAccount.php',
- 'ApiDelete' => 'includes/api/ApiDelete.php',
- 'ApiDisabled' => 'includes/api/ApiDisabled.php',
- 'ApiEditPage' => 'includes/api/ApiEditPage.php',
- 'ApiEmailUser' => 'includes/api/ApiEmailUser.php',
- 'ApiExpandTemplates' => 'includes/api/ApiExpandTemplates.php',
- 'ApiFeedContributions' => 'includes/api/ApiFeedContributions.php',
- 'ApiFeedRecentChanges' => 'includes/api/ApiFeedRecentChanges.php',
- 'ApiFeedWatchlist' => 'includes/api/ApiFeedWatchlist.php',
- 'ApiFileRevert' => 'includes/api/ApiFileRevert.php',
- 'ApiFormatBase' => 'includes/api/ApiFormatBase.php',
- 'ApiFormatDbg' => 'includes/api/ApiFormatDbg.php',
- 'ApiFormatDump' => 'includes/api/ApiFormatDump.php',
- 'ApiFormatFeedWrapper' => 'includes/api/ApiFormatFeedWrapper.php',
- 'ApiFormatJson' => 'includes/api/ApiFormatJson.php',
- 'ApiFormatNone' => 'includes/api/ApiFormatNone.php',
- 'ApiFormatPhp' => 'includes/api/ApiFormatPhp.php',
- 'ApiFormatRaw' => 'includes/api/ApiFormatRaw.php',
- 'ApiFormatTxt' => 'includes/api/ApiFormatTxt.php',
- 'ApiFormatWddx' => 'includes/api/ApiFormatWddx.php',
- 'ApiFormatXml' => 'includes/api/ApiFormatXml.php',
- 'ApiFormatXmlRsd' => 'includes/api/ApiRsd.php',
- 'ApiFormatYaml' => 'includes/api/ApiFormatYaml.php',
- 'ApiHelp' => 'includes/api/ApiHelp.php',
- 'ApiImageRotate' => 'includes/api/ApiImageRotate.php',
- 'ApiImport' => 'includes/api/ApiImport.php',
- 'ApiImportReporter' => 'includes/api/ApiImport.php',
- 'ApiLogin' => 'includes/api/ApiLogin.php',
- 'ApiLogout' => 'includes/api/ApiLogout.php',
- 'ApiMain' => 'includes/api/ApiMain.php',
- 'ApiModuleManager' => 'includes/api/ApiModuleManager.php',
- 'ApiMove' => 'includes/api/ApiMove.php',
- 'ApiOpenSearch' => 'includes/api/ApiOpenSearch.php',
- 'ApiOptions' => 'includes/api/ApiOptions.php',
- 'ApiPageSet' => 'includes/api/ApiPageSet.php',
- 'ApiParamInfo' => 'includes/api/ApiParamInfo.php',
- 'ApiParse' => 'includes/api/ApiParse.php',
- 'ApiPatrol' => 'includes/api/ApiPatrol.php',
- 'ApiProtect' => 'includes/api/ApiProtect.php',
- 'ApiPurge' => 'includes/api/ApiPurge.php',
- 'ApiQuery' => 'includes/api/ApiQuery.php',
- 'ApiQueryAllCategories' => 'includes/api/ApiQueryAllCategories.php',
- 'ApiQueryAllImages' => 'includes/api/ApiQueryAllImages.php',
- 'ApiQueryAllLinks' => 'includes/api/ApiQueryAllLinks.php',
- 'ApiQueryAllMessages' => 'includes/api/ApiQueryAllMessages.php',
- 'ApiQueryAllPages' => 'includes/api/ApiQueryAllPages.php',
- 'ApiQueryAllUsers' => 'includes/api/ApiQueryAllUsers.php',
- 'ApiQueryBacklinks' => 'includes/api/ApiQueryBacklinks.php',
- 'ApiQueryBacklinksprop' => 'includes/api/ApiQueryBacklinksprop.php',
- 'ApiQueryBase' => 'includes/api/ApiQueryBase.php',
- 'ApiQueryBlocks' => 'includes/api/ApiQueryBlocks.php',
- 'ApiQueryCategories' => 'includes/api/ApiQueryCategories.php',
- 'ApiQueryCategoryInfo' => 'includes/api/ApiQueryCategoryInfo.php',
- 'ApiQueryCategoryMembers' => 'includes/api/ApiQueryCategoryMembers.php',
- 'ApiQueryContributions' => 'includes/api/ApiQueryUserContributions.php',
- 'ApiQueryContributors' => 'includes/api/ApiQueryContributors.php',
- 'ApiQueryDeletedrevs' => 'includes/api/ApiQueryDeletedrevs.php',
- 'ApiQueryDisabled' => 'includes/api/ApiQueryDisabled.php',
- 'ApiQueryDuplicateFiles' => 'includes/api/ApiQueryDuplicateFiles.php',
- 'ApiQueryExternalLinks' => 'includes/api/ApiQueryExternalLinks.php',
- 'ApiQueryExtLinksUsage' => 'includes/api/ApiQueryExtLinksUsage.php',
- 'ApiQueryFilearchive' => 'includes/api/ApiQueryFilearchive.php',
- 'ApiQueryGeneratorBase' => 'includes/api/ApiQueryBase.php',
- 'ApiQueryImageInfo' => 'includes/api/ApiQueryImageInfo.php',
- 'ApiQueryImages' => 'includes/api/ApiQueryImages.php',
- 'ApiQueryInfo' => 'includes/api/ApiQueryInfo.php',
- 'ApiQueryIWBacklinks' => 'includes/api/ApiQueryIWBacklinks.php',
- 'ApiQueryIWLinks' => 'includes/api/ApiQueryIWLinks.php',
- 'ApiQueryLangBacklinks' => 'includes/api/ApiQueryLangBacklinks.php',
- 'ApiQueryLangLinks' => 'includes/api/ApiQueryLangLinks.php',
- 'ApiQueryLinks' => 'includes/api/ApiQueryLinks.php',
- 'ApiQueryLogEvents' => 'includes/api/ApiQueryLogEvents.php',
- 'ApiQueryORM' => 'includes/api/ApiQueryORM.php',
- 'ApiQueryPageProps' => 'includes/api/ApiQueryPageProps.php',
- 'ApiQueryPagesWithProp' => 'includes/api/ApiQueryPagesWithProp.php',
- 'ApiQueryPagePropNames' => 'includes/api/ApiQueryPagePropNames.php',
- 'ApiQueryPrefixSearch' => 'includes/api/ApiQueryPrefixSearch.php',
- 'ApiQueryProtectedTitles' => 'includes/api/ApiQueryProtectedTitles.php',
- 'ApiQueryQueryPage' => 'includes/api/ApiQueryQueryPage.php',
- 'ApiQueryRandom' => 'includes/api/ApiQueryRandom.php',
- 'ApiQueryRecentChanges' => 'includes/api/ApiQueryRecentChanges.php',
- 'ApiQueryFileRepoInfo' => 'includes/api/ApiQueryFileRepoInfo.php',
- 'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php',
- 'ApiQuerySearch' => 'includes/api/ApiQuerySearch.php',
- 'ApiQuerySiteinfo' => 'includes/api/ApiQuerySiteinfo.php',
- 'ApiQueryStashImageInfo' => 'includes/api/ApiQueryStashImageInfo.php',
- 'ApiQueryTags' => 'includes/api/ApiQueryTags.php',
- 'ApiQueryTokens' => 'includes/api/ApiQueryTokens.php',
- 'ApiQueryUserInfo' => 'includes/api/ApiQueryUserInfo.php',
- 'ApiQueryUsers' => 'includes/api/ApiQueryUsers.php',
- 'ApiQueryWatchlist' => 'includes/api/ApiQueryWatchlist.php',
- 'ApiQueryWatchlistRaw' => 'includes/api/ApiQueryWatchlistRaw.php',
- 'ApiResult' => 'includes/api/ApiResult.php',
- 'ApiRevisionDelete' => 'includes/api/ApiRevisionDelete.php',
- 'ApiRollback' => 'includes/api/ApiRollback.php',
- 'ApiRsd' => 'includes/api/ApiRsd.php',
- 'ApiSetNotificationTimestamp' => 'includes/api/ApiSetNotificationTimestamp.php',
- 'ApiTokens' => 'includes/api/ApiTokens.php',
- 'ApiUnblock' => 'includes/api/ApiUnblock.php',
- 'ApiUndelete' => 'includes/api/ApiUndelete.php',
- 'ApiUpload' => 'includes/api/ApiUpload.php',
- 'ApiUserrights' => 'includes/api/ApiUserrights.php',
- 'ApiWatch' => 'includes/api/ApiWatch.php',
- 'UsageException' => 'includes/api/ApiMain.php',
-
- # includes/cache
- 'BacklinkCache' => 'includes/cache/BacklinkCache.php',
- 'CacheDependency' => 'includes/cache/CacheDependency.php',
- 'CacheHelper' => 'includes/cache/CacheHelper.php',
- 'ConstantDependency' => 'includes/cache/CacheDependency.php',
- 'DependencyWrapper' => 'includes/cache/CacheDependency.php',
- 'FileCacheBase' => 'includes/cache/FileCacheBase.php',
- 'FileDependency' => 'includes/cache/CacheDependency.php',
- 'GenderCache' => 'includes/cache/GenderCache.php',
- 'GlobalDependency' => 'includes/cache/CacheDependency.php',
- 'HTMLFileCache' => 'includes/cache/HTMLFileCache.php',
- 'ICacheHelper' => 'includes/cache/CacheHelper.php',
- 'LCStore' => 'includes/cache/LocalisationCache.php',
- 'LCStoreCDB' => 'includes/cache/LocalisationCache.php',
- 'LCStoreDB' => 'includes/cache/LocalisationCache.php',
- 'LCStoreNull' => 'includes/cache/LocalisationCache.php',
- 'LinkBatch' => 'includes/cache/LinkBatch.php',
- 'LinkCache' => 'includes/cache/LinkCache.php',
- 'LocalisationCache' => 'includes/cache/LocalisationCache.php',
- 'LocalisationCacheBulkLoad' => 'includes/cache/LocalisationCache.php',
- 'MapCacheLRU' => 'includes/cache/MapCacheLRU.php',
- 'MessageCache' => 'includes/cache/MessageCache.php',
- 'ObjectFileCache' => 'includes/cache/ObjectFileCache.php',
- 'ResourceFileCache' => 'includes/cache/ResourceFileCache.php',
- 'UserCache' => 'includes/cache/UserCache.php',
-
- # includes/changes
- 'ChangesFeed' => 'includes/changes/ChangesFeed.php',
- 'ChangesList' => 'includes/changes/ChangesList.php',
- 'EnhancedChangesList' => 'includes/changes/EnhancedChangesList.php',
- 'OldChangesList' => 'includes/changes/OldChangesList.php',
- 'RCCacheEntry' => 'includes/changes/RCCacheEntry.php',
- 'RCCacheEntryFactory' => 'includes/changes/RCCacheEntryFactory.php',
- 'RecentChange' => 'includes/changes/RecentChange.php',
-
- # includes/clientpool
- 'RedisConnectionPool' => 'includes/clientpool/RedisConnectionPool.php',
- 'RedisConnRef' => 'includes/clientpool/RedisConnectionPool.php',
-
- # includes/composer
- 'ComposerPackageModifier' => 'includes/composer/ComposerPackageModifier.php',
- 'ComposerVersionNormalizer' => 'includes/composer/ComposerVersionNormalizer.php',
-
- # includes/config
- 'Config' => 'includes/config/Config.php',
- 'ConfigException' => 'includes/config/ConfigException.php',
- 'ConfigFactory' => 'includes/config/ConfigFactory.php',
- 'GlobalVarConfig' => 'includes/config/GlobalVarConfig.php',
- 'HashConfig' => 'includes/config/HashConfig.php',
- 'MultiConfig' => 'includes/config/MultiConfig.php',
- 'MutableConfig' => 'includes/config/MutableConfig.php',
-
- # includes/content
- 'AbstractContent' => 'includes/content/AbstractContent.php',
- 'CodeContentHandler' => 'includes/content/CodeContentHandler.php',
- 'Content' => 'includes/content/Content.php',
- 'ContentHandler' => 'includes/content/ContentHandler.php',
- 'CssContent' => 'includes/content/CssContent.php',
- 'CssContentHandler' => 'includes/content/CssContentHandler.php',
- 'JavaScriptContent' => 'includes/content/JavaScriptContent.php',
- 'JavaScriptContentHandler' => 'includes/content/JavaScriptContentHandler.php',
- 'JsonContent' => 'includes/content/JsonContent.php',
- 'JsonContentHandler' => 'includes/content/JsonContentHandler.php',
- 'MessageContent' => 'includes/content/MessageContent.php',
- 'MWContentSerializationException' => 'includes/content/ContentHandler.php',
- 'TextContent' => 'includes/content/TextContent.php',
- 'TextContentHandler' => 'includes/content/TextContentHandler.php',
- 'WikitextContent' => 'includes/content/WikitextContent.php',
- 'WikitextContentHandler' => 'includes/content/WikitextContentHandler.php',
-
- # includes/context
- 'ContextSource' => 'includes/context/ContextSource.php',
- 'DerivativeContext' => 'includes/context/DerivativeContext.php',
- 'IContextSource' => 'includes/context/IContextSource.php',
- 'RequestContext' => 'includes/context/RequestContext.php',
-
- # includes/dao
- 'IDBAccessObject' => 'includes/dao/IDBAccessObject.php',
- 'DBAccessBase' => 'includes/dao/DBAccessBase.php',
-
- # includes/db
- 'Blob' => 'includes/db/DatabaseUtility.php',
- 'ChronologyProtector' => 'includes/db/ChronologyProtector.php',
- 'CloneDatabase' => 'includes/db/CloneDatabase.php',
- 'DatabaseBase' => 'includes/db/Database.php',
- 'DatabaseMssql' => 'includes/db/DatabaseMssql.php',
- 'DatabaseMysql' => 'includes/db/DatabaseMysql.php',
- 'DatabaseMysqlBase' => 'includes/db/DatabaseMysqlBase.php',
- 'DatabaseMysqli' => 'includes/db/DatabaseMysqli.php',
- 'DatabaseOracle' => 'includes/db/DatabaseOracle.php',
- 'DatabasePostgres' => 'includes/db/DatabasePostgres.php',
- 'DatabaseSqlite' => 'includes/db/DatabaseSqlite.php',
- 'DatabaseSqliteStandalone' => 'includes/db/DatabaseSqlite.php',
- 'DatabaseType' => 'includes/db/Database.php',
- 'DBAccessError' => 'includes/db/LBFactory.php',
- 'DBConnectionError' => 'includes/db/DatabaseError.php',
- 'DBConnRef' => 'includes/db/LoadBalancer.php',
- 'DBError' => 'includes/db/DatabaseError.php',
- 'DBExpectedError' => 'includes/db/DatabaseError.php',
- 'DBObject' => 'includes/db/DatabaseUtility.php',
- 'IDatabase' => 'includes/db/Database.php',
- 'IORMRow' => 'includes/db/IORMRow.php',
- 'IORMTable' => 'includes/db/IORMTable.php',
- 'DBMasterPos' => 'includes/db/DatabaseUtility.php',
- 'DBQueryError' => 'includes/db/DatabaseError.php',
- 'DBUnexpectedError' => 'includes/db/DatabaseError.php',
- 'FakeResultWrapper' => 'includes/db/DatabaseUtility.php',
- 'Field' => 'includes/db/DatabaseUtility.php',
- 'LBFactory' => 'includes/db/LBFactory.php',
- 'LBFactoryFake' => 'includes/db/LBFactory.php',
- 'LBFactoryMulti' => 'includes/db/LBFactoryMulti.php',
- 'LBFactorySimple' => 'includes/db/LBFactory.php',
- 'LBFactorySingle' => 'includes/db/LBFactorySingle.php',
- 'LikeMatch' => 'includes/db/DatabaseUtility.php',
- 'LoadBalancer' => 'includes/db/LoadBalancer.php',
- 'LoadBalancerSingle' => 'includes/db/LBFactorySingle.php',
- 'LoadMonitor' => 'includes/db/LoadMonitor.php',
- 'LoadMonitorMySQL' => 'includes/db/LoadMonitor.php',
- 'LoadMonitorNull' => 'includes/db/LoadMonitor.php',
- 'MssqlField' => 'includes/db/DatabaseMssql.php',
- 'MssqlBlob' => 'includes/db/DatabaseMssql.php',
- 'MssqlResultWrapper' => 'includes/db/DatabaseMssql.php',
- 'MySQLField' => 'includes/db/DatabaseMysqlBase.php',
- 'MySQLMasterPos' => 'includes/db/DatabaseMysqlBase.php',
- 'ORAField' => 'includes/db/DatabaseOracle.php',
- 'ORAResult' => 'includes/db/DatabaseOracle.php',
- 'ORMIterator' => 'includes/db/ORMIterator.php',
- 'ORMResult' => 'includes/db/ORMResult.php',
- 'ORMRow' => 'includes/db/ORMRow.php',
- 'ORMTable' => 'includes/db/ORMTable.php',
- 'PostgresField' => 'includes/db/DatabasePostgres.php',
- 'PostgresTransactionState' => 'includes/db/DatabasePostgres.php',
- 'ResultWrapper' => 'includes/db/DatabaseUtility.php',
- 'SavepointPostgres' => 'includes/db/DatabasePostgres.php',
- 'SQLiteField' => 'includes/db/DatabaseSqlite.php',
-
- # includes/debug
- 'MWDebug' => 'includes/debug/MWDebug.php',
-
- # includes/deferred
- 'DataUpdate' => 'includes/deferred/DataUpdate.php',
- 'DeferrableUpdate' => 'includes/deferred/DeferredUpdates.php',
- 'DeferredUpdates' => 'includes/deferred/DeferredUpdates.php',
- 'HTMLCacheUpdate' => 'includes/deferred/HTMLCacheUpdate.php',
- 'LinksDeletionUpdate' => 'includes/deferred/LinksUpdate.php',
- 'LinksUpdate' => 'includes/deferred/LinksUpdate.php',
- 'MWCallableUpdate' => 'includes/deferred/CallableUpdate.php',
- 'SearchUpdate' => 'includes/deferred/SearchUpdate.php',
- 'SiteStatsUpdate' => 'includes/deferred/SiteStatsUpdate.php',
- 'SqlDataUpdate' => 'includes/deferred/SqlDataUpdate.php',
- 'SquidUpdate' => 'includes/deferred/SquidUpdate.php',
- 'ViewCountUpdate' => 'includes/deferred/ViewCountUpdate.php',
-
- # includes/diff
- 'DiffEngine' => 'includes/diff/DairikiDiff.php',
- 'DiffOp' => 'includes/diff/DairikiDiff.php',
- 'DiffOpAdd' => 'includes/diff/DairikiDiff.php',
- 'DiffOpChange' => 'includes/diff/DairikiDiff.php',
- 'DiffOpCopy' => 'includes/diff/DairikiDiff.php',
- 'DiffOpDelete' => 'includes/diff/DairikiDiff.php',
- 'HWLDFWordAccumulator' => 'includes/diff/DairikiDiff.php',
- 'ArrayDiffFormatter' => 'includes/diff/ArrayDiffFormatter.php',
- 'Diff' => 'includes/diff/DairikiDiff.php',
- 'DifferenceEngine' => 'includes/diff/DifferenceEngine.php',
- 'DiffFormatter' => 'includes/diff/DiffFormatter.php',
- 'MappedDiff' => 'includes/diff/DairikiDiff.php',
- 'RangeDifference' => 'includes/diff/WikiDiff3.php',
- 'TableDiffFormatter' => 'includes/diff/TableDiffFormatter.php',
- 'UnifiedDiffFormatter' => 'includes/diff/UnifiedDiffFormatter.php',
- 'WikiDiff3' => 'includes/diff/WikiDiff3.php',
- 'WordLevelDiff' => 'includes/diff/DairikiDiff.php',
-
- # includes/exception
- 'UserBlockedError' => 'includes/exception/UserBlockedError.php',
- 'UserNotLoggedIn' => 'includes/exception/UserNotLoggedIn.php',
- 'ThrottledError' => 'includes/exception/ThrottledError.php',
- 'ReadOnlyError' => 'includes/exception/ReadOnlyError.php',
- 'PermissionsError' => 'includes/exception/PermissionsError.php',
- 'MWException' => 'includes/exception/MWException.php',
- 'MWExceptionHandler' => 'includes/exception/MWExceptionHandler.php',
- 'HttpError' => 'includes/exception/HttpError.php',
- 'BadTitleError' => 'includes/exception/BadTitleError.php',
- 'ErrorPageError' => 'includes/exception/ErrorPageError.php',
- 'FatalError' => 'includes/exception/FatalError.php',
-
- # includes/externalstore
- 'ExternalStore' => 'includes/externalstore/ExternalStore.php',
- 'ExternalStoreDB' => 'includes/externalstore/ExternalStoreDB.php',
- 'ExternalStoreHttp' => 'includes/externalstore/ExternalStoreHttp.php',
- 'ExternalStoreMedium' => 'includes/externalstore/ExternalStoreMedium.php',
- 'ExternalStoreMwstore' => 'includes/externalstore/ExternalStoreMwstore.php',
-
- # includes/filebackend
- 'FileBackendGroup' => 'includes/filebackend/FileBackendGroup.php',
- 'FileBackend' => 'includes/filebackend/FileBackend.php',
- 'FileBackendError' => 'includes/filebackend/FileBackend.php',
- 'FileBackendException' => 'includes/filebackend/FileBackend.php',
- 'FileBackendStore' => 'includes/filebackend/FileBackendStore.php',
- 'FileBackendStoreShardListIterator' => 'includes/filebackend/FileBackendStore.php',
- 'FileBackendStoreShardDirIterator' => 'includes/filebackend/FileBackendStore.php',
- 'FileBackendStoreShardFileIterator' => 'includes/filebackend/FileBackendStore.php',
- 'FileBackendMultiWrite' => 'includes/filebackend/FileBackendMultiWrite.php',
- 'FileBackendStoreOpHandle' => 'includes/filebackend/FileBackendStore.php',
- 'FSFile' => 'includes/filebackend/FSFile.php',
- 'FSFileBackend' => 'includes/filebackend/FSFileBackend.php',
- 'FSFileBackendList' => 'includes/filebackend/FSFileBackend.php',
- 'FSFileBackendDirList' => 'includes/filebackend/FSFileBackend.php',
- 'FSFileBackendFileList' => 'includes/filebackend/FSFileBackend.php',
- 'FSFileOpHandle' => 'includes/filebackend/FSFileBackend.php',
- 'MemoryFileBackend' => 'includes/filebackend/MemoryFileBackend.php',
- 'SwiftFileBackend' => 'includes/filebackend/SwiftFileBackend.php',
- 'SwiftFileBackendList' => 'includes/filebackend/SwiftFileBackend.php',
- 'SwiftFileBackendDirList' => 'includes/filebackend/SwiftFileBackend.php',
- 'SwiftFileBackendFileList' => 'includes/filebackend/SwiftFileBackend.php',
- 'SwiftFileOpHandle' => 'includes/filebackend/SwiftFileBackend.php',
- 'TempFSFile' => 'includes/filebackend/TempFSFile.php',
- 'FileJournal' => 'includes/filebackend/filejournal/FileJournal.php',
- 'DBFileJournal' => 'includes/filebackend/filejournal/DBFileJournal.php',
- 'NullFileJournal' => 'includes/filebackend/filejournal/FileJournal.php',
- 'LockManagerGroup' => 'includes/filebackend/lockmanager/LockManagerGroup.php',
- 'LockManager' => 'includes/filebackend/lockmanager/LockManager.php',
- 'ScopedLock' => 'includes/filebackend/lockmanager/ScopedLock.php',
- 'FSLockManager' => 'includes/filebackend/lockmanager/FSLockManager.php',
- 'DBLockManager' => 'includes/filebackend/lockmanager/DBLockManager.php',
- 'MemcLockManager' => 'includes/filebackend/lockmanager/MemcLockManager.php',
- 'QuorumLockManager' => 'includes/filebackend/lockmanager/QuorumLockManager.php',
- 'MySqlLockManager' => 'includes/filebackend/lockmanager/DBLockManager.php',
- 'PostgreSqlLockManager' => 'includes/filebackend/lockmanager/DBLockManager.php',
- 'RedisLockManager' => 'includes/filebackend/lockmanager/RedisLockManager.php',
- 'NullLockManager' => 'includes/filebackend/lockmanager/LockManager.php',
- 'FileOp' => 'includes/filebackend/FileOp.php',
- 'FileOpBatch' => 'includes/filebackend/FileOpBatch.php',
- 'StoreFileOp' => 'includes/filebackend/FileOp.php',
- 'CopyFileOp' => 'includes/filebackend/FileOp.php',
- 'MoveFileOp' => 'includes/filebackend/FileOp.php',
- 'DeleteFileOp' => 'includes/filebackend/FileOp.php',
- 'CreateFileOp' => 'includes/filebackend/FileOp.php',
- 'DescribeFileOp' => 'includes/filebackend/FileOp.php',
- 'NullFileOp' => 'includes/filebackend/FileOp.php',
-
- # includes/filerepo
- 'FileRepo' => 'includes/filerepo/FileRepo.php',
- 'FileRepoStatus' => 'includes/filerepo/FileRepoStatus.php',
- 'ForeignAPIRepo' => 'includes/filerepo/ForeignAPIRepo.php',
- 'ForeignDBRepo' => 'includes/filerepo/ForeignDBRepo.php',
- 'ForeignDBViaLBRepo' => 'includes/filerepo/ForeignDBViaLBRepo.php',
- 'FSRepo' => 'includes/filerepo/FSRepo.php',
- 'LocalRepo' => 'includes/filerepo/LocalRepo.php',
- 'NullRepo' => 'includes/filerepo/NullRepo.php',
- 'RepoGroup' => 'includes/filerepo/RepoGroup.php',
- 'TempFileRepo' => 'includes/filerepo/FileRepo.php',
-
- # includes/filerepo/file
- 'ArchivedFile' => 'includes/filerepo/file/ArchivedFile.php',
- 'File' => 'includes/filerepo/file/File.php',
- 'ForeignAPIFile' => 'includes/filerepo/file/ForeignAPIFile.php',
- 'ForeignDBFile' => 'includes/filerepo/file/ForeignDBFile.php',
- 'LocalFile' => 'includes/filerepo/file/LocalFile.php',
- 'LocalFileDeleteBatch' => 'includes/filerepo/file/LocalFile.php',
- 'LocalFileMoveBatch' => 'includes/filerepo/file/LocalFile.php',
- 'LocalFileRestoreBatch' => 'includes/filerepo/file/LocalFile.php',
- 'OldLocalFile' => 'includes/filerepo/file/OldLocalFile.php',
- 'UnregisteredLocalFile' => 'includes/filerepo/file/UnregisteredLocalFile.php',
-
- # includes/installer
- 'CliInstaller' => 'includes/installer/CliInstaller.php',
- 'DatabaseInstaller' => 'includes/installer/DatabaseInstaller.php',
- 'DatabaseUpdater' => 'includes/installer/DatabaseUpdater.php',
- 'InstallDocFormatter' => 'includes/installer/InstallDocFormatter.php',
- 'Installer' => 'includes/installer/Installer.php',
- 'LocalSettingsGenerator' => 'includes/installer/LocalSettingsGenerator.php',
- 'MssqlInstaller' => 'includes/installer/MssqlInstaller.php',
- 'MssqlUpdater' => 'includes/installer/MssqlUpdater.php',
- 'MysqlInstaller' => 'includes/installer/MysqlInstaller.php',
- 'MysqlUpdater' => 'includes/installer/MysqlUpdater.php',
- 'OracleInstaller' => 'includes/installer/OracleInstaller.php',
- 'OracleUpdater' => 'includes/installer/OracleUpdater.php',
- 'PhpXmlBugTester' => 'includes/installer/PhpBugTests.php',
- 'PostgresInstaller' => 'includes/installer/PostgresInstaller.php',
- 'PostgresUpdater' => 'includes/installer/PostgresUpdater.php',
- 'SqliteInstaller' => 'includes/installer/SqliteInstaller.php',
- 'SqliteUpdater' => 'includes/installer/SqliteUpdater.php',
- 'WebInstaller' => 'includes/installer/WebInstaller.php',
- 'WebInstallerComplete' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerCopying' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerDBConnect' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerDBSettings' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerDocument' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerExistingWiki' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerInstall' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerLanguage' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerName' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerOptions' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerReadme' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerReleaseNotes' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerRestart' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerUpgrade' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerUpgradeDoc' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerWelcome' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerOutput' => 'includes/installer/WebInstallerOutput.php',
- 'WebInstallerPage' => 'includes/installer/WebInstallerPage.php',
-
- # includes/job
- 'IJobSpecification' => 'includes/jobqueue/JobSpecification.php',
- 'Job' => 'includes/jobqueue/Job.php',
- 'JobQueue' => 'includes/jobqueue/JobQueue.php',
- 'JobQueueAggregator' => 'includes/jobqueue/aggregator/JobQueueAggregator.php',
- 'JobQueueAggregatorMemc' => 'includes/jobqueue/aggregator/JobQueueAggregatorMemc.php',
- 'JobQueueAggregatorRedis' => 'includes/jobqueue/aggregator/JobQueueAggregatorRedis.php',
- 'JobQueueDB' => 'includes/jobqueue/JobQueueDB.php',
- 'JobQueueConnectionError' => 'includes/jobqueue/JobQueue.php',
- 'JobQueueError' => 'includes/jobqueue/JobQueue.php',
- 'JobQueueGroup' => 'includes/jobqueue/JobQueueGroup.php',
- 'JobQueueFederated' => 'includes/jobqueue/JobQueueFederated.php',
- 'JobQueueRedis' => 'includes/jobqueue/JobQueueRedis.php',
- 'JobRunner' => 'includes/jobqueue/JobRunner.php',
- 'JobSpecification' => 'includes/jobqueue/JobSpecification.php',
-
- # includes/jobqueue/jobs
- 'DoubleRedirectJob' => 'includes/jobqueue/jobs/DoubleRedirectJob.php',
- 'DuplicateJob' => 'includes/jobqueue/jobs/DuplicateJob.php',
- 'EmaillingJob' => 'includes/jobqueue/jobs/EmaillingJob.php',
- 'EnotifNotifyJob' => 'includes/jobqueue/jobs/EnotifNotifyJob.php',
- 'HTMLCacheUpdateJob' => 'includes/jobqueue/jobs/HTMLCacheUpdateJob.php',
- 'NullJob' => 'includes/jobqueue/jobs/NullJob.php',
- 'RefreshLinksJob' => 'includes/jobqueue/jobs/RefreshLinksJob.php',
- 'RefreshLinksJob2' => 'includes/jobqueue/jobs/RefreshLinksJob2.php',
- 'UploadFromUrlJob' => 'includes/jobqueue/jobs/UploadFromUrlJob.php',
- 'AssembleUploadChunksJob' => 'includes/jobqueue/jobs/AssembleUploadChunksJob.php',
- 'PublishStashedFileJob' => 'includes/jobqueue/jobs/PublishStashedFileJob.php',
-
- # includes/jobqueue/utils
- 'BacklinkJobUtils' => 'includes/jobqueue/utils/BacklinkJobUtils.php',
-
- # includes/json
- 'FormatJson' => 'includes/json/FormatJson.php',
-
- # includes/libs
- 'CSSJanus' => 'includes/libs/CSSJanus.php',
- 'CSSJanusTokenizer' => 'includes/libs/CSSJanus.php',
- 'CSSMin' => 'includes/libs/CSSMin.php',
- 'GenericArrayObject' => 'includes/libs/GenericArrayObject.php',
- 'HashRing' => 'includes/libs/HashRing.php',
- 'HttpStatus' => 'includes/libs/HttpStatus.php',
- 'IEContentAnalyzer' => 'includes/libs/IEContentAnalyzer.php',
- 'IEUrlExtension' => 'includes/libs/IEUrlExtension.php',
- 'MappedIterator' => 'includes/libs/MappedIterator.php',
- 'IPSet' => 'includes/libs/IPSet.php',
- 'JavaScriptMinifier' => 'includes/libs/JavaScriptMinifier.php',
- 'JSCompilerContext' => 'includes/libs/jsminplus.php',
- 'JSMinPlus' => 'includes/libs/jsminplus.php',
- 'JSNode' => 'includes/libs/jsminplus.php',
- 'JSParser' => 'includes/libs/jsminplus.php',
- 'JSToken' => 'includes/libs/jsminplus.php',
- 'JSTokenizer' => 'includes/libs/jsminplus.php',
- 'MultiHttpClient' => 'includes/libs/MultiHttpClient.php',
- 'MWMessagePack' => 'includes/libs/MWMessagePack.php',
- 'ProcessCacheLRU' => 'includes/libs/ProcessCacheLRU.php',
- 'RunningStat' => 'includes/libs/RunningStat.php',
- 'ScopedCallback' => 'includes/libs/ScopedCallback.php',
- 'ScopedPHPTimeout' => 'includes/libs/ScopedPHPTimeout.php',
- 'SwiftVirtualRESTService' => 'includes/libs/virtualrest/SwiftVirtualRESTService.php',
- 'VirtualRESTService' => 'includes/libs/virtualrest/VirtualRESTService.php',
- 'VirtualRESTServiceClient' => 'includes/libs/virtualrest/VirtualRESTServiceClient.php',
- 'XmlTypeCheck' => 'includes/libs/XmlTypeCheck.php',
-
- # includes/libs/lessphp
- 'lessc' => 'includes/libs/lessc.inc.php',
- 'lessc_parser' => 'includes/libs/lessc.inc.php',
- 'lessc_formatter_classic' => 'includes/libs/lessc.inc.php',
- 'lessc_formatter_compressed' => 'includes/libs/lessc.inc.php',
- 'lessc_formatter_lessjs' => 'includes/libs/lessc.inc.php',
-
- # includes/logging
- 'DatabaseLogEntry' => 'includes/logging/LogEntry.php',
- 'DeleteLogFormatter' => 'includes/logging/DeleteLogFormatter.php',
- 'LegacyLogFormatter' => 'includes/logging/LogFormatter.php',
- 'LogEntry' => 'includes/logging/LogEntry.php',
- 'LogEventsList' => 'includes/logging/LogEventsList.php',
- 'LogEntryBase' => 'includes/logging/LogEntry.php',
- 'LogFormatter' => 'includes/logging/LogFormatter.php',
- 'LogPage' => 'includes/logging/LogPage.php',
- 'LogPager' => 'includes/logging/LogPager.php',
- 'ManualLogEntry' => 'includes/logging/LogEntry.php',
- 'MoveLogFormatter' => 'includes/logging/MoveLogFormatter.php',
- 'NewUsersLogFormatter' => 'includes/logging/NewUsersLogFormatter.php',
- 'PageLangLogFormatter' => 'includes/logging/PageLangLogFormatter.php',
- 'PatrolLog' => 'includes/logging/PatrolLog.php',
- 'PatrolLogFormatter' => 'includes/logging/PatrolLogFormatter.php',
- 'RCDatabaseLogEntry' => 'includes/logging/LogEntry.php',
- 'RightsLogFormatter' => 'includes/logging/RightsLogFormatter.php',
-
- # Image gallery
-
- 'ImageGallery' => 'includes/gallery/TraditionalImageGallery.php',
- 'ImageGalleryBase' => 'includes/gallery/ImageGalleryBase.php',
- 'NolinesImageGallery' => 'includes/gallery/NolinesImageGallery.php',
- 'TraditionalImageGallery' => 'includes/gallery/TraditionalImageGallery.php',
- 'PackedImageGallery' => 'includes/gallery/PackedImageGallery.php',
- 'PackedHoverImageGallery' => 'includes/gallery/PackedOverlayImageGallery.php',
- 'PackedOverlayImageGallery' => 'includes/gallery/PackedOverlayImageGallery.php',
-
- # includes/mail
- 'EmailNotification' => 'includes/mail/EmailNotification.php',
- 'MailAddress' => 'includes/mail/MailAddress.php',
- 'UserMailer' => 'includes/mail/UserMailer.php',
-
- # includes/media
- 'BitmapHandler' => 'includes/media/Bitmap.php',
- 'BitmapHandler_ClientOnly' => 'includes/media/Bitmap_ClientOnly.php',
- 'BitmapMetadataHandler' => 'includes/media/BitmapMetadataHandler.php',
- 'BmpHandler' => 'includes/media/BMP.php',
- 'DjVuHandler' => 'includes/media/DjVu.php',
- 'DjVuImage' => 'includes/media/DjVuImage.php',
- 'Exif' => 'includes/media/Exif.php',
- 'ExifBitmapHandler' => 'includes/media/ExifBitmap.php',
- 'FormatMetadata' => 'includes/media/FormatMetadata.php',
- 'GIFHandler' => 'includes/media/GIF.php',
- 'GIFMetadataExtractor' => 'includes/media/GIFMetadataExtractor.php',
- 'ImageHandler' => 'includes/media/ImageHandler.php',
- 'IPTC' => 'includes/media/IPTC.php',
- 'JpegHandler' => 'includes/media/Jpeg.php',
- 'JpegMetadataExtractor' => 'includes/media/JpegMetadataExtractor.php',
- 'MediaHandler' => 'includes/media/MediaHandler.php',
- 'MediaTransformError' => 'includes/media/MediaTransformOutput.php',
- 'MediaTransformOutput' => 'includes/media/MediaTransformOutput.php',
- 'PNGHandler' => 'includes/media/PNG.php',
- 'PNGMetadataExtractor' => 'includes/media/PNGMetadataExtractor.php',
- 'SvgHandler' => 'includes/media/SVG.php',
- 'SVGMetadataExtractor' => 'includes/media/SVGMetadataExtractor.php',
- 'SVGReader' => 'includes/media/SVGMetadataExtractor.php',
- 'ThumbnailImage' => 'includes/media/MediaTransformOutput.php',
- 'TiffHandler' => 'includes/media/Tiff.php',
- 'TransformationalImageHandler' => 'includes/media/TransformationalImageHandler.php',
- 'TransformParameterError' => 'includes/media/MediaTransformOutput.php',
- 'XCFHandler' => 'includes/media/XCF.php',
- 'XMPInfo' => 'includes/media/XMPInfo.php',
- 'XMPReader' => 'includes/media/XMP.php',
- 'XMPValidate' => 'includes/media/XMPValidate.php',
-
- # includes/normal
- 'UtfNormal' => 'includes/normal/UtfNormal.php',
-
- # includes/objectcache
- 'APCBagOStuff' => 'includes/objectcache/APCBagOStuff.php',
- 'BagOStuff' => 'includes/objectcache/BagOStuff.php',
- 'EmptyBagOStuff' => 'includes/objectcache/EmptyBagOStuff.php',
- 'HashBagOStuff' => 'includes/objectcache/HashBagOStuff.php',
- 'MediaWikiBagOStuff' => 'includes/objectcache/SqlBagOStuff.php',
- 'MemCachedClientforWiki' => 'includes/objectcache/MemcachedClient.php',
- 'MemcachedBagOStuff' => 'includes/objectcache/MemcachedBagOStuff.php',
- 'MemcachedPeclBagOStuff' => 'includes/objectcache/MemcachedPeclBagOStuff.php',
- 'MemcachedPhpBagOStuff' => 'includes/objectcache/MemcachedPhpBagOStuff.php',
- 'MultiWriteBagOStuff' => 'includes/objectcache/MultiWriteBagOStuff.php',
- 'MWMemcached' => 'includes/objectcache/MemcachedClient.php',
- 'ObjectCache' => 'includes/objectcache/ObjectCache.php',
- 'ObjectCacheSessionHandler' => 'includes/objectcache/ObjectCacheSessionHandler.php',
- 'RedisBagOStuff' => 'includes/objectcache/RedisBagOStuff.php',
- 'SqlBagOStuff' => 'includes/objectcache/SqlBagOStuff.php',
- 'WinCacheBagOStuff' => 'includes/objectcache/WinCacheBagOStuff.php',
- 'XCacheBagOStuff' => 'includes/objectcache/XCacheBagOStuff.php',
-
- # includes/page
- 'Article' => 'includes/page/Article.php',
- 'CategoryPage' => 'includes/page/CategoryPage.php',
- 'ImageHistoryList' => 'includes/page/ImagePage.php',
- 'ImageHistoryPseudoPager' => 'includes/page/ImagePage.php',
- 'ImagePage' => 'includes/page/ImagePage.php',
- 'Page' => 'includes/page/WikiPage.php',
- 'WikiCategoryPage' => 'includes/page/WikiCategoryPage.php',
- 'WikiFilePage' => 'includes/page/WikiFilePage.php',
- 'WikiPage' => 'includes/page/WikiPage.php',
-
- # includes/pager
- 'AlphabeticPager' => 'includes/pager/AlphabeticPager.php',
- 'IndexPager' => 'includes/pager/IndexPager.php',
- 'Pager' => 'includes/pager/Pager.php',
- 'ReverseChronologicalPager' => 'includes/pager/ReverseChronologicalPager.php',
- 'TablePager' => 'includes/pager/TablePager.php',
-
- # includes/parser
- 'CacheTime' => 'includes/parser/CacheTime.php',
- 'CoreParserFunctions' => 'includes/parser/CoreParserFunctions.php',
- 'CoreTagHooks' => 'includes/parser/CoreTagHooks.php',
- 'DateFormatter' => 'includes/parser/DateFormatter.php',
- 'LinkHolderArray' => 'includes/parser/LinkHolderArray.php',
- 'MWTidy' => 'includes/parser/MWTidy.php',
- 'MWTidyWrapper' => 'includes/parser/MWTidy.php',
- 'PPCustomFrame_DOM' => 'includes/parser/Preprocessor_DOM.php',
- 'PPCustomFrame_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'PPDAccum_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'PPDPart' => 'includes/parser/Preprocessor_DOM.php',
- 'PPDPart_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'PPDStack' => 'includes/parser/Preprocessor_DOM.php',
- 'PPDStackElement' => 'includes/parser/Preprocessor_DOM.php',
- 'PPDStackElement_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'PPDStack_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'PPFrame' => 'includes/parser/Preprocessor.php',
- 'PPFrame_DOM' => 'includes/parser/Preprocessor_DOM.php',
- 'PPFrame_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'PPNode' => 'includes/parser/Preprocessor.php',
- 'PPNode_DOM' => 'includes/parser/Preprocessor_DOM.php',
- 'PPNode_Hash_Array' => 'includes/parser/Preprocessor_Hash.php',
- 'PPNode_Hash_Attr' => 'includes/parser/Preprocessor_Hash.php',
- 'PPNode_Hash_Text' => 'includes/parser/Preprocessor_Hash.php',
- 'PPNode_Hash_Tree' => 'includes/parser/Preprocessor_Hash.php',
- 'PPTemplateFrame_DOM' => 'includes/parser/Preprocessor_DOM.php',
- 'PPTemplateFrame_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'Parser' => 'includes/parser/Parser.php',
- 'ParserCache' => 'includes/parser/ParserCache.php',
- 'ParserOptions' => 'includes/parser/ParserOptions.php',
- 'ParserOutput' => 'includes/parser/ParserOutput.php',
- 'ParserDiffTest' => 'includes/parser/ParserDiffTest.php',
- 'Preprocessor' => 'includes/parser/Preprocessor.php',
- 'Preprocessor_DOM' => 'includes/parser/Preprocessor_DOM.php',
- 'Preprocessor_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'StripState' => 'includes/parser/StripState.php',
-
- # includes/password
- 'BcryptPassword' => 'includes/password/BcryptPassword.php',
- 'InvalidPassword' => 'includes/password/InvalidPassword.php',
- 'LayeredParameterizedPassword' => 'includes/password/LayeredParameterizedPassword.php',
- 'MWSaltedPassword' => 'includes/password/MWSaltedPassword.php',
- 'MWOldPassword' => 'includes/password/MWOldPassword.php',
- 'ParameterizedPassword' => 'includes/password/ParameterizedPassword.php',
- 'Password' => 'includes/password/Password.php',
- 'PasswordError' => 'includes/password/PasswordError.php',
- 'PasswordFactory' => 'includes/password/PasswordFactory.php',
- 'Pbkdf2Password' => 'includes/password/Pbkdf2Password.php',
- 'EncryptedPassword' => 'includes/password/EncryptedPassword.php',
-
- # includes/profiler
- 'Profiler' => 'includes/profiler/Profiler.php',
- 'ProfilerMwprof' => 'includes/profiler/ProfilerMwprof.php',
- 'ProfilerSimpleDB' => 'includes/profiler/ProfilerSimpleDB.php',
- 'ProfilerSimpleText' => 'includes/profiler/ProfilerSimpleText.php',
- 'ProfilerSimpleTrace' => 'includes/profiler/ProfilerSimpleTrace.php',
- 'ProfilerSimpleUDP' => 'includes/profiler/ProfilerSimpleUDP.php',
- 'ProfilerStandard' => 'includes/profiler/ProfilerStandard.php',
- 'ProfilerStub' => 'includes/profiler/ProfilerStub.php',
- 'ProfileSection' => 'includes/profiler/Profiler.php',
- 'TransactionProfiler' => 'includes/profiler/Profiler.php',
-
- # includes/rcfeed
- 'RCFeedEngine' => 'includes/rcfeed/RCFeedEngine.php',
- 'RedisPubSubFeedEngine' => 'includes/rcfeed/RedisPubSubFeedEngine.php',
- 'UDPRCFeedEngine' => 'includes/rcfeed/UDPRCFeedEngine.php',
- 'RCFeedFormatter' => 'includes/rcfeed/RCFeedFormatter.php',
- 'IRCColourfulRCFeedFormatter' => 'includes/rcfeed/IRCColourfulRCFeedFormatter.php',
- 'JSONRCFeedFormatter' => 'includes/rcfeed/JSONRCFeedFormatter.php',
- 'XMLRCFeedFormatter' => 'includes/rcfeed/XMLRCFeedFormatter.php',
- 'MachineReadableRCFeedFormatter' => 'includes/rcfeed/MachineReadableRCFeedFormatter.php',
-
- # includes/resourceloader
- 'DerivativeResourceLoaderContext' =>
- 'includes/resourceloader/DerivativeResourceLoaderContext.php',
- 'ResourceLoader' => 'includes/resourceloader/ResourceLoader.php',
- 'ResourceLoaderContext' => 'includes/resourceloader/ResourceLoaderContext.php',
- 'ResourceLoaderEditToolbarModule' => 'includes/resourceloader/ResourceLoaderEditToolbarModule.php',
- 'ResourceLoaderFileModule' => 'includes/resourceloader/ResourceLoaderFileModule.php',
- 'ResourceLoaderFilePageModule' => 'includes/resourceloader/ResourceLoaderFilePageModule.php',
- 'ResourceLoaderFilePath' => 'includes/resourceloader/ResourceLoaderFilePath.php',
- 'ResourceLoaderModule' => 'includes/resourceloader/ResourceLoaderModule.php',
- 'ResourceLoaderNoscriptModule' => 'includes/resourceloader/ResourceLoaderNoscriptModule.php',
- 'ResourceLoaderSiteModule' => 'includes/resourceloader/ResourceLoaderSiteModule.php',
- 'ResourceLoaderStartUpModule' => 'includes/resourceloader/ResourceLoaderStartUpModule.php',
- 'ResourceLoaderUserCSSPrefsModule' =>
- 'includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php',
- 'ResourceLoaderUserGroupsModule' => 'includes/resourceloader/ResourceLoaderUserGroupsModule.php',
- 'ResourceLoaderUserModule' => 'includes/resourceloader/ResourceLoaderUserModule.php',
- 'ResourceLoaderUserOptionsModule' => 'includes/resourceloader/ResourceLoaderUserOptionsModule.php',
- 'ResourceLoaderUserTokensModule' => 'includes/resourceloader/ResourceLoaderUserTokensModule.php',
- 'ResourceLoaderLanguageDataModule' =>
- 'includes/resourceloader/ResourceLoaderLanguageDataModule.php',
- 'ResourceLoaderLanguageNamesModule' =>
- 'includes/resourceloader/ResourceLoaderLanguageNamesModule.php',
- 'ResourceLoaderWikiModule' => 'includes/resourceloader/ResourceLoaderWikiModule.php',
-
- # includes/revisiondelete
- 'RevDelArchivedFileItem' => 'includes/revisiondelete/RevDelArchivedFileItem.php',
- 'RevDelArchivedFileList' => 'includes/revisiondelete/RevDelArchivedFileList.php',
- 'RevDelArchivedRevisionItem' => 'includes/revisiondelete/RevDelArchivedRevisionItem.php',
- 'RevDelArchiveItem' => 'includes/revisiondelete/RevDelArchiveItem.php',
- 'RevDelArchiveList' => 'includes/revisiondelete/RevDelArchiveList.php',
- 'RevDelFileItem' => 'includes/revisiondelete/RevDelFileItem.php',
- 'RevDelFileList' => 'includes/revisiondelete/RevDelFileList.php',
- 'RevDelItem' => 'includes/revisiondelete/RevDelItem.php',
- 'RevDelList' => 'includes/revisiondelete/RevDelList.php',
- 'RevDelLogItem' => 'includes/revisiondelete/RevDelLogItem.php',
- 'RevDelLogList' => 'includes/revisiondelete/RevDelLogList.php',
- 'RevDelRevisionItem' => 'includes/revisiondelete/RevDelRevisionItem.php',
- 'RevDelRevisionList' => 'includes/revisiondelete/RevDelRevisionList.php',
- 'RevisionDeleter' => 'includes/revisiondelete/RevisionDeleter.php',
- 'RevisionDeleteUser' => 'includes/revisiondelete/RevisionDeleteUser.php',
-
- # includes/search
- 'SearchDatabase' => 'includes/search/SearchDatabase.php',
- 'SearchEngine' => 'includes/search/SearchEngine.php',
- 'SearchEngineDummy' => 'includes/search/SearchEngine.php',
- 'SearchHighlighter' => 'includes/search/SearchHighlighter.php',
- 'SearchMssql' => 'includes/search/SearchMssql.php',
- 'SearchMySQL' => 'includes/search/SearchMySQL.php',
- 'SearchNearMatchResultSet' => 'includes/search/SearchResultSet.php',
- 'SearchOracle' => 'includes/search/SearchOracle.php',
- 'SearchPostgres' => 'includes/search/SearchPostgres.php',
- 'SearchResult' => 'includes/search/SearchResult.php',
- 'SearchResultSet' => 'includes/search/SearchResultSet.php',
- 'SearchSqlite' => 'includes/search/SearchSqlite.php',
- 'SqlSearchResultSet' => 'includes/search/SearchResultSet.php',
-
- # includes/site
- 'MediaWikiSite' => 'includes/site/MediaWikiSite.php',
- 'Site' => 'includes/site/Site.php',
- 'SiteObject' => 'includes/site/Site.php',
- 'SiteArray' => 'includes/site/SiteList.php',
- 'SiteList' => 'includes/site/SiteList.php',
- 'SiteSQLStore' => 'includes/site/SiteSQLStore.php',
- 'Sites' => 'includes/site/SiteSQLStore.php',
- 'SiteStore' => 'includes/site/SiteStore.php',
-
- # includes/skins
- 'BaseTemplate' => 'includes/skins/SkinTemplate.php',
- 'MediaWikiI18N' => 'includes/skins/SkinTemplate.php',
- 'QuickTemplate' => 'includes/skins/SkinTemplate.php',
- 'Skin' => 'includes/skins/Skin.php',
- 'SkinException' => 'includes/skins/SkinException.php',
- 'SkinFactory' => 'includes/skins/SkinFactory.php',
- 'SkinFallback' => 'includes/skins/SkinFallback.php',
- 'SkinFallbackTemplate' => 'includes/skins/SkinFallbackTemplate.php',
- 'SkinTemplate' => 'includes/skins/SkinTemplate.php',
-
- # includes/specialpage
- 'ChangesListSpecialPage' => 'includes/specialpage/ChangesListSpecialPage.php',
- 'FormSpecialPage' => 'includes/specialpage/FormSpecialPage.php',
- 'ImageQueryPage' => 'includes/specialpage/ImageQueryPage.php',
- 'IncludableSpecialPage' => 'includes/specialpage/IncludableSpecialPage.php',
- 'PageQueryPage' => 'includes/specialpage/PageQueryPage.php',
- 'QueryPage' => 'includes/specialpage/QueryPage.php',
- 'RedirectSpecialArticle' => 'includes/specialpage/RedirectSpecialPage.php',
- 'RedirectSpecialPage' => 'includes/specialpage/RedirectSpecialPage.php',
- 'SpecialPage' => 'includes/specialpage/SpecialPage.php',
- 'SpecialPageFactory' => 'includes/specialpage/SpecialPageFactory.php',
- 'SpecialRedirectToSpecial' => 'includes/specialpage/RedirectSpecialPage.php',
- 'UnlistedSpecialPage' => 'includes/specialpage/UnlistedSpecialPage.php',
- 'WantedQueryPage' => 'includes/specialpage/WantedQueryPage.php',
-
- # includes/specials
- 'ActiveUsersPager' => 'includes/specials/SpecialActiveusers.php',
- 'AllMessagesTablePager' => 'includes/specials/SpecialAllMessages.php',
- 'AncientPagesPage' => 'includes/specials/SpecialAncientpages.php',
- 'BlockListPager' => 'includes/specials/SpecialBlockList.php',
- 'BrokenRedirectsPage' => 'includes/specials/SpecialBrokenRedirects.php',
- 'CategoryPager' => 'includes/specials/SpecialCategories.php',
- 'ContribsPager' => 'includes/specials/SpecialContributions.php',
- 'DeadendPagesPage' => 'includes/specials/SpecialDeadendpages.php',
- 'DeletedContribsPager' => 'includes/specials/SpecialDeletedContributions.php',
- 'DeletedContributionsPage' => 'includes/specials/SpecialDeletedContributions.php',
- 'DoubleRedirectsPage' => 'includes/specials/SpecialDoubleRedirects.php',
- 'EditWatchlistCheckboxSeriesField' => 'includes/specials/SpecialEditWatchlist.php',
- 'EditWatchlistNormalHTMLForm' => 'includes/specials/SpecialEditWatchlist.php',
- 'EmailConfirmation' => 'includes/specials/SpecialConfirmemail.php',
- 'EmailInvalidation' => 'includes/specials/SpecialConfirmemail.php',
- 'FewestrevisionsPage' => 'includes/specials/SpecialFewestrevisions.php',
- 'FileDuplicateSearchPage' => 'includes/specials/SpecialFileDuplicateSearch.php',
- 'ImageListPager' => 'includes/specials/SpecialListfiles.php',
- 'ImportReporter' => 'includes/specials/SpecialImport.php',
- 'LinkSearchPage' => 'includes/specials/SpecialLinkSearch.php',
- 'ListredirectsPage' => 'includes/specials/SpecialListredirects.php',
- 'ListDuplicatedFilesPage' => 'includes/specials/SpecialListDuplicatedFiles.php',
- 'LoginForm' => 'includes/specials/SpecialUserlogin.php',
- 'LonelyPagesPage' => 'includes/specials/SpecialLonelypages.php',
- 'LongPagesPage' => 'includes/specials/SpecialLongpages.php',
- 'MediaStatisticsPage' => 'includes/specials/SpecialMediaStatistics.php',
- 'MergeHistoryPager' => 'includes/specials/SpecialMergeHistory.php',
- 'MIMEsearchPage' => 'includes/specials/SpecialMIMEsearch.php',
- 'MostcategoriesPage' => 'includes/specials/SpecialMostcategories.php',
- 'MostimagesPage' => 'includes/specials/SpecialMostimages.php',
- 'MostinterwikisPage' => 'includes/specials/SpecialMostinterwikis.php',
- 'MostlinkedCategoriesPage' => 'includes/specials/SpecialMostlinkedcategories.php',
- 'MostlinkedPage' => 'includes/specials/SpecialMostlinked.php',
- 'MostlinkedTemplatesPage' => 'includes/specials/SpecialMostlinkedtemplates.php',
- 'MostrevisionsPage' => 'includes/specials/SpecialMostrevisions.php',
- 'MovePageForm' => 'includes/specials/SpecialMovepage.php',
- 'NewFilesPager' => 'includes/specials/SpecialNewimages.php',
- 'NewPagesPager' => 'includes/specials/SpecialNewpages.php',
- 'PageArchive' => 'includes/specials/SpecialUndelete.php',
- 'PopularPagesPage' => 'includes/specials/SpecialPopularpages.php',
- 'ProtectedPagesPager' => 'includes/specials/SpecialProtectedpages.php',
- 'ProtectedTitlesPager' => 'includes/specials/SpecialProtectedtitles.php',
- 'RandomPage' => 'includes/specials/SpecialRandompage.php',
- 'ShortPagesPage' => 'includes/specials/SpecialShortpages.php',
- 'SpecialActiveUsers' => 'includes/specials/SpecialActiveusers.php',
- 'SpecialAllMessages' => 'includes/specials/SpecialAllMessages.php',
- 'SpecialAllMyUploads' => 'includes/specials/SpecialMyRedirectPages.php',
- 'SpecialAllPages' => 'includes/specials/SpecialAllPages.php',
- 'SpecialBlankpage' => 'includes/specials/SpecialBlankpage.php',
- 'SpecialBlock' => 'includes/specials/SpecialBlock.php',
- 'SpecialBlockList' => 'includes/specials/SpecialBlockList.php',
- 'SpecialBookSources' => 'includes/specials/SpecialBooksources.php',
- 'SpecialCachedPage' => 'includes/specials/SpecialCachedPage.php',
- 'SpecialCategories' => 'includes/specials/SpecialCategories.php',
- 'SpecialChangeEmail' => 'includes/specials/SpecialChangeEmail.php',
- 'SpecialChangePassword' => 'includes/specials/SpecialChangePassword.php',
- 'SpecialComparePages' => 'includes/specials/SpecialComparePages.php',
- 'SpecialContributions' => 'includes/specials/SpecialContributions.php',
- 'SpecialCreateAccount' => 'includes/specials/SpecialCreateAccount.php',
- 'SpecialDiff' => 'includes/specials/SpecialDiff.php',
- 'SpecialEditWatchlist' => 'includes/specials/SpecialEditWatchlist.php',
- 'SpecialEmailUser' => 'includes/specials/SpecialEmailuser.php',
- 'SpecialExpandTemplates' => 'includes/specials/SpecialExpandTemplates.php',
- 'SpecialExport' => 'includes/specials/SpecialExport.php',
- 'SpecialFilepath' => 'includes/specials/SpecialFilepath.php',
- 'SpecialImport' => 'includes/specials/SpecialImport.php',
- 'SpecialJavaScriptTest' => 'includes/specials/SpecialJavaScriptTest.php',
- 'SpecialListAdmins' => 'includes/specials/SpecialListusers.php',
- 'SpecialListBots' => 'includes/specials/SpecialListusers.php',
- 'SpecialListFiles' => 'includes/specials/SpecialListfiles.php',
- 'SpecialListGroupRights' => 'includes/specials/SpecialListgrouprights.php',
- 'SpecialListUsers' => 'includes/specials/SpecialListusers.php',
- 'SpecialLockdb' => 'includes/specials/SpecialLockdb.php',
- 'SpecialLog' => 'includes/specials/SpecialLog.php',
- 'SpecialMergeHistory' => 'includes/specials/SpecialMergeHistory.php',
- 'SpecialMycontributions' => 'includes/specials/SpecialMyRedirectPages.php',
- 'SpecialMyLanguage' => 'includes/specials/SpecialMyLanguage.php',
- 'SpecialMypage' => 'includes/specials/SpecialMyRedirectPages.php',
- 'SpecialMytalk' => 'includes/specials/SpecialMyRedirectPages.php',
- 'SpecialMyuploads' => 'includes/specials/SpecialMyRedirectPages.php',
- 'SpecialNewFiles' => 'includes/specials/SpecialNewimages.php',
- 'SpecialNewpages' => 'includes/specials/SpecialNewpages.php',
- 'SpecialPageLanguage' => 'includes/specials/SpecialPageLanguage.php',
- 'SpecialPasswordReset' => 'includes/specials/SpecialPasswordReset.php',
- 'SpecialPagesWithProp' => 'includes/specials/SpecialPagesWithProp.php',
- 'SpecialPermanentLink' => 'includes/specials/SpecialPermanentLink.php',
- 'SpecialPreferences' => 'includes/specials/SpecialPreferences.php',
- 'SpecialPrefixindex' => 'includes/specials/SpecialPrefixindex.php',
- 'SpecialProtectedpages' => 'includes/specials/SpecialProtectedpages.php',
- 'SpecialProtectedtitles' => 'includes/specials/SpecialProtectedtitles.php',
- 'SpecialRandomInCategory' => 'includes/specials/SpecialRandomInCategory.php',
- 'SpecialRandomredirect' => 'includes/specials/SpecialRandomredirect.php',
- 'SpecialRecentChanges' => 'includes/specials/SpecialRecentchanges.php',
- 'SpecialRecentChangesLinked' => 'includes/specials/SpecialRecentchangeslinked.php',
- 'SpecialRedirect' => 'includes/specials/SpecialRedirect.php',
- 'SpecialResetTokens' => 'includes/specials/SpecialResetTokens.php',
- 'SpecialRevisionDelete' => 'includes/specials/SpecialRevisiondelete.php',
- 'SpecialRunJobs' => 'includes/specials/SpecialRunJobs.php',
- 'SpecialSearch' => 'includes/specials/SpecialSearch.php',
- 'SpecialSpecialpages' => 'includes/specials/SpecialSpecialpages.php',
- 'SpecialStatistics' => 'includes/specials/SpecialStatistics.php',
- 'SpecialTags' => 'includes/specials/SpecialTags.php',
- 'SpecialTrackingCategories' => 'includes/specials/SpecialTrackingCategories.php',
- 'SpecialUnblock' => 'includes/specials/SpecialUnblock.php',
- 'SpecialUndelete' => 'includes/specials/SpecialUndelete.php',
- 'SpecialUnlockdb' => 'includes/specials/SpecialUnlockdb.php',
- 'SpecialUpload' => 'includes/specials/SpecialUpload.php',
- 'SpecialUploadStash' => 'includes/specials/SpecialUploadStash.php',
- 'SpecialUploadStashTooLargeException' => 'includes/specials/SpecialUploadStash.php',
- 'SpecialUserlogout' => 'includes/specials/SpecialUserlogout.php',
- 'SpecialVersion' => 'includes/specials/SpecialVersion.php',
- 'SpecialWatchlist' => 'includes/specials/SpecialWatchlist.php',
- 'SpecialWhatLinksHere' => 'includes/specials/SpecialWhatlinkshere.php',
- 'UncategorizedCategoriesPage' => 'includes/specials/SpecialUncategorizedcategories.php',
- 'UncategorizedImagesPage' => 'includes/specials/SpecialUncategorizedimages.php',
- 'UncategorizedPagesPage' => 'includes/specials/SpecialUncategorizedpages.php',
- 'UncategorizedTemplatesPage' => 'includes/specials/SpecialUncategorizedtemplates.php',
- 'UnusedCategoriesPage' => 'includes/specials/SpecialUnusedcategories.php',
- 'UnusedimagesPage' => 'includes/specials/SpecialUnusedimages.php',
- 'UnusedtemplatesPage' => 'includes/specials/SpecialUnusedtemplates.php',
- 'UnwatchedpagesPage' => 'includes/specials/SpecialUnwatchedpages.php',
- 'UploadChunkFileException' => 'includes/upload/UploadFromChunks.php',
- 'UploadChunkZeroLengthFileException' => 'includes/upload/UploadFromChunks.php',
- 'UploadChunkVerificationException' => 'includes/upload/UploadFromChunks.php',
- 'UploadForm' => 'includes/specials/SpecialUpload.php',
- 'UploadSourceField' => 'includes/specials/SpecialUpload.php',
- 'UserrightsPage' => 'includes/specials/SpecialUserrights.php',
- 'UsersPager' => 'includes/specials/SpecialListusers.php',
- 'WantedCategoriesPage' => 'includes/specials/SpecialWantedcategories.php',
- 'WantedFilesPage' => 'includes/specials/SpecialWantedfiles.php',
- 'WantedPagesPage' => 'includes/specials/SpecialWantedpages.php',
- 'WantedTemplatesPage' => 'includes/specials/SpecialWantedtemplates.php',
- 'WithoutInterwikiPage' => 'includes/specials/SpecialWithoutinterwiki.php',
-
- # includes/templates
- 'UserloginTemplate' => 'includes/templates/Userlogin.php',
- 'UsercreateTemplate' => 'includes/templates/Usercreate.php',
-
- # includes/title
- 'PageLinkRenderer' => 'includes/title/PageLinkRenderer.php',
- 'TitleFormatter' => 'includes/title/TitleFormatter.php',
- 'TitleParser' => 'includes/title/TitleParser.php',
- 'TitleValue' => 'includes/title/TitleValue.php',
- 'MalformedTitleException' => 'includes/title/MalformedTitleException.php',
- 'MediaWikiPageLinkRenderer' => 'includes/title/MediaWikiPageLinkRenderer.php',
- 'MediaWikiTitleCodec' => 'includes/title/MediaWikiTitleCodec.php',
-
- # includes/upload
- 'UploadBase' => 'includes/upload/UploadBase.php',
- 'UploadFromFile' => 'includes/upload/UploadFromFile.php',
- 'UploadFromChunks' => 'includes/upload/UploadFromChunks.php',
- 'UploadFromStash' => 'includes/upload/UploadFromStash.php',
- 'UploadFromUrl' => 'includes/upload/UploadFromUrl.php',
- 'UploadStash' => 'includes/upload/UploadStash.php',
- 'UploadStashBadPathException' => 'includes/upload/UploadStash.php',
- 'UploadStashException' => 'includes/upload/UploadStash.php',
- 'UploadStashFile' => 'includes/upload/UploadStash.php',
- 'UploadStashFileException' => 'includes/upload/UploadStash.php',
- 'UploadStashFileNotFoundException' => 'includes/upload/UploadStash.php',
- 'UploadStashNotAvailableException' => 'includes/upload/UploadStash.php',
- 'UploadStashZeroLengthFileException' => 'includes/upload/UploadStash.php',
- 'UploadStashNotLoggedInException' => 'includes/upload/UploadStash.php',
- 'UploadStashWrongOwnerException' => 'includes/upload/UploadStash.php',
- 'UploadStashNoSuchKeyException' => 'includes/upload/UploadStash.php',
-
- # includes/utils
- 'ArrayUtils' => 'includes/utils/ArrayUtils.php',
- 'CdbException' => 'includes/utils/Cdb.php',
- 'CdbFunctions' => 'includes/utils/CdbPHP.php',
- 'CdbReader' => 'includes/utils/Cdb.php',
- 'CdbReaderDBA' => 'includes/utils/CdbDBA.php',
- 'CdbReaderPHP' => 'includes/utils/CdbPHP.php',
- 'CdbWriter' => 'includes/utils/Cdb.php',
- 'CdbWriterDBA' => 'includes/utils/CdbDBA.php',
- 'CdbWriterPHP' => 'includes/utils/CdbPHP.php',
- 'DoubleReplacer' => 'includes/utils/StringUtils.php',
- 'ExplodeIterator' => 'includes/utils/StringUtils.php',
- 'HashtableReplacer' => 'includes/utils/StringUtils.php',
- 'IP' => 'includes/utils/IP.php',
- 'MWCryptRand' => 'includes/utils/MWCryptRand.php',
- 'MWCryptHKDF' => 'includes/utils/MWCryptHKDF.php',
- 'MWFunction' => 'includes/utils/MWFunction.php',
- 'RegexlikeReplacer' => 'includes/utils/StringUtils.php',
- 'ReplacementArray' => 'includes/utils/StringUtils.php',
- 'Replacer' => 'includes/utils/StringUtils.php',
- 'StringUtils' => 'includes/utils/StringUtils.php',
- 'UIDGenerator' => 'includes/utils/UIDGenerator.php',
- 'ZipDirectoryReader' => 'includes/utils/ZipDirectoryReader.php',
- 'ZipDirectoryReaderError' => 'includes/utils/ZipDirectoryReader.php',
-
- # languages
- 'ConverterRule' => 'languages/ConverterRule.php',
- 'FakeConverter' => 'languages/FakeConverter.php',
- 'Language' => 'languages/Language.php',
- 'LanguageConverter' => 'languages/LanguageConverter.php',
- 'CLDRPluralRuleConverter' => 'languages/utils/CLDRPluralRuleConverter.php',
- 'CLDRPluralRuleConverterExpression' => 'languages/utils/CLDRPluralRuleConverterExpression.php',
- 'CLDRPluralRuleConverterFragment' => 'languages/utils/CLDRPluralRuleConverterFragment.php',
- 'CLDRPluralRuleConverterOperator' => 'languages/utils/CLDRPluralRuleConverterOperator.php',
- 'CLDRPluralRuleEvaluator' => 'languages/utils/CLDRPluralRuleEvaluator.php',
- 'CLDRPluralRuleEvaluatorRange' => 'languages/utils/CLDRPluralRuleEvaluatorRange.php',
- 'CLDRPluralRuleError' => 'languages/utils/CLDRPluralRuleError.php',
-
- # maintenance
- 'BackupDumper' => 'maintenance/backup.inc',
- 'ConvertLinks' => 'maintenance/convertLinks.php',
- 'DeleteArchivedFilesImplementation' => 'maintenance/deleteArchivedFiles.inc',
- 'DeleteArchivedRevisionsImplementation' => 'maintenance/deleteArchivedRevisions.inc',
- 'DeleteDefaultMessages' => 'maintenance/deleteDefaultMessages.php',
- 'DumpDBZip2Output' => 'maintenance/backup.inc',
- 'ExportProgressFilter' => 'maintenance/backup.inc',
- 'FakeMaintenance' => 'maintenance/Maintenance.php',
- 'FixExtLinksProtocolRelative' => 'maintenance/fixExtLinksProtocolRelative.php',
- 'LoggedUpdateMaintenance' => 'maintenance/Maintenance.php',
- 'Maintenance' => 'maintenance/Maintenance.php',
- 'PopulateBacklinkNamespace' => 'maintenance/populateBacklinkNamespace.php',
- 'PopulateCategory' => 'maintenance/populateCategory.php',
- 'PopulateImageSha1' => 'maintenance/populateImageSha1.php',
- 'PopulateFilearchiveSha1' => 'maintenance/populateFilearchiveSha1.php',
- 'PopulateLogSearch' => 'maintenance/populateLogSearch.php',
- 'PopulateLogUsertext' => 'maintenance/populateLogUsertext.php',
- 'PopulateParentId' => 'maintenance/populateParentId.php',
- 'PopulateRevisionLength' => 'maintenance/populateRevisionLength.php',
- 'PopulateRevisionSha1' => 'maintenance/populateRevisionSha1.php',
- 'RefreshLinks' => 'maintenance/refreshLinks.php',
- 'SevenZipStream' => 'maintenance/7zip.inc',
- 'Sqlite' => 'maintenance/sqlite.inc',
- 'UpdateCollation' => 'maintenance/updateCollation.php',
- 'UpdateRestrictions' => 'maintenance/updateRestrictions.php',
- 'UserDupes' => 'maintenance/userDupes.inc',
-
- # maintenance/language
- 'CsvStatsOutput' => 'maintenance/language/StatOutputs.php',
- 'ExtensionLanguages' => 'maintenance/language/languages.inc',
- 'Languages' => 'maintenance/language/languages.inc',
- 'StatsOutput' => 'maintenance/language/StatOutputs.php',
- 'TextStatsOutput' => 'maintenance/language/StatOutputs.php',
- 'WikiStatsOutput' => 'maintenance/language/StatOutputs.php',
-
- # maintenance/term
- 'AnsiTermColorer' => 'maintenance/term/MWTerm.php',
- 'DummyTermColorer' => 'maintenance/term/MWTerm.php',
-
- # mw-config
- 'InstallerOverrides' => 'mw-config/overrides.php',
- 'MyLocalSettingsGenerator' => 'mw-config/overrides.php',
-);
+require_once __DIR__ . '/../autoload.php';
class AutoLoader {
static protected $autoloadLocalClassesLower = null;
diff --git a/includes/Autopromote.php b/includes/Autopromote.php
index 81f3b7aa..d492d196 100644
--- a/includes/Autopromote.php
+++ b/includes/Autopromote.php
@@ -43,7 +43,7 @@ class Autopromote {
}
}
- wfRunHooks( 'GetAutoPromoteGroups', array( $user, &$promote ) );
+ Hooks::run( 'GetAutoPromoteGroups', array( $user, &$promote ) );
return $promote;
}
@@ -197,7 +197,7 @@ class Autopromote {
return in_array( 'bot', User::getGroupPermissions( $user->getGroups() ) );
default:
$result = null;
- wfRunHooks( 'AutopromoteCondition', array( $cond[0],
+ Hooks::run( 'AutopromoteCondition', array( $cond[0],
array_slice( $cond, 1 ), $user, &$result ) );
if ( $result === null ) {
throw new MWException( "Unrecognized condition {$cond[0]} for autopromotion!" );
diff --git a/includes/Block.php b/includes/Block.php
index 6a29a056..873a26d8 100644
--- a/includes/Block.php
+++ b/includes/Block.php
@@ -113,7 +113,7 @@ class Block {
$this->forcedTargetID = $user; // needed for foreign users
}
if ( $by ) { // local user
- $this->setBlocker( User::newFromID( $by ) );
+ $this->setBlocker( User::newFromId( $by ) );
} else { // foreign user
$this->setBlocker( $byText );
}
@@ -366,7 +366,7 @@ class Block {
protected function initFromRow( $row ) {
$this->setTarget( $row->ipb_address );
if ( $row->ipb_by ) { // local user
- $this->setBlocker( User::newFromID( $row->ipb_by ) );
+ $this->setBlocker( User::newFromId( $row->ipb_by ) );
} else { // foreign user
$this->setBlocker( $row->ipb_by_text );
}
@@ -442,19 +442,33 @@ class Block {
$dbw = wfGetDB( DB_MASTER );
}
- # Don't collide with expired blocks
- Block::purgeExpired();
+ # Periodic purge via commit hooks
+ if ( mt_rand( 0, 9 ) == 0 ) {
+ Block::purgeExpired();
+ }
$row = $this->getDatabaseArray();
$row['ipb_id'] = $dbw->nextSequenceValue( "ipblocks_ipb_id_seq" );
- $dbw->insert(
- 'ipblocks',
- $row,
- __METHOD__,
- array( 'IGNORE' )
- );
+ $dbw->insert( 'ipblocks', $row, __METHOD__, array( 'IGNORE' ) );
$affected = $dbw->affectedRows();
+
+ # Don't collide with expired blocks.
+ # Do this after trying to insert to avoid pointless gap locks.
+ if ( !$affected ) {
+ $dbw->delete( 'ipblocks',
+ array(
+ 'ipb_address' => $row['ipb_address'],
+ 'ipb_user' => $row['ipb_user'],
+ 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() )
+ ),
+ __METHOD__
+ );
+
+ $dbw->insert( 'ipblocks', $row, __METHOD__, array( 'IGNORE' ) );
+ $affected = $dbw->affectedRows();
+ }
+
$this->mId = $dbw->insertId();
if ( $affected ) {
@@ -580,7 +594,7 @@ class Block {
if ( $this->isAutoblocking() && $this->getType() == self::TYPE_USER ) {
wfDebug( "Doing retroactive autoblocks for " . $this->getTarget() . "\n" );
- $continue = wfRunHooks(
+ $continue = Hooks::run(
'PerformRetroactiveAutoblock', array( $this, &$blockIds ) );
if ( $continue ) {
@@ -693,7 +707,7 @@ class Block {
}
# Allow hooks to cancel the autoblock.
- if ( !wfRunHooks( 'AbortAutoblock', array( $autoblockIP, &$this ) ) ) {
+ if ( !Hooks::run( 'AbortAutoblock', array( $autoblockIP, &$this ) ) ) {
wfDebug( "Autoblock aborted by hook.\n" );
return false;
}
@@ -752,7 +766,6 @@ class Block {
* @return bool
*/
public function deleteIfExpired() {
- wfProfileIn( __METHOD__ );
if ( $this->isExpired() ) {
wfDebug( "Block::deleteIfExpired() -- deleting\n" );
@@ -763,7 +776,6 @@ class Block {
$retVal = false;
}
- wfProfileOut( __METHOD__ );
return $retVal;
}
@@ -885,7 +897,7 @@ class Block {
/**
* Get/set a flag determining whether the master is used for reads
*
- * @param bool $x
+ * @param bool|null $x
* @return bool
*/
public function fromMaster( $x = null ) {
@@ -893,8 +905,8 @@ class Block {
}
/**
- * Get/set whether the Block is a hardblock (affects logged-in users on a given IP/range
- * @param bool $x
+ * Get/set whether the Block is a hardblock (affects logged-in users on a given IP/range)
+ * @param bool|null $x
* @return bool
*/
public function isHardblock( $x = null ) {
@@ -906,6 +918,10 @@ class Block {
: $this->isHardblock;
}
+ /**
+ * @param null|bool $x
+ * @return bool
+ */
public function isAutoblocking( $x = null ) {
wfSetVar( $this->isAutoblocking, $x );
@@ -919,7 +935,7 @@ class Block {
/**
* Get/set whether the Block prevents a given action
* @param string $action
- * @param bool $x
+ * @param bool|null $x
* @return bool
*/
public function prevents( $action, $x = null ) {
@@ -1051,7 +1067,6 @@ class Block {
return array();
}
- wfProfileIn( __METHOD__ );
$conds = array();
foreach ( array_unique( $ipChain ) as $ipaddr ) {
# Discard invalid IP addresses. Since XFF can be spoofed and we do not
@@ -1073,7 +1088,6 @@ class Block {
}
if ( !count( $conds ) ) {
- wfProfileOut( __METHOD__ );
return array();
}
@@ -1104,12 +1118,12 @@ class Block {
}
}
- wfProfileOut( __METHOD__ );
return $blocks;
}
/**
* From a list of multiple blocks, find the most exact and strongest Block.
+ *
* The logic for finding the "best" block is:
* - Blocks that match the block's target IP are preferred over ones in a range
* - Hardblocks are chosen over softblocks that prevent account creation
@@ -1117,12 +1131,15 @@ class Block {
* - Other softblocks are chosen over autoblocks
* - If there are multiple exact or range blocks at the same level, the one chosen
* is random
+ * This should be used when $blocks where retrieved from the user's IP address
+ * and $ipChain is populated from the same IP address information.
*
- * @param array $blocks Array of blocks
+ * @param array $blocks Array of Block objects
* @param array $ipChain List of IPs (strings). This is used to determine how "close"
* a block is to the server, and if a block matches exactly, or is in a range.
* The order is furthest from the server to nearest e.g., (Browser, proxy1, proxy2,
* local-squid, ...)
+ * @throws MWException
* @return Block|null The "best" block from the list
*/
public static function chooseBlock( array $blocks, array $ipChain ) {
@@ -1132,8 +1149,6 @@ class Block {
return $blocks[0];
}
- wfProfileIn( __METHOD__ );
-
// Sort hard blocks before soft ones and secondarily sort blocks
// that disable account creation before those that don't.
usort( $blocks, function ( Block $a, Block $b ) {
@@ -1156,6 +1171,7 @@ class Block {
);
$ipChain = array_reverse( $ipChain );
+ /** @var Block $block */
foreach ( $blocks as $block ) {
// Stop searching if we have already have a "better" block. This
// is why the order of the blocks matters
@@ -1213,11 +1229,9 @@ class Block {
} elseif ( $blocksList['auto'] ) {
$chosenBlock = $blocksList['auto'];
} else {
- wfProfileOut( __METHOD__ );
throw new MWException( "Proxy block found, but couldn't be classified." );
}
- wfProfileOut( __METHOD__ );
return $chosenBlock;
}
diff --git a/includes/Category.php b/includes/Category.php
index 322b0530..3a21e256 100644
--- a/includes/Category.php
+++ b/includes/Category.php
@@ -60,8 +60,6 @@ class Category {
return true;
}
- wfProfileIn( __METHOD__ );
-
$dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow(
'category',
@@ -70,8 +68,6 @@ class Category {
__METHOD__
);
- wfProfileOut( __METHOD__ );
-
if ( !$row ) {
# Okay, there were no contents. Nothing to initialize.
if ( $this->mTitle ) {
@@ -258,7 +254,6 @@ class Category {
* @return TitleArray TitleArray object for category members.
*/
public function getMembers( $limit = false, $offset = '' ) {
- wfProfileIn( __METHOD__ );
$dbr = wfGetDB( DB_SLAVE );
@@ -284,8 +279,6 @@ class Category {
)
);
- wfProfileOut( __METHOD__ );
-
return $result;
}
@@ -318,8 +311,6 @@ class Category {
}
}
- wfProfileIn( __METHOD__ );
-
$dbw = wfGetDB( DB_MASTER );
$dbw->startAtomic( __METHOD__ );
@@ -363,8 +354,6 @@ class Category {
);
$dbw->endAtomic( __METHOD__ );
- wfProfileOut( __METHOD__ );
-
# Now we should update our local counts.
$this->mPages = $result->pages;
$this->mSubcats = $result->subcats;
diff --git a/includes/CategoryFinder.php b/includes/CategoryFinder.php
index cf537e15..33de7404 100644
--- a/includes/CategoryFinder.php
+++ b/includes/CategoryFinder.php
@@ -185,7 +185,6 @@ class CategoryFinder {
* Scans a "parent layer" of the articles/categories in $this->next
*/
private function scanNextLayer() {
- $profiler = new ProfileSection( __METHOD__ );
# Find all parents of the article currently in $this->next
$layer = array();
diff --git a/includes/CategoryViewer.php b/includes/CategoryViewer.php
index 7581ae40..66079c01 100644
--- a/includes/CategoryViewer.php
+++ b/includes/CategoryViewer.php
@@ -89,6 +89,9 @@ class CategoryViewer extends ContextSource {
) {
$this->title = $title;
$this->setContext( $context );
+ $this->getOutput()->addModuleStyles( array(
+ 'mediawiki.action.view.categoryPage.styles'
+ ) );
$this->from = $from;
$this->until = $until;
$this->limit = $context->getConfig()->get( 'CategoryPagingLimit' );
@@ -104,7 +107,6 @@ class CategoryViewer extends ContextSource {
* @return string HTML output
*/
public function getHTML() {
- wfProfileIn( __METHOD__ );
$this->showGallery = $this->getConfig()->get( 'CategoryMagicGallery' )
&& !$this->getOutput()->mNoGallery;
@@ -136,11 +138,10 @@ class CategoryViewer extends ContextSource {
}
$lang = $this->getLanguage();
- $langAttribs = array( 'lang' => $lang->getCode(), 'dir' => $lang->getDir() );
+ $langAttribs = array( 'lang' => $lang->getHtmlCode(), 'dir' => $lang->getDir() );
# put a div around the headings which are in the user language
$r = Html::openElement( 'div', $langAttribs ) . $r . '</div>';
- wfProfileOut( __METHOD__ );
return $r;
}
@@ -154,7 +155,7 @@ class CategoryViewer extends ContextSource {
$mode = $this->getRequest()->getVal( 'gallerymode', null );
try {
$this->gallery = ImageGalleryBase::factory( $mode, $this->getContext() );
- } catch ( MWException $e ) {
+ } catch ( Exception $e ) {
// User specified something invalid, fallback to default.
$this->gallery = ImageGalleryBase::factory( false, $this->getContext() );
}
@@ -176,19 +177,30 @@ class CategoryViewer extends ContextSource {
// Subcategory; strip the 'Category' namespace from the link text.
$title = $cat->getTitle();
- $link = Linker::link( $title, htmlspecialchars( $title->getText() ) );
- if ( $title->isRedirect() ) {
- // This didn't used to add redirect-in-category, but might
- // as well be consistent with the rest of the sections
- // on a category page.
- $link = '<span class="redirect-in-category">' . $link . '</span>';
- }
- $this->children[] = $link;
+ $this->children[] = $this->generateLink(
+ 'subcat',
+ $title,
+ $title->isRedirect(),
+ htmlspecialchars( $title->getText() )
+ );
$this->children_start_char[] =
$this->getSubcategorySortChar( $cat->getTitle(), $sortkey );
}
+ function generateLink( $type, Title $title, $isRedirect, $html = null ) {
+ $link = null;
+ Hooks::run( 'CategoryViewer::generateLink', array( $type, $title, $html, &$link ) );
+ if ( $link === null ) {
+ $link = Linker::link( $title, $html );
+ }
+ if ( $isRedirect ) {
+ $link = '<span class="redirect-in-category">' . $link . '</span>';
+ }
+
+ return $link;
+ }
+
/**
* Get the character to be used for sorting subcategories.
* If there's a link from Category:A to Category:B, the sortkey of the resulting
@@ -231,13 +243,7 @@ class CategoryViewer extends ContextSource {
$this->gallery->add( $title );
}
} else {
- $link = Linker::link( $title );
- if ( $isRedirect ) {
- // This seems kind of pointless given 'mw-redirect' class,
- // but keeping for back-compatibility with user css.
- $link = '<span class="redirect-in-category">' . $link . '</span>';
- }
- $this->imgsNoGallery[] = $link;
+ $this->imgsNoGallery[] = $this->generateLink( 'image', $title, $isRedirect );
$this->imgsNoGallery_start_char[] = $wgContLang->convert(
$this->collation->getFirstLetter( $sortkey ) );
@@ -254,13 +260,7 @@ class CategoryViewer extends ContextSource {
function addPage( $title, $sortkey, $pageLength, $isRedirect = false ) {
global $wgContLang;
- $link = Linker::link( $title );
- if ( $isRedirect ) {
- // This seems kind of pointless given 'mw-redirect' class,
- // but keeping for back-compatibility with user css.
- $link = '<span class="redirect-in-category">' . $link . '</span>';
- }
- $this->articles[] = $link;
+ $this->articles[] = $this->generateLink( 'page', $title, $isRedirect );
$this->articles_start_char[] = $wgContLang->convert(
$this->collation->getFirstLetter( $sortkey ) );
@@ -333,6 +333,8 @@ class CategoryViewer extends ContextSource {
)
);
+ Hooks::run( 'CategoryViewer::doCategoryQuery', array( $type, $res ) );
+
$count = 0;
foreach ( $res as $row ) {
$title = Title::newFromRow( $row );
@@ -390,7 +392,7 @@ class CategoryViewer extends ContextSource {
if ( $rescnt > 0 ) {
# Showing subcategories
$r .= "<div id=\"mw-subcategories\">\n";
- $r .= '<h2>' . $this->msg( 'subcategories' )->text() . "</h2>\n";
+ $r .= '<h2>' . $this->msg( 'subcategories' )->parse() . "</h2>\n";
$r .= $countmsg;
$r .= $this->getSectionPagingLinks( 'subcat' );
$r .= $this->formatList( $this->children, $this->children_start_char );
@@ -419,7 +421,7 @@ class CategoryViewer extends ContextSource {
if ( $rescnt > 0 ) {
$r = "<div id=\"mw-pages\">\n";
- $r .= '<h2>' . $this->msg( 'category_header', $ti )->text() . "</h2>\n";
+ $r .= '<h2>' . $this->msg( 'category_header', $ti )->parse() . "</h2>\n";
$r .= $countmsg;
$r .= $this->getSectionPagingLinks( 'page' );
$r .= $this->formatList( $this->articles, $this->articles_start_char );
@@ -515,7 +517,7 @@ class CategoryViewer extends ContextSource {
}
$pageLang = $this->title->getPageLanguage();
- $attribs = array( 'lang' => $pageLang->getCode(), 'dir' => $pageLang->getDir(),
+ $attribs = array( 'lang' => $pageLang->getHtmlCode(), 'dir' => $pageLang->getDir(),
'class' => 'mw-content-' . $pageLang->getDir() );
$list = Html::rawElement( 'div', $attribs, $list );
@@ -529,8 +531,7 @@ class CategoryViewer extends ContextSource {
* TODO: Take the headers into account when creating columns, so they're
* more visually equal.
*
- * More distant TODO: Scrap this and use CSS columns, whenever IE finally
- * supports those.
+ * TODO: shortList and columnList are similar, need merging
*
* @param array $articles
* @param string[] $articles_start_char
@@ -539,50 +540,34 @@ class CategoryViewer extends ContextSource {
*/
static function columnList( $articles, $articles_start_char ) {
$columns = array_combine( $articles, $articles_start_char );
- # Split into three columns
- $columns = array_chunk( $columns, ceil( count( $columns ) / 3 ), true /* preserve keys */ );
- $ret = '<table style="width: 100%;"><tr style="vertical-align: top;">';
- $prevchar = null;
+ $ret = Html::openElement( 'div', array( 'class' => 'mw-category' ) );
- foreach ( $columns as $column ) {
- $ret .= '<td style="width: 33.3%;">';
- $colContents = array();
+ $colContents = array();
- # Kind of like array_flip() here, but we keep duplicates in an
- # array instead of dropping them.
- foreach ( $column as $article => $char ) {
- if ( !isset( $colContents[$char] ) ) {
- $colContents[$char] = array();
- }
- $colContents[$char][] = $article;
+ # Kind of like array_flip() here, but we keep duplicates in an
+ # array instead of dropping them.
+ foreach ( $columns as $article => $char ) {
+ if ( !isset( $colContents[$char] ) ) {
+ $colContents[$char] = array();
}
+ $colContents[$char][] = $article;
+ }
- $first = true;
- foreach ( $colContents as $char => $articles ) {
- # Change space to non-breaking space to keep headers aligned
- $h3char = $char === ' ' ? '&#160;' : htmlspecialchars( $char );
+ foreach ( $colContents as $char => $articles ) {
+ # Change space to non-breaking space to keep headers aligned
+ $h3char = $char === ' ' ? '&#160;' : htmlspecialchars( $char );
- $ret .= '<h3>' . $h3char;
- if ( $first && $char === $prevchar ) {
- # We're continuing a previous chunk at the top of a new
- # column, so add " cont." after the letter.
- $ret .= ' ' . wfMessage( 'listingcontinuesabbrev' )->escaped();
- }
- $ret .= "</h3>\n";
+ $ret .= '<div class="mw-category-group"><h3>' . $h3char;
+ $ret .= "</h3>\n";
- $ret .= '<ul><li>';
- $ret .= implode( "</li>\n<li>", $articles );
- $ret .= '</li></ul>';
-
- $first = false;
- $prevchar = $char;
- }
+ $ret .= '<ul><li>';
+ $ret .= implode( "</li>\n<li>", $articles );
+ $ret .= '</li></ul></div>';
- $ret .= "</td>\n";
}
- $ret .= '</tr></table>';
+ $ret .= Html::closeElement( 'div' );
return $ret;
}
@@ -618,7 +603,7 @@ class CategoryViewer extends ContextSource {
* @return string HTML
*/
private function pagingLinks( $first, $last, $type = '' ) {
- $prevLink = $this->msg( 'prevn' )->numParams( $this->limit )->escaped();
+ $prevLink = $this->msg( 'prev-page' )->text();
if ( $first != '' ) {
$prevQuery = $this->query;
@@ -632,7 +617,7 @@ class CategoryViewer extends ContextSource {
);
}
- $nextLink = $this->msg( 'nextn' )->numParams( $this->limit )->escaped();
+ $nextLink = $this->msg( 'next-page' )->text();
if ( $last != '' ) {
$lastQuery = $this->query;
diff --git a/includes/CdbCompat.php b/includes/CdbCompat.php
new file mode 100644
index 00000000..0074cc96
--- /dev/null
+++ b/includes/CdbCompat.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/***
+ * This file contains a set of backwards-compatability class names
+ * after the cdb functions were moved out into a separate library
+ * and put under a proper namespace
+ *
+ * @since 1.25
+ */
+
+/**
+ * @deprecated since 1.25
+ */
+abstract class CdbReader extends \Cdb\Reader {
+}
+
+/**
+ * @deprecated since 1.25
+ */
+abstract class CdbWriter extends \Cdb\Writer {
+}
+
+/**
+ * @deprecated since 1.25
+ */
+class CdbException extends \Cdb\Exception {
+}
diff --git a/includes/ChangeTags.php b/includes/ChangeTags.php
index 94b7b7a9..09665dfb 100644
--- a/includes/ChangeTags.php
+++ b/includes/ChangeTags.php
@@ -18,10 +18,18 @@
* http://www.gnu.org/copyleft/gpl.html
*
* @file
+ * @ingroup Change tagging
*/
class ChangeTags {
/**
+ * Can't delete tags with more than this many uses. Similar in intent to
+ * the bigdelete user right
+ * @todo Use the job queue for tag deletion to avoid this restriction
+ */
+ const MAX_DELETE_USES = 5000;
+
+ /**
* Creates HTML for the given tags
*
* @param string $tags Comma-separated list of tags
@@ -84,21 +92,50 @@ class ChangeTags {
*
* @throws MWException
* @return bool False if no changes are made, otherwise true
- *
- * @exception MWException When $rc_id, $rev_id and $log_id are all null
*/
public static function addTags( $tags, $rc_id = null, $rev_id = null,
$log_id = null, $params = null
) {
- if ( !is_array( $tags ) ) {
- $tags = array( $tags );
- }
+ $result = self::updateTags( $tags, null, $rc_id, $rev_id, $log_id, $params );
+ return (bool)$result[0];
+ }
+
+ /**
+ * Add and remove tags to/from a change given its rc_id, rev_id and/or log_id,
+ * without verifying that the tags exist or are valid. If a tag is present in
+ * both $tagsToAdd and $tagsToRemove, it will be removed.
+ *
+ * This function should only be used by extensions to manipulate tags they
+ * have registered using the ListDefinedTags hook. When dealing with user
+ * input, call updateTagsWithChecks() instead.
+ *
+ * @param string|array|null $tagsToAdd Tags to add to the change
+ * @param string|array|null $tagsToRemove Tags to remove from the change
+ * @param int|null &$rc_id The rc_id of the change to add the tags to.
+ * Pass a variable whose value is null if the rc_id is not relevant or unknown.
+ * @param int|null &$rev_id The rev_id of the change to add the tags to.
+ * Pass a variable whose value is null if the rev_id is not relevant or unknown.
+ * @param int|null &$log_id The log_id of the change to add the tags to.
+ * Pass a variable whose value is null if the log_id is not relevant or unknown.
+ * @param string $params Params to put in the ct_params field of table
+ * 'change_tag' when adding tags
+ *
+ * @throws MWException When $rc_id, $rev_id and $log_id are all null
+ * @return array Index 0 is an array of tags actually added, index 1 is an
+ * array of tags actually removed, index 2 is an array of tags present on the
+ * revision or log entry before any changes were made
+ *
+ * @since 1.25
+ */
+ public static function updateTags( $tagsToAdd, $tagsToRemove, &$rc_id = null,
+ &$rev_id = null, &$log_id = null, $params = null ) {
- $tags = array_filter( $tags ); // Make sure we're submitting all tags...
+ $tagsToAdd = array_filter( (array)$tagsToAdd ); // Make sure we're submitting all tags...
+ $tagsToRemove = array_filter( (array)$tagsToRemove );
if ( !$rc_id && !$rev_id && !$log_id ) {
throw new MWException( 'At least one of: RCID, revision ID, and log ID MUST be ' .
- 'specified when adding a tag to a change!' );
+ 'specified when adding or removing a tag from a change!' );
}
$dbw = wfGetDB( DB_MASTER );
@@ -137,11 +174,85 @@ class ChangeTags {
);
}
+ // update the tag_summary row
+ $prevTags = array();
+ if ( !self::updateTagSummaryRow( $tagsToAdd, $tagsToRemove, $rc_id, $rev_id,
+ $log_id, $prevTags ) ) {
+
+ // nothing to do
+ return array( array(), array(), $prevTags );
+ }
+
+ // insert a row into change_tag for each new tag
+ if ( count( $tagsToAdd ) ) {
+ $tagsRows = array();
+ foreach ( $tagsToAdd as $tag ) {
+ // Filter so we don't insert NULLs as zero accidentally.
+ // Keep in mind that $rc_id === null means "I don't care/know about the
+ // rc_id, just delete $tag on this revision/log entry". It doesn't
+ // mean "only delete tags on this revision/log WHERE rc_id IS NULL".
+ $tagsRows[] = array_filter(
+ array(
+ 'ct_tag' => $tag,
+ 'ct_rc_id' => $rc_id,
+ 'ct_log_id' => $log_id,
+ 'ct_rev_id' => $rev_id,
+ 'ct_params' => $params
+ )
+ );
+ }
+
+ $dbw->insert( 'change_tag', $tagsRows, __METHOD__, array( 'IGNORE' ) );
+ }
+
+ // delete from change_tag
+ if ( count( $tagsToRemove ) ) {
+ foreach ( $tagsToRemove as $tag ) {
+ $conds = array_filter(
+ array(
+ 'ct_tag' => $tag,
+ 'ct_rc_id' => $rc_id,
+ 'ct_log_id' => $log_id,
+ 'ct_rev_id' => $rev_id
+ )
+ );
+ $dbw->delete( 'change_tag', $conds, __METHOD__ );
+ }
+ }
+
+ self::purgeTagUsageCache();
+ return array( $tagsToAdd, $tagsToRemove, $prevTags );
+ }
+
+ /**
+ * Adds or removes a given set of tags to/from the relevant row of the
+ * tag_summary table. Modifies the tagsToAdd and tagsToRemove arrays to
+ * reflect the tags that were actually added and/or removed.
+ *
+ * @param array &$tagsToAdd
+ * @param array &$tagsToRemove If a tag is present in both $tagsToAdd and
+ * $tagsToRemove, it will be removed
+ * @param int|null $rc_id Null if not known or not applicable
+ * @param int|null $rev_id Null if not known or not applicable
+ * @param int|null $log_id Null if not known or not applicable
+ * @param array &$prevTags Optionally outputs a list of the tags that were
+ * in the tag_summary row to begin with
+ * @return bool True if any modifications were made, otherwise false
+ * @since 1.25
+ */
+ protected static function updateTagSummaryRow( &$tagsToAdd, &$tagsToRemove,
+ $rc_id, $rev_id, $log_id, &$prevTags = array() ) {
+
+ $dbw = wfGetDB( DB_MASTER );
+
$tsConds = array_filter( array(
'ts_rc_id' => $rc_id,
'ts_rev_id' => $rev_id,
- 'ts_log_id' => $log_id )
- );
+ 'ts_log_id' => $log_id
+ ) );
+
+ // Can't both add and remove a tag at the same time...
+ $tagsToAdd = array_diff( $tagsToAdd, $tagsToRemove );
// Update the summary row.
// $prevTags can be out of date on slaves, especially when addTags is called consecutively,
@@ -149,42 +260,277 @@ class ChangeTags {
$prevTags = $dbw->selectField( 'tag_summary', 'ts_tags', $tsConds, __METHOD__ );
$prevTags = $prevTags ? $prevTags : '';
$prevTags = array_filter( explode( ',', $prevTags ) );
- $newTags = array_unique( array_merge( $prevTags, $tags ) );
+
+ // add tags
+ $tagsToAdd = array_values( array_diff( $tagsToAdd, $prevTags ) );
+ $newTags = array_unique( array_merge( $prevTags, $tagsToAdd ) );
+
+ // remove tags
+ $tagsToRemove = array_values( array_intersect( $tagsToRemove, $newTags ) );
+ $newTags = array_values( array_diff( $newTags, $tagsToRemove ) );
+
sort( $prevTags );
sort( $newTags );
-
if ( $prevTags == $newTags ) {
// No change.
return false;
}
- $dbw->replace(
- 'tag_summary',
- array( 'ts_rev_id', 'ts_rc_id', 'ts_log_id' ),
- array_filter( array_merge( $tsConds, array( 'ts_tags' => implode( ',', $newTags ) ) ) ),
- __METHOD__
- );
-
- // Insert the tags rows.
- $tagsRows = array();
- foreach ( $tags as $tag ) { // Filter so we don't insert NULLs as zero accidentally.
- $tagsRows[] = array_filter(
- array(
- 'ct_tag' => $tag,
- 'ct_rc_id' => $rc_id,
- 'ct_log_id' => $log_id,
- 'ct_rev_id' => $rev_id,
- 'ct_params' => $params
- )
+ if ( !$newTags ) {
+ // no tags left, so delete the row altogether
+ $dbw->delete( 'tag_summary', $tsConds, __METHOD__ );
+ } else {
+ $dbw->replace( 'tag_summary',
+ array( 'ts_rev_id', 'ts_rc_id', 'ts_log_id' ),
+ array_filter( array_merge( $tsConds, array( 'ts_tags' => implode( ',', $newTags ) ) ) ),
+ __METHOD__
);
}
- $dbw->insert( 'change_tag', $tagsRows, __METHOD__, array( 'IGNORE' ) );
-
return true;
}
/**
+ * Helper function to generate a fatal status with a 'not-allowed' type error.
+ *
+ * @param string $msgOne Message key to use in the case of one tag
+ * @param string $msgMulti Message key to use in the case of more than one tag
+ * @param array $tags Restricted tags (passed as $1 into the message, count of
+ * $tags passed as $2)
+ * @return Status
+ * @since 1.25
+ */
+ protected static function restrictedTagError( $msgOne, $msgMulti, $tags ) {
+ $lang = RequestContext::getMain()->getLanguage();
+ $count = count( $tags );
+ return Status::newFatal( ( $count > 1 ) ? $msgMulti : $msgOne,
+ $lang->commaList( $tags ), $count );
+ }
+
+ /**
+ * Is it OK to allow the user to apply all the specified tags at the same time
+ * as they edit/make the change?
+ *
+ * @param array $tags Tags that you are interested in applying
+ * @param User|null $user User whose permission you wish to check, or null if
+ * you don't care (e.g. maintenance scripts)
+ * @return Status
+ * @since 1.25
+ */
+ public static function canAddTagsAccompanyingChange( array $tags,
+ User $user = null ) {
+
+ if ( !is_null( $user ) && !$user->isAllowed( 'applychangetags' ) ) {
+ return Status::newFatal( 'tags-apply-no-permission' );
+ }
+
+ // to be applied, a tag has to be explicitly defined
+ // @todo Allow extensions to define tags that can be applied by users...
+ $allowedTags = self::listExplicitlyDefinedTags();
+ $disallowedTags = array_diff( $tags, $allowedTags );
+ if ( $disallowedTags ) {
+ return self::restrictedTagError( 'tags-apply-not-allowed-one',
+ 'tags-apply-not-allowed-multi', $disallowedTags );
+ }
+
+ return Status::newGood();
+ }
+
+ /**
+ * Adds tags to a given change, checking whether it is allowed first, but
+ * without adding a log entry. Useful for cases where the tag is being added
+ * along with the action that generated the change (e.g. tagging an edit as
+ * it is being made).
+ *
+ * Extensions should not use this function, unless directly handling a user
+ * request to add a particular tag. Normally, extensions should call
+ * ChangeTags::updateTags() instead.
+ *
+ * @param array $tags Tags to apply
+ * @param int|null $rc_id The rc_id of the change to add the tags to
+ * @param int|null $rev_id The rev_id of the change to add the tags to
+ * @param int|null $log_id The log_id of the change to add the tags to
+ * @param string $params Params to put in the ct_params field of table
+ * 'change_tag' when adding tags
+ * @param User $user Who to give credit for the action
+ * @return Status
+ * @since 1.25
+ */
+ public static function addTagsAccompanyingChangeWithChecks( array $tags,
+ $rc_id, $rev_id, $log_id, $params, User $user ) {
+
+ // are we allowed to do this?
+ $result = self::canAddTagsAccompanyingChange( $tags, $user );
+ if ( !$result->isOK() ) {
+ $result->value = null;
+ return $result;
+ }
+
+ // do it!
+ self::addTags( $tagsToAdd, $rc_id, $rev_id, $log_id, $params );
+
+ return Status::newGood( true );
+ }
+
+ /**
+ * Is it OK to allow the user to adds and remove the given tags tags to/from a
+ * change?
+ *
+ * @param array $tagsToAdd Tags that you are interested in adding
+ * @param array $tagsToRemove Tags that you are interested in removing
+ * @param User|null $user User whose permission you wish to check, or null if
+ * you don't care (e.g. maintenance scripts)
+ * @return Status
+ * @since 1.25
+ */
+ public static function canUpdateTags( array $tagsToAdd, array $tagsToRemove,
+ User $user = null ) {
+
+ if ( !is_null( $user ) && !$user->isAllowed( 'changetags' ) ) {
+ return Status::newFatal( 'tags-update-no-permission' );
+ }
+
+ // to be added, a tag has to be explicitly defined
+ // @todo Allow extensions to define tags that can be applied by users...
+ $explicitlyDefinedTags = self::listExplicitlyDefinedTags();
+ $diff = array_diff( $tagsToAdd, $explicitlyDefinedTags );
+ if ( $diff ) {
+ return self::restrictedTagError( 'tags-update-add-not-allowed-one',
+ 'tags-update-add-not-allowed-multi', $diff );
+ }
+
+ // to be removed, a tag has to be either explicitly defined or not defined
+ // at all
+ $definedTags = self::listDefinedTags();
+ $diff = array_diff( $tagsToRemove, $explicitlyDefinedTags );
+ if ( $diff ) {
+ $intersect = array_intersect( $diff, $definedTags );
+ if ( $intersect ) {
+ return self::restrictedTagError( 'tags-update-remove-not-allowed-one',
+ 'tags-update-remove-not-allowed-multi', $intersect );
+ }
+ }
+
+ return Status::newGood();
+ }
+
+ /**
+ * Adds and/or removes tags to/from a given change, checking whether it is
+ * allowed first, and adding a log entry afterwards.
+ *
+ * Includes a call to ChangeTag::canUpdateTags(), so your code doesn't need
+ * to do that. However, it doesn't check whether the *_id parameters are a
+ * valid combination. That is up to you to enforce. See ApiTag::execute() for
+ * an example.
+ *
+ * @param array|null $tagsToAdd If none, pass array() or null
+ * @param array|null $tagsToRemove If none, pass array() or null
+ * @param int|null $rc_id The rc_id of the change to add the tags to
+ * @param int|null $rev_id The rev_id of the change to add the tags to
+ * @param int|null $log_id The log_id of the change to add the tags to
+ * @param string $params Params to put in the ct_params field of table
+ * 'change_tag' when adding tags
+ * @param string $reason Comment for the log
+ * @param User $user Who to give credit for the action
+ * @return Status If successful, the value of this Status object will be an
+ * object (stdClass) with the following fields:
+ * - logId: the ID of the added log entry, or null if no log entry was added
+ * (i.e. no operation was performed)
+ * - addedTags: an array containing the tags that were actually added
+ * - removedTags: an array containing the tags that were actually removed
+ * @since 1.25
+ */
+ public static function updateTagsWithChecks( $tagsToAdd, $tagsToRemove,
+ $rc_id, $rev_id, $log_id, $params, $reason, User $user ) {
+
+ if ( is_null( $tagsToAdd ) ) {
+ $tagsToAdd = array();
+ }
+ if ( is_null( $tagsToRemove ) ) {
+ $tagsToRemove = array();
+ }
+ if ( !$tagsToAdd && !$tagsToRemove ) {
+ // no-op, don't bother
+ return Status::newGood( (object)array(
+ 'logId' => null,
+ 'addedTags' => array(),
+ 'removedTags' => array(),
+ ) );
+ }
+
+ // are we allowed to do this?
+ $result = self::canUpdateTags( $tagsToAdd, $tagsToRemove, $user );
+ if ( !$result->isOK() ) {
+ $result->value = null;
+ return $result;
+ }
+
+ // basic rate limiting
+ if ( $user->pingLimiter( 'changetag' ) ) {
+ return Status::newFatal( 'actionthrottledtext' );
+ }
+
+ // do it!
+ list( $tagsAdded, $tagsRemoved, $initialTags ) = self::updateTags( $tagsToAdd,
+ $tagsToRemove, $rc_id, $rev_id, $log_id, $params );
+ if ( !$tagsAdded && !$tagsRemoved ) {
+ // no-op, don't log it
+ return Status::newGood( (object)array(
+ 'logId' => null,
+ 'addedTags' => array(),
+ 'removedTags' => array(),
+ ) );
+ }
+
+ // log it
+ $logEntry = new ManualLogEntry( 'tag', 'update' );
+ $logEntry->setPerformer( $user );
+ $logEntry->setComment( $reason );
+
+ // find the appropriate target page
+ if ( $rev_id ) {
+ $rev = Revision::newFromId( $rev_id );
+ if ( $rev ) {
+ $title = $rev->getTitle();
+ $logEntry->setTarget( $rev->getTitle() );
+ }
+ } elseif ( $log_id ) {
+ // This function is from revision deletion logic and has nothing to do with
+ // change tags, but it appears to be the only other place in core where we
+ // perform logged actions on log items.
+ $logEntry->setTarget( RevDelLogList::suggestTarget( 0, array( $log_id ) ) );
+ }
+
+ if ( !$logEntry->getTarget() ) {
+ // target is required, so we have to set something
+ $logEntry->setTarget( SpecialPage::getTitleFor( 'Tags' ) );
+ }
+
+ $logParams = array(
+ '4::revid' => $rev_id,
+ '5::logid' => $log_id,
+ '6:list:tagsAdded' => $tagsAdded,
+ '7:number:tagsAddedCount' => count( $tagsAdded ),
+ '8:list:tagsRemoved' => $tagsRemoved,
+ '9:number:tagsRemovedCount' => count( $tagsRemoved ),
+ 'initialTags' => $initialTags,
+ );
+ $logEntry->setParameters( $logParams );
+ $logEntry->setRelations( array( 'Tag' => array_merge( $tagsAdded, $tagsRemoved ) ) );
+
+ $dbw = wfGetDB( DB_MASTER );
+ $logId = $logEntry->insert( $dbw );
+ // Only send this to UDP, not RC, similar to patrol events
+ $logEntry->publish( $logId, 'udp' );
+
+ return Status::newGood( (object)array(
+ 'logId' => $logId,
+ 'addedTags' => $tagsAdded,
+ 'removedTags' => $tagsRemoved,
+ ) );
+ }
+
+ /**
* Applies all tags-related changes to a query.
* Handles selecting tags, and filtering.
* Needs $tables to be set up properly, so we can figure out which join conditions to use.
@@ -265,7 +611,7 @@ class ChangeTags {
'tagfilter',
20,
$selected,
- array( 'class' => 'mw-tagfilter-input', 'id' => 'tagfilter' )
+ array( 'class' => 'mw-tagfilter-input mw-ui-input mw-ui-input-inline', 'id' => 'tagfilter' )
)
);
@@ -290,18 +636,451 @@ class ChangeTags {
}
/**
+ * Defines a tag in the valid_tag table, without checking that the tag name
+ * is valid.
+ * Extensions should NOT use this function; they can use the ListDefinedTags
+ * hook instead.
+ *
+ * @param string $tag Tag to create
+ * @since 1.25
+ */
+ public static function defineTag( $tag ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->replace( 'valid_tag',
+ array( 'vt_tag' ),
+ array( 'vt_tag' => $tag ),
+ __METHOD__ );
+
+ // clear the memcache of defined tags
+ self::purgeTagCacheAll();
+ }
+
+ /**
+ * Removes a tag from the valid_tag table. The tag may remain in use by
+ * extensions, and may still show up as 'defined' if an extension is setting
+ * it from the ListDefinedTags hook.
+ *
+ * @param string $tag Tag to remove
+ * @since 1.25
+ */
+ public static function undefineTag( $tag ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->delete( 'valid_tag', array( 'vt_tag' => $tag ), __METHOD__ );
+
+ // clear the memcache of defined tags
+ self::purgeTagCacheAll();
+ }
+
+ /**
+ * Writes a tag action into the tag management log.
+ *
+ * @param string $action
+ * @param string $tag
+ * @param string $reason
+ * @param User $user Who to attribute the action to
+ * @param int $tagCount For deletion only, how many usages the tag had before
+ * it was deleted.
+ * @since 1.25
+ */
+ protected static function logTagManagementAction( $action, $tag, $reason,
+ User $user, $tagCount = null ) {
+
+ $dbw = wfGetDB( DB_MASTER );
+
+ $logEntry = new ManualLogEntry( 'managetags', $action );
+ $logEntry->setPerformer( $user );
+ // target page is not relevant, but it has to be set, so we just put in
+ // the title of Special:Tags
+ $logEntry->setTarget( Title::newFromText( 'Special:Tags' ) );
+ $logEntry->setComment( $reason );
+
+ $params = array( '4::tag' => $tag );
+ if ( !is_null( $tagCount ) ) {
+ $params['5:number:count'] = $tagCount;
+ }
+ $logEntry->setParameters( $params );
+ $logEntry->setRelations( array( 'Tag' => $tag ) );
+
+ $logId = $logEntry->insert( $dbw );
+ $logEntry->publish( $logId );
+ return $logId;
+ }
+
+ /**
+ * Is it OK to allow the user to activate this tag?
+ *
+ * @param string $tag Tag that you are interested in activating
+ * @param User|null $user User whose permission you wish to check, or null if
+ * you don't care (e.g. maintenance scripts)
+ * @return Status
+ * @since 1.25
+ */
+ public static function canActivateTag( $tag, User $user = null ) {
+ if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
+ return Status::newFatal( 'tags-manage-no-permission' );
+ }
+
+ // non-existing tags cannot be activated
+ $tagUsage = self::tagUsageStatistics();
+ if ( !isset( $tagUsage[$tag] ) ) {
+ return Status::newFatal( 'tags-activate-not-found', $tag );
+ }
+
+ // defined tags cannot be activated (a defined tag is either extension-
+ // defined, in which case the extension chooses whether or not to active it;
+ // or user-defined, in which case it is considered active)
+ $definedTags = self::listDefinedTags();
+ if ( in_array( $tag, $definedTags ) ) {
+ return Status::newFatal( 'tags-activate-not-allowed', $tag );
+ }
+
+ return Status::newGood();
+ }
+
+ /**
+ * Activates a tag, checking whether it is allowed first, and adding a log
+ * entry afterwards.
+ *
+ * Includes a call to ChangeTag::canActivateTag(), so your code doesn't need
+ * to do that.
+ *
+ * @param string $tag
+ * @param string $reason
+ * @param User $user Who to give credit for the action
+ * @param bool $ignoreWarnings Can be used for API interaction, default false
+ * @return Status If successful, the Status contains the ID of the added log
+ * entry as its value
+ * @since 1.25
+ */
+ public static function activateTagWithChecks( $tag, $reason, User $user,
+ $ignoreWarnings = false ) {
+
+ // are we allowed to do this?
+ $result = self::canActivateTag( $tag, $user );
+ if ( $ignoreWarnings ? !$result->isOK() : !$result->isGood() ) {
+ $result->value = null;
+ return $result;
+ }
+
+ // do it!
+ self::defineTag( $tag );
+
+ // log it
+ $logId = self::logTagManagementAction( 'activate', $tag, $reason, $user );
+ return Status::newGood( $logId );
+ }
+
+ /**
+ * Is it OK to allow the user to deactivate this tag?
+ *
+ * @param string $tag Tag that you are interested in deactivating
+ * @param User|null $user User whose permission you wish to check, or null if
+ * you don't care (e.g. maintenance scripts)
+ * @return Status
+ * @since 1.25
+ */
+ public static function canDeactivateTag( $tag, User $user = null ) {
+ if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
+ return Status::newFatal( 'tags-manage-no-permission' );
+ }
+
+ // only explicitly-defined tags can be deactivated
+ $explicitlyDefinedTags = self::listExplicitlyDefinedTags();
+ if ( !in_array( $tag, $explicitlyDefinedTags ) ) {
+ return Status::newFatal( 'tags-deactivate-not-allowed', $tag );
+ }
+ return Status::newGood();
+ }
+
+ /**
+ * Deactivates a tag, checking whether it is allowed first, and adding a log
+ * entry afterwards.
+ *
+ * Includes a call to ChangeTag::canDeactivateTag(), so your code doesn't need
+ * to do that.
+ *
+ * @param string $tag
+ * @param string $reason
+ * @param User $user Who to give credit for the action
+ * @param bool $ignoreWarnings Can be used for API interaction, default false
+ * @return Status If successful, the Status contains the ID of the added log
+ * entry as its value
+ * @since 1.25
+ */
+ public static function deactivateTagWithChecks( $tag, $reason, User $user,
+ $ignoreWarnings = false ) {
+
+ // are we allowed to do this?
+ $result = self::canDeactivateTag( $tag, $user );
+ if ( $ignoreWarnings ? !$result->isOK() : !$result->isGood() ) {
+ $result->value = null;
+ return $result;
+ }
+
+ // do it!
+ self::undefineTag( $tag );
+
+ // log it
+ $logId = self::logTagManagementAction( 'deactivate', $tag, $reason, $user );
+ return Status::newGood( $logId );
+ }
+
+ /**
+ * Is it OK to allow the user to create this tag?
+ *
+ * @param string $tag Tag that you are interested in creating
+ * @param User|null $user User whose permission you wish to check, or null if
+ * you don't care (e.g. maintenance scripts)
+ * @return Status
+ * @since 1.25
+ */
+ public static function canCreateTag( $tag, User $user = null ) {
+ if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
+ return Status::newFatal( 'tags-manage-no-permission' );
+ }
+
+ // no empty tags
+ if ( $tag === '' ) {
+ return Status::newFatal( 'tags-create-no-name' );
+ }
+
+ // tags cannot contain commas (used as a delimiter in tag_summary table) or
+ // slashes (would break tag description messages in MediaWiki namespace)
+ if ( strpos( $tag, ',' ) !== false || strpos( $tag, '/' ) !== false ) {
+ return Status::newFatal( 'tags-create-invalid-chars' );
+ }
+
+ // could the MediaWiki namespace description messages be created?
+ $title = Title::makeTitleSafe( NS_MEDIAWIKI, "Tag-$tag-description" );
+ if ( is_null( $title ) ) {
+ return Status::newFatal( 'tags-create-invalid-title-chars' );
+ }
+
+ // does the tag already exist?
+ $tagUsage = self::tagUsageStatistics();
+ if ( isset( $tagUsage[$tag] ) ) {
+ return Status::newFatal( 'tags-create-already-exists', $tag );
+ }
+
+ // check with hooks
+ $canCreateResult = Status::newGood();
+ Hooks::run( 'ChangeTagCanCreate', array( $tag, $user, &$canCreateResult ) );
+ return $canCreateResult;
+ }
+
+ /**
+ * Creates a tag by adding a row to the `valid_tag` table.
+ *
+ * Includes a call to ChangeTag::canDeleteTag(), so your code doesn't need to
+ * do that.
+ *
+ * @param string $tag
+ * @param string $reason
+ * @param User $user Who to give credit for the action
+ * @param bool $ignoreWarnings Can be used for API interaction, default false
+ * @return Status If successful, the Status contains the ID of the added log
+ * entry as its value
+ * @since 1.25
+ */
+ public static function createTagWithChecks( $tag, $reason, User $user,
+ $ignoreWarnings = false ) {
+
+ // are we allowed to do this?
+ $result = self::canCreateTag( $tag, $user );
+ if ( $ignoreWarnings ? !$result->isOK() : !$result->isGood() ) {
+ $result->value = null;
+ return $result;
+ }
+
+ // do it!
+ self::defineTag( $tag );
+
+ // log it
+ $logId = self::logTagManagementAction( 'create', $tag, $reason, $user );
+ return Status::newGood( $logId );
+ }
+
+ /**
+ * Permanently removes all traces of a tag from the DB. Good for removing
+ * misspelt or temporary tags.
+ *
+ * This function should be directly called by maintenance scripts only, never
+ * by user-facing code. See deleteTagWithChecks() for functionality that can
+ * safely be exposed to users.
+ *
+ * @param string $tag Tag to remove
+ * @return Status The returned status will be good unless a hook changed it
+ * @since 1.25
+ */
+ public static function deleteTagEverywhere( $tag ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin( __METHOD__ );
+
+ // delete from valid_tag
+ self::undefineTag( $tag );
+
+ // find out which revisions use this tag, so we can delete from tag_summary
+ $result = $dbw->select( 'change_tag',
+ array( 'ct_rc_id', 'ct_log_id', 'ct_rev_id', 'ct_tag' ),
+ array( 'ct_tag' => $tag ),
+ __METHOD__ );
+ foreach ( $result as $row ) {
+ // remove the tag from the relevant row of tag_summary
+ $tagsToAdd = array();
+ $tagsToRemove = array( $tag );
+ self::updateTagSummaryRow( $tagsToAdd, $tagsToRemove, $row->ct_rc_id,
+ $row->ct_rev_id, $row->ct_log_id );
+ }
+
+ // delete from change_tag
+ $dbw->delete( 'change_tag', array( 'ct_tag' => $tag ), __METHOD__ );
+
+ $dbw->commit( __METHOD__ );
+
+ // give extensions a chance
+ $status = Status::newGood();
+ Hooks::run( 'ChangeTagAfterDelete', array( $tag, &$status ) );
+ // let's not allow error results, as the actual tag deletion succeeded
+ if ( !$status->isOK() ) {
+ wfDebug( 'ChangeTagAfterDelete error condition downgraded to warning' );
+ $status->ok = true;
+ }
+
+ // clear the memcache of defined tags
+ self::purgeTagCacheAll();
+
+ return $status;
+ }
+
+ /**
+ * Is it OK to allow the user to delete this tag?
+ *
+ * @param string $tag Tag that you are interested in deleting
+ * @param User|null $user User whose permission you wish to check, or null if
+ * you don't care (e.g. maintenance scripts)
+ * @return Status
+ * @since 1.25
+ */
+ public static function canDeleteTag( $tag, User $user = null ) {
+ $tagUsage = self::tagUsageStatistics();
+
+ if ( !is_null( $user ) && !$user->isAllowed( 'managechangetags' ) ) {
+ return Status::newFatal( 'tags-manage-no-permission' );
+ }
+
+ if ( !isset( $tagUsage[$tag] ) ) {
+ return Status::newFatal( 'tags-delete-not-found', $tag );
+ }
+
+ if ( $tagUsage[$tag] > self::MAX_DELETE_USES ) {
+ return Status::newFatal( 'tags-delete-too-many-uses', $tag, self::MAX_DELETE_USES );
+ }
+
+ $extensionDefined = self::listExtensionDefinedTags();
+ if ( in_array( $tag, $extensionDefined ) ) {
+ // extension-defined tags can't be deleted unless the extension
+ // specifically allows it
+ $status = Status::newFatal( 'tags-delete-not-allowed' );
+ } else {
+ // user-defined tags are deletable unless otherwise specified
+ $status = Status::newGood();
+ }
+
+ Hooks::run( 'ChangeTagCanDelete', array( $tag, $user, &$status ) );
+ return $status;
+ }
+
+ /**
+ * Deletes a tag, checking whether it is allowed first, and adding a log entry
+ * afterwards.
+ *
+ * Includes a call to ChangeTag::canDeleteTag(), so your code doesn't need to
+ * do that.
+ *
+ * @param string $tag
+ * @param string $reason
+ * @param User $user Who to give credit for the action
+ * @param bool $ignoreWarnings Can be used for API interaction, default false
+ * @return Status If successful, the Status contains the ID of the added log
+ * entry as its value
+ * @since 1.25
+ */
+ public static function deleteTagWithChecks( $tag, $reason, User $user,
+ $ignoreWarnings = false ) {
+
+ // are we allowed to do this?
+ $result = self::canDeleteTag( $tag, $user );
+ if ( $ignoreWarnings ? !$result->isOK() : !$result->isGood() ) {
+ $result->value = null;
+ return $result;
+ }
+
+ // store the tag usage statistics
+ $tagUsage = self::tagUsageStatistics();
+
+ // do it!
+ $deleteResult = self::deleteTagEverywhere( $tag );
+ if ( !$deleteResult->isOK() ) {
+ return $deleteResult;
+ }
+
+ // log it
+ $logId = self::logTagManagementAction( 'delete', $tag, $reason, $user, $tagUsage[$tag] );
+ $deleteResult->value = $logId;
+ return $deleteResult;
+ }
+
+ /**
+ * Lists those tags which extensions report as being "active".
+ *
+ * @return array
+ * @since 1.25
+ */
+ public static function listExtensionActivatedTags() {
+ // Caching...
+ global $wgMemc;
+ $key = wfMemcKey( 'active-tags' );
+ $tags = $wgMemc->get( $key );
+ if ( $tags ) {
+ return $tags;
+ }
+
+ // ask extensions which tags they consider active
+ $extensionActive = array();
+ Hooks::run( 'ChangeTagsListActive', array( &$extensionActive ) );
+
+ // Short-term caching.
+ $wgMemc->set( $key, $extensionActive, 300 );
+ return $extensionActive;
+ }
+
+ /**
* Basically lists defined tags which count even if they aren't applied to anything.
- * Tags on items in table 'change_tag' which are not (or no longer) in table 'valid_tag'
- * are not included.
+ * It returns a union of the results of listExplicitlyDefinedTags() and
+ * listExtensionDefinedTags().
+ *
+ * @return string[] Array of strings: tags
+ */
+ public static function listDefinedTags() {
+ $tags1 = self::listExplicitlyDefinedTags();
+ $tags2 = self::listExtensionDefinedTags();
+ return array_values( array_unique( array_merge( $tags1, $tags2 ) ) );
+ }
+
+ /**
+ * Lists tags explicitly defined in the `valid_tag` table of the database.
+ * Tags in table 'change_tag' which are not in table 'valid_tag' are not
+ * included.
*
* Tries memcached first.
*
* @return string[] Array of strings: tags
+ * @since 1.25
*/
- public static function listDefinedTags() {
+ public static function listExplicitlyDefinedTags() {
// Caching...
global $wgMemc;
- $key = wfMemcKey( 'valid-tags' );
+ $key = wfMemcKey( 'valid-tags-db' );
$tags = $wgMemc->get( $key );
if ( $tags ) {
return $tags;
@@ -316,8 +1095,33 @@ class ChangeTags {
$emptyTags[] = $row->vt_tag;
}
- wfRunHooks( 'ListDefinedTags', array( &$emptyTags ) );
+ $emptyTags = array_filter( array_unique( $emptyTags ) );
+ // Short-term caching.
+ $wgMemc->set( $key, $emptyTags, 300 );
+ return $emptyTags;
+ }
+
+ /**
+ * Lists tags defined by extensions using the ListDefinedTags hook.
+ * Extensions need only define those tags they deem to be in active use.
+ *
+ * Tries memcached first.
+ *
+ * @return string[] Array of strings: tags
+ * @since 1.25
+ */
+ public static function listExtensionDefinedTags() {
+ // Caching...
+ global $wgMemc;
+ $key = wfMemcKey( 'valid-tags-hook' );
+ $tags = $wgMemc->get( $key );
+ if ( $tags ) {
+ return $tags;
+ }
+
+ $emptyTags = array();
+ Hooks::run( 'ListDefinedTags', array( &$emptyTags ) );
$emptyTags = array_filter( array_unique( $emptyTags ) );
// Short-term caching.
@@ -326,12 +1130,45 @@ class ChangeTags {
}
/**
+ * Invalidates the short-term cache of defined tags used by the
+ * list*DefinedTags functions, as well as the tag statistics cache.
+ * @since 1.25
+ */
+ public static function purgeTagCacheAll() {
+ global $wgMemc;
+ $wgMemc->delete( wfMemcKey( 'active-tags' ) );
+ $wgMemc->delete( wfMemcKey( 'valid-tags-db' ) );
+ $wgMemc->delete( wfMemcKey( 'valid-tags-hook' ) );
+ self::purgeTagUsageCache();
+ }
+
+ /**
+ * Invalidates the tag statistics cache only.
+ * @since 1.25
+ */
+ public static function purgeTagUsageCache() {
+ global $wgMemc;
+ $wgMemc->delete( wfMemcKey( 'change-tag-statistics' ) );
+ }
+
+ /**
* Returns a map of any tags used on the wiki to number of edits
* tagged with them, ordered descending by the hitcount.
*
+ * Keeps a short-term cache in memory, so calling this multiple times in the
+ * same request should be fine.
+ *
* @return array Array of string => int
*/
public static function tagUsageStatistics() {
+ // Caching...
+ global $wgMemc;
+ $key = wfMemcKey( 'change-tag-statistics' );
+ $stats = $wgMemc->get( $key );
+ if ( $stats ) {
+ return $stats;
+ }
+
$out = array();
$dbr = wfGetDB( DB_SLAVE );
@@ -352,6 +1189,26 @@ class ChangeTags {
}
}
+ // Cache for a very short time
+ $wgMemc->set( $key, $out, 300 );
return $out;
}
+
+ /**
+ * Indicate whether change tag editing UI is relevant
+ *
+ * Returns true if the user has the necessary right and there are any
+ * editable tags defined.
+ *
+ * This intentionally doesn't check "any addable || any deletable", because
+ * it seems like it would be more confusing than useful if the checkboxes
+ * suddenly showed up because some abuse filter stopped defining a tag and
+ * then suddenly disappeared when someone deleted all uses of that tag.
+ *
+ * @param User $user
+ * @return bool
+ */
+ public static function showTagEditingUI( User $user ) {
+ return $user->isAllowed( 'changetags' ) && (bool)self::listExplicitlyDefinedTags();
+ }
}
diff --git a/includes/Collation.php b/includes/Collation.php
index 1c2c2db3..481d8e70 100644
--- a/includes/Collation.php
+++ b/includes/Collation.php
@@ -59,7 +59,7 @@ abstract class Collation {
# Provide a mechanism for extensions to hook in.
$collationObject = null;
- wfRunHooks( 'Collation::factory', array( $collationName, &$collationObject ) );
+ Hooks::run( 'Collation::factory', array( $collationName, &$collationObject ) );
if ( $collationObject instanceof Collation ) {
return $collationObject;
@@ -341,7 +341,7 @@ class IcuCollation extends Collation {
// Check for CJK
$firstChar = mb_substr( $string, 0, 1, 'UTF-8' );
- if ( ord( $firstChar ) > 0x7f && self::isCjk( utf8ToCodepoint( $firstChar ) ) ) {
+ if ( ord( $firstChar ) > 0x7f && self::isCjk( UtfNormal\Utils::utf8ToCodepoint( $firstChar ) ) ) {
return $firstChar;
}
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index aad42aac..010c471c 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -52,6 +52,8 @@ if ( !defined( 'MEDIAWIKI' ) ) {
die( 1 );
}
+/** @endcond */
+
/**
* wgConf hold the site configuration.
* Not used for much in a default install.
@@ -71,11 +73,9 @@ $wgConfigRegistry = array(
/**
* MediaWiki version number
- * Note that MediaWikiVersionFetcher::fetchVersion() uses a regex to check this.
- * Using single quotes is, therefore, important here.
* @since 1.2
*/
-$wgVersion = '1.24.2';
+$wgVersion = '1.25.1';
/**
* Name of the site. It must be changed in LocalSettings.php
@@ -154,12 +154,15 @@ $wgUsePathInfo = ( strpos( PHP_SAPI, 'cgi' ) === false ) &&
( strpos( PHP_SAPI, 'isapi' ) === false );
/**
- * The extension to append to script names by default. This can either be .php
- * or .php5.
+ * The extension to append to script names by default.
+ *
+ * Some hosting providers used PHP 4 for *.php files, and PHP 5 for *.php5.
+ * This variable was provided to support those providers.
*
- * Some hosting providers use PHP 4 for *.php files, and PHP 5 for *.php5. This
- * variable is provided to support those providers.
* @since 1.11
+ * @deprecated since 1.25; support for '.php5' is being phased out of MediaWiki
+ * proper. Backward-compatibility can be maintained by configuring your web
+ * server to rewrite URLs. See RELEASE-NOTES for details.
*/
$wgScriptExtension = '.php';
@@ -221,11 +224,18 @@ $wgLocalStylePath = false;
$wgExtensionAssetsPath = false;
/**
+ * Filesystem extensions directory.
+ * Defaults to "{$IP}/extensions".
+ * @since 1.25
+ */
+$wgExtensionDirectory = "{$IP}/extensions";
+
+/**
* Filesystem stylesheets directory.
* Defaults to "{$IP}/skins".
* @since 1.3
*/
-$wgStyleDirectory = false;
+$wgStyleDirectory = "{$IP}/skins";
/**
* The URL path for primary article page views. This path should contain $1,
@@ -260,6 +270,23 @@ $wgFileCacheDirectory = false;
$wgLogo = false;
/**
+ * Array with URL paths to HD versions of the wiki logo. The scaled logo size
+ * should be under 135x155 pixels.
+ * Only 1.5x and 2x versions are supported.
+ *
+ * @par Example:
+ * @code
+ * $wgLogoHD = array(
+ * "1.5x" => "path/to/1.5x_version.png",
+ * "2x" => "path/to/2x_version.png"
+ * );
+ * @endcode
+ *
+ * @since 1.25
+ */
+$wgLogoHD = false;
+
+/**
* The URL path of the shortcut icon.
* @since 1.6
*/
@@ -273,6 +300,16 @@ $wgFavicon = '/favicon.ico';
$wgAppleTouchIcon = false;
/**
+ * Value for the referrer policy meta tag.
+ * One of 'never', 'default', 'origin', 'always'. Setting it to false just
+ * prevents the meta tag from being output.
+ * See http://www.w3.org/TR/referrer-policy/ for details.
+ *
+ * @since 1.25
+ */
+$wgReferrerPolicy = false;
+
+/**
* The local filesystem path to a temporary directory. This is not required to
* be web accessible.
*
@@ -943,12 +980,13 @@ $wgExiv2Command = '/usr/bin/exiv2';
* are passed as parameters after $srcPath, $dstPath, $width, $height
*/
$wgSVGConverters = array(
- 'ImageMagick' => '$path/convert -background white -thumbnail $widthx$height\! $input PNG:$output',
+ 'ImageMagick' =>
+ '$path/convert -background "#ffffff00" -thumbnail $widthx$height\! $input PNG:$output',
'sodipodi' => '$path/sodipodi -z -w $width -f $input -e $output',
'inkscape' => '$path/inkscape -z -w $width -f $input -e $output',
'batik' => 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d '
. '$output $input',
- 'rsvg' => '$path/rsvg -w $width -h $height $input $output',
+ 'rsvg' => '$path/rsvg-convert -w $width -h $height -o $output $input',
'imgserv' => '$path/imgserv-wrapper -i svg -o png -w$width $input $output',
'ImagickExt' => array( 'SvgHandler::rasterizeImagickExt' ),
);
@@ -1245,6 +1283,46 @@ $wgThumbnailBuckets = null;
$wgThumbnailMinimumBucketDistance = 50;
/**
+ * When defined, is an array of thumbnail widths to be rendered at upload time. The idea is to
+ * prerender common thumbnail sizes, in order to avoid the necessity to render them on demand, which
+ * has a performance impact for the first client to view a certain size.
+ *
+ * This obviously means that more disk space is needed per upload upfront.
+ *
+ * @since 1.25
+ */
+
+$wgUploadThumbnailRenderMap = array();
+
+/**
+ * The method through which the thumbnails will be prerendered for the entries in
+ * $wgUploadThumbnailRenderMap
+ *
+ * The method can be either "http" or "jobqueue". The former uses an http request to hit the
+ * thumbnail's URL.
+ * This method only works if thumbnails are configured to be rendered by a 404 handler. The latter
+ * option uses the job queue to render the thumbnail.
+ *
+ * @since 1.25
+ */
+$wgUploadThumbnailRenderMethod = 'jobqueue';
+
+/**
+ * When using the "http" wgUploadThumbnailRenderMethod, lets one specify a custom Host HTTP header.
+ *
+ * @since 1.25
+ */
+$wgUploadThumbnailRenderHttpCustomHost = false;
+
+/**
+ * When using the "http" wgUploadThumbnailRenderMethod, lets one specify a custom domain to send the
+ * HTTP request to.
+ *
+ * @since 1.25
+ */
+$wgUploadThumbnailRenderHttpCustomDomain = false;
+
+/**
* Default parameters for the "<gallery>" tag
*/
$wgGalleryOptions = array(
@@ -1272,9 +1350,11 @@ $wgDirectoryMode = 0777;
* Generate and use thumbnails suitable for screens with 1.5 and 2.0 pixel densities.
*
* This means a 320x240 use of an image on the wiki will also generate 480x360 and 640x480
- * thumbnails, output via data-src-1-5 and data-src-2-0. Runtime JavaScript switches the
- * images in after loading the original low-resolution versions depending on the reported
- * window.devicePixelRatio.
+ * thumbnails, output via the srcset attribute.
+ *
+ * On older browsers, a JavaScript polyfill switches the appropriate images in after loading
+ * the original low-resolution versions depending on the reported window.devicePixelRatio.
+ * The polyfill can be found in the jquery.hidpi module.
*/
$wgResponsiveImages = true;
@@ -1345,7 +1425,7 @@ $wgDjvuOutputExtension = 'jpg';
/**
* Site admin email address.
*
- * Defaults to "wikiadmin@{$wgServerName}".
+ * Defaults to "wikiadmin@$wgServerName".
*/
$wgEmergencyContact = false;
@@ -1354,7 +1434,7 @@ $wgEmergencyContact = false;
*
* The address we should use as sender when a user is requesting his password.
*
- * Defaults to "apache@{$wgServerName}".
+ * Defaults to "apache@$wgServerName".
*/
$wgPasswordSender = false;
@@ -1664,6 +1744,9 @@ $wgAllDBsAreLocalhost = false;
* $wgSharedPrefix is the table prefix for the shared database. It defaults to
* $wgDBprefix.
*
+ * $wgSharedSchema is the table schema for the shared database. It defaults to
+ * $wgDBmwschema.
+ *
* @deprecated since 1.21 In new code, use the $wiki parameter to wfGetLB() to
* access remote databases. Using wfGetLB() allows the shared database to
* reside on separate servers to the wiki's own database, with suitable
@@ -1682,6 +1765,12 @@ $wgSharedPrefix = false;
$wgSharedTables = array( 'user', 'user_properties' );
/**
+ * @see $wgSharedDB
+ * @since 1.23
+ */
+$wgSharedSchema = false;
+
+/**
* Database load balancer
* This is a two-dimensional array, an array of server info structures
* Fields are:
@@ -1960,15 +2049,6 @@ $wgAllowSlowParserFunctions = false;
$wgAllowSchemaUpdates = true;
/**
- * Anti-lock flags - bitfield
- * - ALF_NO_LINK_LOCK:
- * Don't use locking reads when updating the link table. This is
- * necessary for wikis with a high edit rate for performance
- * reasons, but may cause link table inconsistency
- */
-$wgAntiLockFlags = 0;
-
-/**
* Maximum article size in kilobytes
*/
$wgMaxArticleSize = 2048;
@@ -2063,43 +2143,21 @@ $wgLanguageConverterCacheType = CACHE_ANYTHING;
*/
$wgObjectCaches = array(
CACHE_NONE => array( 'class' => 'EmptyBagOStuff' ),
- CACHE_DB => array( 'class' => 'SqlBagOStuff', 'table' => 'objectcache' ),
+ CACHE_DB => array( 'class' => 'SqlBagOStuff', 'loggroup' => 'SQLBagOStuff' ),
CACHE_ANYTHING => array( 'factory' => 'ObjectCache::newAnything' ),
CACHE_ACCEL => array( 'factory' => 'ObjectCache::newAccelerator' ),
- CACHE_MEMCACHED => array( 'factory' => 'ObjectCache::newMemcached' ),
+ CACHE_MEMCACHED => array( 'factory' => 'ObjectCache::newMemcached', 'loggroup' => 'memcached' ),
'apc' => array( 'class' => 'APCBagOStuff' ),
'xcache' => array( 'class' => 'XCacheBagOStuff' ),
'wincache' => array( 'class' => 'WinCacheBagOStuff' ),
- 'memcached-php' => array( 'class' => 'MemcachedPhpBagOStuff' ),
- 'memcached-pecl' => array( 'class' => 'MemcachedPeclBagOStuff' ),
+ 'memcached-php' => array( 'class' => 'MemcachedPhpBagOStuff', 'loggroup' => 'memcached' ),
+ 'memcached-pecl' => array( 'class' => 'MemcachedPeclBagOStuff', 'loggroup' => 'memcached' ),
'hash' => array( 'class' => 'HashBagOStuff' ),
);
/**
- * Map of bloom filter store names to configuration arrays.
- *
- * Example:
- * $wgBloomFilterStores['main'] = array(
- * 'cacheId' => 'main-v1',
- * 'class' => 'BloomCacheRedis',
- * 'redisServers' => array( '127.0.0.1:6379' ),
- * 'redisConfig' => array( 'connectTimeout' => 2 )
- * );
- *
- * A primary bloom filter must be created manually.
- * Example in eval.php:
- * <code>
- * BloomCache::get( 'main' )->init( 'shared', 1000000000, .001 );
- * </code>
- * The size should be as large as practical given wiki size and resources.
- *
- * @since 1.24
- */
-$wgBloomFilterStores = array();
-
-/**
* The expiry time for the parser cache, in seconds.
* The default is 86400 (one day).
*/
@@ -2310,6 +2368,23 @@ $wgClockSkewFudge = 5;
*/
$wgInvalidateCacheOnLocalSettingsChange = true;
+/**
+ * When loading extensions through the extension registration system, this
+ * can be used to invalidate the cache. A good idea would be to set this to
+ * one file, you can just `touch` that one to invalidate the cache
+ *
+ * @par Example:
+ * @code
+ * $wgExtensionInfoMtime = filemtime( "$IP/LocalSettings.php" );
+ * @endcode
+ *
+ * If set to false, the mtime for each individual JSON file will be checked,
+ * which can be slow if a large number of extensions are being loaded.
+ *
+ * @var int|bool
+ */
+$wgExtensionInfoMTime = false;
+
/** @} */ # end of cache settings
/************************************************************************//**
@@ -2686,8 +2761,8 @@ $wgBrowserBlackList = array(
$wgLegacySchemaConversion = false;
/**
- * Enable dates like 'May 12' instead of '12 May', this only takes effect if
- * the interface is set to English.
+ * Enable dates like 'May 12' instead of '12 May', if the default date format
+ * is 'dmy or mdy'.
*/
$wgAmericanDates = false;
@@ -3050,6 +3125,7 @@ $wgEditPageFrameOptions = 'DENY';
* - 'DENY': Do not allow framing. This is recommended for most wikis.
* - 'SAMEORIGIN': Allow framing by pages on the same domain.
* - false: Allow all framing.
+ * Note: $wgBreakFrames will override this for human formatted API output.
*/
$wgApiFrameOptions = 'DENY';
@@ -3088,6 +3164,7 @@ $wgExperimentalHtmlIds = false;
* for the icon, the following keys are used:
* - src: An absolute url to the image to use for the icon, this is recommended
* but not required, however some skins will ignore icons without an image
+ * - srcset: optional additional-resolution images; see HTML5 specs
* - url: The url to use in the a element around the text or icon, if not set an a element will
* not be outputted
* - alt: This is the text form of the icon, it will be displayed without an image in
@@ -3104,7 +3181,9 @@ $wgFooterIcons = array(
),
"poweredby" => array(
"mediawiki" => array(
- // src defaults to "$wgResourceBasePath/resources/assets/poweredby_mediawiki_88x31.png"
+ // Defaults to point at
+ // "$wgResourceBasePath/resources/assets/poweredby_mediawiki_88x31.png"
+ // plus srcset for 1.5x, 2x resolution variants.
"src" => null,
"url" => "//www.mediawiki.org/",
"alt" => "Powered by MediaWiki",
@@ -3161,6 +3240,8 @@ $wgEnableCanonicalServerLink = false;
* <cross-domain-policy>. Without this, an attacker can send their own
* cross-domain policy unless it is prevented by the crossdomain.xml file at
* the domain root.
+ *
+ * @since 1.25
*/
$wgMangleFlashPolicy = true;
@@ -3239,8 +3320,8 @@ $wgResourceModules = array();
* ),
* );
* // Note the '+' character:
- * $wgResourceModuleSkinStyles['+foo'] = array(
- * 'bar' => 'skins/Foo/bar.css',
+ * $wgResourceModuleSkinStyles['foo'] = array(
+ * '+bar' => 'skins/Foo/bar.css',
* );
* @endcode
*
@@ -3267,8 +3348,6 @@ $wgResourceModules = array();
*
* As with $wgResourceModules, paths default to being relative to the MediaWiki root.
* You should always provide a localBasePath and remoteBasePath (or remoteExtPath/remoteSkinPath).
- * Either for all skin styles at once (first example below) or for each module separately (second
- * example).
*
* @par Example:
* @code
@@ -3278,19 +3357,6 @@ $wgResourceModules = array();
* 'remoteSkinPath' => 'Foo',
* 'localBasePath' => __DIR__,
* );
- *
- * $wgResourceModuleSkinStyles['foo'] = array(
- * 'bar' => array(
- * 'bar.css',
- * 'remoteSkinPath' => 'Foo',
- * 'localBasePath' => __DIR__,
- * ),
- * 'quux' => array(
- * 'quux.css',
- * 'remoteSkinPath' => 'Foo',
- * 'localBasePath' => __DIR__,
- * ),
- * );
* @endcode
*/
$wgResourceModuleSkinStyles = array();
@@ -3374,15 +3440,6 @@ $wgResourceLoaderMinifierMaxLineLength = 1000;
$wgIncludeLegacyJavaScript = true;
/**
- * Whether to include the jQuery Migrate library, which lets legacy JS that
- * requires jQuery 1.8.x to work and breaks with 1.9.x+.
- *
- * @since 1.24
- * @deprecated since 1.24, to be removed in 1.25
- */
-$wgIncludejQueryMigrate = false;
-
-/**
* Whether to preload the mediawiki.util module as blocking module in the top
* queue.
*
@@ -3480,6 +3537,9 @@ $wgResourceLoaderExperimentalAsyncLoading = false;
*
* Changes to LESS variables do not trigger cache invalidation.
*
+ * If the LESS variables need to be dynamic, you can use the
+ * ResourceLoaderGetLessVars hook (since 1.25).
+ *
* @par Example:
* @code
* $wgResourceLoaderLESSVars = array(
@@ -3715,6 +3775,18 @@ $wgInterwikiFallbackSite = 'wiki';
/** @} */ # end of Interwiki caching settings.
/**
+ * @name SiteStore caching settings.
+ * @{
+ */
+
+/**
+ * Specify the file location for the Sites json cache file.
+ */
+$wgSitesCacheFile = false;
+
+/** @} */ # end of SiteStore caching settings.
+
+/**
* If local interwikis are set up which allow redirects,
* set this regexp to restrict URLs which will be displayed
* as 'redirected from' links.
@@ -3785,19 +3857,12 @@ $wgNamespacesWithSubpages = array(
* A message with the suffix '-desc' should be added as a description message
* to have extra information on Special:TrackingCategories.
*
+ * @deprecated since 1.25 Extensions should now register tracking categories using
+ * the new extension registration system.
+ *
* @since 1.23
*/
-$wgTrackingCategories = array(
- 'index-category',
- 'noindex-category',
- 'expensive-parserfunction-category',
- 'post-expand-template-argument-category',
- 'post-expand-template-inclusion-category',
- 'hidden-category-category',
- 'broken-file-category',
- 'node-count-exceeded-category',
- 'expansion-depth-exceeded-category',
-);
+$wgTrackingCategories = array();
/**
* Array of namespaces which can be deemed to contain valid "content", as far
@@ -3913,7 +3978,7 @@ $wgUrlProtocols = array(
);
/**
- * If true, removes (substitutes) templates in "~~~~" signatures.
+ * If true, removes (by substituting) templates in signatures.
*/
$wgCleanSignatures = true;
@@ -4109,15 +4174,6 @@ $wgTranscludeCacheExpiry = 3600;
$wgArticleCountMethod = 'link';
/**
- * wgHitcounterUpdateFreq sets how often page counters should be updated, higher
- * values are easier on the database. A value of 1 causes the counters to be
- * updated on every hit, any higher value n cause them to update *on average*
- * every n hits. Should be set to either 1 or something largish, eg 1000, for
- * maximum efficiency.
- */
-$wgHitcounterUpdateFreq = 1;
-
-/**
* How many days user must be idle before he is considered inactive. Will affect
* the number shown on Special:Statistics, Special:ActiveUsers, and the
* {{NUMBEROFACTIVEUSERS}} magic word in wikitext.
@@ -4279,7 +4335,7 @@ $wgDefaultUserOptions = array(
'enotifrevealaddr' => 0,
'enotifusertalkpages' => 1,
'enotifwatchlistpages' => 1,
- 'extendwatchlist' => 0,
+ 'extendwatchlist' => 1,
'fancysig' => 0,
'forceeditsummary' => 0,
'gender' => 'unknown',
@@ -4305,7 +4361,7 @@ $wgDefaultUserOptions = array(
'thumbsize' => 5,
'underline' => 2,
'uselivepreview' => 0,
- 'usenewrc' => 0,
+ 'usenewrc' => 1,
'watchcreations' => 1,
'watchdefault' => 1,
'watchdeletion' => 0,
@@ -4517,6 +4573,8 @@ $wgGroupPermissions['user']['reupload-shared'] = true;
$wgGroupPermissions['user']['minoredit'] = true;
$wgGroupPermissions['user']['purge'] = true; // can use ?action=purge without clicking "ok"
$wgGroupPermissions['user']['sendemail'] = true;
+$wgGroupPermissions['user']['applychangetags'] = true;
+$wgGroupPermissions['user']['changetags'] = true;
// Implicit group for accounts that pass $wgAutoConfirmAge
$wgGroupPermissions['autoconfirmed']['autoconfirmed'] = true;
@@ -4577,6 +4635,7 @@ $wgGroupPermissions['sysop']['suppressredirect'] = true;
#$wgGroupPermissions['sysop']['pagelang'] = true;
#$wgGroupPermissions['sysop']['upload_by_url'] = true;
$wgGroupPermissions['sysop']['mergehistory'] = true;
+$wgGroupPermissions['sysop']['managechangetags'] = true;
// Permission to change users' group assignments
$wgGroupPermissions['bureaucrat']['userrights'] = true;
@@ -4793,7 +4852,6 @@ $wgAutopromote = array(
* @endcode
* Where event is either:
* - 'onEdit' (when user edits)
- * - 'onView' (when user views the wiki)
*
* Criteria has the same format as $wgAutopromote
*
@@ -4802,7 +4860,6 @@ $wgAutopromote = array(
*/
$wgAutopromoteOnce = array(
'onEdit' => array(),
- 'onView' => array()
);
/**
@@ -4992,6 +5049,17 @@ $wgRateLimits = array(
'ip' => null,
'subnet' => null,
),
+ 'stashedit' => array( // stashing edits into cache before save
+ 'anon' => null,
+ 'user' => null,
+ 'newbie' => null,
+ 'ip' => null,
+ 'subnet' => null,
+ ),
+ 'changetag' => array( // adding or removing change tags
+ 'user' => null,
+ 'newbie' => null,
+ ),
);
/**
@@ -5209,9 +5277,11 @@ $wgDebugDumpSqlLength = 500;
* Log destinations may be one of the following:
* - false to completely remove from the output, including from $wgDebugLogFile.
* - string values specifying a filename or URI.
- * - associative array mapping 'destination' key to the desired filename or URI.
- * The associative array may also contain a 'sample' key with an integer value,
- * specifying a sampling factor.
+ * - associative array with keys:
+ * - 'destination' desired filename or URI.
+ * - 'sample' an integer value, specifying a sampling factor (optional)
+ * - 'level' A \Psr\Log\LogLevel constant, indicating the minimum level
+ * to log (optional, since 1.25)
*
* @par Example:
* @code
@@ -5220,15 +5290,41 @@ $wgDebugDumpSqlLength = 500;
*
* @par Advanced example:
* @code
- * $wgDebugLogGroups['memcached'] = (
+ * $wgDebugLogGroups['memcached'] = array(
* 'destination' => '/var/log/mediawiki/memcached.log',
* 'sample' => 1000, // log 1 message out of every 1,000.
+ * 'level' => \Psr\Log\LogLevel::WARNING
* );
* @endcode
*/
$wgDebugLogGroups = array();
/**
+ * Default service provider for creating Psr\Log\LoggerInterface instances.
+ *
+ * The value should be an array suitable for use with
+ * ObjectFactory::getObjectFromSpec(). The created object is expected to
+ * implement the MediaWiki\Logger\Spi interface. See ObjectFactory for additional
+ * details.
+ *
+ * Alternately the MediaWiki\Logger\LoggerFactory::registerProvider method can
+ * be called to inject an MediaWiki\Logger\Spi instance into the LoggerFactory
+ * and bypass the use of this configuration variable entirely.
+ *
+ * @par To completely disable logging:
+ * @code
+ * $wgMWLoggerDefaultSpi = array( 'class' => '\\MediaWiki\\Logger\\NullSpi' );
+ * @endcode
+ *
+ * @since 1.25
+ * @var array $wgMWLoggerDefaultSpi
+ * @see MwLogger
+ */
+$wgMWLoggerDefaultSpi = array(
+ 'class' => '\\MediaWiki\\Logger\\LegacySpi',
+);
+
+/**
* Display debug data at the bottom of the main content area.
*
* Useful for developers and technical users trying to working on a closed wiki.
@@ -5308,6 +5404,7 @@ $wgDeprecationReleaseLimit = false;
/**
* Only record profiling info for pages that took longer than this
+ * @deprecated since 1.25: set $wgProfiler['threshold'] instead.
*/
$wgProfileLimit = 0.0;
@@ -5326,8 +5423,10 @@ $wgProfileCallTree = false;
/**
* Should application server host be put into profiling table
+ *
+ * @deprecated set $wgProfiler['perhost'] = true instead
*/
-$wgProfilePerHost = false;
+$wgProfilePerHost = null;
/**
* Host for UDP profiler.
@@ -5335,14 +5434,18 @@ $wgProfilePerHost = false;
* The host should be running a daemon which can be obtained from MediaWiki
* Git at:
* http://git.wikimedia.org/tree/operations%2Fsoftware.git/master/udpprofile
+ *
+ * @deprecated set $wgProfiler['udphost'] instead
*/
-$wgUDPProfilerHost = '127.0.0.1';
+$wgUDPProfilerHost = null;
/**
* Port for UDP profiler.
* @see $wgUDPProfilerHost
+ *
+ * @deprecated set $wgProfiler['udpport'] instead
*/
-$wgUDPProfilerPort = '3811';
+$wgUDPProfilerPort = null;
/**
* Format string for the UDP profiler. The UDP profiler invokes sprintf() with
@@ -5352,13 +5455,10 @@ $wgUDPProfilerPort = '3811';
*
* @see $wgStatsFormatString
* @since 1.22
+ *
+ * @deprecated set $wgProfiler['udpformat'] instead
*/
-$wgUDPProfilerFormatString = "%s - %d %f %f %f %f %s\n";
-
-/**
- * Output debug message on every wfProfileIn/wfProfileOut
- */
-$wgDebugFunctionEntry = false;
+$wgUDPProfilerFormatString = null;
/**
* Destination for wfIncrStats() data...
@@ -5390,12 +5490,6 @@ $wgAggregateStatsID = false;
$wgStatsFormatString = "stats/%s - %s 1 1 1 1 %s\n";
/**
- * Whereas to count the number of time an article is viewed.
- * Does not work if pages are cached (for example with squid).
- */
-$wgDisableCounters = false;
-
-/**
* InfoAction retrieves a list of transclusion links (both to and from).
* This number puts a limit on that query in the case of highly transcluded
* templates.
@@ -5430,25 +5524,6 @@ $wgParserTestFiles = array(
$wgEnableJavaScriptTest = false;
/**
- * Configuration for javascript testing.
- */
-$wgJavaScriptTestConfig = array(
- 'qunit' => array(
- // Page where documentation can be found relevant to the QUnit test suite being ran.
- // Used in the intro paragraph on [[Special:JavaScriptTest/qunit]] for the
- // documentation link in the "javascripttest-qunit-intro" message.
- 'documentation' => '//www.mediawiki.org/wiki/Manual:JavaScript_unit_testing',
- // If you are submitting the QUnit test suite to a TestSwarm instance,
- // point this to the "inject.js" script of that instance. This is was registers
- // the QUnit hooks to extract the test results and push them back up into the
- // TestSwarm database.
- // @example 'http://localhost/testswarm/js/inject.js'
- // @example '//integration.mediawiki.org/testswarm/js/inject.js'
- 'testswarm-injectjs' => false,
- ),
-);
-
-/**
* Overwrite the caching key prefix with custom value.
* @since 1.19
*/
@@ -5494,10 +5569,25 @@ $wgSearchHighlightBoundaries = '[\p{Z}\p{P}\p{C}]';
* PHP wrapper to avoid firing up mediawiki for every keystroke
*
* Placeholders: {searchTerms}
+ *
+ * @deprecated since 1.25 Use $wgOpenSearchTemplates['application/x-suggestions+json'] instead
*/
$wgOpenSearchTemplate = false;
/**
+ * Templates for OpenSearch suggestions, defaults to API action=opensearch
+ *
+ * Sites with heavy load would typically have these point to a custom
+ * PHP wrapper to avoid firing up mediawiki for every keystroke
+ *
+ * Placeholders: {searchTerms}
+ */
+$wgOpenSearchTemplates = array(
+ 'application/x-suggestions+json' => false,
+ 'application/x-suggestions+xml' => false,
+);
+
+/**
* Enable OpenSearch suggestions requested by MediaWiki. Set this to
* false if you've disabled scripts that use api?action=opensearch and
* want reduce load caused by cached scripts still pulling suggestions.
@@ -5512,6 +5602,11 @@ $wgEnableOpenSearchSuggest = true;
$wgOpenSearchDefaultLimit = 10;
/**
+ * Minimum length of extract in <Description>. Actual extracts will last until the end of sentence.
+ */
+$wgOpenSearchDescriptionLength = 100;
+
+/**
* Expiry time for search suggestion responses
*/
$wgSearchSuggestCacheExpiry = 1200;
@@ -5722,9 +5817,9 @@ $wgGitRepositoryViewers = array(
/**
* Recentchanges items are periodically purged; entries older than this many
* seconds will go.
- * Default: 13 weeks = about three months
+ * Default: 90 days = about three months
*/
-$wgRCMaxAge = 13 * 7 * 24 * 3600;
+$wgRCMaxAge = 90 * 24 * 3600;
/**
* Filter $wgRCLinkDays by $wgRCMaxAge to avoid showing links for numbers
@@ -5877,11 +5972,6 @@ $wgAdvertisedFeedTypes = array( 'atom' );
$wgRCShowWatchingUsers = false; # UPO
/**
- * Show watching users in Page views
- */
-$wgPageShowWatchingUsers = false;
-
-/**
* Show the amount of changed characters in recent changes
*/
$wgRCShowChangedSize = true;
@@ -6160,6 +6250,8 @@ $wgExtensionMessagesFiles = array();
* en.json, de.json, etc. Extensions with messages in multiple places may specify an array of
* message directories.
*
+ * Message directories in core should be added to LocalisationCache::getMessagesDirs()
+ *
* @par Simple example:
* @code
* $wgMessagesDirs['Example'] = __DIR__ . '/i18n';
@@ -6175,10 +6267,7 @@ $wgExtensionMessagesFiles = array();
* @endcode
* @since 1.23
*/
-$wgMessagesDirs = array(
- 'core' => "$IP/languages/i18n",
- 'oojs-ui' => "$IP/resources/lib/oojs-ui/i18n",
-);
+$wgMessagesDirs = array();
/**
* Array of files with list(s) of extension entry points to be used in
@@ -6254,7 +6343,7 @@ $wgAutoloadAttemptLowercase = true;
* 'version' => '1.9.0',
* 'url' => 'http://example.org/example-extension/',
* 'descriptionmsg' => 'exampleextension-desc',
- * 'license-name' => 'GPL-2.0',
+ * 'license-name' => 'GPL-2.0+',
* );
* @endcode
*
@@ -6288,7 +6377,7 @@ $wgAutoloadAttemptLowercase = true;
* localizable message (omit in favour of 'descriptionmsg').
*
* - license-name: Short name of the license (used as label for the link), such
- * as "GPL-2.0" or "MIT" (https://spdx.org/licenses/ for a list of identifiers).
+ * as "GPL-2.0+" or "MIT" (https://spdx.org/licenses/ for a list of identifiers).
*/
$wgExtensionCredits = array();
@@ -6340,7 +6429,6 @@ $wgHooks = array();
*/
$wgJobClasses = array(
'refreshLinks' => 'RefreshLinksJob',
- 'refreshLinks2' => 'RefreshLinksJob2', // b/c
'htmlCacheUpdate' => 'HTMLCacheUpdateJob',
'sendMail' => 'EmaillingJob',
'enotifNotify' => 'EnotifNotifyJob',
@@ -6348,6 +6436,10 @@ $wgJobClasses = array(
'uploadFromUrl' => 'UploadFromUrlJob',
'AssembleUploadChunks' => 'AssembleUploadChunksJob',
'PublishStashedFile' => 'PublishStashedFileJob',
+ 'ThumbnailRender' => 'ThumbnailRenderJob',
+ 'recentChangesUpdate' => 'RecentChangesUpdateJob',
+ 'refreshLinksPrioritized' => 'RefreshLinksJob', // for cascading protection
+ 'enqueue' => 'EnqueueJob', // local queue for multi-DC setups
'null' => 'NullJob'
);
@@ -6390,7 +6482,7 @@ $wgJobTypeConf = array(
* These settings should be global to all wikis.
*/
$wgJobQueueAggregator = array(
- 'class' => 'JobQueueAggregatorMemc'
+ 'class' => 'JobQueueAggregatorNull'
);
/**
@@ -6398,8 +6490,7 @@ $wgJobQueueAggregator = array(
* Expensive Querypages are already updated.
*/
$wgSpecialPageCacheUpdates = array(
- 'Statistics' => array( 'SiteStatsUpdate', 'cacheUpdate' ),
- 'Activeusers' => array( 'SpecialActiveUsers', 'cacheUpdate' ),
+ 'Statistics' => array( 'SiteStatsUpdate', 'cacheUpdate' )
);
/**
@@ -6497,6 +6588,8 @@ $wgLogTypes = array(
'patrol',
'merge',
'suppress',
+ 'tag',
+ 'managetags',
);
/**
@@ -6533,7 +6626,8 @@ $wgLogRestrictions = array(
* for the link text.
*/
$wgFilterLogTypes = array(
- 'patrol' => true
+ 'patrol' => true,
+ 'tag' => true,
);
/**
@@ -6589,22 +6683,14 @@ $wgLogHeaders = array(
* Extensions with custom log types may add to this array.
*/
$wgLogActions = array(
- 'block/block' => 'blocklogentry',
- 'block/unblock' => 'unblocklogentry',
- 'block/reblock' => 'reblock-logentry',
'protect/protect' => 'protectedarticle',
'protect/modify' => 'modifiedarticleprotection',
'protect/unprotect' => 'unprotectedarticle',
'protect/move_prot' => 'movedarticleprotection',
- 'import/upload' => 'import-logentry-upload',
- 'import/interwiki' => 'import-logentry-interwiki',
- 'merge/merge' => 'pagemerge-logentry',
- 'suppress/block' => 'blocklogentry',
- 'suppress/reblock' => 'reblock-logentry',
);
/**
- * The same as above, but here values are names of functions,
+ * The same as above, but here values are names of classes,
* not messages.
* @see LogPage::actionText
* @see LogFormatter
@@ -6622,9 +6708,22 @@ $wgLogActionsHandlers = array(
'patrol/patrol' => 'PatrolLogFormatter',
'rights/rights' => 'RightsLogFormatter',
'rights/autopromote' => 'RightsLogFormatter',
- 'upload/upload' => 'LogFormatter',
- 'upload/overwrite' => 'LogFormatter',
- 'upload/revert' => 'LogFormatter',
+ 'upload/upload' => 'UploadLogFormatter',
+ 'upload/overwrite' => 'UploadLogFormatter',
+ 'upload/revert' => 'UploadLogFormatter',
+ 'merge/merge' => 'MergeLogFormatter',
+ 'tag/update' => 'TagLogFormatter',
+ 'managetags/create' => 'LogFormatter',
+ 'managetags/delete' => 'LogFormatter',
+ 'managetags/activate' => 'LogFormatter',
+ 'managetags/deactivate' => 'LogFormatter',
+ 'block/block' => 'BlockLogFormatter',
+ 'block/unblock' => 'BlockLogFormatter',
+ 'block/reblock' => 'BlockLogFormatter',
+ 'suppress/block' => 'BlockLogFormatter',
+ 'suppress/reblock' => 'BlockLogFormatter',
+ 'import/upload' => 'LogFormatter',
+ 'import/interwiki' => 'LogFormatter',
);
/**
@@ -6691,6 +6790,7 @@ $wgActions = array(
'credits' => true,
'delete' => true,
'edit' => true,
+ 'editchangetags' => 'SpecialPageAction',
'history' => true,
'info' => true,
'markpatrolled' => true,
@@ -6699,7 +6799,7 @@ $wgActions = array(
'raw' => true,
'render' => true,
'revert' => true,
- 'revisiondelete' => true,
+ 'revisiondelete' => 'SpecialPageAction',
'rollback' => true,
'submit' => true,
'unprotect' => true,
@@ -6967,6 +7067,12 @@ $wgAjaxUploadDestCheck = true;
$wgAjaxLicensePreview = true;
/**
+ * Have clients send edits to be prepared when filling in edit summaries.
+ * This gives the server a head start on the expensive parsing operation.
+ */
+$wgAjaxEditStash = true;
+
+/**
* Settings for incoming cross-site AJAX requests:
* Newer browsers support cross-site AJAX when the target resource allows requests
* from the origin domain by the Access-Control-Allow-Origin header.
@@ -7084,6 +7190,18 @@ $wgAsyncHTTPTimeout = 25;
$wgHTTPProxy = false;
/**
+ * Local virtual hosts.
+ *
+ * This lists domains that are configured as virtual hosts on the same machine.
+ * If a request is to be made to a domain listed here, or any subdomain thereof,
+ * then no proxy will be used.
+ * Command-line scripts are not affected by this setting and will always use
+ * proxy if it is configured.
+ * @since 1.25
+ */
+$wgLocalVirtualHosts = array();
+
+/**
* Timeout for connections done internally (in seconds)
* Only works for curl
*/
@@ -7286,13 +7404,21 @@ $wgPagePropsHaveSortkey = true;
$wgHttpsPort = 443;
/**
- * Secret and algorithm for hmac-based key derivation function (fast,
+ * Secret for hmac-based key derivation function (fast,
* cryptographically secure random numbers).
* This should be set in LocalSettings.php, otherwise wgSecretKey will
* be used.
+ * See also: $wgHKDFAlgorithm
* @since 1.24
*/
$wgHKDFSecret = false;
+
+/**
+ * Algorithm for hmac-based key derivation function (fast,
+ * cryptographically secure random numbers).
+ * See also: $wgHKDFSecret
+ * @since 1.24
+ */
$wgHKDFAlgorithm = 'sha256';
/**
@@ -7312,6 +7438,34 @@ $wgPageLanguageUseDB = false;
$wgUseLinkNamespaceDBFields = true;
/**
+ * Global configuration variable for Virtual REST Services.
+ * Parameters for different services are to be declared inside
+ * $wgVirtualRestConfig['modules'], which is to be treated as an associative
+ * array. Global parameters will be merged with service-specific ones. The
+ * result will then be passed to VirtualRESTService::__construct() in the
+ * module.
+ *
+ * Example config for Parsoid:
+ *
+ * $wgVirtualRestConfig['modules']['parsoid'] = array(
+ * 'url' => 'http://localhost:8000',
+ * 'prefix' => 'enwiki',
+ * );
+ *
+ * @var array
+ * @since 1.25
+ */
+$wgVirtualRestConfig = array(
+ 'modules' => array(),
+ 'global' => array(
+ # Timeout in seconds
+ 'timeout' => 360,
+ 'forwardCookies' => false,
+ 'HTTPProxy' => null
+ )
+);
+
+/**
* For really cool vim folding this needs to be at the end:
* vim: foldmarker=@{,@} foldmethod=marker
* @}
diff --git a/includes/Defines.php b/includes/Defines.php
index 017e9ea4..c9263da9 100644
--- a/includes/Defines.php
+++ b/includes/Defines.php
@@ -2,10 +2,6 @@
/**
* A few constants that might be needed during LocalSettings.php.
*
- * Note: these constants must all be resolvable at compile time by HipHop,
- * since this file will not be executed during request startup for a compiled
- * MediaWiki.
- *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -152,12 +148,13 @@ define( 'AV_SCAN_FAILED', false ); #scan failed (scanner not found or error in
/**@{
* Anti-lock flags
- * See DefaultSettings.php for a description
+ * Was used by $wgAntiLockFlags, which was removed with 1.25
+ * Constants kept to not have warnings when used in LocalSettings
*/
define( 'ALF_PRELOAD_LINKS', 1 ); // unused
define( 'ALF_PRELOAD_EXISTENCE', 2 ); // unused
-define( 'ALF_NO_LINK_LOCK', 4 );
-define( 'ALF_NO_BLOCK_LOCK', 8 );
+define( 'ALF_NO_LINK_LOCK', 4 ); // unused
+define( 'ALF_NO_BLOCK_LOCK', 8 ); // unused
/**@}*/
/**@{
@@ -206,15 +203,15 @@ define( 'LIST_OR', 4 );
/**
* Unicode and normalisation related
*/
-require_once __DIR__ . '/normal/UtfNormalDefines.php';
+require_once __DIR__ . '/libs/normal/UtfNormalDefines.php';
/**@{
* Hook support constants
*/
-define( 'MW_SUPPORTS_EDITFILTERMERGED', 1 );
define( 'MW_SUPPORTS_PARSERFIRSTCALLINIT', 1 );
define( 'MW_SUPPORTS_LOCALISATIONCACHE', 1 );
define( 'MW_SUPPORTS_CONTENTHANDLER', 1 );
+define( 'MW_EDITFILTERMERGED_SUPPORTS_API', 1 );
/**@}*/
/** Support for $wgResourceModules */
@@ -223,6 +220,12 @@ define( 'MW_SUPPORTS_RESOURCE_MODULES', 1 );
/**@{
* Allowed values for Parser::$mOutputType
* Parameter to Parser::startExternalParse().
+ * Use of Parser consts is preferred:
+ * - Parser::OT_HTML
+ * - Parser::OT_WIKI
+ * - Parser::OT_PREPROCESS
+ * - Parser::OT_MSG
+ * - Parser::OT_PLAIN
*/
define( 'OT_HTML', 1 );
define( 'OT_WIKI', 2 );
@@ -233,16 +236,14 @@ define( 'OT_PLAIN', 4 );
/**@{
* Flags for Parser::setFunctionHook
+ * Use of Parser consts is preferred:
+ * - Parser::SFH_NO_HASH
+ * - Parser::SFH_OBJECT_ARGS
*/
define( 'SFH_NO_HASH', 1 );
define( 'SFH_OBJECT_ARGS', 2 );
/**@}*/
-/**
- * Flags for Parser::replaceLinkHolders
- */
-define( 'RLH_FOR_UPDATE', 1 );
-
/**@{
* Autopromote conditions (must be here and not in Autopromote.php, so that
* they're loaded for DefaultSettings.php before AutoLoader.php)
diff --git a/includes/EditPage.php b/includes/EditPage.php
index 38c80ba8..8d27eac8 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -151,6 +151,18 @@ class EditPage {
const AS_NO_CHANGE_CONTENT_MODEL = 235;
/**
+ * Status: user tried to create self-redirect (redirect to the same article) and
+ * wpIgnoreSelfRedirect == false
+ */
+ const AS_SELF_REDIRECT = 236;
+
+ /**
+ * Status: an error relating to change tagging. Look at the message key for
+ * more details
+ */
+ const AS_CHANGE_TAG_ERROR = 237;
+
+ /**
* Status: can't parse content
*/
const AS_PARSE_ERROR = 240;
@@ -256,6 +268,12 @@ class EditPage {
/** @var bool */
protected $allowBlankArticle = false;
+ /** @var bool */
+ protected $selfRedirect = false;
+
+ /** @var bool */
+ protected $allowSelfRedirect = false;
+
/** @var string */
public $autoSumm = '';
@@ -321,6 +339,9 @@ class EditPage {
/** @var int */
public $oldid = 0;
+ /** @var int */
+ public $parentRevId = 0;
+
/** @var string */
public $editintro = '';
@@ -336,6 +357,9 @@ class EditPage {
/** @var null|string */
public $contentFormat = null;
+ /** @var null|array */
+ public $changeTags = null;
+
# Placeholders for text injection by hooks (must be HTML)
# extensions should take care to _append_ to the present value
@@ -362,9 +386,6 @@ class EditPage {
/** @var bool */
protected $edit;
- /** @var bool */
- public $live;
-
/**
* @param Article $article
*/
@@ -448,29 +469,21 @@ class EditPage {
function edit() {
global $wgOut, $wgRequest, $wgUser;
// Allow extensions to modify/prevent this form or submission
- if ( !wfRunHooks( 'AlternateEdit', array( $this ) ) ) {
+ if ( !Hooks::run( 'AlternateEdit', array( $this ) ) ) {
return;
}
- wfProfileIn( __METHOD__ );
wfDebug( __METHOD__ . ": enter\n" );
// If they used redlink=1 and the page exists, redirect to the main article
if ( $wgRequest->getBool( 'redlink' ) && $this->mTitle->exists() ) {
$wgOut->redirect( $this->mTitle->getFullURL() );
- wfProfileOut( __METHOD__ );
return;
}
$this->importFormData( $wgRequest );
$this->firsttime = false;
- if ( $this->live ) {
- $this->livePreview();
- wfProfileOut( __METHOD__ );
- return;
- }
-
if ( wfReadOnly() && $this->save ) {
// Force preview
$this->save = false;
@@ -492,7 +505,7 @@ class EditPage {
}
}
- $permErrors = $this->getEditPermissionErrors();
+ $permErrors = $this->getEditPermissionErrors( $this->save ? 'secure' : 'full' );
if ( $permErrors ) {
wfDebug( __METHOD__ . ": User can't edit\n" );
// Auto-block user's IP if the account was "hard" blocked
@@ -500,12 +513,9 @@ class EditPage {
$this->displayPermissionsError( $permErrors );
- wfProfileOut( __METHOD__ );
return;
}
- wfProfileIn( __METHOD__ . "-business-end" );
-
$this->isConflict = false;
// css / js subpages of user pages get a special treatment
$this->isCssJsSubpage = $this->mTitle->isCssJsSubpage();
@@ -525,9 +535,9 @@ class EditPage {
# in the back door with a hand-edited submission URL.
if ( 'save' == $this->formtype ) {
- if ( !$this->attemptSave() ) {
- wfProfileOut( __METHOD__ . "-business-end" );
- wfProfileOut( __METHOD__ );
+ $resultDetails = null;
+ $status = $this->attemptSave( $resultDetails );
+ if ( !$this->handleStatus( $status, $resultDetails ) ) {
return;
}
}
@@ -537,34 +547,37 @@ class EditPage {
if ( 'initial' == $this->formtype || $this->firsttime ) {
if ( $this->initialiseForm() === false ) {
$this->noSuchSectionPage();
- wfProfileOut( __METHOD__ . "-business-end" );
- wfProfileOut( __METHOD__ );
return;
}
if ( !$this->mTitle->getArticleID() ) {
- wfRunHooks( 'EditFormPreloadText', array( &$this->textbox1, &$this->mTitle ) );
+ Hooks::run( 'EditFormPreloadText', array( &$this->textbox1, &$this->mTitle ) );
} else {
- wfRunHooks( 'EditFormInitialText', array( $this ) );
+ Hooks::run( 'EditFormInitialText', array( $this ) );
}
}
$this->showEditForm();
- wfProfileOut( __METHOD__ . "-business-end" );
- wfProfileOut( __METHOD__ );
}
/**
+ * @param string $rigor Same format as Title::getUserPermissionErrors()
* @return array
*/
- protected function getEditPermissionErrors() {
+ protected function getEditPermissionErrors( $rigor = 'secure' ) {
global $wgUser;
- $permErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser );
+
+ $permErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser, $rigor );
# Can this title be created?
if ( !$this->mTitle->exists() ) {
- $permErrors = array_merge( $permErrors,
- wfArrayDiff2( $this->mTitle->getUserPermissionsErrors( 'create', $wgUser ), $permErrors ) );
+ $permErrors = array_merge(
+ $permErrors,
+ wfArrayDiff2(
+ $this->mTitle->getUserPermissionsErrors( 'create', $wgUser, $rigor ),
+ $permErrors
+ )
+ );
}
# Ignore some permissions errors when a user is just previewing/viewing diffs
$remove = array();
@@ -576,6 +589,7 @@ class EditPage {
}
}
$permErrors = wfArrayDiff2( $permErrors, $remove );
+
return $permErrors;
}
@@ -612,7 +626,7 @@ class EditPage {
throw new PermissionsError( $action, $permErrors );
}
- wfRunHooks( 'EditPage::showReadOnlyForm:initial', array( $this, &$wgOut ) );
+ Hooks::run( 'EditPage::showReadOnlyForm:initial', array( $this, &$wgOut ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->setPageTitle( wfMessage(
@@ -717,13 +731,10 @@ class EditPage {
function importFormData( &$request ) {
global $wgContLang, $wgUser;
- wfProfileIn( __METHOD__ );
-
# Section edit can come from either the form or a link
$this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) );
if ( $this->section !== null && $this->section !== '' && !$this->isSectionEditSupported() ) {
- wfProfileOut( __METHOD__ );
throw new ErrorPageError( 'sectioneditnotsupported-title', 'sectioneditnotsupported-text' );
}
@@ -738,13 +749,10 @@ class EditPage {
// Skip this if wpTextbox2 has input, it indicates that we came
// from a conflict page with raw page text, not a custom form
// modified by subclasses
- wfProfileIn( get_class( $this ) . "::importContentFormData" );
$textbox1 = $this->importContentFormData( $request );
if ( $textbox1 !== null ) {
$this->textbox1 = $textbox1;
}
-
- wfProfileOut( get_class( $this ) . "::importContentFormData" );
}
# Truncate for whole multibyte characters
@@ -785,7 +793,8 @@ class EditPage {
// TODO: softened the check for cutover. Once we determine
// that it is safe, we should complete the transition by
// removing the "edittime" clause.
- $this->incompleteForm = ( !$request->getVal( 'wpUltimateParam' ) && is_null( $this->edittime ) );
+ $this->incompleteForm = ( !$request->getVal( 'wpUltimateParam' )
+ && is_null( $this->edittime ) );
}
if ( $this->incompleteForm ) {
# If the form is incomplete, force to preview.
@@ -793,8 +802,7 @@ class EditPage {
wfDebug( "POST DATA: " . var_export( $_POST, true ) . "\n" );
$this->preview = true;
} else {
- /* Fallback for live preview */
- $this->preview = $request->getCheck( 'wpPreview' ) || $request->getCheck( 'wpLivePreview' );
+ $this->preview = $request->getCheck( 'wpPreview' );
$this->diff = $request->getCheck( 'wpDiff' );
// Remember whether a save was requested, so we can indicate
@@ -844,6 +852,15 @@ class EditPage {
$this->autoSumm = $request->getText( 'wpAutoSummary' );
$this->allowBlankArticle = $request->getBool( 'wpIgnoreBlankArticle' );
+ $this->allowSelfRedirect = $request->getBool( 'wpIgnoreSelfRedirect' );
+
+ $changeTags = $request->getVal( 'wpChangeTags' );
+ if ( is_null( $changeTags ) || $changeTags === '' ) {
+ $this->changeTags = array();
+ } else {
+ $this->changeTags = array_filter( array_map( 'trim', explode( ',',
+ $changeTags ) ) );
+ }
} else {
# Not a posted form? Start with nothing.
wfDebug( __METHOD__ . ": Not a posted form.\n" );
@@ -880,6 +897,7 @@ class EditPage {
}
$this->oldid = $request->getInt( 'oldid' );
+ $this->parentRevId = $request->getInt( 'parentRevId' );
$this->bot = $request->getBool( 'bot', true );
$this->nosummary = $request->getBool( 'nosummary' );
@@ -905,15 +923,13 @@ class EditPage {
* allowed.
*/
- $this->live = $request->getCheck( 'live' );
$this->editintro = $request->getText( 'editintro',
// Custom edit intro for new sections
$this->section === 'new' ? 'MediaWiki:addsection-editintro' : '' );
// Allow extensions to modify form data
- wfRunHooks( 'EditPage::importFormData', array( $this, $request ) );
+ Hooks::run( 'EditPage::importFormData', array( $this, $request ) );
- wfProfileOut( __METHOD__ );
}
/**
@@ -974,8 +990,6 @@ class EditPage {
protected function getContentObject( $def_content = null ) {
global $wgOut, $wgRequest, $wgUser, $wgContLang;
- wfProfileIn( __METHOD__ );
-
$content = false;
// For message page not locally set, use the i18n message.
@@ -1087,7 +1101,6 @@ class EditPage {
}
}
- wfProfileOut( __METHOD__ );
return $content;
}
@@ -1280,18 +1293,20 @@ class EditPage {
/**
* Attempt submission
+ * @param array $resultDetails See docs for $result in internalAttemptSave
* @throws UserBlockedError|ReadOnlyError|ThrottledError|PermissionsError
- * @return bool False if output is done, true if the rest of the form should be displayed
+ * @return Status The resulting status object.
*/
- public function attemptSave() {
+ public function attemptSave( &$resultDetails = false ) {
global $wgUser;
- $resultDetails = false;
# Allow bots to exempt some edits from bot flagging
$bot = $wgUser->isAllowed( 'bot' ) && $this->bot;
$status = $this->internalAttemptSave( $resultDetails, $bot );
- return $this->handleStatus( $status, $resultDetails );
+ Hooks::run( 'EditPage::attemptSave:after', array( $this, $status, $resultDetails ) );
+
+ return $status;
}
/**
@@ -1329,6 +1344,7 @@ class EditPage {
case self::AS_MAX_ARTICLE_SIZE_EXCEEDED:
case self::AS_END:
case self::AS_BLANK_ARTICLE:
+ case self::AS_SELF_REDIRECT:
return true;
case self::AS_HOOK_ERROR:
@@ -1349,7 +1365,7 @@ class EditPage {
$sectionanchor = $resultDetails['sectionanchor'];
// Give extensions a chance to modify URL query on update
- wfRunHooks(
+ Hooks::run(
'ArticleUpdateBeforeRedirect',
array( $this->mArticle, &$sectionanchor, &$extraQuery )
);
@@ -1415,8 +1431,8 @@ class EditPage {
protected function runPostMergeFilters( Content $content, Status $status, User $user ) {
// Run old style post-section-merge edit filter
if ( !ContentHandler::runLegacyHooks( 'EditFilterMerged',
- array( $this, $content, &$this->hookError, $this->summary ) ) ) {
-
+ array( $this, $content, &$this->hookError, $this->summary ) )
+ ) {
# Error messages etc. could be handled within the hook...
$status->fatal( 'hookaborted' );
$status->value = self::AS_HOOK_ERROR;
@@ -1429,15 +1445,24 @@ class EditPage {
}
// Run new style post-section-merge edit filter
- if ( !wfRunHooks( 'EditFilterMergedContent',
- array( $this->mArticle->getContext(), $content, $status, $this->summary,
- $user, $this->minoredit ) ) ) {
-
+ if ( !Hooks::run( 'EditFilterMergedContent',
+ array( $this->mArticle->getContext(), $content, $status, $this->summary,
+ $user, $this->minoredit ) )
+ ) {
# Error messages etc. could be handled within the hook...
- // XXX: $status->value may already be something informative...
- $this->hookError = $status->getWikiText();
- $status->fatal( 'hookaborted' );
- $status->value = self::AS_HOOK_ERROR;
+ if ( $status->isGood() ) {
+ $status->fatal( 'hookaborted' );
+ // Not setting $this->hookError here is a hack to allow the hook
+ // to cause a return to the edit page without $this->hookError
+ // being set. This is used by ConfirmEdit to display a captcha
+ // without any error message cruft.
+ } else {
+ $this->hookError = $status->getWikiText();
+ }
+ // Use the existing $status->value if the hook set it
+ if ( !$status->value ) {
+ $status->value = self::AS_HOOK_ERROR;
+ }
return false;
} elseif ( !$status->isOK() ) {
# ...or the hook could be expecting us to produce an error
@@ -1510,15 +1535,10 @@ class EditPage {
$status = Status::newGood();
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-checks' );
-
- if ( !wfRunHooks( 'EditPage::attemptSave', array( $this ) ) ) {
+ if ( !Hooks::run( 'EditPage::attemptSave', array( $this ) ) ) {
wfDebug( "Hook 'EditPage::attemptSave' aborted article saving\n" );
$status->fatal( 'hookaborted' );
$status->value = self::AS_HOOK_ERROR;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
return $status;
}
@@ -1535,8 +1555,6 @@ class EditPage {
);
$status->fatal( 'spamprotectionmatch', false );
$status->value = self::AS_SPAM_ERROR;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
return $status;
}
@@ -1551,8 +1569,6 @@ class EditPage {
$ex->getMessage()
);
$status->value = self::AS_PARSE_ERROR;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
return $status;
}
@@ -1564,9 +1580,6 @@ class EditPage {
$code = $wgUser->isAnon() ? self::AS_IMAGE_REDIRECT_ANON : self::AS_IMAGE_REDIRECT_LOGGED;
$status->setResult( false, $code );
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
-
return $status;
}
@@ -1595,26 +1608,20 @@ class EditPage {
wfDebugLog( 'SpamRegex', "$ip spam regex hit [[$pdbk]]: \"$match\"" );
$status->fatal( 'spamprotectionmatch', $match );
$status->value = self::AS_SPAM_ERROR;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
return $status;
}
- if ( !wfRunHooks(
+ if ( !Hooks::run(
'EditFilter',
array( $this, $this->textbox1, $this->section, &$this->hookError, $this->summary ) )
) {
# Error messages etc. could be handled within the hook...
$status->fatal( 'hookaborted' );
$status->value = self::AS_HOOK_ERROR;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
return $status;
} elseif ( $this->hookError != '' ) {
# ...or the hook could be expecting us to produce an error
$status->fatal( 'hookaborted' );
$status->value = self::AS_HOOK_ERROR_EXPECTED;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
return $status;
}
@@ -1623,8 +1630,6 @@ class EditPage {
$wgUser->spreadAnyEditBlock();
# Check block state against master, thus 'false'.
$status->setResult( false, self::AS_BLOCKED_PAGE_FOR_USER );
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
return $status;
}
@@ -1633,22 +1638,16 @@ class EditPage {
// Error will be displayed by showEditForm()
$this->tooBig = true;
$status->setResult( false, self::AS_CONTENT_TOO_BIG );
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
return $status;
}
if ( !$wgUser->isAllowed( 'edit' ) ) {
if ( $wgUser->isAnon() ) {
$status->setResult( false, self::AS_READ_ONLY_PAGE_ANON );
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
return $status;
} else {
$status->fatal( 'readonlytext' );
$status->value = self::AS_READ_ONLY_PAGE_LOGGED;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
return $status;
}
}
@@ -1657,23 +1656,26 @@ class EditPage {
&& !$wgUser->isAllowed( 'editcontentmodel' )
) {
$status->setResult( false, self::AS_NO_CHANGE_CONTENT_MODEL );
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
return $status;
}
+ if ( $this->changeTags ) {
+ $changeTagsStatus = ChangeTags::canAddTagsAccompanyingChange(
+ $this->changeTags, $wgUser );
+ if ( !$changeTagsStatus->isOK() ) {
+ $changeTagsStatus->value = self::AS_CHANGE_TAG_ERROR;
+ return $changeTagsStatus;
+ }
+ }
+
if ( wfReadOnly() ) {
$status->fatal( 'readonlytext' );
$status->value = self::AS_READ_ONLY_PAGE;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
return $status;
}
if ( $wgUser->pingLimiter() || $wgUser->pingLimiter( 'linkpurge', 0 ) ) {
$status->fatal( 'actionthrottledtext' );
$status->value = self::AS_RATE_LIMITED;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
return $status;
}
@@ -1681,13 +1683,9 @@ class EditPage {
# confirmation
if ( $this->wasDeletedSinceLastEdit() && !$this->recreate ) {
$status->setResult( false, self::AS_ARTICLE_WAS_DELETED );
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
return $status;
}
- wfProfileOut( __METHOD__ . '-checks' );
-
# Load the page data from the master. If anything changes in the meantime,
# we detect it by using page_latest like a token in a 1 try compare-and-swap.
$this->mArticle->loadPageData( 'fromdbmaster' );
@@ -1699,7 +1697,6 @@ class EditPage {
$status->fatal( 'nocreatetext' );
$status->value = self::AS_NO_CREATE_PERMISSION;
wfDebug( __METHOD__ . ": no create permission\n" );
- wfProfileOut( __METHOD__ );
return $status;
}
@@ -1717,12 +1714,10 @@ class EditPage {
$this->blankArticle = true;
$status->fatal( 'blankarticle' );
$status->setResult( false, self::AS_BLANK_ARTICLE );
- wfProfileOut( __METHOD__ );
return $status;
}
if ( !$this->runPostMergeFilters( $textbox_content, $status, $wgUser ) ) {
- wfProfileOut( __METHOD__ );
return $status;
}
@@ -1827,12 +1822,10 @@ class EditPage {
if ( $this->isConflict ) {
$status->setResult( false, self::AS_CONFLICT_DETECTED );
- wfProfileOut( __METHOD__ );
return $status;
}
if ( !$this->runPostMergeFilters( $content, $status, $wgUser ) ) {
- wfProfileOut( __METHOD__ );
return $status;
}
@@ -1842,7 +1835,6 @@ class EditPage {
$this->missingSummary = true;
$status->fatal( 'missingsummary' ); // or 'missingcommentheader' if $section == 'new'. Blegh
$status->value = self::AS_SUMMARY_NEEDED;
- wfProfileOut( __METHOD__ );
return $status;
}
@@ -1851,7 +1843,6 @@ class EditPage {
$this->missingComment = true;
$status->fatal( 'missingcommenttext' );
$status->value = self::AS_TEXTBOX_EMPTY;
- wfProfileOut( __METHOD__ );
return $status;
}
} elseif ( !$this->allowBlankSummary
@@ -1862,12 +1853,10 @@ class EditPage {
$this->missingSummary = true;
$status->fatal( 'missingsummary' );
$status->value = self::AS_SUMMARY_NEEDED;
- wfProfileOut( __METHOD__ );
return $status;
}
# All's well
- wfProfileIn( __METHOD__ . '-sectionanchor' );
$sectionanchor = '';
if ( $this->section == 'new' ) {
$this->summary = $this->newSectionSummary( $sectionanchor );
@@ -1884,7 +1873,6 @@ class EditPage {
}
}
$result['sectionanchor'] = $sectionanchor;
- wfProfileOut( __METHOD__ . '-sectionanchor' );
// Save errors may fall down to the edit form, but we've now
// merged the section into full text. Clear the section field
@@ -1896,12 +1884,25 @@ class EditPage {
$status->value = self::AS_SUCCESS_UPDATE;
}
+ if ( !$this->allowSelfRedirect
+ && $content->isRedirect()
+ && $content->getRedirectTarget()->equals( $this->getTitle() )
+ ) {
+ // If the page already redirects to itself, don't warn.
+ $currentTarget = $this->getCurrentContent()->getRedirectTarget();
+ if ( !$currentTarget || !$currentTarget->equals( $this->getTitle() ) ) {
+ $this->selfRedirect = true;
+ $status->fatal( 'selfredirect' );
+ $status->value = self::AS_SELF_REDIRECT;
+ return $status;
+ }
+ }
+
// Check for length errors again now that the section is merged in
$this->kblength = (int)( strlen( $this->toEditText( $content ) ) / 1024 );
if ( $this->kblength > $wgMaxArticleSize ) {
$this->tooBig = true;
$status->setResult( false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED );
- wfProfileOut( __METHOD__ );
return $status;
}
@@ -1931,7 +1932,6 @@ class EditPage {
// Destroys data doEdit() put in $status->value but who cares
$doEditStatus->value = self::AS_END;
}
- wfProfileOut( __METHOD__ );
return $doEditStatus;
}
@@ -1941,8 +1941,18 @@ class EditPage {
$wgUser->pingLimiter( 'linkpurge' );
}
$result['redirect'] = $content->isRedirect();
+
$this->updateWatchlist();
- wfProfileOut( __METHOD__ );
+
+ if ( $this->changeTags && isset( $doEditStatus->value['revision'] ) ) {
+ // If a revision was created, apply any change tags that were requested
+ ChangeTags::addTags(
+ $this->changeTags,
+ isset( $doEditStatus->value['rc'] ) ? $doEditStatus->value['rc']->mAttribs['rc_id'] : null,
+ $doEditStatus->value['revision']->getId()
+ );
+ }
+
return $status;
}
@@ -1979,7 +1989,6 @@ class EditPage {
* @return bool
*/
private function mergeChangesIntoContent( &$editContent ) {
- wfProfileIn( __METHOD__ );
$db = wfGetDB( DB_MASTER );
@@ -1988,7 +1997,6 @@ class EditPage {
$baseContent = $baseRevision ? $baseRevision->getContent() : null;
if ( is_null( $baseContent ) ) {
- wfProfileOut( __METHOD__ );
return false;
}
@@ -1997,7 +2005,6 @@ class EditPage {
$currentContent = $currentRevision ? $currentRevision->getContent() : null;
if ( is_null( $currentContent ) ) {
- wfProfileOut( __METHOD__ );
return false;
}
@@ -2007,11 +2014,9 @@ class EditPage {
if ( $result ) {
$editContent = $result;
- wfProfileOut( __METHOD__ );
return true;
}
- wfProfileOut( __METHOD__ );
return false;
}
@@ -2070,19 +2075,31 @@ class EditPage {
}
function setHeaders() {
- global $wgOut, $wgUser;
+ global $wgOut, $wgUser, $wgAjaxEditStash;
$wgOut->addModules( 'mediawiki.action.edit' );
$wgOut->addModuleStyles( 'mediawiki.action.edit.styles' );
- if ( $wgUser->getOption( 'uselivepreview', false ) ) {
+ if ( $wgUser->getOption( 'showtoolbar' ) ) {
+ // The addition of default buttons is handled by getEditToolbar() which
+ // has its own dependency on this module. The call here ensures the module
+ // is loaded in time (it has position "top") for other modules to register
+ // buttons (e.g. extensions, gadgets, user scripts).
+ $wgOut->addModules( 'mediawiki.toolbar' );
+ }
+
+ if ( $wgUser->getOption( 'uselivepreview' ) ) {
$wgOut->addModules( 'mediawiki.action.edit.preview' );
}
- if ( $wgUser->getOption( 'useeditwarning', false ) ) {
+ if ( $wgUser->getOption( 'useeditwarning' ) ) {
$wgOut->addModules( 'mediawiki.action.edit.editWarning' );
}
+ if ( $wgAjaxEditStash ) {
+ $wgOut->addModules( 'mediawiki.action.edit.stash' );
+ }
+
$wgOut->setRobotPolicy( 'noindex,nofollow' );
# Enabled article-related sidebar, toplinks, etc.
@@ -2108,6 +2125,9 @@ class EditPage {
$displayTitle = $contextTitle->getPrefixedText();
}
$wgOut->setPageTitle( wfMessage( $msg, $displayTitle ) );
+ # Transmit the name of the message to JavaScript for live preview
+ # Keep Resources.php/mediawiki.action.edit.preview in sync with the possible keys
+ $wgOut->addJsConfigVars( 'wgEditMessage', $msg );
}
/**
@@ -2124,6 +2144,17 @@ class EditPage {
if ( $namespace == NS_MEDIAWIKI ) {
# Show a warning if editing an interface message
$wgOut->wrapWikiMsg( "<div class='mw-editinginterface'>\n$1\n</div>", 'editinginterface' );
+ # If this is a default message (but not css or js),
+ # show a hint that it is translatable on translatewiki.net
+ if ( !$this->mTitle->hasContentModel( CONTENT_MODEL_CSS )
+ && !$this->mTitle->hasContentModel( CONTENT_MODEL_JAVASCRIPT )
+ ) {
+ $defaultMessageText = $this->mTitle->getDefaultMessageText();
+ if ( $defaultMessageText !== false ) {
+ $wgOut->wrapWikiMsg( "<div class='mw-translateinterface'>\n$1\n</div>",
+ 'translateinterface' );
+ }
+ }
} elseif ( $namespace == NS_FILE ) {
# Show a hint to shared repo
$file = wfFindFile( $this->mTitle );
@@ -2155,7 +2186,8 @@ class EditPage {
if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
$wgOut->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n$1\n</div>",
array( 'userpage-userdoesnotexist', wfEscapeWikiText( $username ) ) );
- } elseif ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) { # Show log extract if the user is currently blocked
+ } elseif ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) {
+ # Show log extract if the user is currently blocked
LogEventsList::showLogExtract(
$wgOut,
'block',
@@ -2222,7 +2254,10 @@ class EditPage {
if ( $title instanceof Title && $title->exists() && $title->userCan( 'read' ) ) {
global $wgOut;
// Added using template syntax, to take <noinclude>'s into account.
- $wgOut->addWikiTextTitleTidy( '<div class="mw-editintro">{{:' . $title->getFullText() . '}}</div>', $this->mTitle );
+ $wgOut->addWikiTextTitleTidy(
+ '<div class="mw-editintro">{{:' . $title->getFullText() . '}}</div>',
+ $this->mTitle
+ );
return true;
}
}
@@ -2248,11 +2283,7 @@ class EditPage {
* $this->allowNonTextContent is not true.
*/
protected function toEditText( $content ) {
- if ( $content === null || $content === false ) {
- return $content;
- }
-
- if ( is_string( $content ) ) {
+ if ( $content === null || $content === false || is_string( $content ) ) {
return $content;
}
@@ -2300,12 +2331,13 @@ class EditPage {
* Send the edit form and related headers to $wgOut
* @param callable|null $formCallback That takes an OutputPage parameter; will be called
* during form output near the top, for captchas and the like.
+ *
+ * The $formCallback parameter is deprecated since MediaWiki 1.25. Please
+ * use the EditPage::showEditForm:fields hook instead.
*/
function showEditForm( $formCallback = null ) {
global $wgOut, $wgUser;
- wfProfileIn( __METHOD__ );
-
# need to parse the preview early so that we know which templates are used,
# otherwise users with "show preview after edit box" will get a blank list
# we parse this near the beginning so that setHeaders can do the title
@@ -2315,12 +2347,11 @@ class EditPage {
$previewOutput = $this->getPreviewText();
}
- wfRunHooks( 'EditPage::showEditForm:initial', array( &$this, &$wgOut ) );
+ Hooks::run( 'EditPage::showEditForm:initial', array( &$this, &$wgOut ) );
$this->setHeaders();
if ( $this->showHeader() === false ) {
- wfProfileOut( __METHOD__ );
return;
}
@@ -2358,6 +2389,7 @@ class EditPage {
) );
if ( is_callable( $formCallback ) ) {
+ wfWarn( 'The $formCallback parameter to ' . __METHOD__ . 'is deprecated' );
call_user_func_array( $formCallback, array( &$wgOut ) );
}
@@ -2381,7 +2413,7 @@ class EditPage {
. Xml::closeElement( 'div' )
);
- wfRunHooks( 'EditPage::showEditForm:fields', array( &$this, &$wgOut ) );
+ Hooks::run( 'EditPage::showEditForm:fields', array( &$this, &$wgOut ) );
// Put these up at the top to ensure they aren't lost on early form submission
$this->showFormBeforeText();
@@ -2425,6 +2457,10 @@ class EditPage {
$wgOut->addHTML( Html::hidden( 'wpUndidRevision', $this->undidRev ) );
}
+ if ( $this->selfRedirect ) {
+ $wgOut->addHTML( Html::hidden( 'wpIgnoreSelfRedirect', true ) );
+ }
+
if ( $this->hasPresetSummary ) {
// If a summary has been preset using &summary= we don't want to prompt for
// a different summary. Only prompt for a summary if the summary is blanked.
@@ -2436,6 +2472,8 @@ class EditPage {
$wgOut->addHTML( Html::hidden( 'wpAutoSummary', $autosumm ) );
$wgOut->addHTML( Html::hidden( 'oldid', $this->oldid ) );
+ $wgOut->addHTML( Html::hidden( 'parentRevId',
+ $this->parentRevId ?: $this->mArticle->getRevIdFetched() ) );
$wgOut->addHTML( Html::hidden( 'format', $this->contentFormat ) );
$wgOut->addHTML( Html::hidden( 'model', $this->contentModel ) );
@@ -2517,7 +2555,6 @@ class EditPage {
$this->displayPreviewArea( $previewOutput, false );
}
- wfProfileOut( __METHOD__ );
}
/**
@@ -2548,7 +2585,19 @@ class EditPage {
}
// Add edit notices
- $wgOut->addHTML( implode( "\n", $this->mTitle->getEditNotices( $this->oldid ) ) );
+ $editNotices = $this->mTitle->getEditNotices( $this->oldid );
+ if ( count( $editNotices ) ) {
+ $wgOut->addHTML( implode( "\n", $editNotices ) );
+ } else {
+ $msg = wfMessage( 'editnotice-notext' );
+ if ( !$msg->isDisabled() ) {
+ $wgOut->addHTML(
+ '<div class="mw-editnotice-notext">'
+ . $msg->parseAsBlock()
+ . '</div>'
+ );
+ }
+ }
if ( $this->isConflict ) {
$wgOut->wrapWikiMsg( "<div class='mw-explainconflict'>\n$1\n</div>", 'explainconflict' );
@@ -2587,6 +2636,10 @@ class EditPage {
$wgOut->wrapWikiMsg( "<div id='mw-blankarticle'>\n$1\n</div>", 'blankarticle' );
}
+ if ( $this->selfRedirect ) {
+ $wgOut->wrapWikiMsg( "<div id='mw-selfredirect'>\n$1\n</div>", 'selfredirect' );
+ }
+
if ( $this->hookError !== '' ) {
$wgOut->addWikiText( $this->hookError );
}
@@ -2846,7 +2899,7 @@ class EditPage {
global $wgOut;
$section = htmlspecialchars( $this->section );
$wgOut->addHTML( <<<HTML
-<input type='hidden' value="{$section}" name="wpSection" />
+<input type='hidden' value="{$section}" name="wpSection"/>
<input type='hidden' value="{$this->starttime}" name="wpStarttime" />
<input type='hidden' value="{$this->edittime}" name="wpEdittime" />
<input type='hidden' value="{$this->scrolltop}" name="wpScrolltop" id="wpScrolltop" />
@@ -2964,7 +3017,7 @@ HTML
);
$pageLang = $this->mTitle->getPageLanguage();
- $attribs['lang'] = $pageLang->getCode();
+ $attribs['lang'] = $pageLang->getHtmlCode();
$attribs['dir'] = $pageLang->getDir();
$wgOut->addHTML( Html::textarea( $name, $wikitext, $attribs ) );
@@ -2987,6 +3040,12 @@ HTML
if ( $this->formtype == 'preview' ) {
$this->showPreview( $previewOutput );
+ } else {
+ // Empty content container for LivePreview
+ $pageViewLang = $this->mTitle->getPageViewLanguage();
+ $attribs = array( 'lang' => $pageViewLang->getHtmlCode(), 'dir' => $pageViewLang->getDir(),
+ 'class' => 'mw-content-' . $pageViewLang->getDir() );
+ $wgOut->addHTML( Html::rawElement( 'div', $attribs ) );
}
$wgOut->addHTML( '</div>' );
@@ -3019,7 +3078,7 @@ HTML
}
# This hook seems slightly odd here, but makes things more
# consistent for extensions.
- wfRunHooks( 'OutputPageBeforeHTML', array( &$wgOut, &$text ) );
+ Hooks::run( 'OutputPageBeforeHTML', array( &$wgOut, &$text ) );
$wgOut->addHTML( $text );
if ( $this->mTitle->getNamespace() == NS_CATEGORY ) {
$this->mArticle->closeShowCategory();
@@ -3058,7 +3117,7 @@ HTML
if ( $newContent ) {
ContentHandler::runLegacyHooks( 'EditPageGetDiffText', array( $this, &$newContent ) );
- wfRunHooks( 'EditPageGetDiffContent', array( $this, &$newContent ) );
+ Hooks::run( 'EditPageGetDiffContent', array( $this, &$newContent ) );
$popts = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang );
$newContent = $newContent->preSaveTransform( $this->mTitle, $wgUser, $popts );
@@ -3110,7 +3169,7 @@ HTML
*/
protected function showTosSummary() {
$msg = 'editpage-tos-summary';
- wfRunHooks( 'EditPageTosSummary', array( $this->mTitle, &$msg ) );
+ Hooks::run( 'EditPageTosSummary', array( $this->mTitle, &$msg ) );
if ( !wfMessage( $msg )->isDisabled() ) {
global $wgOut;
$wgOut->addHTML( '<div class="mw-tos-summary">' );
@@ -3154,7 +3213,7 @@ HTML
'[[' . wfMessage( 'copyrightpage' )->inContentLanguage()->text() . ']]' );
}
// Allow for site and per-namespace customization of contribution/copyright notice.
- wfRunHooks( 'EditPageCopyrightWarning', array( $title, &$copywarnMsg ) );
+ Hooks::run( 'EditPageCopyrightWarning', array( $title, &$copywarnMsg ) );
return "<div id=\"editpage-copywarn\">\n" .
call_user_func_array( 'wfMessage', $copywarnMsg )->$format() . "\n</div>";
@@ -3172,8 +3231,6 @@ HTML
return '';
}
- wfProfileIn( __METHOD__ );
-
$limitReport = Html::rawElement( 'div', array( 'class' => 'mw-limitReportExplanation' ),
wfMessage( 'limitreport-title' )->parseAsBlock()
);
@@ -3187,7 +3244,7 @@ HTML
Html::openElement( 'tbody' );
foreach ( $output->getLimitReportData() as $key => $value ) {
- if ( wfRunHooks( 'ParserLimitReportFormat',
+ if ( Hooks::run( 'ParserLimitReportFormat',
array( $key, &$value, &$limitReport, true, true )
) ) {
$keyMsg = wfMessage( $key );
@@ -3208,13 +3265,11 @@ HTML
Html::closeElement( 'table' ) .
Html::closeElement( 'div' );
- wfProfileOut( __METHOD__ );
-
return $limitReport;
}
protected function showStandardInputs( &$tabindex = 2 ) {
- global $wgOut, $wgUseMediaWikiUIEverywhere;
+ global $wgOut;
$wgOut->addHTML( "<div class='editOptions'>\n" );
if ( $this->section != 'new' ) {
@@ -3246,10 +3301,8 @@ HTML
'target' => 'helpwindow',
'href' => $edithelpurl,
);
- if ( $wgUseMediaWikiUIEverywhere ) {
- $attrs['class'] = 'mw-ui-button mw-ui-quiet';
- }
- $edithelp = Html::element( 'a', $attrs, wfMessage( 'edithelp' )->text() ) .
+ $edithelp = Html::linkButton( wfMessage( 'edithelp' )->text(),
+ $attrs, array( 'mw-ui-quiet' ) ) .
wfMessage( 'word-separator' )->escaped() .
wfMessage( 'newwindow' )->parse();
@@ -3257,7 +3310,7 @@ HTML
$wgOut->addHTML( " <span class='editHelp'>{$edithelp}</span>\n" );
$wgOut->addHTML( "</div><!-- editButtons -->\n" );
- wfRunHooks( 'EditPage::showStandardInputs:options', array( $this, $wgOut, &$tabindex ) );
+ Hooks::run( 'EditPage::showStandardInputs:options', array( $this, $wgOut, &$tabindex ) );
$wgOut->addHTML( "</div><!-- editOptions -->\n" );
}
@@ -3269,7 +3322,7 @@ HTML
protected function showConflict() {
global $wgOut;
- if ( wfRunHooks( 'EditPageBeforeConflictDiff', array( &$this, &$wgOut ) ) ) {
+ if ( Hooks::run( 'EditPageBeforeConflictDiff', array( &$this, &$wgOut ) ) ) {
$wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourdiff" );
$content1 = $this->toEditContent( $this->textbox1 );
@@ -3292,20 +3345,16 @@ HTML
* @return string
*/
public function getCancelLink() {
- global $wgUseMediaWikiUIEverywhere;
$cancelParams = array();
if ( !$this->isConflict && $this->oldid > 0 ) {
$cancelParams['oldid'] = $this->oldid;
}
$attrs = array( 'id' => 'mw-editform-cancel' );
- if ( $wgUseMediaWikiUIEverywhere ) {
- $attrs['class'] = 'mw-ui-button mw-ui-quiet';
- }
return Linker::linkKnown(
$this->getContextTitle(),
wfMessage( 'cancel' )->parse(),
- $attrs,
+ Html::buttonAttributes( $attrs, array( 'mw-ui-quiet' ) ),
$cancelParams
);
}
@@ -3401,8 +3450,6 @@ HTML
global $wgOut, $wgUser, $wgRawHtml, $wgLang;
global $wgAllowUserCss, $wgAllowUserJs;
- wfProfileIn( __METHOD__ );
-
if ( $wgRawHtml && !$this->mTokenOk ) {
// Could be an offsite preview attempt. This is very unsafe if
// HTML is enabled, as it could be an attack.
@@ -3414,7 +3461,6 @@ HTML
$parsedNote = $wgOut->parse( "<div class='previewnote'>" .
wfMessage( 'session_fail_preview_html' )->text() . "</div>", true, /* interface */true );
}
- wfProfileOut( __METHOD__ );
return $parsedNote;
}
@@ -3424,11 +3470,10 @@ HTML
$content = $this->toEditContent( $this->textbox1 );
$previewHTML = '';
- if ( !wfRunHooks(
+ if ( !Hooks::run(
'AlternateEditPreview',
array( $this, &$content, &$previewHTML, &$this->mParserOutput ) )
) {
- wfProfileOut( __METHOD__ );
return $previewHTML;
}
@@ -3449,7 +3494,6 @@ HTML
}
$parserOptions = $this->mArticle->makeParserOptions( $this->mArticle->getContext() );
- $parserOptions->setEditSection( false );
$parserOptions->setIsPreview( true );
$parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !== '' );
@@ -3494,20 +3538,26 @@ HTML
$hook_args = array( $this, &$content );
ContentHandler::runLegacyHooks( 'EditPageGetPreviewText', $hook_args );
- wfRunHooks( 'EditPageGetPreviewContent', $hook_args );
+ Hooks::run( 'EditPageGetPreviewContent', $hook_args );
$parserOptions->enableLimitReport();
# For CSS/JS pages, we should have called the ShowRawCssJs hook here.
# But it's now deprecated, so never mind
- $content = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions );
- $parserOutput = $content->getParserOutput(
- $this->getArticle()->getTitle(),
- null,
- $parserOptions
+ $pstContent = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions );
+ $scopedCallback = $parserOptions->setupFakeRevision(
+ $this->mTitle, $pstContent, $wgUser );
+ $parserOutput = $pstContent->getParserOutput( $this->mTitle, null, $parserOptions );
+
+ # Try to stash the edit for the final submission step
+ # @todo: different date format preferences cause cache misses
+ ApiStashEdit::stashEditFromPreview(
+ $this->getArticle(), $content, $pstContent,
+ $parserOutput, $parserOptions, $parserOptions, wfTimestampNow()
);
+ $parserOutput->setEditSectionTokens( false ); // no section edit links
$previewHTML = $parserOutput->getText();
$this->mParserOutput = $parserOutput;
$wgOut->addParserOutputMetadata( $parserOutput );
@@ -3542,7 +3592,6 @@ HTML
'class' => 'mw-content-' . $pageViewLang->getDir() );
$previewHTML = Html::rawElement( 'div', $attribs, $previewHTML );
- wfProfileOut( __METHOD__ );
return $previewhead . $previewHTML . $this->previewTextAfterContent;
}
@@ -3660,7 +3709,7 @@ HTML
)
);
- $script = 'mw.loader.using("mediawiki.action.edit", function() {';
+ $script = 'mw.loader.using("mediawiki.toolbar", function () {';
foreach ( $toolarray as $tool ) {
if ( !$tool ) {
continue;
@@ -3680,21 +3729,19 @@ HTML
$tool['id'],
);
- $script .= Xml::encodeJsCall( 'mw.toolbar.addButton', $params );
+ $script .= Xml::encodeJsCall(
+ 'mw.toolbar.addButton',
+ $params,
+ ResourceLoader::inDebugMode()
+ );
}
- // This used to be called on DOMReady from mediawiki.action.edit, which
- // ended up causing race conditions with the setup code above.
- $script .= "\n" .
- "// Create button bar\n" .
- "$(function() { mw.toolbar.init(); } );\n";
-
$script .= '});';
$wgOut->addScript( Html::inlineScript( ResourceLoader::makeLoaderConditionalScript( $script ) ) );
$toolbar = '<div id="toolbar"></div>';
- wfRunHooks( 'EditPageBeforeEditToolbar', array( &$toolbar ) );
+ Hooks::run( 'EditPageBeforeEditToolbar', array( &$toolbar ) );
return $toolbar;
}
@@ -3761,7 +3808,7 @@ HTML
$checkboxes['watch'] = $watchThisHtml;
}
}
- wfRunHooks( 'EditPageBeforeEditChecks', array( &$this, &$checkboxes, &$tabindex ) );
+ Hooks::run( 'EditPageBeforeEditChecks', array( &$this, &$checkboxes, &$tabindex ) );
return $checkboxes;
}
@@ -3774,83 +3821,39 @@ HTML
* @return array
*/
public function getEditButtons( &$tabindex ) {
- global $wgUseMediaWikiUIEverywhere;
-
$buttons = array();
$attribs = array(
'id' => 'wpSave',
'name' => 'wpSave',
- 'type' => 'submit',
'tabindex' => ++$tabindex,
- 'value' => wfMessage( 'savearticle' )->text(),
) + Linker::tooltipAndAccesskeyAttribs( 'save' );
- if ( $wgUseMediaWikiUIEverywhere ) {
- $attribs['class'] = 'mw-ui-button mw-ui-constructive';
- }
- $buttons['save'] = Xml::element( 'input', $attribs, '' );
+ $buttons['save'] = Html::submitButton( wfMessage( 'savearticle' )->text(),
+ $attribs, array( 'mw-ui-constructive' ) );
++$tabindex; // use the same for preview and live preview
$attribs = array(
'id' => 'wpPreview',
'name' => 'wpPreview',
- 'type' => 'submit',
'tabindex' => $tabindex,
- 'value' => wfMessage( 'showpreview' )->text(),
) + Linker::tooltipAndAccesskeyAttribs( 'preview' );
- if ( $wgUseMediaWikiUIEverywhere ) {
- $attribs['class'] = 'mw-ui-button mw-ui-progressive';
- }
- $buttons['preview'] = Xml::element( 'input', $attribs, '' );
+ $buttons['preview'] = Html::submitButton( wfMessage( 'showpreview' )->text(),
+ $attribs );
$buttons['live'] = '';
$attribs = array(
'id' => 'wpDiff',
'name' => 'wpDiff',
- 'type' => 'submit',
'tabindex' => ++$tabindex,
- 'value' => wfMessage( 'showdiff' )->text(),
) + Linker::tooltipAndAccesskeyAttribs( 'diff' );
- if ( $wgUseMediaWikiUIEverywhere ) {
- $attribs['class'] = 'mw-ui-button mw-ui-progressive';
- }
- $buttons['diff'] = Xml::element( 'input', $attribs, '' );
+ $buttons['diff'] = Html::submitButton( wfMessage( 'showdiff' )->text(),
+ $attribs );
- wfRunHooks( 'EditPageBeforeEditButtons', array( &$this, &$buttons, &$tabindex ) );
+ Hooks::run( 'EditPageBeforeEditButtons', array( &$this, &$buttons, &$tabindex ) );
return $buttons;
}
/**
- * Output preview text only. This can be sucked into the edit page
- * via JavaScript, and saves the server time rendering the skin as
- * well as theoretically being more robust on the client (doesn't
- * disturb the edit box's undo history, won't eat your text on
- * failure, etc).
- *
- * @todo This doesn't include category or interlanguage links.
- * Would need to enhance it a bit, "<s>maybe wrap them in XML
- * or something...</s>" that might also require more skin
- * initialization, so check whether that's a problem.
- */
- function livePreview() {
- global $wgOut;
- $wgOut->disable();
- header( 'Content-type: text/xml; charset=utf-8' );
- header( 'Cache-control: no-cache' );
-
- $previewText = $this->getPreviewText();
- #$categories = $skin->getCategoryLinks();
-
- $s =
- '<?xml version="1.0" encoding="UTF-8" ?>' . "\n" .
- Xml::tags( 'livepreview', null,
- Xml::element( 'preview', null, $previewText )
- #. Xml::element( 'category', null, $categories )
- );
- echo $s;
- }
-
- /**
* Creates a basic error page which informs the user that
* they have attempted to edit a nonexistent section.
*/
@@ -3860,7 +3863,7 @@ HTML
$wgOut->prepareErrorPage( wfMessage( 'nosuchsectiontitle' ) );
$res = wfMessage( 'nosuchsectiontext', $this->section )->parseAsBlock();
- wfRunHooks( 'EditPageNoSuchSection', array( &$this, &$res ) );
+ Hooks::run( 'EditPageNoSuchSection', array( &$this, &$res ) );
$wgOut->addHTML( $res );
$wgOut->returnToMain( false, $this->mTitle );
@@ -4018,9 +4021,9 @@ HTML
// Do some sanity checks. These aren't needed for reversibility,
// but should help keep the breakage down if the editor
// breaks one of the entities whilst editing.
- if ( ( substr( $invalue, $i, 1 ) == ";" ) and ( strlen( $hexstring ) <= 6 ) ) {
+ if ( ( substr( $invalue, $i, 1 ) == ";" ) && ( strlen( $hexstring ) <= 6 ) ) {
$codepoint = hexdec( $hexstring );
- $result .= codepointToUtf8( $codepoint );
+ $result .= UtfNormal\Utils::codepointToUtf8( $codepoint );
} else {
$result .= "&#x" . $hexstring . substr( $invalue, $i, 1 );
}
diff --git a/includes/Export.php b/includes/Export.php
index 84f5c60c..4600feb5 100644
--- a/includes/Export.php
+++ b/includes/Export.php
@@ -69,7 +69,7 @@ class WikiExporter {
* @return string
*/
public static function schemaVersion() {
- return "0.9";
+ return "0.10";
}
/**
@@ -213,7 +213,6 @@ class WikiExporter {
* @param array $cond
*/
protected function do_list_authors( $cond ) {
- wfProfileIn( __METHOD__ );
$this->author_list = "<contributors>";
// rev_deleted
@@ -239,7 +238,6 @@ class WikiExporter {
"</contributor>";
}
$this->author_list .= "</contributors>";
- wfProfileOut( __METHOD__ );
}
/**
@@ -248,7 +246,6 @@ class WikiExporter {
* @throws Exception
*/
protected function dumpFrom( $cond = '' ) {
- wfProfileIn( __METHOD__ );
# For logging dumps...
if ( $this->history & self::LOGS ) {
$where = array( 'user_id = log_user' );
@@ -304,7 +301,6 @@ class WikiExporter {
}
// Inform caller about problem
- wfProfileOut( __METHOD__ );
throw $e;
}
# For page dumps...
@@ -348,8 +344,7 @@ class WikiExporter {
# Default JOIN, to be overridden...
$join['revision'] = array( 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' );
# One, and only one hook should set this, and return false
- if ( wfRunHooks( 'WikiExporter::dumpStableQuery', array( &$tables, &$opts, &$join ) ) ) {
- wfProfileOut( __METHOD__ );
+ if ( Hooks::run( 'WikiExporter::dumpStableQuery', array( &$tables, &$opts, &$join ) ) ) {
throw new MWException( __METHOD__ . " given invalid history dump type." );
}
} elseif ( $this->history & WikiExporter::RANGE ) {
@@ -358,7 +353,6 @@ class WikiExporter {
$opts['ORDER BY'] = array( 'rev_page ASC', 'rev_id ASC' );
} else {
# Unknown history specification parameter?
- wfProfileOut( __METHOD__ );
throw new MWException( __METHOD__ . " given invalid history dump type." );
}
# Query optimization hacks
@@ -378,7 +372,7 @@ class WikiExporter {
$result = null; // Assuring $result is not undefined, if exception occurs early
try {
- wfRunHooks( 'ModifyExportQuery',
+ Hooks::run( 'ModifyExportQuery',
array( $this->db, &$tables, &$cond, &$opts, &$join ) );
# Do the query!
@@ -417,7 +411,6 @@ class WikiExporter {
throw $e;
}
}
- wfProfileOut( __METHOD__ );
}
/**
@@ -480,16 +473,6 @@ class WikiExporter {
*/
class XmlDumpWriter {
/**
- * Returns the export schema version.
- * @deprecated since 1.20; use WikiExporter::schemaVersion() instead
- * @return string
- */
- function schemaVersion() {
- wfDeprecated( __METHOD__, '1.20' );
- return WikiExporter::schemaVersion();
- }
-
- /**
* Opens the XML output stream's root "<mediawiki>" element.
* This does not include an xml directive, so is safe to include
* as a subelement in a larger XML stream. Namespace and XML Schema
@@ -637,7 +620,7 @@ class XmlDumpWriter {
strval( $row->page_restrictions ) ) . "\n";
}
- wfRunHooks( 'XmlDumpWriterOpenPage', array( $this, &$out, $row, $title ) );
+ Hooks::run( 'XmlDumpWriterOpenPage', array( $this, &$out, $row, $title ) );
return $out;
}
@@ -661,7 +644,6 @@ class XmlDumpWriter {
* @access private
*/
function writeRevision( $row ) {
- wfProfileIn( __METHOD__ );
$out = " <revision>\n";
$out .= " " . Xml::element( 'id', null, strval( $row->rev_id ) ) . "\n";
@@ -703,6 +685,9 @@ class XmlDumpWriter {
$content_format = $content_handler->getDefaultFormat();
}
+ $out .= " " . Xml::element( 'model', null, strval( $content_model ) ) . "\n";
+ $out .= " " . Xml::element( 'format', null, strval( $content_format ) ) . "\n";
+
$text = '';
if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_TEXT ) ) {
$out .= " " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
@@ -729,14 +714,10 @@ class XmlDumpWriter {
$out .= " <sha1/>\n";
}
- $out .= " " . Xml::element( 'model', null, strval( $content_model ) ) . "\n";
- $out .= " " . Xml::element( 'format', null, strval( $content_format ) ) . "\n";
-
- wfRunHooks( 'XmlDumpWriterWriteRevision', array( &$this, &$out, $row, $text ) );
+ Hooks::run( 'XmlDumpWriterWriteRevision', array( &$this, &$out, $row, $text ) );
$out .= " </revision>\n";
- wfProfileOut( __METHOD__ );
return $out;
}
@@ -749,7 +730,6 @@ class XmlDumpWriter {
* @access private
*/
function writeLogItem( $row ) {
- wfProfileIn( __METHOD__ );
$out = " <logitem>\n";
$out .= " " . Xml::element( 'id', null, strval( $row->log_id ) ) . "\n";
@@ -783,7 +763,6 @@ class XmlDumpWriter {
$out .= " </logitem>\n";
- wfProfileOut( __METHOD__ );
return $out;
}
diff --git a/includes/Feed.php b/includes/Feed.php
index 2fdfa424..600b136d 100644
--- a/includes/Feed.php
+++ b/includes/Feed.php
@@ -92,7 +92,7 @@ class FeedItem {
*/
public function getUniqueId() {
if ( $this->uniqueId ) {
- return $this->xmlEncode( $this->uniqueId );
+ return $this->xmlEncode( wfExpandUrl( $this->uniqueId, PROTO_CURRENT ) );
}
}
@@ -184,7 +184,8 @@ class FeedItem {
}
/**
- * @todo document (needs one-sentence top-level class description).
+ * Class to support the outputting of syndication feeds in Atom and RSS format.
+ *
* @ingroup Feed
*/
abstract class ChannelFeed extends FeedItem {
@@ -338,13 +339,14 @@ class RSSFeed extends ChannelFeed {
*/
class AtomFeed extends ChannelFeed {
/**
- * @todo document
- * @param string|int $ts
+ * Format a date given timestamp.
+ *
+ * @param string|int $timestamp
* @return string
*/
- function formatTime( $ts ) {
+ function formatTime( $timestamp ) {
// need to use RFC 822 time format at least for rss2.0
- return gmdate( 'Y-m-d\TH:i:s', wfTimestamp( TS_UNIX, $ts ) );
+ return gmdate( 'Y-m-d\TH:i:s', wfTimestamp( TS_UNIX, $timestamp ) );
}
/**
diff --git a/includes/FeedUtils.php b/includes/FeedUtils.php
index 6937c32d..a01d6420 100644
--- a/includes/FeedUtils.php
+++ b/includes/FeedUtils.php
@@ -106,7 +106,6 @@ class FeedUtils {
$comment, $actiontext = ''
) {
global $wgFeedDiffCutoff, $wgLang;
- wfProfileIn( __METHOD__ );
// log entries
$completeText = '<p>' . implode( ' ',
@@ -124,12 +123,10 @@ class FeedUtils {
// Can't diff special pages, unreadable pages or pages with no new revision
// to compare against: just return the text.
if ( $title->getNamespace() < 0 || $accErrors || !$newid ) {
- wfProfileOut( __METHOD__ );
return $completeText;
}
if ( $oldid ) {
- wfProfileIn( __METHOD__ . "-dodiff" );
#$diffText = $de->getDiff( wfMessage( 'revisionasof',
# $wgLang->timeanddate( $timestamp ),
@@ -167,10 +164,9 @@ class FeedUtils {
$diffText = "<p>Can't load revision $newid</p>";
} else {
// Diff output fine, clean up any illegal UTF-8
- $diffText = UtfNormal::cleanUp( $diffText );
+ $diffText = UtfNormal\Validator::cleanUp( $diffText );
$diffText = self::applyDiffStyle( $diffText );
}
- wfProfileOut( __METHOD__ . "-dodiff" );
} else {
$rev = Revision::newFromId( $newid );
if ( $wgFeedDiffCutoff <= 0 || is_null( $rev ) ) {
@@ -208,7 +204,6 @@ class FeedUtils {
}
$completeText .= $diffText;
- wfProfileOut( __METHOD__ );
return $completeText;
}
diff --git a/includes/FileDeleteForm.php b/includes/FileDeleteForm.php
index b4e24581..c1d14db0 100644
--- a/includes/FileDeleteForm.php
+++ b/includes/FileDeleteForm.php
@@ -201,7 +201,7 @@ class FileDeleteForm {
$dbw->rollback( __METHOD__ );
}
}
- } catch ( MWException $e ) {
+ } catch ( Exception $e ) {
// Rollback before returning to prevent UI from displaying
// incorrect "View or restore N deleted edits?"
$dbw->rollback( __METHOD__ );
@@ -210,7 +210,7 @@ class FileDeleteForm {
}
if ( $status->isOK() ) {
- wfRunHooks( 'FileDeleteComplete', array( &$file, &$oldimage, &$page, &$user, &$reason ) );
+ Hooks::run( 'FileDeleteComplete', array( &$file, &$oldimage, &$page, &$user, &$reason ) );
}
return $status;
diff --git a/includes/GitInfo.php b/includes/GitInfo.php
index 7052820e..fb298cfe 100644
--- a/includes/GitInfo.php
+++ b/includes/GitInfo.php
@@ -392,7 +392,7 @@ class GitInfo {
if ( self::$viewers === false ) {
self::$viewers = $wgGitRepositoryViewers;
- wfRunHooks( 'GitViewers', array( &self::$viewers ) );
+ Hooks::run( 'GitViewers', array( &self::$viewers ) );
}
return self::$viewers;
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index 27f7cacb..240cb97b 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -24,13 +24,17 @@ if ( !defined( 'MEDIAWIKI' ) ) {
die( "This file is part of MediaWiki, it is not a valid entry point" );
}
+use Liuggio\StatsdClient\StatsdClient;
+use Liuggio\StatsdClient\Sender\SocketSender;
+use MediaWiki\Logger\LoggerFactory;
+
// Hide compatibility functions from Doxygen
/// @cond
/**
* Compatibility functions
*
- * We support PHP 5.3.2 and up.
+ * We support PHP 5.3.3 and up.
* Re-implementations of newer functions or functions in non-standard
* PHP extensions may be included here.
*/
@@ -161,6 +165,72 @@ if ( !function_exists( 'hash_equals' ) ) {
/// @endcond
/**
+ * Load an extension
+ *
+ * This queues an extension to be loaded through
+ * the ExtensionRegistry system.
+ *
+ * @param string $ext Name of the extension to load
+ * @param string|null $path Absolute path of where to find the extension.json file
+ */
+function wfLoadExtension( $ext, $path = null ) {
+ if ( !$path ) {
+ global $wgExtensionDirectory;
+ $path = "$wgExtensionDirectory/$ext/extension.json";
+ }
+ ExtensionRegistry::getInstance()->queue( $path );
+}
+
+/**
+ * Load multiple extensions at once
+ *
+ * Same as wfLoadExtension, but more efficient if you
+ * are loading multiple extensions.
+ *
+ * If you want to specify custom paths, you should interact with
+ * ExtensionRegistry directly.
+ *
+ * @see wfLoadExtension
+ * @param string[] $exts Array of extension names to load
+ */
+function wfLoadExtensions( array $exts ) {
+ global $wgExtensionDirectory;
+ $registry = ExtensionRegistry::getInstance();
+ foreach ( $exts as $ext ) {
+ $registry->queue( "$wgExtensionDirectory/$ext/extension.json" );
+ }
+}
+
+/**
+ * Load a skin
+ *
+ * @see wfLoadExtension
+ * @param string $skin Name of the extension to load
+ * @param string|null $path Absolute path of where to find the skin.json file
+ */
+function wfLoadSkin( $skin, $path = null ) {
+ if ( !$path ) {
+ global $wgStyleDirectory;
+ $path = "$wgStyleDirectory/$skin/skin.json";
+ }
+ ExtensionRegistry::getInstance()->queue( $path );
+}
+
+/**
+ * Load multiple skins at once
+ *
+ * @see wfLoadExtensions
+ * @param string[] $skins Array of extension names to load
+ */
+function wfLoadSkins( array $skins ) {
+ global $wgStyleDirectory;
+ $registry = ExtensionRegistry::getInstance();
+ foreach ( $skins as $skin ) {
+ $registry->queue( "$wgStyleDirectory/$skin/skin.json" );
+ }
+}
+
+/**
* Like array_diff( $a, $b ) except that it works with two-dimensional arrays.
* @param array $a
* @param array $b
@@ -950,44 +1020,40 @@ function wfMatchesDomainList( $url, $domains ) {
* $wgDebugRawPage - if false, 'action=raw' hits will not result in debug output.
* $wgDebugComments - if on, some debug items may appear in comments in the HTML output.
*
+ * @since 1.25 support for additional context data
+ *
* @param string $text
- * @param string|bool $dest Destination of the message:
- * - 'all': both to the log and HTML (debug toolbar or HTML comments)
- * - 'log': only to the log and not in HTML
- * For backward compatibility, it can also take a boolean:
- * - true: same as 'all'
- * - false: same as 'log'
+ * @param string|bool $dest Unused
+ * @param array $context Additional logging context data
*/
-function wfDebug( $text, $dest = 'all' ) {
- global $wgDebugLogFile, $wgDebugRawPage, $wgDebugLogPrefix;
+function wfDebug( $text, $dest = 'all', array $context = array() ) {
+ global $wgDebugRawPage, $wgDebugLogPrefix;
+ global $wgDebugTimestamps, $wgRequestTime;
if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
return;
}
- // Turn $dest into a string if it's a boolean (for b/c)
- if ( $dest === true ) {
- $dest = 'all';
- } elseif ( $dest === false ) {
- $dest = 'log';
- }
+ $text = trim( $text );
- $timer = wfDebugTimer();
- if ( $timer !== '' ) {
- $text = preg_replace( '/[^\n]/', $timer . '\0', $text, 1 );
+ // Inline logic from deprecated wfDebugTimer()
+ if ( $wgDebugTimestamps ) {
+ $context['seconds_elapsed'] = sprintf(
+ '%6.4f',
+ microtime( true ) - $wgRequestTime
+ );
+ $context['memory_used'] = sprintf(
+ '%5.1fM',
+ ( memory_get_usage( true ) / ( 1024 * 1024 ) )
+ );
}
- if ( $dest === 'all' ) {
- MWDebug::debugMsg( $text );
+ if ( $wgDebugLogPrefix !== '' ) {
+ $context['prefix'] = $wgDebugLogPrefix;
}
- if ( $wgDebugLogFile != '' ) {
- # Strip unprintables; they can switch terminal modes when binary data
- # gets dumped, which is pretty annoying.
- $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
- $text = $wgDebugLogPrefix . $text;
- wfErrorLog( $text, $wgDebugLogFile );
- }
+ $logger = LoggerFactory::getInstance( 'wfDebug' );
+ $logger->debug( $text, $context );
}
/**
@@ -1016,11 +1082,14 @@ function wfIsDebugRawPage() {
/**
* Get microsecond timestamps for debug logs
*
+ * @deprecated since 1.25
* @return string
*/
function wfDebugTimer() {
global $wgDebugTimestamps, $wgRequestTime;
+ wfDeprecated( __METHOD__, '1.25' );
+
if ( !$wgDebugTimestamps ) {
return '';
}
@@ -1046,30 +1115,34 @@ function wfDebugMem( $exact = false ) {
}
/**
- * Send a line to a supplementary debug log file, if configured, or main debug log if not.
- * To configure a supplementary log file, set $wgDebugLogGroups[$logGroup] to a string
- * filename or an associative array mapping 'destination' to the desired filename. The
- * associative array may also contain a 'sample' key with an integer value, specifying
- * a sampling factor.
+ * Send a line to a supplementary debug log file, if configured, or main debug
+ * log if not.
+ *
+ * To configure a supplementary log file, set $wgDebugLogGroups[$logGroup] to
+ * a string filename or an associative array mapping 'destination' to the
+ * desired filename. The associative array may also contain a 'sample' key
+ * with an integer value, specifying a sampling factor. Sampled log events
+ * will be emitted with a 1 in N random chance.
*
* @since 1.23 support for sampling log messages via $wgDebugLogGroups.
+ * @since 1.25 support for additional context data
+ * @since 1.25 sample behavior dependent on configured $wgMWLoggerDefaultSpi
*
* @param string $logGroup
* @param string $text
* @param string|bool $dest Destination of the message:
* - 'all': both to the log and HTML (debug toolbar or HTML comments)
* - 'log': only to the log and not in HTML
- * - 'private': only to the specifc log if set in $wgDebugLogGroups and
+ * - 'private': only to the specific log if set in $wgDebugLogGroups and
* discarded otherwise
* For backward compatibility, it can also take a boolean:
* - true: same as 'all'
* - false: same as 'private'
+ * @param array $context Additional logging context data
*/
-function wfDebugLog( $logGroup, $text, $dest = 'all' ) {
- global $wgDebugLogGroups;
-
- $text = trim( $text ) . "\n";
-
+function wfDebugLog(
+ $logGroup, $text, $dest = 'all', array $context = array()
+) {
// Turn $dest into a string if it's a boolean (for b/c)
if ( $dest === true ) {
$dest = 'all';
@@ -1077,66 +1150,24 @@ function wfDebugLog( $logGroup, $text, $dest = 'all' ) {
$dest = 'private';
}
- if ( !isset( $wgDebugLogGroups[$logGroup] ) ) {
- if ( $dest !== 'private' ) {
- wfDebug( "[$logGroup] $text", $dest );
- }
- return;
- }
-
- if ( $dest === 'all' ) {
- MWDebug::debugMsg( "[$logGroup] $text" );
- }
-
- $logConfig = $wgDebugLogGroups[$logGroup];
- if ( $logConfig === false ) {
- return;
- }
- if ( is_array( $logConfig ) ) {
- if ( isset( $logConfig['sample'] ) && mt_rand( 1, $logConfig['sample'] ) !== 1 ) {
- return;
- }
- $destination = $logConfig['destination'];
- } else {
- $destination = strval( $logConfig );
- }
+ $text = trim( $text );
- $time = wfTimestamp( TS_DB );
- $wiki = wfWikiID();
- $host = wfHostname();
- wfErrorLog( "$time $host $wiki: $text", $destination );
+ $logger = LoggerFactory::getInstance( $logGroup );
+ $context['private'] = ( $dest === 'private' );
+ $logger->info( $text, $context );
}
/**
* Log for database errors
*
+ * @since 1.25 support for additional context data
+ *
* @param string $text Database error message.
+ * @param array $context Additional logging context data
*/
-function wfLogDBError( $text ) {
- global $wgDBerrorLog, $wgDBerrorLogTZ;
- static $logDBErrorTimeZoneObject = null;
-
- if ( $wgDBerrorLog ) {
- $host = wfHostname();
- $wiki = wfWikiID();
-
- if ( $wgDBerrorLogTZ && !$logDBErrorTimeZoneObject ) {
- $logDBErrorTimeZoneObject = new DateTimeZone( $wgDBerrorLogTZ );
- }
-
- // Workaround for https://bugs.php.net/bug.php?id=52063
- // Can be removed when min PHP > 5.3.2
- if ( $logDBErrorTimeZoneObject === null ) {
- $d = date_create( "now" );
- } else {
- $d = date_create( "now", $logDBErrorTimeZoneObject );
- }
-
- $date = $d->format( 'D M j G:i:s T Y' );
-
- $text = "$date\t$host\t$wiki\t" . trim( $text ) . "\n";
- wfErrorLog( $text, $wgDBerrorLog );
- }
+function wfLogDBError( $text, array $context = array() ) {
+ $logger = LoggerFactory::getInstance( 'wfLogDBError' );
+ $logger->error( trim( $text ), $context );
}
/**
@@ -1188,138 +1219,93 @@ function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
*
* Can also log to TCP or UDP with the syntax udp://host:port/prefix. This will
* send lines to the specified port, prefixed by the specified prefix and a space.
+ * @since 1.25 support for additional context data
*
* @param string $text
* @param string $file Filename
+ * @param array $context Additional logging context data
* @throws MWException
+ * @deprecated since 1.25 Use MediaWiki\Logger\LegacyLogger::emit or UDPTransport
*/
-function wfErrorLog( $text, $file ) {
- if ( substr( $file, 0, 4 ) == 'udp:' ) {
- # Needs the sockets extension
- if ( preg_match( '!^(tcp|udp):(?://)?\[([0-9a-fA-F:]+)\]:(\d+)(?:/(.*))?$!', $file, $m ) ) {
- // IPv6 bracketed host
- $host = $m[2];
- $port = intval( $m[3] );
- $prefix = isset( $m[4] ) ? $m[4] : false;
- $domain = AF_INET6;
- } elseif ( preg_match( '!^(tcp|udp):(?://)?([a-zA-Z0-9.-]+):(\d+)(?:/(.*))?$!', $file, $m ) ) {
- $host = $m[2];
- if ( !IP::isIPv4( $host ) ) {
- $host = gethostbyname( $host );
- }
- $port = intval( $m[3] );
- $prefix = isset( $m[4] ) ? $m[4] : false;
- $domain = AF_INET;
- } else {
- throw new MWException( __METHOD__ . ': Invalid UDP specification' );
- }
-
- // Clean it up for the multiplexer
- if ( strval( $prefix ) !== '' ) {
- $text = preg_replace( '/^/m', $prefix . ' ', $text );
-
- // Limit to 64KB
- if ( strlen( $text ) > 65506 ) {
- $text = substr( $text, 0, 65506 );
- }
-
- if ( substr( $text, -1 ) != "\n" ) {
- $text .= "\n";
- }
- } elseif ( strlen( $text ) > 65507 ) {
- $text = substr( $text, 0, 65507 );
- }
-
- $sock = socket_create( $domain, SOCK_DGRAM, SOL_UDP );
- if ( !$sock ) {
- return;
- }
-
- socket_sendto( $sock, $text, strlen( $text ), 0, $host, $port );
- socket_close( $sock );
- } else {
- wfSuppressWarnings();
- $exists = file_exists( $file );
- $size = $exists ? filesize( $file ) : false;
- if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) {
- file_put_contents( $file, $text, FILE_APPEND );
- }
- wfRestoreWarnings();
- }
+function wfErrorLog( $text, $file, array $context = array() ) {
+ wfDeprecated( __METHOD__, '1.25' );
+ $logger = LoggerFactory::getInstance( 'wfErrorLog' );
+ $context['destination'] = $file;
+ $logger->info( trim( $text ), $context );
}
/**
* @todo document
*/
function wfLogProfilingData() {
- global $wgRequestTime, $wgDebugLogFile, $wgDebugLogGroups, $wgDebugRawPage;
- global $wgProfileLimit, $wgUser, $wgRequest;
+ global $wgDebugLogGroups, $wgDebugRawPage;
- StatCounter::singleton()->flush();
+ $context = RequestContext::getMain();
+ $request = $context->getRequest();
$profiler = Profiler::instance();
+ $profiler->setContext( $context );
+ $profiler->logData();
- # Profiling must actually be enabled...
- if ( $profiler->isStub() ) {
- return;
+ $config = $context->getConfig();
+ if ( $config->has( 'StatsdServer' ) ) {
+ $statsdServer = explode( ':', $config->get( 'StatsdServer' ) );
+ $statsdHost = $statsdServer[0];
+ $statsdPort = isset( $statsdServer[1] ) ? $statsdServer[1] : 8125;
+ $statsdSender = new SocketSender( $statsdHost, $statsdPort );
+ $statsdClient = new StatsdClient( $statsdSender );
+ $statsdClient->send( $context->getStats()->getBuffer() );
}
- // Get total page request time and only show pages that longer than
- // $wgProfileLimit time (default is 0)
- $elapsed = microtime( true ) - $wgRequestTime;
- if ( $elapsed <= $wgProfileLimit ) {
+ # Profiling must actually be enabled...
+ if ( $profiler instanceof ProfilerStub ) {
return;
}
- $profiler->logData();
-
- // Check whether this should be logged in the debug file.
if ( isset( $wgDebugLogGroups['profileoutput'] )
&& $wgDebugLogGroups['profileoutput'] === false
) {
- // Explicitely disabled
- return;
- }
- if ( !isset( $wgDebugLogGroups['profileoutput'] ) && $wgDebugLogFile == '' ) {
- // Logging not enabled; no point going further
+ // Explicitly disabled
return;
}
if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
return;
}
- $forward = '';
+ $ctx = array( 'elapsed' => $request->getElapsedTime() );
if ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
- $forward = ' forwarded for ' . $_SERVER['HTTP_X_FORWARDED_FOR'];
+ $ctx['forwarded_for'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
if ( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
- $forward .= ' client IP ' . $_SERVER['HTTP_CLIENT_IP'];
+ $ctx['client_ip'] = $_SERVER['HTTP_CLIENT_IP'];
}
if ( !empty( $_SERVER['HTTP_FROM'] ) ) {
- $forward .= ' from ' . $_SERVER['HTTP_FROM'];
+ $ctx['from'] = $_SERVER['HTTP_FROM'];
}
- if ( $forward ) {
- $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})";
+ if ( isset( $ctx['forwarded_for'] ) ||
+ isset( $ctx['client_ip'] ) ||
+ isset( $ctx['from'] ) ) {
+ $ctx['proxy'] = $_SERVER['REMOTE_ADDR'];
}
+
// Don't load $wgUser at this late stage just for statistics purposes
- // @todo FIXME: We can detect some anons even if it is not loaded. See User::getId()
- if ( $wgUser->isItemLoaded( 'id' ) && $wgUser->isAnon() ) {
- $forward .= ' anon';
- }
+ // @todo FIXME: We can detect some anons even if it is not loaded.
+ // See User::getId()
+ $user = $context->getUser();
+ $ctx['anon'] = $user->isItemLoaded( 'id' ) && $user->isAnon();
// Command line script uses a FauxRequest object which does not have
// any knowledge about an URL and throw an exception instead.
try {
- $requestUrl = $wgRequest->getRequestURL();
- } catch ( MWException $e ) {
- $requestUrl = 'n/a';
+ $ctx['url'] = urldecode( $request->getRequestURL() );
+ } catch ( Exception $ignored ) {
+ // no-op
}
- $log = sprintf( "%s\t%04.3f\t%s\n",
- gmdate( 'YmdHis' ), $elapsed,
- urldecode( $requestUrl . $forward ) );
+ $ctx['output'] = $profiler->getOutput();
- wfDebugLog( 'profileoutput', $log . $profiler->getOutput() );
+ $log = LoggerFactory::getInstance( 'profileoutput' );
+ $log->info( "Elapsed: {elapsed}; URL: <{url}>\n{output}", $ctx );
}
/**
@@ -1330,7 +1316,8 @@ function wfLogProfilingData() {
* @return void
*/
function wfIncrStats( $key, $count = 1 ) {
- StatCounter::singleton()->incr( $key, $count );
+ $stats = RequestContext::getMain()->getStats();
+ $stats->updateCount( $key, $count );
}
/**
@@ -1573,10 +1560,8 @@ function wfMsgForContentNoTrans( $key ) {
function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform = true ) {
wfDeprecated( __METHOD__, '1.21' );
- wfProfileIn( __METHOD__ );
$message = wfMsgGetKey( $key, $useDB, $forContent, $transform );
$message = wfMsgReplaceArgs( $message, $args );
- wfProfileOut( __METHOD__ );
return $message;
}
@@ -1595,7 +1580,7 @@ function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform
function wfMsgGetKey( $key, $useDB = true, $langCode = false, $transform = true ) {
wfDeprecated( __METHOD__, '1.21' );
- wfRunHooks( 'NormalizeMessageKey', array( &$key, &$useDB, &$langCode, &$transform ) );
+ Hooks::run( 'NormalizeMessageKey', array( &$key, &$useDB, &$langCode, &$transform ) );
$cache = MessageCache::singleton();
$message = $cache->get( $key, $useDB, $langCode );
@@ -1710,15 +1695,15 @@ function wfMsgExt( $key, $options ) {
array_shift( $args );
array_shift( $args );
$options = (array)$options;
+ $validOptions = array( 'parse', 'parseinline', 'escape', 'escapenoentities', 'replaceafter',
+ 'parsemag', 'content' );
foreach ( $options as $arrayKey => $option ) {
if ( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) {
- # An unknown index, neither numeric nor "language"
+ // An unknown index, neither numeric nor "language"
wfWarn( "wfMsgExt called with incorrect parameter key $arrayKey", 1, E_USER_WARNING );
- } elseif ( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option,
- array( 'parse', 'parseinline', 'escape', 'escapenoentities',
- 'replaceafter', 'parsemag', 'content' ) ) ) {
- # A numeric index with unknown value
+ } elseif ( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option, $validOptions ) ) {
+ // A numeric index with unknown value
wfWarn( "wfMsgExt called with incorrect parameter $option", 1, E_USER_WARNING );
}
}
@@ -1879,52 +1864,37 @@ function wfDebugBacktrace( $limit = 0 ) {
/**
* Get a debug backtrace as a string
*
+ * @param bool|null $raw If true, the return value is plain text. If false, HTML.
+ * Defaults to $wgCommandLineMode if unset.
* @return string
+ * @since 1.25 Supports $raw parameter.
*/
-function wfBacktrace() {
+function wfBacktrace( $raw = null ) {
global $wgCommandLineMode;
- if ( $wgCommandLineMode ) {
- $msg = '';
- } else {
- $msg = "<ul>\n";
+ if ( $raw === null ) {
+ $raw = $wgCommandLineMode;
}
- $backtrace = wfDebugBacktrace();
- foreach ( $backtrace as $call ) {
- if ( isset( $call['file'] ) ) {
- $f = explode( DIRECTORY_SEPARATOR, $call['file'] );
- $file = $f[count( $f ) - 1];
- } else {
- $file = '-';
- }
- if ( isset( $call['line'] ) ) {
- $line = $call['line'];
- } else {
- $line = '-';
- }
- if ( $wgCommandLineMode ) {
- $msg .= "$file line $line calls ";
- } else {
- $msg .= '<li>' . $file . ' line ' . $line . ' calls ';
- }
- if ( !empty( $call['class'] ) ) {
- $msg .= $call['class'] . $call['type'];
- }
- $msg .= $call['function'] . '()';
- if ( $wgCommandLineMode ) {
- $msg .= "\n";
- } else {
- $msg .= "</li>\n";
- }
- }
- if ( $wgCommandLineMode ) {
- $msg .= "\n";
+ if ( $raw ) {
+ $frameFormat = "%s line %s calls %s()\n";
+ $traceFormat = "%s";
} else {
- $msg .= "</ul>\n";
+ $frameFormat = "<li>%s line %s calls %s()</li>\n";
+ $traceFormat = "<ul>\n%s</ul>\n";
}
- return $msg;
+ $frames = array_map( function ( $frame ) use ( $frameFormat ) {
+ $file = !empty( $frame['file'] ) ? basename( $frame['file'] ) : '-';
+ $line = isset( $frame['line'] ) ? $frame['line'] : '-';
+ $call = $frame['function'];
+ if ( !empty( $frame['class'] ) ) {
+ $call = $frame['class'] . $frame['type'] . $call;
+ }
+ return sprintf( $frameFormat, $file, $line, $call );
+ }, wfDebugBacktrace() );
+
+ return sprintf( $traceFormat, implode( '', $frames ) );
}
/**
@@ -2148,10 +2118,12 @@ function wfVarDump( $var ) {
*/
function wfHttpError( $code, $label, $desc ) {
global $wgOut;
- $wgOut->disable();
header( "HTTP/1.0 $code $label" );
header( "Status: $code $label" );
- $wgOut->sendCacheControl();
+ if ( $wgOut ) {
+ $wgOut->disable();
+ $wgOut->sendCacheControl();
+ }
header( 'Content-type: text/html; charset=utf-8' );
print "<!doctype html>" .
@@ -2496,20 +2468,6 @@ function wfIsHHVM() {
}
/**
- * Swap two variables
- *
- * @deprecated since 1.24
- * @param mixed $x
- * @param mixed $y
- */
-function swap( &$x, &$y ) {
- wfDeprecated( __FUNCTION__, '1.24' );
- $z = $x;
- $x = $y;
- $y = $z;
-}
-
-/**
* Tries to get the system directory for temporary files. First
* $wgTmpDirectory is checked, and then the TMPDIR, TMP, and TEMP
* environment variables are then checked in sequence, and if none are
@@ -2659,13 +2617,19 @@ function wfIniGetBool( $setting ) {
* Also fixes the locale problems on Linux in PHP 5.2.6+ (bug backported to
* earlier distro releases of PHP)
*
- * @param string $args,...
+ * @param string ... strings to escape and glue together, or a single array of strings parameter
* @return string
*/
function wfEscapeShellArg( /*...*/ ) {
wfInitShellLocale();
$args = func_get_args();
+ if ( count( $args ) === 1 && is_array( reset( $args ) ) ) {
+ // If only one argument has been passed, and that argument is an array,
+ // treat it as a list of arguments
+ $args = reset( $args );
+ }
+
$first = true;
$retVal = '';
foreach ( $args as $arg ) {
@@ -2756,6 +2720,8 @@ function wfShellExecDisabled() {
* @param array $options Array of options:
* - duplicateStderr: Set this to true to duplicate stderr to stdout,
* including errors from limit.sh
+ * - profileMethod: By default this function will profile based on the calling
+ * method. Set this to a string for an alternative method to profile from
*
* @return string Collected stdout as a string
*/
@@ -2774,6 +2740,7 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
}
$includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
+ $profileMethod = isset( $options['profileMethod'] ) ? $options['profileMethod'] : wfGetCaller();
wfInitShellLocale();
@@ -2795,12 +2762,7 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
}
}
if ( is_array( $cmd ) ) {
- // Command line may be given as an array, escape each value and glue them together with a space
- $cmdVals = array();
- foreach ( $cmd as $val ) {
- $cmdVals[] = wfEscapeShellArg( $val );
- }
- $cmd = implode( ' ', $cmdVals );
+ $cmd = wfEscapeShellArg( $cmd );
}
$cmd = $envcmd . $cmd;
@@ -2847,6 +2809,7 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
$desc[3] = array( 'pipe', 'w' );
}
$pipes = null;
+ $scoped = Profiler::instance()->scopedProfileIn( __FUNCTION__ . '-' . $profileMethod );
$proc = proc_open( $cmd, $desc, $pipes );
if ( !$proc ) {
wfDebugLog( 'exec', "proc_open() failed: $cmd" );
@@ -2986,7 +2949,9 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
* function, as all the arguments to wfShellExec can become unwieldy.
*
* @note This also includes errors from limit.sh, e.g. if $wgMaxShellFileSize is exceeded.
- * @param string $cmd Command line, properly escaped for shell.
+ * @param string|string[] $cmd If string, a properly shell-escaped command line,
+ * or an array of unescaped arguments, in which case each value will be escaped
+ * Example: [ 'convert', '-font', 'font name' ] would produce "'convert' '-font' 'font name'"
* @param null|mixed &$retval Optional, will receive the program's exit code.
* (non-zero is usually failure)
* @param array $environ Optional environment variables which should be
@@ -2996,7 +2961,8 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
* @return string Collected stdout and stderr as a string
*/
function wfShellExecWithStderr( $cmd, &$retval = null, $environ = array(), $limits = array() ) {
- return wfShellExec( $cmd, $retval, $environ, $limits, array( 'duplicateStderr' => true ) );
+ return wfShellExec( $cmd, $retval, $environ, $limits,
+ array( 'duplicateStderr' => true, 'profileMethod' => wfGetCaller() ) );
}
/**
@@ -3017,15 +2983,6 @@ function wfInitShellLocale() {
}
/**
- * Alias to wfShellWikiCmd()
- *
- * @see wfShellWikiCmd()
- */
-function wfShellMaintenanceCmd( $script, array $parameters = array(), array $options = array() ) {
- return wfShellWikiCmd( $script, $parameters, $options );
-}
-
-/**
* Generate a shell-escaped command line string to run a MediaWiki cli script.
* Note that $parameters should be a flat array and an option with an argument
* should consist of two consecutive items in the array (do not use "--option value").
@@ -3041,14 +2998,14 @@ function wfShellWikiCmd( $script, array $parameters = array(), array $options =
global $wgPhpCli;
// Give site config file a chance to run the script in a wrapper.
// The caller may likely want to call wfBasename() on $script.
- wfRunHooks( 'wfShellWikiCmd', array( &$script, &$parameters, &$options ) );
+ Hooks::run( 'wfShellWikiCmd', array( &$script, &$parameters, &$options ) );
$cmd = isset( $options['php'] ) ? array( $options['php'] ) : array( $wgPhpCli );
if ( isset( $options['wrapper'] ) ) {
$cmd[] = $options['wrapper'];
}
$cmd[] = $script;
// Escape each parameter for shell
- return implode( " ", array_map( 'wfEscapeShellArg', array_merge( $cmd, $parameters ) ) );
+ return wfEscapeShellArg( array_merge( $cmd, $parameters ) );
}
/**
@@ -3093,10 +3050,8 @@ function wfMerge( $old, $mine, $yours, &$result ) {
fclose( $yourtextFile );
# Check for a conflict
- $cmd = wfEscapeShellArg( $wgDiff3 ) . ' -a --overlap-only ' .
- wfEscapeShellArg( $mytextName ) . ' ' .
- wfEscapeShellArg( $oldtextName ) . ' ' .
- wfEscapeShellArg( $yourtextName );
+ $cmd = wfEscapeShellArg( $wgDiff3, '-a', '--overlap-only', $mytextName,
+ $oldtextName, $yourtextName );
$handle = popen( $cmd, 'r' );
if ( fgets( $handle, 1024 ) ) {
@@ -3107,8 +3062,8 @@ function wfMerge( $old, $mine, $yours, &$result ) {
pclose( $handle );
# Merge differences
- $cmd = wfEscapeShellArg( $wgDiff3 ) . ' -a -e --merge ' .
- wfEscapeShellArg( $mytextName, $oldtextName, $yourtextName );
+ $cmd = wfEscapeShellArg( $wgDiff3, '-a', '-e', '--merge', $mytextName,
+ $oldtextName, $yourtextName );
$handle = popen( $cmd, 'r' );
$result = '';
do {
@@ -3132,7 +3087,9 @@ function wfMerge( $old, $mine, $yours, &$result ) {
/**
* Returns unified plain-text diff of two texts.
- * Useful for machine processing of diffs.
+ * "Useful" for machine processing of diffs.
+ *
+ * @deprecated since 1.25, use DiffEngine/UnifiedDiffFormatter directly
*
* @param string $before The text before the changes.
* @param string $after The text after the changes.
@@ -3172,6 +3129,11 @@ function wfDiff( $before, $after, $params = '-u' ) {
$cmd = "$wgDiff " . $params . ' ' . wfEscapeShellArg( $oldtextName, $newtextName );
$h = popen( $cmd, 'r' );
+ if ( !$h ) {
+ unlink( $oldtextName );
+ unlink( $newtextName );
+ throw new Exception( __METHOD__ . '(): popen() failed' );
+ }
$diff = '';
@@ -3493,7 +3455,7 @@ function wfResetSessionID() {
$_SESSION = $tmp;
}
$newSessionId = session_id();
- wfRunHooks( 'ResetSessionID', array( $oldSessionId, $newSessionId ) );
+ Hooks::run( 'ResetSessionID', array( $oldSessionId, $newSessionId ) );
}
/**
@@ -3628,7 +3590,7 @@ function wfSplitWikiID( $wiki ) {
*
* @return DatabaseBase
*/
-function &wfGetDB( $db, $groups = array(), $wiki = false ) {
+function wfGetDB( $db, $groups = array(), $wiki = false ) {
return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki );
}
@@ -3647,7 +3609,7 @@ function wfGetLB( $wiki = false ) {
*
* @return LBFactory
*/
-function &wfGetLBFactory() {
+function wfGetLBFactory() {
return LBFactory::singleton();
}
@@ -3656,19 +3618,7 @@ function &wfGetLBFactory() {
* Shortcut for RepoGroup::singleton()->findFile()
*
* @param string $title String or Title object
- * @param array $options Associative array of options:
- * time: requested time for an archived image, or false for the
- * current version. An image object will be returned which was
- * created at the specified time.
- *
- * ignoreRedirect: If true, do not follow file redirects
- *
- * private: If true, return restricted (deleted) files if the current
- * user is allowed to view them. Otherwise, such files will not
- * be found.
- *
- * bypassCache: If true, do not use the process-local cache of File objects
- *
+ * @param array $options Associative array of options (see RepoGroup::findFile)
* @return File|bool File, or false if the file does not exist
*/
function wfFindFile( $title, $options = array() ) {
@@ -3763,46 +3713,79 @@ function wfGetNull() {
}
/**
- * Modern version of wfWaitForSlaves(). Instead of looking at replication lag
- * and waiting for it to go down, this waits for the slaves to catch up to the
- * master position. Use this when updating very large numbers of rows, as
- * in maintenance scripts, to avoid causing too much lag. Of course, this is
- * a no-op if there are no slaves.
+ * Waits for the slaves to catch up to the master position
+ *
+ * Use this when updating very large numbers of rows, as in maintenance scripts,
+ * to avoid causing too much lag. Of course, this is a no-op if there are no slaves.
+ *
+ * By default this waits on the main DB cluster of the current wiki.
+ * If $cluster is set to "*" it will wait on all DB clusters, including
+ * external ones. If the lag being waiting on is caused by the code that
+ * does this check, it makes since to use $ifWritesSince, particularly if
+ * cluster is "*", to avoid excess overhead.
+ *
+ * Never call this function after a big DB write that is still in a transaction.
+ * This only makes sense after the possible lag inducing changes were committed.
*
* @param float|null $ifWritesSince Only wait if writes were done since this UNIX timestamp
* @param string|bool $wiki Wiki identifier accepted by wfGetLB
* @param string|bool $cluster Cluster name accepted by LBFactory. Default: false.
+ * @param int|null $timeout Max wait time. Default: 1 day (cli), ~10 seconds (web)
* @return bool Success (able to connect and no timeouts reached)
*/
-function wfWaitForSlaves( $ifWritesSince = false, $wiki = false, $cluster = false ) {
+function wfWaitForSlaves(
+ $ifWritesSince = null, $wiki = false, $cluster = false, $timeout = null
+) {
// B/C: first argument used to be "max seconds of lag"; ignore such values
- $ifWritesSince = ( $ifWritesSince > 1e9 ) ? $ifWritesSince : false;
+ $ifWritesSince = ( $ifWritesSince > 1e9 ) ? $ifWritesSince : null;
- if ( $cluster !== false ) {
- $lb = wfGetLBFactory()->getExternalLB( $cluster );
- } else {
- $lb = wfGetLB( $wiki );
+ if ( $timeout === null ) {
+ $timeout = ( PHP_SAPI === 'cli' ) ? 86400 : 10;
}
- // bug 27975 - Don't try to wait for slaves if there are none
- // Prevents permission error when getting master position
- if ( $lb->getServerCount() > 1 ) {
- if ( $ifWritesSince && !$lb->hasMasterConnection() ) {
- return true; // assume no writes done
- }
- $dbw = $lb->getConnection( DB_MASTER, array(), $wiki );
- if ( $ifWritesSince && $dbw->lastDoneWrites() < $ifWritesSince ) {
- return true; // no writes since the last wait
+ // Figure out which clusters need to be checked
+ $lbs = array();
+ if ( $cluster === '*' ) {
+ wfGetLBFactory()->forEachLB( function ( LoadBalancer $lb ) use ( &$lbs ) {
+ $lbs[] = $lb;
+ } );
+ } elseif ( $cluster !== false ) {
+ $lbs[] = wfGetLBFactory()->getExternalLB( $cluster );
+ } else {
+ $lbs[] = wfGetLB( $wiki );
+ }
+
+ // Get all the master positions of applicable DBs right now.
+ // This can be faster since waiting on one cluster reduces the
+ // time needed to wait on the next clusters.
+ $masterPositions = array_fill( 0, count( $lbs ), false );
+ foreach ( $lbs as $i => $lb ) {
+ // bug 27975 - Don't try to wait for slaves if there are none
+ // Prevents permission error when getting master position
+ if ( $lb->getServerCount() > 1 ) {
+ if ( $ifWritesSince && !$lb->hasMasterConnection() ) {
+ continue; // assume no writes done
+ }
+ // Use the empty string to not trigger selectDB() since the connection
+ // may have been to a server that does not have a DB for the current wiki.
+ $dbw = $lb->getConnection( DB_MASTER, array(), '' );
+ if ( $ifWritesSince && $dbw->lastDoneWrites() < $ifWritesSince ) {
+ continue; // no writes since the last wait
+ }
+ $masterPositions[$i] = $dbw->getMasterPos();
}
- $pos = $dbw->getMasterPos();
- // The DBMS may not support getMasterPos() or the whole
- // load balancer might be fake (e.g. $wgAllDBsAreLocalhost).
- if ( $pos !== false ) {
- return $lb->waitForAll( $pos, PHP_SAPI === 'cli' ? 86400 : null );
+ }
+
+ $ok = true;
+ foreach ( $lbs as $i => $lb ) {
+ if ( $masterPositions[$i] ) {
+ // The DBMS may not support getMasterPos() or the whole
+ // load balancer might be fake (e.g. $wgAllDBsAreLocalhost).
+ $ok = $lb->waitForAll( $masterPositions[$i], $timeout ) && $ok;
}
}
- return true;
+ return $ok;
}
/**
@@ -3935,7 +3918,7 @@ function wfBCP47( $code ) {
/**
* Get a cache object.
*
- * @param int $inputType Cache type, one the the CACHE_* constants.
+ * @param int $inputType Cache type, one of the CACHE_* constants.
* @return BagOStuff
*/
function wfGetCache( $inputType ) {
@@ -3973,16 +3956,6 @@ function wfGetParserCacheStorage() {
}
/**
- * Get the cache object used by the language converter
- *
- * @return BagOStuff
- */
-function wfGetLangConverterCacheStorage() {
- global $wgLanguageConverterCacheType;
- return ObjectCache::getInstance( $wgLanguageConverterCacheType );
-}
-
-/**
* Call hook functions defined in $wgHooks
*
* @param string $event Event name
@@ -3990,6 +3963,7 @@ function wfGetLangConverterCacheStorage() {
* @param string|null $deprecatedVersion Optionally mark hook as deprecated with version number
*
* @return bool True if no handler aborted the hook
+ * @deprecated 1.25 - use Hooks::run
*/
function wfRunHooks( $event, array $args = array(), $deprecatedVersion = null ) {
return Hooks::run( $event, $args, $deprecatedVersion );
@@ -4047,7 +4021,6 @@ function wfUnpack( $format, $data, $length = false ) {
*/
function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
static $badImageCache = null; // based on bad_image_list msg
- wfProfileIn( __METHOD__ );
# Handle redirects
$redirectTitle = RepoGroup::singleton()->checkRedirect( Title::makeTitle( NS_FILE, $name ) );
@@ -4057,8 +4030,7 @@ function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
# Run the extension hook
$bad = false;
- if ( !wfRunHooks( 'BadImage', array( $name, &$bad ) ) ) {
- wfProfileOut( __METHOD__ );
+ if ( !Hooks::run( 'BadImage', array( $name, &$bad ) ) ) {
return $bad;
}
@@ -4108,7 +4080,6 @@ function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
$contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false;
$bad = isset( $badImages[$name] ) && !isset( $badImages[$name][$contextKey] );
- wfProfileOut( __METHOD__ );
return $bad;
}
@@ -4121,11 +4092,23 @@ function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
*/
function wfCanIPUseHTTPS( $ip ) {
$canDo = true;
- wfRunHooks( 'CanIPUseHTTPS', array( $ip, &$canDo ) );
+ Hooks::run( 'CanIPUseHTTPS', array( $ip, &$canDo ) );
return !!$canDo;
}
/**
+ * Determine input string is represents as infinity
+ *
+ * @param string $str The string to determine
+ * @return bool
+ * @since 1.25
+ */
+function wfIsInfinity( $str ) {
+ $infinityValues = array( 'infinite', 'indefinite', 'infinity', 'never' );
+ return in_array( $str, $infinityValues );
+}
+
+/**
* Work out the IP address based on various globals
* For trusted proxies, use the XFF client IP (first of the chain)
*
@@ -4148,6 +4131,7 @@ function wfGetIP() {
* @return bool
*/
function wfIsTrustedProxy( $ip ) {
+ wfDeprecated( __METHOD__, '1.24' );
return IP::isTrustedProxy( $ip );
}
@@ -4160,5 +4144,94 @@ function wfIsTrustedProxy( $ip ) {
* @since 1.23 Supports CIDR ranges in $wgSquidServersNoPurge
*/
function wfIsConfiguredProxy( $ip ) {
+ wfDeprecated( __METHOD__, '1.24' );
return IP::isConfiguredProxy( $ip );
}
+
+/**
+ * Returns true if these thumbnail parameters match one that MediaWiki
+ * requests from file description pages and/or parser output.
+ *
+ * $params is considered non-standard if they involve a non-standard
+ * width or any non-default parameters aside from width and page number.
+ * The number of possible files with standard parameters is far less than
+ * that of all combinations; rate-limiting for them can thus be more generious.
+ *
+ * @param File $file
+ * @param array $params
+ * @return bool
+ * @since 1.24 Moved from thumb.php to GlobalFunctions in 1.25
+ */
+function wfThumbIsStandard( File $file, array $params ) {
+ global $wgThumbLimits, $wgImageLimits, $wgResponsiveImages;
+
+ $multipliers = array( 1 );
+ if ( $wgResponsiveImages ) {
+ // These available sizes are hardcoded currently elsewhere in MediaWiki.
+ // @see Linker::processResponsiveImages
+ $multipliers[] = 1.5;
+ $multipliers[] = 2;
+ }
+
+ $handler = $file->getHandler();
+ if ( !$handler || !isset( $params['width'] ) ) {
+ return false;
+ }
+
+ $basicParams = array();
+ if ( isset( $params['page'] ) ) {
+ $basicParams['page'] = $params['page'];
+ }
+
+ $thumbLimits = array();
+ $imageLimits = array();
+ // Expand limits to account for multipliers
+ foreach ( $multipliers as $multiplier ) {
+ $thumbLimits = array_merge( $thumbLimits, array_map(
+ function ( $width ) use ( $multiplier ) {
+ return round( $width * $multiplier );
+ }, $wgThumbLimits )
+ );
+ $imageLimits = array_merge( $imageLimits, array_map(
+ function ( $pair ) use ( $multiplier ) {
+ return array(
+ round( $pair[0] * $multiplier ),
+ round( $pair[1] * $multiplier ),
+ );
+ }, $wgImageLimits )
+ );
+ }
+
+ // Check if the width matches one of $wgThumbLimits
+ if ( in_array( $params['width'], $thumbLimits ) ) {
+ $normalParams = $basicParams + array( 'width' => $params['width'] );
+ // Append any default values to the map (e.g. "lossy", "lossless", ...)
+ $handler->normaliseParams( $file, $normalParams );
+ } else {
+ // If not, then check if the width matchs one of $wgImageLimits
+ $match = false;
+ foreach ( $imageLimits as $pair ) {
+ $normalParams = $basicParams + array( 'width' => $pair[0], 'height' => $pair[1] );
+ // Decide whether the thumbnail should be scaled on width or height.
+ // Also append any default values to the map (e.g. "lossy", "lossless", ...)
+ $handler->normaliseParams( $file, $normalParams );
+ // Check if this standard thumbnail size maps to the given width
+ if ( $normalParams['width'] == $params['width'] ) {
+ $match = true;
+ break;
+ }
+ }
+ if ( !$match ) {
+ return false; // not standard for description pages
+ }
+ }
+
+ // Check that the given values for non-page, non-width, params are just defaults
+ foreach ( $params as $key => $value ) {
+ if ( !isset( $normalParams[$key] ) || $normalParams[$key] != $value ) {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/includes/Hooks.php b/includes/Hooks.php
index 29287483..dffc7bcf 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -127,14 +127,17 @@ class Hooks {
* @param string|null $deprecatedVersion Optionally, mark hook as deprecated with version number
* @return bool True if no handler aborted the hook
*
+ * @throws Exception
+ * @throws FatalError
+ * @throws MWException
* @since 1.22 A hook function is not required to return a value for
* processing to continue. Not returning a value (or explicitly
* returning null) is equivalent to returning true.
- * @throws MWException
- * @throws FatalError
*/
public static function run( $event, array $args = array(), $deprecatedVersion = null ) {
- wfProfileIn( 'hook: ' . $event );
+ $profiler = Profiler::instance();
+ $eventPS = $profiler->scopedProfileIn( 'hook: ' . $event );
+
foreach ( self::getHandlers( $event ) as $hook ) {
// Turn non-array values into an array. (Can't use casting because of objects.)
if ( !is_array( $hook ) ) {
@@ -179,7 +182,7 @@ class Hooks {
// Run autoloader (workaround for call_user_func_array bug)
// and throw error if not callable.
if ( !is_callable( $callback ) ) {
- throw new MWException( 'Invalid callback in hooks for ' . $event . "\n" );
+ throw new MWException( 'Invalid callback ' . $func . ' in hooks for ' . $event . "\n" );
}
/*
@@ -193,8 +196,8 @@ class Hooks {
$badhookmsg = null;
$hook_args = array_merge( $hook, $args );
- // Profile first in case the Profiler causes errors.
- wfProfileIn( $func );
+ // Profile first in case the Profiler causes errors
+ $funcPS = $profiler->scopedProfileIn( $func );
set_error_handler( 'Hooks::hookErrorHandler' );
// mark hook as deprecated, if deprecation version is specified
@@ -210,8 +213,9 @@ class Hooks {
restore_error_handler();
throw $e;
}
+
restore_error_handler();
- wfProfileOut( $func );
+ $profiler->scopedProfileOut( $funcPS );
// Process the return value.
if ( is_string( $retval ) ) {
@@ -224,13 +228,11 @@ class Hooks {
"Hook $func has invalid call signature; " . $badhookmsg
);
} elseif ( $retval === false ) {
- wfProfileOut( 'hook: ' . $event );
// False was returned. Stop processing, but no error.
return false;
}
}
- wfProfileOut( 'hook: ' . $event );
return true;
}
diff --git a/includes/Html.php b/includes/Html.php
index 2e148140..d312e0a6 100644
--- a/includes/Html.php
+++ b/includes/Html.php
@@ -102,6 +102,35 @@ class Html {
);
/**
+ * Modifies a set of attributes meant for button elements
+ * and apply a set of default attributes when $wgUseMediaWikiUIEverywhere enabled.
+ * @param array $attrs
+ * @param string[] $modifiers to add to the button
+ * @see https://tools.wmflabs.org/styleguide/desktop/index.html for guidance on available modifiers
+ * @return array $attrs A modified attribute array
+ */
+ public static function buttonAttributes( $attrs, $modifiers = array() ) {
+ global $wgUseMediaWikiUIEverywhere;
+ if ( $wgUseMediaWikiUIEverywhere ) {
+ if ( isset( $attrs['class'] ) ) {
+ if ( is_array( $attrs['class'] ) ) {
+ $attrs['class'][] = 'mw-ui-button';
+ $attrs = array_merge( $attrs, $modifiers );
+ // ensure compatibility with Xml
+ $attrs['class'] = implode( ' ', $attrs['class'] );
+ } else {
+ $attrs['class'] .= ' mw-ui-button ' . implode( ' ', $modifiers );
+ }
+ } else {
+ $attrs['class'] = array( 'mw-ui-button' );
+ // ensure compatibility with Xml
+ $attrs['class'] = implode( ' ', array_merge( $attrs['class'], $modifiers ) );
+ }
+ }
+ return $attrs;
+ }
+
+ /**
* Modifies a set of attributes meant for text input elements
* and apply a set of default attributes.
* Removes size attribute when $wgUseMediaWikiUIEverywhere enabled.
@@ -113,28 +142,63 @@ class Html {
if ( !$attrs ) {
$attrs = array();
}
- if ( isset( $attrs['class'] ) ) {
- if ( is_array( $attrs['class'] ) ) {
- $attrs['class'][] = 'mw-ui-input';
+ if ( $wgUseMediaWikiUIEverywhere ) {
+ if ( isset( $attrs['class'] ) ) {
+ if ( is_array( $attrs['class'] ) ) {
+ $attrs['class'][] = 'mw-ui-input';
+ } else {
+ $attrs['class'] .= ' mw-ui-input';
+ }
} else {
- $attrs['class'] .= ' mw-ui-input';
+ $attrs['class'] = 'mw-ui-input';
}
- } else {
- $attrs['class'] = 'mw-ui-input';
- }
- if ( $wgUseMediaWikiUIEverywhere ) {
- // Note that size can effect the desired width rendering of mw-ui-input elements
- // so it is removed. Left intact when mediawiki ui not enabled.
- unset( $attrs['size'] );
}
return $attrs;
}
/**
+ * Returns an HTML link element in a string styled as a button
+ * (when $wgUseMediaWikiUIEverywhere is enabled).
+ *
+ * @param string $contents The raw HTML contents of the element: *not*
+ * escaped!
+ * @param array $attrs Associative array of attributes, e.g., array(
+ * 'href' => 'http://www.mediawiki.org/' ). See expandAttributes() for
+ * further documentation.
+ * @param string[] $modifiers to add to the button
+ * @see http://tools.wmflabs.org/styleguide/desktop/index.html for guidance on available modifiers
+ * @return string Raw HTML
+ */
+ public static function linkButton( $contents, $attrs, $modifiers = array() ) {
+ return self::element( 'a',
+ self::buttonAttributes( $attrs, $modifiers ),
+ $contents
+ );
+ }
+
+ /**
+ * Returns an HTML link element in a string styled as a button
+ * (when $wgUseMediaWikiUIEverywhere is enabled).
+ *
+ * @param string $contents The raw HTML contents of the element: *not*
+ * escaped!
+ * @param array $attrs Associative array of attributes, e.g., array(
+ * 'href' => 'http://www.mediawiki.org/' ). See expandAttributes() for
+ * further documentation.
+ * @param string[] $modifiers to add to the button
+ * @see http://tools.wmflabs.org/styleguide/desktop/index.html for guidance on available modifiers
+ * @return string Raw HTML
+ */
+ public static function submitButton( $contents, $attrs, $modifiers = array() ) {
+ $attrs['type'] = 'submit';
+ $attrs['value'] = $contents;
+ return self::element( 'input', self::buttonAttributes( $attrs, $modifiers ) );
+ }
+
+ /**
* Returns an HTML element in a string. The major advantage here over
* manually typing out the HTML is that it will escape all attribute
- * values. If you're hardcoding all the attributes, or there are none, you
- * should probably just type out the html element yourself.
+ * values.
*
* This is quite similar to Xml::tags(), but it implements some useful
* HTML-specific logic. For instance, there is no $allowShortTag
@@ -193,20 +257,11 @@ class Html {
* @return string
*/
public static function openElement( $element, $attribs = array() ) {
- global $wgWellFormedXml;
$attribs = (array)$attribs;
// This is not required in HTML5, but let's do it anyway, for
// consistency and better compression.
$element = strtolower( $element );
- // In text/html, initial <html> and <head> tags can be omitted under
- // pretty much any sane circumstances, if they have no attributes. See:
- // <http://www.whatwg.org/html/syntax.html#optional-tags>
- if ( !$wgWellFormedXml && !$attribs
- && in_array( $element, array( 'html', 'head' ) ) ) {
- return '';
- }
-
// Remove invalid input types
if ( $element == 'input' ) {
$validTypes = array(
@@ -236,8 +291,7 @@ class Html {
'tel',
'color',
);
- if ( isset( $attribs['type'] )
- && !in_array( $attribs['type'], $validTypes ) ) {
+ if ( isset( $attribs['type'] ) && !in_array( $attribs['type'], $validTypes ) ) {
unset( $attribs['type'] );
}
}
@@ -331,8 +385,9 @@ class Html {
}
// Simple checks using $attribDefaults
- if ( isset( $attribDefaults[$element][$lcattrib] ) &&
- $attribDefaults[$element][$lcattrib] == $value ) {
+ if ( isset( $attribDefaults[$element][$lcattrib] )
+ && $attribDefaults[$element][$lcattrib] == $value
+ ) {
unset( $attribs[$attrib] );
}
@@ -342,8 +397,9 @@ class Html {
}
// More subtle checks
- if ( $element === 'link' && isset( $attribs['type'] )
- && strval( $attribs['type'] ) == 'text/css' ) {
+ if ( $element === 'link'
+ && isset( $attribs['type'] ) && strval( $attribs['type'] ) == 'text/css'
+ ) {
unset( $attribs['type'] );
}
if ( $element === 'input' ) {
@@ -442,8 +498,7 @@ class Html {
// For boolean attributes, support array( 'foo' ) instead of
// requiring array( 'foo' => 'meaningless' ).
- if ( is_int( $key )
- && in_array( strtolower( $value ), self::$boolAttribs ) ) {
+ if ( is_int( $key ) && in_array( strtolower( $value ), self::$boolAttribs ) ) {
$key = $value;
}
@@ -522,14 +577,13 @@ class Html {
// marks omitted, but not all. (Although a literal " is not
// permitted, we don't check for that, since it will be escaped
// anyway.)
- #
+
// See also research done on further characters that need to be
// escaped: http://code.google.com/p/html5lib/issues/detail?id=93
$badChars = "\\x00- '=<>`/\x{00a0}\x{1680}\x{180e}\x{180F}\x{2000}\x{2001}"
. "\x{2002}\x{2003}\x{2004}\x{2005}\x{2006}\x{2007}\x{2008}\x{2009}"
. "\x{200A}\x{2028}\x{2029}\x{202F}\x{205F}\x{3000}";
- if ( $wgWellFormedXml || $value === ''
- || preg_match( "![$badChars]!u", $value ) ) {
+ if ( $wgWellFormedXml || $value === '' || preg_match( "![$badChars]!u", $value ) ) {
$quote = '"';
} else {
$quote = '';
@@ -654,7 +708,7 @@ class Html {
* new HTML5 input types and attributes.
*
* @param string $name Name attribute
- * @param array $value Value attribute
+ * @param string $value Value attribute
* @param string $type Type attribute
* @param array $attribs Associative array of miscellaneous extra
* attributes, passed to Html::element()
@@ -665,7 +719,7 @@ class Html {
$attribs['value'] = $value;
$attribs['name'] = $name;
if ( in_array( $type, array( 'text', 'search', 'email', 'password', 'number' ) ) ) {
- $attribs = Html::getTextInputAttributes( $attribs );
+ $attribs = self::getTextInputAttributes( $attribs );
}
return self::element( 'input', $attribs );
}
@@ -676,7 +730,7 @@ class Html {
* @param string $name Name attribute
* @param bool $checked Whether the checkbox is checked or not
* @param array $attribs Array of additional attributes
- * @return string
+ * @return string Raw HTML
*/
public static function check( $name, $checked = false, array $attribs = array() ) {
if ( isset( $attribs['value'] ) ) {
@@ -699,7 +753,7 @@ class Html {
* @param string $name Name attribute
* @param bool $checked Whether the checkbox is checked or not
* @param array $attribs Array of additional attributes
- * @return string
+ * @return string Raw HTML
*/
public static function radio( $name, $checked = false, array $attribs = array() ) {
if ( isset( $attribs['value'] ) ) {
@@ -722,7 +776,7 @@ class Html {
* @param string $label Contents of the label
* @param string $id ID of the element being labeled
* @param array $attribs Additional attributes
- * @return string
+ * @return string Raw HTML
*/
public static function label( $label, $id, array $attribs = array() ) {
$attribs += array(
@@ -768,7 +822,7 @@ class Html {
} else {
$spacedValue = $value;
}
- return self::element( 'textarea', Html::getTextInputAttributes( $attribs ), $spacedValue );
+ return self::element( 'textarea', self::getTextInputAttributes( $attribs ), $spacedValue );
}
/**
@@ -833,13 +887,13 @@ class Html {
continue;
}
if ( $nsId === NS_MAIN ) {
- // For other namespaces use use the namespace prefix as label, but for
+ // For other namespaces use the namespace prefix as label, but for
// main we don't use "" but the user message describing it (e.g. "(Main)" or "(Article)")
$nsName = wfMessage( 'blanknamespace' )->text();
} elseif ( is_int( $nsId ) ) {
$nsName = $wgContLang->convertNamespace( $nsId );
}
- $optionsHtml[] = Html::element(
+ $optionsHtml[] = self::element(
'option', array(
'disabled' => in_array( $nsId, $params['disable'] ),
'value' => $nsId,
@@ -858,7 +912,7 @@ class Html {
$ret = '';
if ( isset( $params['label'] ) ) {
- $ret .= Html::element(
+ $ret .= self::element(
'label', array(
'for' => isset( $selectAttribs['id'] ) ? $selectAttribs['id'] : null,
), $params['label']
@@ -866,11 +920,11 @@ class Html {
}
// Wrap options in a <select>
- $ret .= Html::openElement( 'select', $selectAttribs )
+ $ret .= self::openElement( 'select', $selectAttribs )
. "\n"
. implode( "\n", $optionsHtml )
. "\n"
- . Html::closeElement( 'select' );
+ . self::closeElement( 'select' );
return $ret;
}
@@ -911,7 +965,7 @@ class Html {
$attribs['version'] = $wgHtml5Version;
}
- $html = Html::openElement( 'html', $attribs );
+ $html = self::openElement( 'html', $attribs );
if ( $html ) {
$html .= "\n";
@@ -946,44 +1000,58 @@ class Html {
*
* @return string
*/
- static function infoBox( $text, $icon, $alt, $class = false ) {
- $s = Html::openElement( 'div', array( 'class' => "mw-infobox $class" ) );
+ static function infoBox( $text, $icon, $alt, $class = '' ) {
+ $s = self::openElement( 'div', array( 'class' => "mw-infobox $class" ) );
- $s .= Html::openElement( 'div', array( 'class' => 'mw-infobox-left' ) ) .
- Html::element( 'img',
+ $s .= self::openElement( 'div', array( 'class' => 'mw-infobox-left' ) ) .
+ self::element( 'img',
array(
'src' => $icon,
'alt' => $alt,
)
) .
- Html::closeElement( 'div' );
+ self::closeElement( 'div' );
- $s .= Html::openElement( 'div', array( 'class' => 'mw-infobox-right' ) ) .
+ $s .= self::openElement( 'div', array( 'class' => 'mw-infobox-right' ) ) .
$text .
- Html::closeElement( 'div' );
- $s .= Html::element( 'div', array( 'style' => 'clear: left;' ), ' ' );
+ self::closeElement( 'div' );
+ $s .= self::element( 'div', array( 'style' => 'clear: left;' ), ' ' );
- $s .= Html::closeElement( 'div' );
+ $s .= self::closeElement( 'div' );
- $s .= Html::element( 'div', array( 'style' => 'clear: left;' ), ' ' );
+ $s .= self::element( 'div', array( 'style' => 'clear: left;' ), ' ' );
return $s;
}
/**
- * Generate a srcset attribute value from an array mapping pixel densities
- * to URLs. Note that srcset supports width and height values as well, which
- * are not used here.
+ * Generate a srcset attribute value.
+ *
+ * Generates a srcset attribute value from an array mapping pixel densities
+ * to URLs. A trailing 'x' in pixel density values is optional.
+ *
+ * @note srcset width and height values are not supported.
+ *
+ * @see http://www.whatwg.org/html/embedded-content-1.html#attr-img-srcset
+ *
+ * @par Example:
+ * @code
+ * Html::srcSet( array(
+ * '1x' => 'standard.jpeg',
+ * '1.5x' => 'large.jpeg',
+ * '3x' => 'extra-large.jpeg',
+ * ) );
+ * // gives 'standard.jpeg 1x, large.jpeg 1.5x, extra-large.jpeg 2x'
+ * @endcode
*
- * @param array $urls
+ * @param string[] $urls
* @return string
*/
static function srcSet( $urls ) {
$candidates = array();
foreach ( $urls as $density => $url ) {
- // Image candidate syntax per current whatwg live spec, 2012-09-23:
- // http://www.whatwg.org/html/embedded-content-1.html#attr-img-srcset
- $candidates[] = "{$url} {$density}x";
+ // Cast density to float to strip 'x'.
+ $candidates[] = $url . ' ' . (float)$density . 'x';
}
return implode( ", ", $candidates );
}
diff --git a/includes/HtmlFormatter.php b/includes/HtmlFormatter.php
index ccbfba82..b2926d17 100644
--- a/includes/HtmlFormatter.php
+++ b/includes/HtmlFormatter.php
@@ -133,7 +133,6 @@ class HtmlFormatter {
* @return array Array of removed DOMElements
*/
public function filterContent() {
- wfProfileIn( __METHOD__ );
$removals = $this->parseItemsToRemove();
// Bail out early if nothing to do
@@ -143,7 +142,6 @@ class HtmlFormatter {
},
true
) ) {
- wfProfileOut( __METHOD__ );
return array();
}
@@ -178,7 +176,7 @@ class HtmlFormatter {
// CSS Classes
$domElemsToRemove = array();
- $xpath = new DOMXpath( $doc );
+ $xpath = new DOMXPath( $doc );
foreach ( $removals['CLASS'] as $classToRemove ) {
$elements = $xpath->query( '//*[contains(@class, "' . $classToRemove . '")]' );
@@ -202,7 +200,6 @@ class HtmlFormatter {
$removed = array_merge( $removed, $this->removeElements( $elements ) );
}
- wfProfileOut( __METHOD__ );
return $removed;
}
@@ -235,7 +232,6 @@ class HtmlFormatter {
* @return string
*/
private function fixLibXML( $html ) {
- wfProfileIn( __METHOD__ );
static $replacements;
if ( !$replacements ) {
// We don't include rules like '&#34;' => '&amp;quot;' because entities had already been
@@ -249,7 +245,6 @@ class HtmlFormatter {
}
$html = $replacements->replace( $html );
$html = mb_convert_encoding( $html, 'UTF-8', 'HTML-ENTITIES' );
- wfProfileOut( __METHOD__ );
return $html;
}
@@ -264,10 +259,8 @@ class HtmlFormatter {
* @return string Processed HTML
*/
public function getText( $element = null ) {
- wfProfileIn( __METHOD__ );
if ( $this->doc ) {
- wfProfileIn( __METHOD__ . '-dom' );
if ( $element !== null && !( $element instanceof DOMElement ) ) {
$element = $this->doc->getElementById( $element );
}
@@ -283,9 +276,7 @@ class HtmlFormatter {
$body->appendChild( $element );
}
$html = $this->doc->saveHTML();
- wfProfileOut( __METHOD__ . '-dom' );
- wfProfileIn( __METHOD__ . '-fixes' );
$html = $this->fixLibXml( $html );
if ( wfIsWindows() ) {
// Cleanup for CRLF misprocessing of unknown origin on Windows.
@@ -294,7 +285,6 @@ class HtmlFormatter {
// XML code paths if possible and fix there.
$html = str_replace( '&#13;', '', $html );
}
- wfProfileOut( __METHOD__ . '-fixes' );
} else {
$html = $this->html;
}
@@ -302,14 +292,11 @@ class HtmlFormatter {
$html = preg_replace( '/<!--.*?-->|^.*?<body>|<\/body>.*$/s', '', $html );
$html = $this->onHtmlReady( $html );
- wfProfileIn( __METHOD__ . '-flatten' );
if ( $this->elementsToFlatten ) {
$elements = implode( '|', $this->elementsToFlatten );
$html = preg_replace( "#</?($elements)\\b[^>]*>#is", '', $html );
}
- wfProfileOut( __METHOD__ . '-flatten' );
- wfProfileOut( __METHOD__ );
return $html;
}
@@ -322,6 +309,7 @@ class HtmlFormatter {
* @param string $type The type of selector (ID, CLASS, TAG_CLASS, or TAG)
* @param string $rawName The raw name of the selector
* @return bool Whether the selector was successfully recognised
+ * @throws MWException
*/
protected function parseSelector( $selector, &$type, &$rawName ) {
if ( strpos( $selector, '.' ) === 0 ) {
@@ -349,7 +337,6 @@ class HtmlFormatter {
* @return array
*/
protected function parseItemsToRemove() {
- wfProfileIn( __METHOD__ );
$removals = array(
'ID' => array(),
'TAG' => array(),
@@ -371,7 +358,6 @@ class HtmlFormatter {
$removals['TAG'][] = 'video';
}
- wfProfileOut( __METHOD__ );
return $removals;
}
}
diff --git a/includes/HttpFunctions.php b/includes/HttpFunctions.php
index 83021245..8e05f597 100644
--- a/includes/HttpFunctions.php
+++ b/includes/HttpFunctions.php
@@ -55,11 +55,11 @@ class Http {
* to avoid attacks on intranet services accessible by HTTP.
* - userAgent A user agent, if you want to override the default
* MediaWiki/$wgVersion
+ * @param string $caller The method making this request, for profiling
* @return string|bool (bool)false on failure or a string on success
*/
- public static function request( $method, $url, $options = array() ) {
+ public static function request( $method, $url, $options = array(), $caller = __METHOD__ ) {
wfDebug( "HTTP: $method: $url\n" );
- wfProfileIn( __METHOD__ . "-$method" );
$options['method'] = strtoupper( $method );
@@ -70,29 +70,39 @@ class Http {
$options['connectTimeout'] = 'default';
}
- $req = MWHttpRequest::factory( $url, $options );
+ $req = MWHttpRequest::factory( $url, $options, $caller );
$status = $req->execute();
$content = false;
if ( $status->isOK() ) {
$content = $req->getContent();
}
- wfProfileOut( __METHOD__ . "-$method" );
return $content;
}
/**
* Simple wrapper for Http::request( 'GET' )
* @see Http::request()
+ * @since 1.25 Second parameter $timeout removed. Second parameter
+ * is now $options which can be given a 'timeout'
*
* @param string $url
- * @param string $timeout
* @param array $options
+ * @param string $caller The method making this request, for profiling
* @return string
*/
- public static function get( $url, $timeout = 'default', $options = array() ) {
- $options['timeout'] = $timeout;
- return Http::request( 'GET', $url, $options );
+ public static function get( $url, $options = array(), $caller = __METHOD__ ) {
+ $args = func_get_args();
+ if ( isset( $args[1] ) && ( is_string( $args[1] ) || is_numeric( $args[1] ) ) ) {
+ // Second was used to be the timeout
+ // And third parameter used to be $options
+ wfWarn( "Second parameter should not be a timeout.", 2 );
+ $options = isset( $args[2] ) && is_array( $args[2] ) ?
+ $args[2] : array();
+ $options['timeout'] = $args[1];
+ $caller = __METHOD__;
+ }
+ return Http::request( 'GET', $url, $options, $caller );
}
/**
@@ -101,10 +111,11 @@ class Http {
*
* @param string $url
* @param array $options
+ * @param string $caller The method making this request, for profiling
* @return string
*/
- public static function post( $url, $options = array() ) {
- return Http::request( 'POST', $url, $options );
+ public static function post( $url, $options = array(), $caller = __METHOD__ ) {
+ return Http::request( 'POST', $url, $options, $caller );
}
/**
@@ -114,7 +125,7 @@ class Http {
* @return bool
*/
public static function isLocalURL( $url ) {
- global $wgCommandLineMode, $wgConf;
+ global $wgCommandLineMode, $wgLocalVirtualHosts, $wgConf;
if ( $wgCommandLineMode ) {
return false;
@@ -126,7 +137,7 @@ class Http {
$host = $matches[1];
// Split up dotwise
$domainParts = explode( '.', $host );
- // Check if this domain or any superdomain is listed in $wgConf as a local virtual host
+ // Check if this domain or any superdomain is listed as a local virtual host
$domainParts = array_reverse( $domainParts );
$domain = '';
@@ -139,7 +150,9 @@ class Http {
$domain = $domainPart . '.' . $domain;
}
- if ( $wgConf->isLocalVHost( $domain ) ) {
+ if ( in_array( $domain, $wgLocalVirtualHosts )
+ || $wgConf->isLocalVHost( $domain )
+ ) {
return true;
}
}
@@ -217,10 +230,22 @@ class MWHttpRequest {
public $status;
/**
+ * @var Profiler
+ */
+ protected $profiler;
+
+ /**
+ * @var string
+ */
+ protected $profileName;
+
+ /**
* @param string $url Url to use. If protocol-relative, will be expanded to an http:// URL
* @param array $options (optional) extra params to pass (see Http::request())
+ * @param string $caller The method making this request, for profiling
+ * @param Profiler $profiler An instance of the profiler for profiling, or null
*/
- protected function __construct( $url, $options = array() ) {
+ protected function __construct( $url, $options = array(), $caller = __METHOD__, $profiler = null ) {
global $wgHTTPTimeout, $wgHTTPConnectTimeout;
$this->url = wfExpandUrl( $url, PROTO_HTTP );
@@ -263,6 +288,10 @@ class MWHttpRequest {
if ( $this->noProxy ) {
$this->proxy = ''; // noProxy takes precedence
}
+
+ // Profile based on what's calling us
+ $this->profiler = $profiler;
+ $this->profileName = $caller;
}
/**
@@ -278,11 +307,12 @@ class MWHttpRequest {
* Generate a new request object
* @param string $url Url to use
* @param array $options (optional) extra params to pass (see Http::request())
+ * @param string $caller The method making this request, for profiling
* @throws MWException
* @return CurlHttpRequest|PhpHttpRequest
* @see MWHttpRequest::__construct
*/
- public static function factory( $url, $options = null ) {
+ public static function factory( $url, $options = null, $caller = __METHOD__ ) {
if ( !Http::$httpEngine ) {
Http::$httpEngine = function_exists( 'curl_init' ) ? 'curl' : 'php';
} elseif ( Http::$httpEngine == 'curl' && !function_exists( 'curl_init' ) ) {
@@ -292,7 +322,7 @@ class MWHttpRequest {
switch ( Http::$httpEngine ) {
case 'curl':
- return new CurlHttpRequest( $url, $options );
+ return new CurlHttpRequest( $url, $options, $caller, Profiler::instance() );
case 'php':
if ( !wfIniGetBool( 'allow_url_fopen' ) ) {
throw new MWException( __METHOD__ . ': allow_url_fopen ' .
@@ -301,7 +331,7 @@ class MWHttpRequest {
'http://php.net/curl.'
);
}
- return new PhpHttpRequest( $url, $options );
+ return new PhpHttpRequest( $url, $options, $caller, Profiler::instance() );
default:
throw new MWException( __METHOD__ . ': The setting of Http::$httpEngine is not valid.' );
}
@@ -434,7 +464,6 @@ class MWHttpRequest {
* @return Status
*/
public function execute() {
- wfProfileIn( __METHOD__ );
$this->content = "";
@@ -452,7 +481,6 @@ class MWHttpRequest {
$this->setUserAgent( Http::userAgent() );
}
- wfProfileOut( __METHOD__ );
}
/**
@@ -461,7 +489,6 @@ class MWHttpRequest {
* found in an array in the member variable headerList.
*/
protected function parseHeader() {
- wfProfileIn( __METHOD__ );
$lastname = "";
@@ -480,7 +507,6 @@ class MWHttpRequest {
$this->parseCookies();
- wfProfileOut( __METHOD__ );
}
/**
@@ -614,7 +640,6 @@ class MWHttpRequest {
* Parse the cookies in the response headers and store them in the cookie jar.
*/
protected function parseCookies() {
- wfProfileIn( __METHOD__ );
if ( !$this->cookieJar ) {
$this->cookieJar = new CookieJar;
@@ -627,7 +652,6 @@ class MWHttpRequest {
}
}
- wfProfileOut( __METHOD__ );
}
/**
@@ -715,12 +739,10 @@ class CurlHttpRequest extends MWHttpRequest {
}
public function execute() {
- wfProfileIn( __METHOD__ );
parent::execute();
if ( !$this->status->isOK() ) {
- wfProfileOut( __METHOD__ );
return $this->status;
}
@@ -766,7 +788,6 @@ class CurlHttpRequest extends MWHttpRequest {
$curlHandle = curl_init( $this->url );
if ( !curl_setopt_array( $curlHandle, $this->curlOptions ) ) {
- wfProfileOut( __METHOD__ );
throw new MWException( "Error setting curl options." );
}
@@ -781,6 +802,12 @@ class CurlHttpRequest extends MWHttpRequest {
wfRestoreWarnings();
}
+ if ( $this->profiler ) {
+ $profileSection = $this->profiler->scopedProfileIn(
+ __METHOD__ . '-' . $this->profileName
+ );
+ }
+
$curlRes = curl_exec( $curlHandle );
if ( curl_errno( $curlHandle ) == CURLE_OPERATION_TIMEOUTED ) {
$this->status->fatal( 'http-timed-out', $this->url );
@@ -792,11 +819,13 @@ class CurlHttpRequest extends MWHttpRequest {
curl_close( $curlHandle );
+ if ( $this->profiler ) {
+ $this->profiler->scopedProfileOut( $profileSection );
+ }
+
$this->parseHeader();
$this->setStatus();
- wfProfileOut( __METHOD__ );
-
return $this->status;
}
@@ -832,7 +861,6 @@ class PhpHttpRequest extends MWHttpRequest {
}
public function execute() {
- wfProfileIn( __METHOD__ );
parent::execute();
@@ -903,6 +931,11 @@ class PhpHttpRequest extends MWHttpRequest {
$result = array();
+ if ( $this->profiler ) {
+ $profileSection = $this->profiler->scopedProfileIn(
+ __METHOD__ . '-' . $this->profileName
+ );
+ }
do {
$reqCount++;
wfSuppressWarnings();
@@ -933,18 +966,19 @@ class PhpHttpRequest extends MWHttpRequest {
break;
}
} while ( true );
+ if ( $this->profiler ) {
+ $this->profiler->scopedProfileOut( $profileSection );
+ }
$this->setStatus();
if ( $fh === false ) {
$this->status->fatal( 'http-request-error' );
- wfProfileOut( __METHOD__ );
return $this->status;
}
if ( $result['timed_out'] ) {
$this->status->fatal( 'http-timed-out', $this->url );
- wfProfileOut( __METHOD__ );
return $this->status;
}
@@ -966,8 +1000,6 @@ class PhpHttpRequest extends MWHttpRequest {
}
fclose( $fh );
- wfProfileOut( __METHOD__ );
-
return $this->status;
}
}
diff --git a/includes/Import.php b/includes/Import.php
index 5319076e..d31be43b 100644
--- a/includes/Import.php
+++ b/includes/Import.php
@@ -32,18 +32,31 @@
*/
class WikiImporter {
private $reader = null;
+ private $foreignNamespaces = null;
private $mLogItemCallback, $mUploadCallback, $mRevisionCallback, $mPageCallback;
- private $mSiteInfoCallback, $mTargetNamespace, $mTargetRootPage, $mPageOutCallback;
+ private $mSiteInfoCallback, $mTargetNamespace, $mPageOutCallback;
private $mNoticeCallback, $mDebug;
private $mImportUploads, $mImageBasePath;
private $mNoUpdates = false;
+ /** @var Config */
+ private $config;
+ /** @var ImportTitleFactory */
+ private $importTitleFactory;
+ /** @var array */
+ private $countableCache = array();
/**
* Creates an ImportXMLReader drawing from the source provided
- * @param ImportStreamSource $source
+ * @param ImportSource $source
+ * @param Config $config
*/
- function __construct( ImportStreamSource $source ) {
+ function __construct( ImportSource $source, Config $config = null ) {
$this->reader = new XMLReader();
+ if ( !$config ) {
+ wfDeprecated( __METHOD__ . ' without a Config instance', '1.25' );
+ $config = ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
+ }
+ $this->config = $config;
if ( !in_array( 'uploadsource', stream_get_wrappers() ) ) {
stream_wrapper_register( 'uploadsource', 'UploadSourceAdapter' );
@@ -56,10 +69,13 @@ class WikiImporter {
}
// Default callbacks
+ $this->setPageCallback( array( $this, 'beforeImportPage' ) );
$this->setRevisionCallback( array( $this, "importRevision" ) );
$this->setUploadCallback( array( $this, 'importUpload' ) );
$this->setLogItemCallback( array( $this, 'importLogItem' ) );
$this->setPageOutCallback( array( $this, 'finishImportPage' ) );
+
+ $this->importTitleFactory = new NaiveImportTitleFactory();
}
/**
@@ -192,6 +208,15 @@ class WikiImporter {
}
/**
+ * Sets the factory object to use to convert ForeignTitle objects into local
+ * Title objects
+ * @param ImportTitleFactory $factory
+ */
+ public function setImportTitleFactory( $factory ) {
+ $this->importTitleFactory = $factory;
+ }
+
+ /**
* Set a target namespace to override the defaults
* @param null|int $namespace
* @return bool
@@ -200,9 +225,16 @@ class WikiImporter {
if ( is_null( $namespace ) ) {
// Don't override namespaces
$this->mTargetNamespace = null;
- } elseif ( $namespace >= 0 ) {
- // @todo FIXME: Check for validity
- $this->mTargetNamespace = intval( $namespace );
+ $this->setImportTitleFactory( new NaiveImportTitleFactory() );
+ return true;
+ } elseif (
+ $namespace >= 0 &&
+ MWNamespace::exists( intval( $namespace ) )
+ ) {
+ $namespace = intval( $namespace );
+ $this->mTargetNamespace = $namespace;
+ $this->setImportTitleFactory( new NamespaceImportTitleFactory( $namespace ) );
+ return true;
} else {
return false;
}
@@ -217,7 +249,7 @@ class WikiImporter {
$status = Status::newGood();
if ( is_null( $rootpage ) ) {
// No rootpage
- $this->mTargetRootPage = null;
+ $this->setImportTitleFactory( new NaiveImportTitleFactory() );
} elseif ( $rootpage !== '' ) {
$rootpage = rtrim( $rootpage, '/' ); //avoid double slashes
$title = Title::newFromText( $rootpage, !is_null( $this->mTargetNamespace )
@@ -236,9 +268,9 @@ class WikiImporter {
: $wgContLang->getNsText( $title->getNamespace() );
$status->fatal( 'import-rootpage-nosubpage', $displayNSText );
} else {
- // set namespace to 'all', so the namespace check in processTitle() can passed
+ // set namespace to 'all', so the namespace check in processTitle() can pass
$this->setTargetNamespace( null );
- $this->mTargetRootPage = $title->getPrefixedDBkey();
+ $this->setImportTitleFactory( new SubpageImportTitleFactory( $title ) );
}
}
}
@@ -260,6 +292,19 @@ class WikiImporter {
}
/**
+ * Default per-page callback. Sets up some things related to site statistics
+ * @param array $titleAndForeignTitle Two-element array, with Title object at
+ * index 0 and ForeignTitle object at index 1
+ * @return bool
+ */
+ public function beforeImportPage( $titleAndForeignTitle ) {
+ $title = $titleAndForeignTitle[0];
+ $page = WikiPage::factory( $title );
+ $this->countableCache['title_' . $title->getPrefixedText()] = $page->isCountable();
+ return true;
+ }
+
+ /**
* Default per-revision callback, performs the import.
* @param WikiRevision $revision
* @return bool
@@ -312,15 +357,41 @@ class WikiImporter {
/**
* Mostly for hook use
* @param Title $title
- * @param string $origTitle
+ * @param ForeignTitle $foreignTitle
* @param int $revCount
* @param int $sRevCount
* @param array $pageInfo
* @return bool
*/
- public function finishImportPage( $title, $origTitle, $revCount, $sRevCount, $pageInfo ) {
+ public function finishImportPage( $title, $foreignTitle, $revCount,
+ $sRevCount, $pageInfo ) {
+
+ // Update article count statistics (T42009)
+ // The normal counting logic in WikiPage->doEditUpdates() is designed for
+ // one-revision-at-a-time editing, not bulk imports. In this situation it
+ // suffers from issues of slave lag. We let WikiPage handle the total page
+ // and revision count, and we implement our own custom logic for the
+ // article (content page) count.
+ $page = WikiPage::factory( $title );
+ $page->loadPageData( 'fromdbmaster' );
+ $content = $page->getContent();
+ if ( $content === null ) {
+ wfDebug( __METHOD__ . ': Skipping article count adjustment for ' . $title .
+ ' because WikiPage::getContent() returned null' );
+ } else {
+ $editInfo = $page->prepareContentForEdit( $content );
+ $countKey = 'title_' . $title->getPrefixedText();
+ $countable = $page->isCountable( $editInfo );
+ if ( array_key_exists( $countKey, $this->countableCache ) &&
+ $countable != $this->countableCache[ $countKey ] ) {
+ DeferredUpdates::addUpdate( SiteStatsUpdate::factory( array(
+ 'articles' => ( (int)$countable - (int)$this->countableCache[ $countKey ] )
+ ) ) );
+ }
+ }
+
$args = func_get_args();
- return wfRunHooks( 'AfterImportPage', $args );
+ return Hooks::run( 'AfterImportPage', $args );
}
/**
@@ -341,6 +412,20 @@ class WikiImporter {
}
/**
+ * Notify the callback function of site info
+ * @param array $siteInfo
+ * @return bool|mixed
+ */
+ private function siteInfoCallback( $siteInfo ) {
+ if ( isset( $this->mSiteInfoCallback ) ) {
+ return call_user_func_array( $this->mSiteInfoCallback,
+ array( $siteInfo, $this ) );
+ } else {
+ return false;
+ }
+ }
+
+ /**
* Notify the callback function when a new "<page>" is reached.
* @param Title $title
*/
@@ -353,12 +438,13 @@ class WikiImporter {
/**
* Notify the callback function when a "</page>" is closed.
* @param Title $title
- * @param Title $origTitle
+ * @param ForeignTitle $foreignTitle
* @param int $revCount
* @param int $sucCount Number of revisions for which callback returned true
* @param array $pageInfo Associative array of page information
*/
- private function pageOutCallback( $title, $origTitle, $revCount, $sucCount, $pageInfo ) {
+ private function pageOutCallback( $title, $foreignTitle, $revCount,
+ $sucCount, $pageInfo ) {
if ( isset( $this->mPageOutCallback ) ) {
$args = func_get_args();
call_user_func_array( $this->mPageOutCallback, $args );
@@ -396,7 +482,8 @@ class WikiImporter {
/**
* Retrieves the contents of the named attribute of the current element.
* @param string $attr The name of the attribute
- * @return string The value of the attribute or an empty string if it is not set in the current element.
+ * @return string The value of the attribute or an empty string if it is not set in the current
+ * element.
*/
public function nodeAttribute( $attr ) {
return $this->reader->getAttribute( $attr );
@@ -416,11 +503,11 @@ class WikiImporter {
$buffer = "";
while ( $this->reader->read() ) {
switch ( $this->reader->nodeType ) {
- case XmlReader::TEXT:
- case XmlReader::SIGNIFICANT_WHITESPACE:
+ case XMLReader::TEXT:
+ case XMLReader::SIGNIFICANT_WHITESPACE:
$buffer .= $this->reader->value;
break;
- case XmlReader::END_ELEMENT:
+ case XMLReader::END_ELEMENT:
return $buffer;
}
}
@@ -452,51 +539,76 @@ class WikiImporter {
$keepReading = $this->reader->read();
$skip = false;
- while ( $keepReading ) {
- $tag = $this->reader->name;
- $type = $this->reader->nodeType;
-
- if ( !wfRunHooks( 'ImportHandleToplevelXMLTag', array( $this ) ) ) {
- // Do nothing
- } elseif ( $tag == 'mediawiki' && $type == XmlReader::END_ELEMENT ) {
- break;
- } elseif ( $tag == 'siteinfo' ) {
- $this->handleSiteInfo();
- } elseif ( $tag == 'page' ) {
- $this->handlePage();
- } elseif ( $tag == 'logitem' ) {
- $this->handleLogItem();
- } elseif ( $tag != '#text' ) {
- $this->warn( "Unhandled top-level XML tag $tag" );
-
- $skip = true;
- }
+ $rethrow = null;
+ try {
+ while ( $keepReading ) {
+ $tag = $this->reader->name;
+ $type = $this->reader->nodeType;
+
+ if ( !Hooks::run( 'ImportHandleToplevelXMLTag', array( $this ) ) ) {
+ // Do nothing
+ } elseif ( $tag == 'mediawiki' && $type == XMLReader::END_ELEMENT ) {
+ break;
+ } elseif ( $tag == 'siteinfo' ) {
+ $this->handleSiteInfo();
+ } elseif ( $tag == 'page' ) {
+ $this->handlePage();
+ } elseif ( $tag == 'logitem' ) {
+ $this->handleLogItem();
+ } elseif ( $tag != '#text' ) {
+ $this->warn( "Unhandled top-level XML tag $tag" );
+
+ $skip = true;
+ }
- if ( $skip ) {
- $keepReading = $this->reader->next();
- $skip = false;
- $this->debug( "Skip" );
- } else {
- $keepReading = $this->reader->read();
+ if ( $skip ) {
+ $keepReading = $this->reader->next();
+ $skip = false;
+ $this->debug( "Skip" );
+ } else {
+ $keepReading = $this->reader->read();
+ }
}
+ } catch ( Exception $ex ) {
+ $rethrow = $ex;
}
+ // finally
libxml_disable_entity_loader( $oldDisable );
+ $this->reader->close();
+
+ if ( $rethrow ) {
+ throw $rethrow;
+ }
+
return true;
}
- /**
- * @return bool
- * @throws MWException
- */
private function handleSiteInfo() {
- // Site info is useful, but not actually used for dump imports.
- // Includes a quick short-circuit to save performance.
- if ( !$this->mSiteInfoCallback ) {
- $this->reader->next();
- return true;
+ $this->debug( "Enter site info handler." );
+ $siteInfo = array();
+
+ // Fields that can just be stuffed in the siteInfo object
+ $normalFields = array( 'sitename', 'base', 'generator', 'case' );
+
+ while ( $this->reader->read() ) {
+ if ( $this->reader->nodeType == XmlReader::END_ELEMENT &&
+ $this->reader->name == 'siteinfo' ) {
+ break;
+ }
+
+ $tag = $this->reader->name;
+
+ if ( $tag == 'namespace' ) {
+ $this->foreignNamespaces[ $this->nodeAttribute( 'key' ) ] =
+ $this->nodeContents();
+ } elseif ( in_array( $tag, $normalFields ) ) {
+ $siteInfo[$tag] = $this->nodeContents();
+ }
}
- throw new MWException( "SiteInfo tag is not yet handled, do not set mSiteInfoCallback" );
+
+ $siteInfo['_namespaces'] = $this->foreignNamespaces;
+ $this->siteInfoCallback( $siteInfo );
}
private function handleLogItem() {
@@ -508,14 +620,14 @@ class WikiImporter {
'logtitle', 'params' );
while ( $this->reader->read() ) {
- if ( $this->reader->nodeType == XmlReader::END_ELEMENT &&
+ if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
$this->reader->name == 'logitem' ) {
break;
}
$tag = $this->reader->name;
- if ( !wfRunHooks( 'ImportHandleLogItemXMLTag', array(
+ if ( !Hooks::run( 'ImportHandleLogItemXMLTag', array(
$this, $logInfo
) ) ) {
// Do nothing
@@ -536,7 +648,7 @@ class WikiImporter {
* @return bool|mixed
*/
private function processLogItem( $logInfo ) {
- $revision = new WikiRevision;
+ $revision = new WikiRevision( $this->config );
$revision->setID( $logInfo['id'] );
$revision->setType( $logInfo['type'] );
@@ -566,23 +678,25 @@ class WikiImporter {
$pageInfo = array( 'revisionCount' => 0, 'successfulRevisionCount' => 0 );
// Fields that can just be stuffed in the pageInfo object
- $normalFields = array( 'title', 'id', 'redirect', 'restrictions' );
+ $normalFields = array( 'title', 'ns', 'id', 'redirect', 'restrictions' );
$skip = false;
$badTitle = false;
while ( $skip ? $this->reader->next() : $this->reader->read() ) {
- if ( $this->reader->nodeType == XmlReader::END_ELEMENT &&
+ if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
$this->reader->name == 'page' ) {
break;
}
+ $skip = false;
+
$tag = $this->reader->name;
if ( $badTitle ) {
// The title is invalid, bail out of this page
$skip = true;
- } elseif ( !wfRunHooks( 'ImportHandlePageXMLTag', array( $this,
+ } elseif ( !Hooks::run( 'ImportHandlePageXMLTag', array( $this,
&$pageInfo ) ) ) {
// Do nothing
} elseif ( in_array( $tag, $normalFields ) ) {
@@ -597,29 +711,35 @@ class WikiImporter {
$pageInfo[$tag] = $this->nodeAttribute( 'title' );
} else {
$pageInfo[$tag] = $this->nodeContents();
- if ( $tag == 'title' ) {
- $title = $this->processTitle( $pageInfo['title'] );
+ }
+ } elseif ( $tag == 'revision' || $tag == 'upload' ) {
+ if ( !isset( $title ) ) {
+ $title = $this->processTitle( $pageInfo['title'],
+ isset( $pageInfo['ns'] ) ? $pageInfo['ns'] : null );
+
+ if ( !$title ) {
+ $badTitle = true;
+ $skip = true;
+ }
- if ( !$title ) {
- $badTitle = true;
- $skip = true;
- }
+ $this->pageCallback( $title );
+ list( $pageInfo['_title'], $foreignTitle ) = $title;
+ }
- $this->pageCallback( $title );
- list( $pageInfo['_title'], $origTitle ) = $title;
+ if ( $title ) {
+ if ( $tag == 'revision' ) {
+ $this->handleRevision( $pageInfo );
+ } else {
+ $this->handleUpload( $pageInfo );
}
}
- } elseif ( $tag == 'revision' ) {
- $this->handleRevision( $pageInfo );
- } elseif ( $tag == 'upload' ) {
- $this->handleUpload( $pageInfo );
} elseif ( $tag != '#text' ) {
$this->warn( "Unhandled page XML tag $tag" );
$skip = true;
}
}
- $this->pageOutCallback( $pageInfo['_title'], $origTitle,
+ $this->pageOutCallback( $pageInfo['_title'], $foreignTitle,
$pageInfo['revisionCount'],
$pageInfo['successfulRevisionCount'],
$pageInfo );
@@ -637,14 +757,14 @@ class WikiImporter {
$skip = false;
while ( $skip ? $this->reader->next() : $this->reader->read() ) {
- if ( $this->reader->nodeType == XmlReader::END_ELEMENT &&
+ if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
$this->reader->name == 'revision' ) {
break;
}
$tag = $this->reader->name;
- if ( !wfRunHooks( 'ImportHandleRevisionXMLTag', array(
+ if ( !Hooks::run( 'ImportHandleRevisionXMLTag', array(
$this, $pageInfo, $revisionInfo
) ) ) {
// Do nothing
@@ -670,7 +790,7 @@ class WikiImporter {
* @return bool|mixed
*/
private function processRevision( $pageInfo, $revisionInfo ) {
- $revision = new WikiRevision;
+ $revision = new WikiRevision( $this->config );
if ( isset( $revisionInfo['id'] ) ) {
$revision->setID( $revisionInfo['id'] );
@@ -729,14 +849,14 @@ class WikiImporter {
$skip = false;
while ( $skip ? $this->reader->next() : $this->reader->read() ) {
- if ( $this->reader->nodeType == XmlReader::END_ELEMENT &&
+ if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
$this->reader->name == 'upload' ) {
break;
}
$tag = $this->reader->name;
- if ( !wfRunHooks( 'ImportHandleUploadXMLTag', array(
+ if ( !Hooks::run( 'ImportHandleUploadXMLTag', array(
$this, $pageInfo
) ) ) {
// Do nothing
@@ -786,7 +906,7 @@ class WikiImporter {
* @return mixed
*/
private function processUpload( $pageInfo, $uploadInfo ) {
- $revision = new WikiRevision;
+ $revision = new WikiRevision( $this->config );
$text = isset( $uploadInfo['text'] ) ? $uploadInfo['text'] : '';
$revision->setTitle( $pageInfo['_title'] );
@@ -827,7 +947,7 @@ class WikiImporter {
$info = array();
while ( $this->reader->read() ) {
- if ( $this->reader->nodeType == XmlReader::END_ELEMENT &&
+ if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
$this->reader->name == 'contributor' ) {
break;
}
@@ -844,29 +964,27 @@ class WikiImporter {
/**
* @param string $text
+ * @param string|null $ns
* @return array|bool
*/
- private function processTitle( $text ) {
- global $wgCommandLineMode;
-
- $workTitle = $text;
- $origTitle = Title::newFromText( $workTitle );
-
- if ( !is_null( $this->mTargetNamespace ) && !is_null( $origTitle ) ) {
- # makeTitleSafe, because $origTitle can have a interwiki (different setting of interwiki map)
- # and than dbKey can begin with a lowercase char
- $title = Title::makeTitleSafe( $this->mTargetNamespace,
- $origTitle->getDBkey() );
+ private function processTitle( $text, $ns = null ) {
+ if ( is_null( $this->foreignNamespaces ) ) {
+ $foreignTitleFactory = new NaiveForeignTitleFactory();
} else {
- if ( !is_null( $this->mTargetRootPage ) ) {
- $workTitle = $this->mTargetRootPage . '/' . $workTitle;
- }
- $title = Title::newFromText( $workTitle );
+ $foreignTitleFactory = new NamespaceAwareForeignTitleFactory(
+ $this->foreignNamespaces );
}
+ $foreignTitle = $foreignTitleFactory->createForeignTitle( $text,
+ intval( $ns ) );
+
+ $title = $this->importTitleFactory->createTitleFromForeignTitle(
+ $foreignTitle );
+
+ $commandLineMode = $this->config->get( 'CommandLineMode' );
if ( is_null( $title ) ) {
# Invalid page title? Ignore the page
- $this->notice( 'import-error-invalid', $workTitle );
+ $this->notice( 'import-error-invalid', $foreignTitle->getFullText() );
return false;
} elseif ( $title->isExternal() ) {
$this->notice( 'import-error-interwiki', $title->getPrefixedText() );
@@ -874,17 +992,17 @@ class WikiImporter {
} elseif ( !$title->canExist() ) {
$this->notice( 'import-error-special', $title->getPrefixedText() );
return false;
- } elseif ( !$title->userCan( 'edit' ) && !$wgCommandLineMode ) {
+ } elseif ( !$title->userCan( 'edit' ) && !$commandLineMode ) {
# Do not import if the importing wiki user cannot edit this page
$this->notice( 'import-error-edit', $title->getPrefixedText() );
return false;
- } elseif ( !$title->exists() && !$title->userCan( 'create' ) && !$wgCommandLineMode ) {
+ } elseif ( !$title->exists() && !$title->userCan( 'create' ) && !$commandLineMode ) {
# Do not import if the importing wiki user cannot create this page
$this->notice( 'import-error-create', $title->getPrefixedText() );
return false;
}
- return array( $title, $origTitle );
+ return array( $title, $foreignTitle );
}
}
@@ -903,10 +1021,10 @@ class UploadSourceAdapter {
private $mPosition;
/**
- * @param ImportStreamSource $source
+ * @param ImportSource $source
* @return string
*/
- static function registerSource( ImportStreamSource $source ) {
+ static function registerSource( ImportSource $source ) {
$id = wfRandomString();
self::$sourceRegistrations[$id] = $source;
@@ -1093,6 +1211,13 @@ class WikiRevision {
/** @var bool */
private $mNoUpdates = false;
+ /** @var Config $config */
+ private $config;
+
+ public function __construct( Config $config ) {
+ $this->config = $config;
+ }
+
/**
* @param Title $title
* @throws MWException
@@ -1434,8 +1559,7 @@ class WikiRevision {
}
// avoid memory leak...?
- $linkCache = LinkCache::singleton();
- $linkCache->clear();
+ Title::clearCaches();
$page = WikiPage::factory( $this->title );
$page->loadPageData( 'fromdbmaster' );
@@ -1461,7 +1585,6 @@ class WikiRevision {
$this->title->getPrefixedText() . "]], timestamp " . $this->timestamp . "\n" );
return false;
}
- $oldcountable = $page->isCountable();
}
# @todo FIXME: Use original rev_id optionally (better for backups)
@@ -1484,10 +1607,11 @@ class WikiRevision {
if ( $changed !== false && !$this->mNoUpdates ) {
wfDebug( __METHOD__ . ": running updates\n" );
+ // countable/oldcountable stuff is handled in WikiImporter::finishImportPage
$page->doEditUpdates(
$revision,
$userObj,
- array( 'created' => $created, 'oldcountable' => $oldcountable )
+ array( 'created' => $created, 'oldcountable' => 'no-change' )
);
}
@@ -1550,6 +1674,7 @@ class WikiRevision {
RepoGroup::singleton()->getLocalRepo(), $archiveName );
} else {
$file = wfLocalFile( $this->getTitle() );
+ $file->load( File::READ_LATEST );
wfDebug( __METHOD__ . 'Importing new file as ' . $file->getName() . "\n" );
if ( $file->exists() && $file->getTimestamp() > $this->getTimestamp() ) {
$archiveName = $file->getTimestamp() . '!' . $file->getName();
@@ -1599,7 +1724,7 @@ class WikiRevision {
wfDebug( __METHOD__ . ": Successful\n" );
return true;
} else {
- wfDebug( __METHOD__ . ': failed: ' . $status->getXml() . "\n" );
+ wfDebug( __METHOD__ . ': failed: ' . $status->getHTML() . "\n" );
return false;
}
}
@@ -1608,8 +1733,7 @@ class WikiRevision {
* @return bool|string
*/
function downloadSource() {
- global $wgEnableUploads;
- if ( !$wgEnableUploads ) {
+ if ( !$this->config->get( 'EnableUploads' ) ) {
return false;
}
@@ -1622,7 +1746,7 @@ class WikiRevision {
// @todo FIXME!
$src = $this->getSrc();
- $data = Http::get( $src );
+ $data = Http::get( $src, array(), __METHOD__ );
if ( !$data ) {
wfDebug( "IMPORT: couldn't fetch source $src\n" );
fclose( $f );
@@ -1639,10 +1763,37 @@ class WikiRevision {
}
/**
- * @todo document (e.g. one-sentence class description).
+ * Source interface for XML import.
+ */
+interface ImportSource {
+
+ /**
+ * Indicates whether the end of the input has been reached.
+ * Will return true after a finite number of calls to readChunk.
+ *
+ * @return bool true if there is no more input, false otherwise.
+ */
+ function atEnd();
+
+ /**
+ * Return a chunk of the input, as a (possibly empty) string.
+ * When the end of input is reached, readChunk() returns false.
+ * If atEnd() returns false, readChunk() will return a string.
+ * If atEnd() returns true, readChunk() will return false.
+ *
+ * @return bool|string
+ */
+ function readChunk();
+}
+
+/**
+ * Used for importing XML dumps where the content of the dump is in a string.
+ * This class is ineffecient, and should only be used for small dumps.
+ * For larger dumps, ImportStreamSource should be used instead.
+ *
* @ingroup SpecialPage
*/
-class ImportStringSource {
+class ImportStringSource implements ImportSource {
function __construct( $string ) {
$this->mString = $string;
$this->mRead = false;
@@ -1668,10 +1819,10 @@ class ImportStringSource {
}
/**
- * @todo document (e.g. one-sentence class description).
+ * Imports a XML dump from a file (either from file upload, files on disk, or HTTP)
* @ingroup SpecialPage
*/
-class ImportStreamSource {
+class ImportStreamSource implements ImportSource {
function __construct( $handle ) {
$this->mHandle = $handle;
}
@@ -1752,7 +1903,7 @@ class ImportStreamSource {
# quicker and sorts out user-agent problems which might
# otherwise prevent importing from large sites, such
# as the Wikimedia cluster, etc.
- $data = Http::request( $method, $url, array( 'followRedirects' => true ) );
+ $data = Http::request( $method, $url, array( 'followRedirects' => true ), __METHOD__ );
if ( $data !== false ) {
$file = tmpfile();
fwrite( $file, $data );
diff --git a/includes/LinkFilter.php b/includes/LinkFilter.php
index 340ae8f3..99aaaa09 100644
--- a/includes/LinkFilter.php
+++ b/includes/LinkFilter.php
@@ -92,7 +92,7 @@ class LinkFilter {
* @return array Array to be passed to DatabaseBase::buildLike() or false on error
*/
public static function makeLikeArray( $filterEntry, $protocol = 'http://' ) {
- $db = wfGetDB( DB_MASTER );
+ $db = wfGetDB( DB_SLAVE );
$target = $protocol . $filterEntry;
$bits = wfParseUrl( $target );
diff --git a/includes/Linker.php b/includes/Linker.php
index be850d02..b58dabab 100644
--- a/includes/Linker.php
+++ b/includes/Linker.php
@@ -37,23 +37,10 @@ class Linker {
const TOOL_LINKS_EMAIL = 2;
/**
- * Get the appropriate HTML attributes to add to the "a" element of an
- * external link, as created by [wikisyntax].
- *
- * @param string $class The contents of the class attribute; if an empty
- * string is passed, which is the default value, defaults to 'external'.
- * @return string
- * @deprecated since 1.18 Just pass the external class directly to something
- * using Html::expandAttributes.
- */
- static function getExternalLinkAttributes( $class = 'external' ) {
- wfDeprecated( __METHOD__, '1.18' );
- return self::getLinkAttributesInternal( '', $class );
- }
-
- /**
* Get the appropriate HTML attributes to add to the "a" element of an interwiki link.
*
+ * @deprecated since 1.25
+ *
* @param string $title The title text for the link, URL-encoded (???) but
* not HTML-escaped
* @param string $unused Unused
@@ -64,6 +51,8 @@ class Linker {
static function getInterwikiLinkAttributes( $title, $unused = null, $class = 'external' ) {
global $wgContLang;
+ wfDeprecated( __METHOD__, '1.25' );
+
# @todo FIXME: We have a whole bunch of handling here that doesn't happen in
# getExternalLinkAttributes, why?
$title = urldecode( $title );
@@ -76,6 +65,8 @@ class Linker {
/**
* Get the appropriate HTML attributes to add to the "a" element of an internal link.
*
+ * @deprecated since 1.25
+ *
* @param string $title The title text for the link, URL-encoded (???) but
* not HTML-escaped
* @param string $unused Unused
@@ -83,6 +74,8 @@ class Linker {
* @return string
*/
static function getInternalLinkAttributes( $title, $unused = null, $class = '' ) {
+ wfDeprecated( __METHOD__, '1.25' );
+
$title = urldecode( $title );
$title = str_replace( '_', ' ', $title );
return self::getLinkAttributesInternal( $title, $class );
@@ -92,6 +85,8 @@ class Linker {
* Get the appropriate HTML attributes to add to the "a" element of an internal
* link, given the Title object for the page we want to link to.
*
+ * @deprecated since 1.25
+ *
* @param Title $nt
* @param string $unused Unused
* @param string $class The contents of the class attribute, default none
@@ -100,6 +95,8 @@ class Linker {
* @return string
*/
static function getInternalLinkAttributesObj( $nt, $unused = null, $class = '', $title = false ) {
+ wfDeprecated( __METHOD__, '1.25' );
+
if ( $title === false ) {
$title = $nt->getPrefixedText();
}
@@ -109,12 +106,16 @@ class Linker {
/**
* Common code for getLinkAttributesX functions
*
+ * @deprecated since 1.25
+ *
* @param string $title
* @param string $class
*
* @return string
*/
private static function getLinkAttributesInternal( $title, $class ) {
+ wfDeprecated( __METHOD__, '1.25' );
+
$title = htmlspecialchars( $title );
$class = htmlspecialchars( $class );
$r = '';
@@ -193,10 +194,9 @@ class Linker {
$target, $html = null, $customAttribs = array(), $query = array(), $options = array()
) {
if ( !$target instanceof Title ) {
- wfWarn( __METHOD__ . ': Requires $target to be a Title object.' );
+ wfWarn( __METHOD__ . ': Requires $target to be a Title object.', 2 );
return "<!-- ERROR -->$html";
}
- wfProfileIn( __METHOD__ );
if ( is_string( $query ) ) {
// some functions withing core using this still hand over query strings
@@ -208,9 +208,9 @@ class Linker {
$dummy = new DummyLinker; // dummy linker instance for bc on the hooks
$ret = null;
- if ( !wfRunHooks( 'LinkBegin', array( $dummy, $target, &$html,
- &$customAttribs, &$query, &$options, &$ret ) ) ) {
- wfProfileOut( __METHOD__ );
+ if ( !Hooks::run( 'LinkBegin',
+ array( $dummy, $target, &$html, &$customAttribs, &$query, &$options, &$ret ) )
+ ) {
return $ret;
}
@@ -218,15 +218,13 @@ class Linker {
$target = self::normaliseSpecialPage( $target );
# If we don't know whether the page exists, let's find out.
- wfProfileIn( __METHOD__ . '-checkPageExistence' );
- if ( !in_array( 'known', $options ) and !in_array( 'broken', $options ) ) {
+ if ( !in_array( 'known', $options ) && !in_array( 'broken', $options ) ) {
if ( $target->isKnown() ) {
$options[] = 'known';
} else {
$options[] = 'broken';
}
}
- wfProfileOut( __METHOD__ . '-checkPageExistence' );
$oldquery = array();
if ( in_array( "forcearticlepath", $options ) && $query ) {
@@ -249,11 +247,10 @@ class Linker {
}
$ret = null;
- if ( wfRunHooks( 'LinkEnd', array( $dummy, $target, $options, &$html, &$attribs, &$ret ) ) ) {
+ if ( Hooks::run( 'LinkEnd', array( $dummy, $target, $options, &$html, &$attribs, &$ret ) ) ) {
$ret = Html::rawElement( 'a', $attribs, $html );
}
- wfProfileOut( __METHOD__ );
return $ret;
}
@@ -278,7 +275,6 @@ class Linker {
* @return string
*/
private static function linkUrl( $target, $query, $options ) {
- wfProfileIn( __METHOD__ );
# We don't want to include fragments for broken links, because they
# generally make no sense.
if ( in_array( 'broken', $options ) && $target->hasFragment() ) {
@@ -304,7 +300,6 @@ class Linker {
}
$ret = $target->getLinkURL( $query, false, $proto );
- wfProfileOut( __METHOD__ );
return $ret;
}
@@ -318,12 +313,10 @@ class Linker {
* @return array
*/
private static function linkAttribs( $target, $attribs, $options ) {
- wfProfileIn( __METHOD__ );
global $wgUser;
$defaults = array();
if ( !in_array( 'noclasses', $options ) ) {
- wfProfileIn( __METHOD__ . '-getClasses' );
# Now build the classes.
$classes = array();
@@ -344,7 +337,6 @@ class Linker {
if ( $classes != array() ) {
$defaults['class'] = implode( ' ', $classes );
}
- wfProfileOut( __METHOD__ . '-getClasses' );
}
# Get a default title attribute.
@@ -364,11 +356,10 @@ class Linker {
foreach ( $merged as $key => $val ) {
# A false value suppresses the attribute, and we don't want the
# href attribute to be overridden.
- if ( $key != 'href' and $val !== false ) {
+ if ( $key != 'href' && $val !== false ) {
$ret[$key] = $val;
}
}
- wfProfileOut( __METHOD__ );
return $ret;
}
@@ -409,7 +400,7 @@ class Linker {
*/
public static function makeSelfLinkObj( $nt, $html = '', $query = '', $trail = '', $prefix = '' ) {
$ret = "<strong class=\"selflink\">{$prefix}{$html}</strong>{$trail}";
- if ( !wfRunHooks( 'SelfLinkBegin', array( $nt, &$html, &$trail, &$prefix, &$ret ) ) ) {
+ if ( !Hooks::run( 'SelfLinkBegin', array( $nt, &$html, &$trail, &$prefix, &$ret ) ) ) {
return $ret;
}
@@ -495,7 +486,7 @@ class Linker {
$alt = self::fnamePart( $url );
}
$img = '';
- $success = wfRunHooks( 'LinkerMakeExternalImage', array( &$url, &$alt, &$img ) );
+ $success = Hooks::run( 'LinkerMakeExternalImage', array( &$url, &$alt, &$img ) );
if ( !$success ) {
wfDebug( "Hook LinkerMakeExternalImage changed the output of external image "
. "with url {$url} and alt text {$alt} to {$img}\n", true );
@@ -549,7 +540,7 @@ class Linker {
) {
$res = null;
$dummy = new DummyLinker;
- if ( !wfRunHooks( 'ImageBeforeProduceHTML', array( &$dummy, &$title,
+ if ( !Hooks::run( 'ImageBeforeProduceHTML', array( &$dummy, &$title,
&$file, &$frameParams, &$handlerParams, &$time, &$res ) ) ) {
return $res;
}
@@ -931,7 +922,6 @@ class Linker {
}
global $wgEnableUploads, $wgUploadMissingFileUrl, $wgUploadNavigationUrl;
- wfProfileIn( __METHOD__ );
if ( $label == '' ) {
$label = $title->getPrefixedText();
}
@@ -944,19 +934,16 @@ class Linker {
$redir = RepoGroup::singleton()->getLocalRepo()->checkRedirect( $title );
if ( $redir ) {
- wfProfileOut( __METHOD__ );
return self::linkKnown( $title, $encLabel, array(), wfCgiToArray( $query ) );
}
$href = self::getUploadUrl( $title, $query );
- wfProfileOut( __METHOD__ );
return '<a href="' . htmlspecialchars( $href ) . '" class="new" title="' .
htmlspecialchars( $title->getPrefixedText(), ENT_QUOTES ) . '">' .
$encLabel . '</a>';
}
- wfProfileOut( __METHOD__ );
return self::linkKnown( $title, $encLabel, array(), wfCgiToArray( $query ) );
}
@@ -1029,7 +1016,7 @@ class Linker {
'title' => $alt
);
- if ( !wfRunHooks( 'LinkerMakeMediaLinkFile',
+ if ( !Hooks::run( 'LinkerMakeMediaLinkFile',
array( $title, $file, &$html, &$attribs, &$ret ) ) ) {
wfDebug( "Hook LinkerMakeMediaLinkFile changed the output of link "
. "with url {$url} and text {$html} to {$ret}\n", true );
@@ -1088,7 +1075,7 @@ class Linker {
}
$attribs['rel'] = Parser::getExternalLinkRel( $url, $title );
$link = '';
- $success = wfRunHooks( 'LinkerMakeExternalLink',
+ $success = Hooks::run( 'LinkerMakeExternalLink',
array( &$url, &$text, &$link, &$attribs, $linktype ) );
if ( !$success ) {
wfDebug( "Hook LinkerMakeExternalLink changed the output of link "
@@ -1174,10 +1161,10 @@ class Linker {
$items[] = self::emailLink( $userId, $userText );
}
- wfRunHooks( 'UserToolLinksEdit', array( $userId, $userText, &$items ) );
+ Hooks::run( 'UserToolLinksEdit', array( $userId, $userText, &$items ) );
if ( $items ) {
- return wfMessage( 'word-separator' )->plain()
+ return wfMessage( 'word-separator' )->escaped()
. '<span class="mw-usertoollinks">'
. wfMessage( 'parentheses' )->rawParams( $wgLang->pipeList( $items ) )->escaped()
. '</span>';
@@ -1264,7 +1251,6 @@ class Linker {
$userId = $rev->getUser( Revision::FOR_THIS_USER );
$userText = $rev->getUserText( Revision::FOR_THIS_USER );
$link = self::userLink( $userId, $userText )
- . wfMessage( 'word-separator' )->plain()
. self::userToolLinks( $userId, $userText );
} else {
$link = wfMessage( 'rev-deleted-user' )->escaped();
@@ -1293,7 +1279,6 @@ class Linker {
* @return mixed|string
*/
public static function formatComment( $comment, $title = null, $local = false ) {
- wfProfileIn( __METHOD__ );
# Sanitize text a bit:
$comment = str_replace( "\n", " ", $comment );
@@ -1304,12 +1289,12 @@ class Linker {
$comment = self::formatAutocomments( $comment, $title, $local );
$comment = self::formatLinksInComment( $comment, $title, $local );
- wfProfileOut( __METHOD__ );
return $comment;
}
/**
* Converts autogenerated comments in edit summaries into section links.
+ *
* The pattern for autogen comments is / * foo * /, which makes for
* some nasty regex.
* We look for all comments, match any text before and after the comment,
@@ -1322,16 +1307,30 @@ class Linker {
* @return string Formatted comment
*/
private static function formatAutocomments( $comment, $title = null, $local = false ) {
- return preg_replace_callback(
- '!(.*)/\*\s*(.*?)\s*\*/(.*)!',
- function ( $match ) use ( $title, $local ) {
+ // @todo $append here is something of a hack to preserve the status
+ // quo. Someone who knows more about bidi and such should decide
+ // (1) what sane rendering even *is* for an LTR edit summary on an RTL
+ // wiki, both when autocomments exist and when they don't, and
+ // (2) what markup will make that actually happen.
+ $append = '';
+ $comment = preg_replace_callback(
+ // To detect the presence of content before or after the
+ // auto-comment, we use capturing groups inside optional zero-width
+ // assertions. But older versions of PCRE can't directly make
+ // zero-width assertions optional, so wrap them in a non-capturing
+ // group.
+ '!(?:(?<=(.)))?/\*\s*(.*?)\s*\*/(?:(?=(.)))?!',
+ function ( $match ) use ( $title, $local, &$append ) {
global $wgLang;
- $pre = $match[1];
+ // Ensure all match positions are defined
+ $match += array( '', '', '', '' );
+
+ $pre = $match[1] !== '';
$auto = $match[2];
- $post = $match[3];
+ $post = $match[3] !== '';
$comment = null;
- wfRunHooks( 'FormatAutocomments', array( &$comment, $pre, $auto, $post, $title, $local ) );
+ Hooks::run( 'FormatAutocomments', array( &$comment, $pre, $auto, $post, $title, $local ) );
if ( $comment === null ) {
$link = '';
if ( $title ) {
@@ -1359,7 +1358,7 @@ class Linker {
}
if ( $pre ) {
# written summary $presep autocomment (summary /* section */)
- $pre .= wfMessage( 'autocomment-prefix' )->inContentLanguage()->escaped();
+ $pre = wfMessage( 'autocomment-prefix' )->inContentLanguage()->escaped();
}
if ( $post ) {
# autocomment $postsep written summary (/* section */ summary)
@@ -1367,12 +1366,14 @@ class Linker {
}
$auto = '<span class="autocomment">' . $auto . '</span>';
$comment = $pre . $link . $wgLang->getDirMark()
- . '<span dir="auto">' . $auto . $post . '</span>';
+ . '<span dir="auto">' . $auto;
+ $append .= '</span>';
}
return $comment;
},
$comment
);
+ return $comment . $append;
}
/**
@@ -1383,9 +1384,13 @@ class Linker {
* @param string $comment Text to format links in
* @param Title|null $title An optional title object used to links to sections
* @param bool $local Whether section links should refer to local page
+ * @param string|null $wikiId Id of the wiki to link to (if not the local wiki), as used by WikiMap
+ *
* @return string
*/
- public static function formatLinksInComment( $comment, $title = null, $local = false ) {
+ public static function formatLinksInComment(
+ $comment, $title = null, $local = false, $wikiId = null
+ ) {
return preg_replace_callback(
'/
\[\[
@@ -1399,7 +1404,7 @@ class Linker {
\]\]
([^[]*) # 3. link trail (the text up until the next link)
/x',
- function ( $match ) use ( $title, $local ) {
+ function ( $match ) use ( $title, $local, $wikiId ) {
global $wgContLang;
$medians = '(?:' . preg_quote( MWNamespace::getCanonicalName( NS_MEDIA ), '/' ) . '|';
@@ -1455,11 +1460,22 @@ class Linker {
$newTarget = clone ( $title );
$newTarget->setFragment( '#' . $target->getFragment() );
$target = $newTarget;
+
}
- $thelink = Linker::link(
- $target,
- $linkText . $inside
- ) . $trail;
+
+ if ( $wikiId !== null ) {
+ $thelink = Linker::makeExternalLink(
+ WikiMap::getForeignURL( $wikiId, $target->getFullText() ),
+ $linkText . $inside,
+ /* escape = */ false // Already escaped
+ ) . $trail;
+ } else {
+ $thelink = Linker::link(
+ $target,
+ $linkText . $inside
+ ) . $trail;
+ }
+
}
}
if ( $thelink ) {
@@ -1489,11 +1505,13 @@ class Linker {
# Foobar -- normal
# :Foobar -- override special treatment of prefix (images, language links)
# /Foobar -- convert to CurrentPage/Foobar
- # /Foobar/ -- convert to CurrentPage/Foobar, strip the initial / from text
+ # /Foobar/ -- convert to CurrentPage/Foobar, strip the initial and final / from text
# ../ -- convert to CurrentPage, from CurrentPage/CurrentSubPage
- # ../Foobar -- convert to CurrentPage/Foobar, from CurrentPage/CurrentSubPage
+ # ../Foobar -- convert to CurrentPage/Foobar,
+ # (from CurrentPage/CurrentSubPage)
+ # ../Foobar/ -- convert to CurrentPage/Foobar, use 'Foobar' as text
+ # (from CurrentPage/CurrentSubPage)
- wfProfileIn( __METHOD__ );
$ret = $target; # default return value is no change
# Some namespaces don't allow subpages,
@@ -1537,7 +1555,7 @@ class Linker {
$ret = implode( '/', array_slice( $exploded, 0, -$dotdotcount ) );
# / at the end means don't show full path
if ( substr( $nodotdot, -1, 1 ) === '/' ) {
- $nodotdot = substr( $nodotdot, 0, -1 );
+ $nodotdot = rtrim( $nodotdot, '/' );
if ( $text === '' ) {
$text = $nodotdot . $suffix;
}
@@ -1552,7 +1570,6 @@ class Linker {
}
}
- wfProfileOut( __METHOD__ );
return $ret;
}
@@ -1589,7 +1606,7 @@ class Linker {
* @return string HTML fragment
*/
public static function revComment( Revision $rev, $local = false, $isPublic = false ) {
- if ( $rev->getRawComment() == "" ) {
+ if ( $rev->getComment( Revision::RAW ) == "" ) {
return "";
}
if ( $rev->isDeleted( Revision::DELETED_COMMENT ) && $isPublic ) {
@@ -1807,7 +1824,7 @@ class Linker {
$inner = self::buildRollbackLink( $rev, $context, $editCount );
if ( !in_array( 'noBrackets', $options ) ) {
- $inner = $context->msg( 'brackets' )->rawParams( $inner )->plain();
+ $inner = $context->msg( 'brackets' )->rawParams( $inner )->escaped();
}
return '<span class="mw-rollback-link">' . $inner . '</span>';
@@ -1854,7 +1871,7 @@ class Linker {
$editCount = 0;
$moreRevs = false;
foreach ( $res as $row ) {
- if ( $rev->getRawUserText() != $row->rev_user_text ) {
+ if ( $rev->getUserText( Revision::RAW ) != $row->rev_user_text ) {
if ( $verify &&
( $row->rev_deleted & Revision::DELETED_TEXT
|| $row->rev_deleted & Revision::DELETED_USER
@@ -1892,7 +1909,7 @@ class Linker {
) {
global $wgShowRollbackEditCount, $wgMiserMode;
- // To config which pages are effected by miser mode
+ // To config which pages are affected by miser mode
$disableRollbackEditCountSpecialPage = array( 'Recentchanges', 'Watchlist' );
if ( $context === null ) {
@@ -1975,7 +1992,6 @@ class Linker {
$section = false, $more = null
) {
global $wgLang;
- wfProfileIn( __METHOD__ );
$outText = '';
if ( count( $templates ) > 0 ) {
@@ -2028,14 +2044,14 @@ class Linker {
if ( $titleObj->quickUserCan( 'edit' ) ) {
$editLink = self::link(
$titleObj,
- wfMessage( 'editlink' )->text(),
+ wfMessage( 'editlink' )->escaped(),
array(),
array( 'action' => 'edit' )
);
} else {
$editLink = self::link(
$titleObj,
- wfMessage( 'viewsourcelink' )->text(),
+ wfMessage( 'viewsourcelink' )->escaped(),
array(),
array( 'action' => 'edit' )
);
@@ -2055,7 +2071,6 @@ class Linker {
$outText .= '</ul>';
}
- wfProfileOut( __METHOD__ );
return $outText;
}
@@ -2067,7 +2082,6 @@ class Linker {
* @return string HTML output
*/
public static function formatHiddenCategories( $hiddencats ) {
- wfProfileIn( __METHOD__ );
$outText = '';
if ( count( $hiddencats ) > 0 ) {
@@ -2084,7 +2098,6 @@ class Linker {
}
$outText .= '</ul>';
}
- wfProfileOut( __METHOD__ );
return $outText;
}
@@ -2113,7 +2126,6 @@ class Linker {
* escape), or false for no title attribute
*/
public static function titleAttrib( $name, $options = null ) {
- wfProfileIn( __METHOD__ );
$message = wfMessage( "tooltip-$name" );
@@ -2142,7 +2154,6 @@ class Linker {
}
}
- wfProfileOut( __METHOD__ );
return $tooltip;
}
@@ -2162,7 +2173,6 @@ class Linker {
if ( isset( self::$accesskeycache[$name] ) ) {
return self::$accesskeycache[$name];
}
- wfProfileIn( __METHOD__ );
$message = wfMessage( "accesskey-$name" );
@@ -2178,7 +2188,6 @@ class Linker {
}
}
- wfProfileOut( __METHOD__ );
self::$accesskeycache[$name] = $accesskey;
return self::$accesskeycache[$name];
}
@@ -2286,7 +2295,6 @@ class Linker {
static function makeLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
wfDeprecated( __METHOD__, '1.21' );
- wfProfileIn( __METHOD__ );
$query = wfCgiToArray( $query );
list( $inside, $trail ) = self::splitTrail( $trail );
if ( $text === '' ) {
@@ -2295,7 +2303,6 @@ class Linker {
$ret = self::link( $nt, "$prefix$text$inside", array(), $query ) . $trail;
- wfProfileOut( __METHOD__ );
return $ret;
}
@@ -2320,8 +2327,6 @@ class Linker {
) {
wfDeprecated( __METHOD__, '1.21' );
- wfProfileIn( __METHOD__ );
-
if ( $text == '' ) {
$text = self::linkText( $title );
}
@@ -2335,7 +2340,6 @@ class Linker {
$ret = self::link( $title, "$prefix$text$inside", $attribs, $query,
array( 'known', 'noclasses' ) ) . $trail;
- wfProfileOut( __METHOD__ );
return $ret;
}
diff --git a/includes/MWNamespace.php b/includes/MWNamespace.php
index 392f5582..bd685514 100644
--- a/includes/MWNamespace.php
+++ b/includes/MWNamespace.php
@@ -72,7 +72,7 @@ class MWNamespace {
/**
* @since 1.20
*/
- wfRunHooks( 'NamespaceIsMovable', array( $index, &$result ) );
+ Hooks::run( 'NamespaceIsMovable', array( $index, &$result ) );
return $result;
}
@@ -213,7 +213,7 @@ class MWNamespace {
if ( is_array( $wgExtraNamespaces ) ) {
$namespaces += $wgExtraNamespaces;
}
- wfRunHooks( 'CanonicalNamespaces', array( &$namespaces ) );
+ Hooks::run( 'CanonicalNamespaces', array( &$namespaces ) );
}
return $namespaces;
}
diff --git a/includes/MWTimestamp.php b/includes/MWTimestamp.php
index 26f5e543..ea91470e 100644
--- a/includes/MWTimestamp.php
+++ b/includes/MWTimestamp.php
@@ -182,6 +182,11 @@ class MWTimestamp {
$output .= ' GMT';
}
+ if ( $style == TS_MW && strlen( $output ) !== 14 ) {
+ throw new TimestampException( __METHOD__ . ': The timestamp cannot be represented in ' .
+ 'the specified format' );
+ }
+
return $output;
}
@@ -221,7 +226,7 @@ class MWTimestamp {
$offsetRel = $relativeTo->offsetForUser( $user );
$ts = '';
- if ( wfRunHooks( 'GetHumanTimestamp', array( &$ts, $this, $relativeTo, $user, $lang ) ) ) {
+ if ( Hooks::run( 'GetHumanTimestamp', array( &$ts, $this, $relativeTo, $user, $lang ) ) ) {
$ts = $lang->getHumanTimestamp( $this, $relativeTo, $user );
}
@@ -326,7 +331,7 @@ class MWTimestamp {
$ts = '';
$diff = $this->diff( $relativeTo );
- if ( wfRunHooks(
+ if ( Hooks::run(
'GetRelativeTimestamp',
array( &$ts, &$diff, $this, $relativeTo, $user, $lang )
) ) {
diff --git a/includes/MagicWord.php b/includes/MagicWord.php
index 4d17298b..186821de 100644
--- a/includes/MagicWord.php
+++ b/includes/MagicWord.php
@@ -172,7 +172,6 @@ class MagicWord {
'directionmark',
'contentlanguage',
'numberofadmins',
- 'numberofviews',
'cascadingsources',
);
@@ -215,7 +214,6 @@ class MagicWord {
'localtimestamp' => 3600,
'pagesinnamespace' => 3600,
'numberofadmins' => 3600,
- 'numberofviews' => 3600,
'numberingroup' => 3600,
);
@@ -275,7 +273,7 @@ class MagicWord {
static function getVariableIDs() {
if ( !self::$mVariableIDsInitialised ) {
# Get variable IDs
- wfRunHooks( 'MagicWordwgVariableIDs', array( &self::$mVariableIDs ) );
+ Hooks::run( 'MagicWordwgVariableIDs', array( &self::$mVariableIDs ) );
self::$mVariableIDsInitialised = true;
}
return self::$mVariableIDs;
@@ -310,7 +308,7 @@ class MagicWord {
*/
static function getDoubleUnderscoreArray() {
if ( is_null( self::$mDoubleUnderscoreArray ) ) {
- wfRunHooks( 'GetDoubleUnderscoreIDs', array( &self::$mDoubleUnderscoreIDs ) );
+ Hooks::run( 'GetDoubleUnderscoreIDs', array( &self::$mDoubleUnderscoreIDs ) );
self::$mDoubleUnderscoreArray = new MagicWordArray( self::$mDoubleUnderscoreIDs );
}
return self::$mDoubleUnderscoreArray;
@@ -332,15 +330,12 @@ class MagicWord {
*/
function load( $id ) {
global $wgContLang;
- wfProfileIn( __METHOD__ );
$this->mId = $id;
$wgContLang->getMagic( $this );
if ( !$this->mSynonyms ) {
$this->mSynonyms = array( 'brionmademeputthishere' );
- wfProfileOut( __METHOD__ );
throw new MWException( "Error: invalid magic word '$id'" );
}
- wfProfileOut( __METHOD__ );
}
/**
@@ -657,7 +652,7 @@ class MagicWord {
* This method uses the php feature to do several replacements at the same time,
* thereby gaining some efficiency. The result is placed in the out variable
* $result. The return value is true if something was replaced.
- * @todo Should this be static? It doesn't seem to be used at all
+ * @deprecated since 1.25, unused
*
* @param array $magicarr
* @param string $subject
@@ -666,6 +661,7 @@ class MagicWord {
* @return bool
*/
function replaceMultiple( $magicarr, $subject, &$result ) {
+ wfDeprecated( __METHOD__, '1.25' );
$search = array();
$replace = array();
foreach ( $magicarr as $id => $replacement ) {
diff --git a/includes/MediaWiki.php b/includes/MediaWiki.php
index 402494ec..ec2f40f6 100644
--- a/includes/MediaWiki.php
+++ b/includes/MediaWiki.php
@@ -20,10 +20,10 @@
* @file
*/
+use MediaWiki\Logger\LoggerFactory;
+
/**
* The MediaWiki class is the helper class for the index.php entry point.
- *
- * @internal documentation reviewed 15 Mar 2010
*/
class MediaWiki {
/**
@@ -59,7 +59,7 @@ class MediaWiki {
$request = $this->context->getRequest();
$curid = $request->getInt( 'curid' );
$title = $request->getVal( 'title' );
- $action = $request->getVal( 'action', 'view' );
+ $action = $request->getVal( 'action' );
if ( $request->getCheck( 'search' ) ) {
// Compatibility with old search URLs which didn't use Special:Search
@@ -121,7 +121,7 @@ class MediaWiki {
* @return Title
*/
public function getTitle() {
- if ( $this->context->getTitle() === null ) {
+ if ( !$this->context->hasTitle() ) {
$this->context->setTitle( $this->parseTitle() );
}
return $this->context->getTitle();
@@ -157,8 +157,6 @@ class MediaWiki {
private function performRequest() {
global $wgTitle;
- wfProfileIn( __METHOD__ );
-
$request = $this->context->getRequest();
$requestTitle = $title = $this->context->getTitle();
$output = $this->context->getOutput();
@@ -169,14 +167,13 @@ class MediaWiki {
}
$unused = null; // To pass it by reference
- wfRunHooks( 'BeforeInitialize', array( &$title, &$unused, &$output, &$user, $request, $this ) );
+ Hooks::run( 'BeforeInitialize', array( &$title, &$unused, &$output, &$user, $request, $this ) );
// Invalid titles. Bug 21776: The interwikis must redirect even if the page name is empty.
if ( is_null( $title ) || ( $title->getDBkey() == '' && !$title->isExternal() )
|| $title->isSpecial( 'Badtitle' )
) {
$this->context->setTitle( SpecialPage::getTitleFor( 'Badtitle' ) );
- wfProfileOut( __METHOD__ );
throw new BadTitleError();
}
@@ -201,12 +198,9 @@ class MediaWiki {
$this->context->setTitle( $badTitle );
$wgTitle = $badTitle;
- wfProfileOut( __METHOD__ );
throw new PermissionsError( 'read', $permErrors );
}
- $pageView = false; // was an article or special page viewed?
-
// Interwiki redirects
if ( $title->isExternal() ) {
$rdfrom = $request->getVal( 'rdfrom' );
@@ -225,7 +219,6 @@ class MediaWiki {
$output->redirect( $url, 301 );
} else {
$this->context->setTitle( SpecialPage::getTitleFor( 'Badtitle' ) );
- wfProfileOut( __METHOD__ );
throw new BadTitleError();
}
// Redirect loops, no title in URL, $wgUsePathInfo URLs, and URLs with a variant
@@ -233,7 +226,7 @@ class MediaWiki {
&& ( $request->getVal( 'title' ) === null
|| $title->getPrefixedDBkey() != $request->getVal( 'title' ) )
&& !count( $request->getValueNames( array( 'action', 'title' ) ) )
- && wfRunHooks( 'TestCanonicalRedirect', array( $request, $title, $output ) )
+ && Hooks::run( 'TestCanonicalRedirect', array( $request, $title, $output ) )
) {
if ( $title->isSpecialPage() ) {
list( $name, $subpage ) = SpecialPageFactory::resolveAlias( $title->getDBkey() );
@@ -270,7 +263,6 @@ class MediaWiki {
}
// Special pages
} elseif ( NS_SPECIAL == $title->getNamespace() ) {
- $pageView = true;
// Actions that need to be made when we have a special pages
SpecialPageFactory::executePath( $title, $this->context );
} else {
@@ -278,23 +270,14 @@ class MediaWiki {
// may be a redirect to another article or URL.
$article = $this->initializeArticle();
if ( is_object( $article ) ) {
- $pageView = true;
$this->performAction( $article, $requestTitle );
} elseif ( is_string( $article ) ) {
$output->redirect( $article );
} else {
- wfProfileOut( __METHOD__ );
throw new MWException( "Shouldn't happen: MediaWiki::initializeArticle()"
. " returned neither an object nor a URL" );
}
}
-
- if ( $pageView ) {
- // Promote user to any groups they meet the criteria for
- $user->addAutopromoteOnceGroups( 'onView' );
- }
-
- wfProfileOut( __METHOD__ );
}
/**
@@ -304,7 +287,6 @@ class MediaWiki {
* @return mixed An Article, or a string to redirect to another URL
*/
private function initializeArticle() {
- wfProfileIn( __METHOD__ );
$title = $this->context->getTitle();
if ( $this->context->canUseWikiPage() ) {
@@ -322,7 +304,6 @@ class MediaWiki {
// NS_MEDIAWIKI has no redirects.
// It is also used for CSS/JS, so performance matters here...
if ( $title->getNamespace() == NS_MEDIAWIKI ) {
- wfProfileOut( __METHOD__ );
return $article;
}
@@ -342,7 +323,7 @@ class MediaWiki {
// Give extensions a change to ignore/handle redirects as needed
$ignoreRedirect = $target = false;
- wfRunHooks( 'InitializeArticleMaybeRedirect',
+ Hooks::run( 'InitializeArticleMaybeRedirect',
array( &$title, &$request, &$ignoreRedirect, &$target, &$article ) );
// Follow redirects only for... redirects.
@@ -353,7 +334,6 @@ class MediaWiki {
if ( is_string( $target ) ) {
if ( !$this->config->get( 'DisableHardRedirects' ) ) {
// we'll need to redirect
- wfProfileOut( __METHOD__ );
return $target;
}
}
@@ -374,7 +354,6 @@ class MediaWiki {
}
}
- wfProfileOut( __METHOD__ );
return $article;
}
@@ -385,17 +364,15 @@ class MediaWiki {
* @param Title $requestTitle The original title, before any redirects were applied
*/
private function performAction( Page $page, Title $requestTitle ) {
- wfProfileIn( __METHOD__ );
$request = $this->context->getRequest();
$output = $this->context->getOutput();
$title = $this->context->getTitle();
$user = $this->context->getUser();
- if ( !wfRunHooks( 'MediaWikiPerformAction',
+ if ( !Hooks::run( 'MediaWikiPerformAction',
array( $output, $page, $title, $user, $request, $this ) )
) {
- wfProfileOut( __METHOD__ );
return;
}
@@ -406,22 +383,24 @@ class MediaWiki {
if ( $action instanceof Action ) {
# Let Squid cache things if we can purge them.
if ( $this->config->get( 'UseSquid' ) &&
- in_array( $request->getFullRequestURL(), $requestTitle->getSquidURLs() )
+ in_array(
+ // Use PROTO_INTERNAL because that's what getSquidURLs() uses
+ wfExpandUrl( $request->getRequestURL(), PROTO_INTERNAL ),
+ $requestTitle->getSquidURLs()
+ )
) {
$output->setSquidMaxage( $this->config->get( 'SquidMaxage' ) );
}
$action->show();
- wfProfileOut( __METHOD__ );
return;
}
- if ( wfRunHooks( 'UnknownAction', array( $request->getVal( 'action', 'view' ), $page ) ) ) {
+ if ( Hooks::run( 'UnknownAction', array( $request->getVal( 'action', 'view' ), $page ) ) ) {
$output->setStatusCode( 404 );
$output->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
}
- wfProfileOut( __METHOD__ );
}
/**
@@ -446,7 +425,7 @@ class MediaWiki {
$this->triggerJobs();
$this->restInPeace();
} catch ( Exception $e ) {
- MWExceptionHandler::handle( $e );
+ MWExceptionHandler::handleException( $e );
}
}
@@ -456,7 +435,6 @@ class MediaWiki {
* @return bool
*/
private function checkMaxLag() {
- wfProfileIn( __METHOD__ );
$maxLag = $this->context->getRequest()->getVal( 'maxlag' );
if ( !is_null( $maxLag ) ) {
list( $host, $lag ) = wfGetLB()->getMaxLag();
@@ -472,33 +450,28 @@ class MediaWiki {
echo "Waiting for a database server: $lag seconds lagged\n";
}
- wfProfileOut( __METHOD__ );
-
exit;
}
}
- wfProfileOut( __METHOD__ );
return true;
}
private function main() {
global $wgTitle;
- wfProfileIn( __METHOD__ );
-
$request = $this->context->getRequest();
// Send Ajax requests to the Ajax dispatcher.
- if ( $this->config->get( 'UseAjax' ) && $request->getVal( 'action', 'view' ) == 'ajax' ) {
-
+ if ( $this->config->get( 'UseAjax' ) && $request->getVal( 'action' ) === 'ajax' ) {
// Set a dummy title, because $wgTitle == null might break things
- $title = Title::makeTitle( NS_MAIN, 'AJAX' );
+ $title = Title::makeTitle( NS_SPECIAL, 'Badtitle/performing an AJAX call in '
+ . __METHOD__
+ );
$this->context->setTitle( $title );
$wgTitle = $title;
$dispatcher = new AjaxDispatcher( $this->config );
$dispatcher->performAction( $this->context->getUser() );
- wfProfileOut( __METHOD__ );
return;
}
@@ -508,6 +481,20 @@ class MediaWiki {
$action = $this->getAction();
$wgTitle = $title;
+ $trxProfiler = Profiler::instance()->getTransactionProfiler();
+ $trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) );
+
+ // Aside from rollback, master queries should not happen on GET requests.
+ // Periodic or "in passing" updates on GET should use the job queue.
+ if ( !$request->wasPosted()
+ && in_array( $action, array( 'view', 'edit', 'history' ) )
+ ) {
+ $trxProfiler->setExpectation( 'masterConns', 0, __METHOD__ );
+ $trxProfiler->setExpectation( 'writes', 0, __METHOD__ );
+ } else {
+ $trxProfiler->setExpectation( 'maxAffected', 500, __METHOD__ );
+ }
+
// If the user has forceHTTPS set to true, or if the user
// is in a group requiring HTTPS, or if they have the HTTPS
// preference set, redirect them to HTTPS.
@@ -530,7 +517,7 @@ class MediaWiki {
$redirUrl = preg_replace( '#^http://#', 'https://', $oldUrl );
// ATTENTION: This hook is likely to be removed soon due to overall design of the system.
- if ( wfRunHooks( 'BeforeHttpsRedirect', array( $this->context, &$redirUrl ) ) ) {
+ if ( Hooks::run( 'BeforeHttpsRedirect', array( $this->context, &$redirUrl ) ) ) {
if ( $request->wasPosted() ) {
// This is weird and we'd hope it almost never happens. This
@@ -544,20 +531,18 @@ class MediaWiki {
wfDebugLog( 'RedirectedPosts', "Redirected from HTTP to HTTPS: $oldUrl" );
}
// Setup dummy Title, otherwise OutputPage::redirect will fail
- $title = Title::newFromText( NS_MAIN, 'REDIR' );
+ $title = Title::newFromText( 'REDIR', NS_MAIN );
$this->context->setTitle( $title );
$output = $this->context->getOutput();
// Since we only do this redir to change proto, always send a vary header
$output->addVaryHeader( 'X-Forwarded-Proto' );
$output->redirect( $redirUrl );
$output->output();
- wfProfileOut( __METHOD__ );
return;
}
}
if ( $this->config->get( 'UseFileCache' ) && $title->getNamespace() >= 0 ) {
- wfProfileIn( 'main-try-filecache' );
if ( HTMLFileCache::useFileCache( $this->context ) ) {
// Try low-level file cache hit
$cache = new HTMLFileCache( $title, $action );
@@ -572,12 +557,9 @@ class MediaWiki {
$this->context->getWikiPage()->doViewUpdates( $this->context->getUser() );
// Tell OutputPage that output is taken care of
$this->context->getOutput()->disable();
- wfProfileOut( 'main-try-filecache' );
- wfProfileOut( __METHOD__ );
return;
}
}
- wfProfileOut( 'main-try-filecache' );
}
// Actually do the work of the request and build up any output
@@ -593,13 +575,16 @@ class MediaWiki {
// Output everything!
$this->context->getOutput()->output();
- wfProfileOut( __METHOD__ );
}
/**
* Ends this task peacefully
*/
public function restInPeace() {
+ // Ignore things like master queries/connections on GET requests
+ // as long as they are in deferred updates (which catch errors).
+ Profiler::instance()->getTransactionProfiler()->resetExpectations();
+
// Do any deferred jobs
DeferredUpdates::doUpdates( 'commit' );
@@ -627,8 +612,6 @@ class MediaWiki {
return; // recursion guard
}
- $section = new ProfileSection( __METHOD__ );
-
if ( $jobRunRate < 1 ) {
$max = mt_getrandmax();
if ( mt_rand( 0, $max ) > $max * $jobRunRate ) {
@@ -639,9 +622,11 @@ class MediaWiki {
$n = intval( $jobRunRate );
}
+ $runJobsLogger = LoggerFactory::getInstance( 'runJobs' );
+
if ( !$this->config->get( 'RunJobsAsync' ) ) {
// Fall back to running the job here while the user waits
- $runner = new JobRunner();
+ $runner = new JobRunner( $runJobsLogger );
$runner->run( array( 'maxJobs' => $n ) );
return;
}
@@ -674,29 +659,34 @@ class MediaWiki {
);
wfRestoreWarnings();
if ( !$sock ) {
- wfDebugLog( 'runJobs', "Failed to start cron API (socket error $errno): $errstr\n" );
+ $runJobsLogger->error( "Failed to start cron API (socket error $errno): $errstr" );
// Fall back to running the job here while the user waits
- $runner = new JobRunner();
+ $runner = new JobRunner( $runJobsLogger );
$runner->run( array( 'maxJobs' => $n ) );
return;
}
$url = wfAppendQuery( wfScript( 'index' ), $query );
- $req = "POST $url HTTP/1.1\r\nHost: {$info['host']}\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n";
+ $req = (
+ "POST $url HTTP/1.1\r\n" .
+ "Host: {$info['host']}\r\n" .
+ "Connection: Close\r\n" .
+ "Content-Length: 0\r\n\r\n"
+ );
- wfDebugLog( 'runJobs', "Running $n job(s) via '$url'\n" );
+ $runJobsLogger->info( "Running $n job(s) via '$url'" );
// Send a cron API request to be performed in the background.
// Give up if this takes too long to send (which should be rare).
stream_set_timeout( $sock, 1 );
$bytes = fwrite( $sock, $req );
if ( $bytes !== strlen( $req ) ) {
- wfDebugLog( 'runJobs', "Failed to start cron API (socket write error)\n" );
+ $runJobsLogger->error( "Failed to start cron API (socket write error)" );
} else {
// Do not wait for the response (the script should handle client aborts).
// Make sure that we don't close before that script reaches ignore_user_abort().
$status = fgets( $sock );
if ( !preg_match( '#^HTTP/\d\.\d 202 #', $status ) ) {
- wfDebugLog( 'runJobs', "Failed to start cron API: received '$status'\n" );
+ $runJobsLogger->error( "Failed to start cron API: received '$status'" );
}
}
fclose( $sock );
diff --git a/includes/MediaWikiVersionFetcher.php b/includes/MediaWikiVersionFetcher.php
index 439e53f4..943bc9fc 100644
--- a/includes/MediaWikiVersionFetcher.php
+++ b/includes/MediaWikiVersionFetcher.php
@@ -4,7 +4,6 @@
* Provides access to MediaWiki's version without requiring MediaWiki (or anything else)
* being loaded first.
*
- * @licence GNU GPL v2+
* @author Jeroen De Dauw < jeroendedauw@gmail.com >
*/
class MediaWikiVersionFetcher {
@@ -19,7 +18,7 @@ class MediaWikiVersionFetcher {
$defaultSettings = file_get_contents( __DIR__ . '/DefaultSettings.php' );
$matches = array();
- preg_match( "/wgVersion = '([-0-9a-zA-Z\.]+)';/", $defaultSettings, $matches );
+ preg_match( "/wgVersion = '([0-9a-zA-Z\.\-]+)';/", $defaultSettings, $matches );
if ( count( $matches ) !== 2 ) {
throw new RuntimeException( 'Could not extract the MediaWiki version from DefaultSettings.php' );
diff --git a/includes/Message.php b/includes/Message.php
index 4df0d809..134af0ed 100644
--- a/includes/Message.php
+++ b/includes/Message.php
@@ -156,7 +156,7 @@
*
* @since 1.17
*/
-class Message {
+class Message implements MessageSpecifier {
/**
* In which language to get this message. True, which is the default,
@@ -249,7 +249,7 @@ class Message {
$this->key = reset( $this->keysToTry );
$this->parameters = array_values( $params );
- $this->language = $language ? $language : $wgLang;
+ $this->language = $language ?: $wgLang;
}
/**
@@ -276,7 +276,7 @@ class Message {
* Returns the message key.
*
* If a list of multiple possible keys was supplied to the constructor, this method may
- * return any of these keys. After the message ahs been fetched, this method will return
+ * return any of these keys. After the message has been fetched, this method will return
* the key that was actually used to fetch the message.
*
* @since 1.21
@@ -541,6 +541,30 @@ class Message {
}
/**
+ * Add parameters that are plaintext and will be passed through without
+ * the content being evaluated. Plaintext parameters are not valid as
+ * arguments to parser functions. This differs from self::rawParams in
+ * that the Message class handles escaping to match the output format.
+ *
+ * @since 1.25
+ *
+ * @param string|string[] $param,... plaintext parameters, or a single argument that is
+ * an array of plaintext parameters.
+ *
+ * @return Message $this
+ */
+ public function plaintextParams( /*...*/ ) {
+ $params = func_get_args();
+ if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+ $params = $params[0];
+ }
+ foreach ( $params as $param ) {
+ $this->parameters[] = self::plaintextParam( $param );
+ }
+ return $this;
+ }
+
+ /**
* Set the language and the title from a context object
*
* @since 1.19
@@ -674,11 +698,10 @@ class Message {
$string = $this->fetchMessage();
if ( $string === false ) {
- $key = htmlspecialchars( $this->key );
- if ( $this->format === 'plain' ) {
- return '<' . $key . '>';
+ if ( $this->format === 'plain' || $this->format === 'text' ) {
+ return '<' . $this->key . '>';
}
- return '&lt;' . $key . '&gt;';
+ return '&lt;' . htmlspecialchars( $this->key ) . '&gt;';
}
# Replace $* with a list of parameters for &uselang=qqx.
@@ -735,10 +758,10 @@ class Message {
// Doh! Cause a fatal error after all?
}
- if ( $this->format === 'plain' ) {
+ if ( $this->format === 'plain' || $this->format === 'text' ) {
return '<' . $this->key . '>';
}
- return '&lt;' . $this->key . '&gt;';
+ return '&lt;' . htmlspecialchars( $this->key ) . '&gt;';
}
}
@@ -917,6 +940,17 @@ class Message {
}
/**
+ * @since 1.25
+ *
+ * @param string $plaintext
+ *
+ * @return string[] Array with a single "plaintext" key.
+ */
+ public static function plaintextParam( $plaintext ) {
+ return array( 'plaintext' => $plaintext );
+ }
+
+ /**
* Substitutes any parameters into the message text.
*
* @since 1.17
@@ -965,6 +999,8 @@ class Message {
return array( 'before', $this->language->formatSize( $param['size'] ) );
} elseif ( isset( $param['bitrate'] ) ) {
return array( 'before', $this->language->formatBitrate( $param['bitrate'] ) );
+ } elseif ( isset( $param['plaintext'] ) ) {
+ return array( 'after', $this->formatPlaintext( $param['plaintext'] ) );
} else {
$warning = 'Invalid parameter for message "' . $this->getKey() . '": ' .
htmlspecialchars( serialize( $param ) );
@@ -1050,6 +1086,31 @@ class Message {
return $this->message;
}
+ /**
+ * Formats a message parameter wrapped with 'plaintext'. Ensures that
+ * the entire string is displayed unchanged when displayed in the output
+ * format.
+ *
+ * @since 1.25
+ *
+ * @param string $plaintext String to ensure plaintext output of
+ *
+ * @return string Input plaintext encoded for output to $this->format
+ */
+ protected function formatPlaintext( $plaintext ) {
+ switch ( $this->format ) {
+ case 'text':
+ case 'plain':
+ return $plaintext;
+
+ case 'parse':
+ case 'block-parse':
+ case 'escaped':
+ default:
+ return htmlspecialchars( $plaintext, ENT_QUOTES );
+
+ }
+ }
}
/**
diff --git a/includes/MessageBlobStore.php b/includes/MessageBlobStore.php
index e3b4dbe8..011cae66 100644
--- a/includes/MessageBlobStore.php
+++ b/includes/MessageBlobStore.php
@@ -36,15 +36,12 @@ class MessageBlobStore {
* Get the singleton instance
*
* @since 1.24
+ * @deprecated since 1.25
* @return MessageBlobStore
*/
public static function getInstance() {
- static $instance = null;
- if ( $instance === null ) {
- $instance = new self;
- }
-
- return $instance;
+ wfDeprecated( __METHOD__, '1.25' );
+ return new self;
}
/**
@@ -56,9 +53,7 @@ class MessageBlobStore {
* @return array An array mapping module names to message blobs
*/
public function get( ResourceLoader $resourceLoader, $modules, $lang ) {
- wfProfileIn( __METHOD__ );
if ( !count( $modules ) ) {
- wfProfileOut( __METHOD__ );
return array();
}
// Try getting from the DB first
@@ -73,7 +68,6 @@ class MessageBlobStore {
}
}
- wfProfileOut( __METHOD__ );
return $blobs;
}
@@ -130,7 +124,7 @@ class MessageBlobStore {
);
}
}
- } catch ( Exception $e ) {
+ } catch ( DBError $e ) {
wfDebug( __METHOD__ . " failed to update DB: $e\n" );
}
return $blob;
diff --git a/includes/MimeMagic.php b/includes/MimeMagic.php
index bfd60111..ebe98a3c 100644
--- a/includes/MimeMagic.php
+++ b/includes/MimeMagic.php
@@ -200,7 +200,7 @@ class MimeMagic {
global $IP;
# Allow media handling extensions adding MIME-types and MIME-info
- wfRunHooks( 'MimeMagicInit', array( $this ) );
+ Hooks::run( 'MimeMagicInit', array( $this ) );
$types = MM_WELL_KNOWN_MIME_TYPES;
@@ -210,7 +210,7 @@ class MimeMagic {
}
if ( $mimeTypeFile ) {
- if ( is_file( $mimeTypeFile ) and is_readable( $mimeTypeFile ) ) {
+ if ( is_file( $mimeTypeFile ) && is_readable( $mimeTypeFile ) ) {
wfDebug( __METHOD__ . ": loading mime types from $mimeTypeFile\n" );
$types .= "\n";
$types .= file_get_contents( $mimeTypeFile );
@@ -218,7 +218,7 @@ class MimeMagic {
wfDebug( __METHOD__ . ": can't load mime types from $mimeTypeFile\n" );
}
} else {
- wfDebug( __METHOD__ . ": no mime types file defined, using build-ins only.\n" );
+ wfDebug( __METHOD__ . ": no mime types file defined, using built-ins only.\n" );
}
$types .= "\n" . $this->mExtraTypes;
@@ -287,7 +287,7 @@ class MimeMagic {
$info = MM_WELL_KNOWN_MIME_INFO;
if ( $mimeInfoFile ) {
- if ( is_file( $mimeInfoFile ) and is_readable( $mimeInfoFile ) ) {
+ if ( is_file( $mimeInfoFile ) && is_readable( $mimeInfoFile ) ) {
wfDebug( __METHOD__ . ": loading mime info from $mimeInfoFile\n" );
$info .= "\n";
$info .= file_get_contents( $mimeInfoFile );
@@ -295,7 +295,7 @@ class MimeMagic {
wfDebug( __METHOD__ . ": can't load mime info from $mimeInfoFile\n" );
}
} else {
- wfDebug( __METHOD__ . ": no mime info file defined, using build-ins only.\n" );
+ wfDebug( __METHOD__ . ": no mime info file defined, using built-ins only.\n" );
}
$info .= "\n" . $this->mExtraInfo;
@@ -569,7 +569,7 @@ class MimeMagic {
}
# Media handling extensions can improve the MIME detected
- wfRunHooks( 'MimeMagicImproveFromExtension', array( $this, $ext, &$mime ) );
+ Hooks::run( 'MimeMagicImproveFromExtension', array( $this, $ext, &$mime ) );
if ( isset( $this->mMimeTypeAliases[$mime] ) ) {
$mime = $this->mMimeTypeAliases[$mime];
@@ -802,7 +802,7 @@ class MimeMagic {
# people will hopefully nag and submit patches :)
$mime = false;
# Some strings by reference for performance - assuming well-behaved hooks
- wfRunHooks(
+ Hooks::run(
'MimeMagicGuessFromContent',
array( $this, &$head, &$tail, $file, &$mime )
);
diff --git a/includes/MovePage.php b/includes/MovePage.php
index fdece8d5..de7da3f9 100644
--- a/includes/MovePage.php
+++ b/includes/MovePage.php
@@ -42,6 +42,188 @@ class MovePage {
$this->newTitle = $newTitle;
}
+ public function checkPermissions( User $user, $reason ) {
+ $status = new Status();
+
+ $errors = wfMergeErrorArrays(
+ $this->oldTitle->getUserPermissionsErrors( 'move', $user ),
+ $this->oldTitle->getUserPermissionsErrors( 'edit', $user ),
+ $this->newTitle->getUserPermissionsErrors( 'move-target', $user ),
+ $this->newTitle->getUserPermissionsErrors( 'edit', $user )
+ );
+
+ // Convert into a Status object
+ if ( $errors ) {
+ foreach ( $errors as $error ) {
+ call_user_func_array( array( $status, 'fatal' ), $error );
+ }
+ }
+
+ if ( EditPage::matchSummarySpamRegex( $reason ) !== false ) {
+ // This is kind of lame, won't display nice
+ $status->fatal( 'spamprotectiontext' );
+ }
+
+ # The move is allowed only if (1) the target doesn't exist, or
+ # (2) the target is a redirect to the source, and has no history
+ # (so we can undo bad moves right after they're done).
+
+ if ( $this->newTitle->getArticleID() ) { # Target exists; check for validity
+ if ( !$this->isValidMoveTarget() ) {
+ $status->fatal( 'articleexists' );
+ }
+ } else {
+ $tp = $this->newTitle->getTitleProtection();
+ if ( $tp !== false ) {
+ if ( !$user->isAllowed( $tp['permission'] ) ) {
+ $status->fatal( 'cantmove-titleprotected' );
+ }
+ }
+ }
+
+ Hooks::run( 'MovePageCheckPermissions',
+ array( $this->oldTitle, $this->newTitle, $user, $reason, $status )
+ );
+
+ return $status;
+ }
+
+ /**
+ * Does various sanity checks that the move is
+ * valid. Only things based on the two titles
+ * should be checked here.
+ *
+ * @return Status
+ */
+ public function isValidMove() {
+ global $wgContentHandlerUseDB;
+ $status = new Status();
+
+ if ( $this->oldTitle->equals( $this->newTitle ) ) {
+ $status->fatal( 'selfmove' );
+ }
+ if ( !$this->oldTitle->isMovable() ) {
+ $status->fatal( 'immobile-source-namespace', $this->oldTitle->getNsText() );
+ }
+ if ( $this->newTitle->isExternal() ) {
+ $status->fatal( 'immobile-target-namespace-iw' );
+ }
+ if ( !$this->newTitle->isMovable() ) {
+ $status->fatal( 'immobile-target-namespace', $this->newTitle->getNsText() );
+ }
+
+ $oldid = $this->oldTitle->getArticleID();
+
+ if ( strlen( $this->newTitle->getDBkey() ) < 1 ) {
+ $status->fatal( 'articleexists' );
+ }
+ if (
+ ( $this->oldTitle->getDBkey() == '' ) ||
+ ( !$oldid ) ||
+ ( $this->newTitle->getDBkey() == '' )
+ ) {
+ $status->fatal( 'badarticleerror' );
+ }
+
+ // Content model checks
+ if ( !$wgContentHandlerUseDB &&
+ $this->oldTitle->getContentModel() !== $this->newTitle->getContentModel() ) {
+ // can't move a page if that would change the page's content model
+ $status->fatal(
+ 'bad-target-model',
+ ContentHandler::getLocalizedName( $this->oldTitle->getContentModel() ),
+ ContentHandler::getLocalizedName( $this->newTitle->getContentModel() )
+ );
+ }
+
+ // Image-specific checks
+ if ( $this->oldTitle->inNamespace( NS_FILE ) ) {
+ $status->merge( $this->isValidFileMove() );
+ }
+
+ if ( $this->newTitle->inNamespace( NS_FILE ) && !$this->oldTitle->inNamespace( NS_FILE ) ) {
+ $status->fatal( 'nonfile-cannot-move-to-file' );
+ }
+
+ // Hook for extensions to say a title can't be moved for technical reasons
+ Hooks::run( 'MovePageIsValidMove', array( $this->oldTitle, $this->newTitle, $status ) );
+
+ return $status;
+ }
+
+ /**
+ * Sanity checks for when a file is being moved
+ *
+ * @return Status
+ */
+ protected function isValidFileMove() {
+ $status = new Status();
+ $file = wfLocalFile( $this->oldTitle );
+ $file->load( File::READ_LATEST );
+ if ( $file->exists() ) {
+ if ( $this->newTitle->getText() != wfStripIllegalFilenameChars( $this->newTitle->getText() ) ) {
+ $status->fatal( 'imageinvalidfilename' );
+ }
+ if ( !File::checkExtensionCompatibility( $file, $this->newTitle->getDBkey() ) ) {
+ $status->fatal( 'imagetypemismatch' );
+ }
+ }
+
+ if ( !$this->newTitle->inNamespace( NS_FILE ) ) {
+ $status->fatal( 'imagenocrossnamespace' );
+ }
+
+ return $status;
+ }
+
+ /**
+ * Checks if $this can be moved to a given Title
+ * - Selects for update, so don't call it unless you mean business
+ *
+ * @since 1.25
+ * @return bool
+ */
+ protected function isValidMoveTarget() {
+ # Is it an existing file?
+ if ( $this->newTitle->inNamespace( NS_FILE ) ) {
+ $file = wfLocalFile( $this->newTitle );
+ $file->load( File::READ_LATEST );
+ if ( $file->exists() ) {
+ wfDebug( __METHOD__ . ": file exists\n" );
+ return false;
+ }
+ }
+ # Is it a redirect with no history?
+ if ( !$this->newTitle->isSingleRevRedirect() ) {
+ wfDebug( __METHOD__ . ": not a one-rev redirect\n" );
+ return false;
+ }
+ # Get the article text
+ $rev = Revision::newFromTitle( $this->newTitle, false, Revision::READ_LATEST );
+ if ( !is_object( $rev ) ) {
+ return false;
+ }
+ $content = $rev->getContent();
+ # Does the redirect point to the source?
+ # Or is it a broken self-redirect, usually caused by namespace collisions?
+ $redirTitle = $content ? $content->getRedirectTarget() : null;
+
+ if ( $redirTitle ) {
+ if ( $redirTitle->getPrefixedDBkey() !== $this->oldTitle->getPrefixedDBkey() &&
+ $redirTitle->getPrefixedDBkey() !== $this->newTitle->getPrefixedDBkey() ) {
+ wfDebug( __METHOD__ . ": redirect points to other page\n" );
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ # Fail safe (not a redirect after all. strange.)
+ wfDebug( __METHOD__ . ": failsafe: database says " . $this->newTitle->getPrefixedDBkey() .
+ " is a redirect, but it doesn't contain a valid redirect.\n" );
+ return false;
+ }
+ }
+
/**
* @param User $user
* @param string $reason
@@ -51,11 +233,14 @@ class MovePage {
public function move( User $user, $reason, $createRedirect ) {
global $wgCategoryCollation;
+ Hooks::run( 'TitleMove', array( $this->oldTitle, $this->newTitle, $user ) );
+
// If it is a file, move it first.
// It is done before all other moving stuff is done because it's hard to revert.
$dbw = wfGetDB( DB_MASTER );
if ( $this->oldTitle->getNamespace() == NS_FILE ) {
$file = wfLocalFile( $this->oldTitle );
+ $file->load( File::READ_LATEST );
if ( $file->exists() ) {
$status = $file->move( $this->newTitle );
if ( !$status->isOk() ) {
@@ -188,9 +373,11 @@ class MovePage {
$dbw->commit( __METHOD__ );
- wfRunHooks( 'TitleMoveComplete', array( &$this->oldTitle, &$this->newTitle, &$user, $pageid, $redirid, $reason ) );
+ Hooks::run(
+ 'TitleMoveComplete',
+ array( &$this->oldTitle, &$this->newTitle, &$user, $pageid, $redirid, $reason )
+ );
return Status::newGood();
-
}
/**
@@ -258,6 +445,9 @@ class MovePage {
$dbw = wfGetDB( DB_MASTER );
+ $oldpage = WikiPage::factory( $this->oldTitle );
+ $oldcountable = $oldpage->isCountable();
+
$newpage = WikiPage::factory( $nt );
if ( $moveOverRedirect ) {
@@ -302,10 +492,11 @@ class MovePage {
$newpage->updateRevisionOn( $dbw, $nullRevision );
- wfRunHooks( 'NewRevisionFromEditComplete',
+ Hooks::run( 'NewRevisionFromEditComplete',
array( $newpage, $nullRevision, $nullRevision->getParentId(), $user ) );
- $newpage->doEditUpdates( $nullRevision, $user, array( 'changed' => false ) );
+ $newpage->doEditUpdates( $nullRevision, $user,
+ array( 'changed' => false, 'moved' => true, 'oldcountable' => $oldcountable ) );
if ( !$moveOverRedirect ) {
WikiPage::onArticleCreate( $nt );
@@ -328,7 +519,7 @@ class MovePage {
$redirectRevision->insertOn( $dbw );
$redirectArticle->updateRevisionOn( $dbw, $redirectRevision, 0 );
- wfRunHooks( 'NewRevisionFromEditComplete',
+ Hooks::run( 'NewRevisionFromEditComplete',
array( $redirectArticle, $redirectRevision, false, $user ) );
$redirectArticle->doEditUpdates( $redirectRevision, $user, array( 'created' => true ) );
@@ -339,5 +530,4 @@ class MovePage {
$logid = $logEntry->insert();
$logEntry->publish( $logid );
}
-
-} \ No newline at end of file
+}
diff --git a/includes/templates/NoLocalSettings.php b/includes/NoLocalSettings.php
index 5b88dfd1..6de9bfcd 100644
--- a/includes/templates/NoLocalSettings.php
+++ b/includes/NoLocalSettings.php
@@ -1,7 +1,6 @@
<?php
-// @codingStandardsIgnoreFile
/**
- * Template used when there is no LocalSettings.php file.
+ * Display an error page when there is no LocalSettings.php file.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,17 +18,8 @@
* http://www.gnu.org/copyleft/gpl.html
*
* @file
- * @ingroup Templates
*/
-if ( !defined( 'MEDIAWIKI' ) ) {
- die( "NoLocalSettings.php is not a valid MediaWiki entry point\n" );
-}
-
-if ( !isset( $wgVersion ) ) {
- $wgVersion = 'VERSION';
-}
-
# bug 30219 : can not use pathinfo() on URLs since slashes do not match
$matches = array();
$ext = 'php';
@@ -39,6 +29,7 @@ foreach ( array_filter( explode( '/', $_SERVER['PHP_SELF'] ) ) as $part ) {
$path .= "$part/";
} else {
$ext = $matches[1] == 'php5' ? 'php5' : 'php';
+ break;
}
}
@@ -52,46 +43,21 @@ if ( !function_exists( 'session_name' ) ) {
error_reporting( $oldReporting );
$installerStarted = ( $success && isset( $_SESSION['installData'] ) );
}
-?>
-<!DOCTYPE html>
-<html lang="en" dir="ltr">
- <head>
- <meta charset="UTF-8" />
- <title>MediaWiki <?php echo htmlspecialchars( $wgVersion ) ?></title>
- <style media='screen'>
- html, body {
- color: #000;
- background-color: #fff;
- font-family: sans-serif;
- text-align: center;
- }
-
- h1 {
- font-size: 150%;
- }
- </style>
- </head>
- <body>
- <img src="<?php echo htmlspecialchars( $path ) ?>resources/assets/mediawiki.png" alt='The MediaWiki logo' />
- <h1>MediaWiki <?php echo htmlspecialchars( $wgVersion ) ?></h1>
- <div class='error'>
- <?php if ( !file_exists( MW_CONFIG_FILE ) ) { ?>
- <p>LocalSettings.php not found.</p>
- <p>
- <?php
- if ( $installerStarted ) {
- echo "Please <a href=\"" . htmlspecialchars( $path ) . "mw-config/index." . htmlspecialchars( $ext ) . "\">complete the installation</a> and download LocalSettings.php.";
- } else {
- echo "Please <a href=\"" . htmlspecialchars( $path ) . "mw-config/index." . htmlspecialchars( $ext ) . "\">set up the wiki</a> first.";
- }
- ?>
- </p>
- <?php } else { ?>
- <p>LocalSettings.php not readable.</p>
- <p>Please correct file permissions and try again.</p>
- <?php } ?>
+$templateParser = new TemplateParser();
- </div>
- </body>
-</html>
+# Render error page if no LocalSettings file can be found
+try {
+ echo $templateParser->processTemplate(
+ 'NoLocalSettings',
+ array(
+ 'wgVersion' => ( isset( $wgVersion ) ? $wgVersion : 'VERSION' ),
+ 'path' => $path,
+ 'ext' => $ext,
+ 'localSettingsExists' => file_exists( MW_CONFIG_FILE ),
+ 'installerStarted' => $installerStarted
+ )
+ );
+} catch ( Exception $e ) {
+ echo 'Error: ' . htmlspecialchars( $e->getMessage() );
+}
diff --git a/includes/OutputHandler.php b/includes/OutputHandler.php
index b0bbcddb..c6209eeb 100644
--- a/includes/OutputHandler.php
+++ b/includes/OutputHandler.php
@@ -129,7 +129,8 @@ function wfGzipHandler( $s ) {
$headers = headers_list();
$foundVary = false;
foreach ( $headers as $header ) {
- if ( substr( $header, 0, 5 ) == 'Vary:' ) {
+ $headerName = strtolower( substr( $header, 0, 5 ) );
+ if ( $headerName == 'vary:' ) {
$foundVary = true;
break;
}
diff --git a/includes/OutputPage.php b/includes/OutputPage.php
index 55b1da00..7e671878 100644
--- a/includes/OutputPage.php
+++ b/includes/OutputPage.php
@@ -122,6 +122,9 @@ class OutputPage extends ContextSource {
/** @var array */
protected $mCategories = array();
+ /** @var array */
+ protected $mIndicators = array();
+
/** @var array Array of Interwiki Prefixed (non DB key) Titles (e.g. 'fr:Test page') */
private $mLanguageLinks = array();
@@ -193,12 +196,6 @@ class OutputPage extends ContextSource {
// Parser related.
- /**
- * @var int
- * @todo Unused?
- */
- private $mContainsOldMagic = 0;
-
/** @var int */
protected $mContainsNewMagic = 0;
@@ -244,8 +241,9 @@ class OutputPage extends ContextSource {
protected $mSquidMaxage = 0;
/**
- * @var bool
- * @todo Document
+ * @var bool Controls if anti-clickjacking / frame-breaking headers will
+ * be sent. This should be done for pages where edit actions are possible.
+ * Setters: $this->preventClickjacking() and $this->allowClickjacking().
*/
protected $mPreventClickjacking = true;
@@ -783,7 +781,7 @@ class OutputPage extends ContextSource {
// bug 44570: the core page itself may not change, but resources might
$modifiedTimes['sepoch'] = wfTimestamp( TS_MW, time() - $config->get( 'SquidMaxage' ) );
}
- wfRunHooks( 'OutputPageCheckLastModified', array( &$modifiedTimes ) );
+ Hooks::run( 'OutputPageCheckLastModified', array( &$modifiedTimes ) );
$maxModified = max( $modifiedTimes );
$this->mLastModified = wfTimestamp( TS_RFC2822, $maxModified );
@@ -1030,17 +1028,29 @@ class OutputPage extends ContextSource {
}
/**
- * Add a subtitle containing a backlink to a page
+ * Build message object for a subtitle containing a backlink to a page
*
* @param Title $title Title to link to
* @param array $query Array of additional parameters to include in the link
+ * @return Message
+ * @since 1.25
*/
- public function addBacklinkSubtitle( Title $title, $query = array() ) {
+ public static function buildBacklinkSubtitle( Title $title, $query = array() ) {
if ( $title->isRedirect() ) {
$query['redirect'] = 'no';
}
- $this->addSubtitle( $this->msg( 'backlinksubtitle' )
- ->rawParams( Linker::link( $title, null, array(), $query ) ) );
+ return wfMessage( 'backlinksubtitle' )
+ ->rawParams( Linker::link( $title, null, array(), $query ) );
+ }
+
+ /**
+ * Add a subtitle containing a backlink to a page
+ *
+ * @param Title $title Title to link to
+ * @param array $query Array of additional parameters to include in the link
+ */
+ public function addBacklinkSubtitle( Title $title, $query = array() ) {
+ $this->addSubtitle( self::buildBacklinkSubtitle( $title, $query ) );
}
/**
@@ -1060,7 +1070,7 @@ class OutputPage extends ContextSource {
}
/**
- * Set the page as printable, i.e. it'll be displayed with with all
+ * Set the page as printable, i.e. it'll be displayed with all
* print styles included
*/
public function setPrintable() {
@@ -1310,7 +1320,7 @@ class OutputPage extends ContextSource {
}
# Add the remaining categories to the skin
- if ( wfRunHooks(
+ if ( Hooks::run(
'OutputPageMakeCategoryLinks',
array( &$this, $categories, &$this->mCategoryLinks ) )
) {
@@ -1363,6 +1373,65 @@ class OutputPage extends ContextSource {
}
/**
+ * Add an array of indicators, with their identifiers as array
+ * keys and HTML contents as values.
+ *
+ * In case of duplicate keys, existing values are overwritten.
+ *
+ * @param array $indicators
+ * @since 1.25
+ */
+ public function setIndicators( array $indicators ) {
+ $this->mIndicators = $indicators + $this->mIndicators;
+ // Keep ordered by key
+ ksort( $this->mIndicators );
+ }
+
+ /**
+ * Get the indicators associated with this page.
+ *
+ * The array will be internally ordered by item keys.
+ *
+ * @return array Keys: identifiers, values: HTML contents
+ * @since 1.25
+ */
+ public function getIndicators() {
+ return $this->mIndicators;
+ }
+
+ /**
+ * Adds help link with an icon via page indicators.
+ * Link target can be overridden by a local message containing a wikilink:
+ * the message key is: lowercase action or special page name + '-helppage'.
+ * @param string $to Target MediaWiki.org page title or encoded URL.
+ * @param bool $overrideBaseUrl Whether $url is a full URL, to avoid MW.o.
+ * @since 1.25
+ */
+ public function addHelpLink( $to, $overrideBaseUrl = false ) {
+ $this->addModuleStyles( 'mediawiki.helplink' );
+ $text = $this->msg( 'helppage-top-gethelp' )->escaped();
+
+ if ( $overrideBaseUrl ) {
+ $helpUrl = $to;
+ } else {
+ $toUrlencoded = wfUrlencode( str_replace( ' ', '_', $to ) );
+ $helpUrl = "//www.mediawiki.org/wiki/Special:MyLanguage/$toUrlencoded";
+ }
+
+ $link = Html::rawElement(
+ 'a',
+ array(
+ 'href' => $helpUrl,
+ 'target' => '_blank',
+ 'class' => 'mw-helplink',
+ ),
+ $text
+ );
+
+ $this->setIndicators( array( 'mw-helplink' => $link ) );
+ }
+
+ /**
* Do not allow scripts which can be modified by wiki users to load on this page;
* only allow scripts bundled with, or generated by, the software.
* Site-wide styles are controlled by a config setting, since they can be
@@ -1585,6 +1654,7 @@ class OutputPage extends ContextSource {
* @param string $text
* @param bool $linestart Is this the start of a line?
* @param bool $interface Is this text in the user interface language?
+ * @throws MWException
*/
public function addWikiText( $text, $linestart = true, $interface = true ) {
$title = $this->getTitle(); // Work around E_STRICT
@@ -1642,8 +1712,6 @@ class OutputPage extends ContextSource {
) {
global $wgParser;
- wfProfileIn( __METHOD__ );
-
$popts = $this->parserOptions();
$oldTidy = $popts->setTidy( $tidy );
$popts->setInterfaceMessage( (bool)$interface );
@@ -1657,7 +1725,6 @@ class OutputPage extends ContextSource {
$this->addParserOutput( $parserOutput );
- wfProfileOut( __METHOD__ );
}
/**
@@ -1681,6 +1748,7 @@ class OutputPage extends ContextSource {
public function addParserOutputMetadata( $parserOutput ) {
$this->mLanguageLinks += $parserOutput->getLanguageLinks();
$this->addCategoryLinks( $parserOutput->getCategories() );
+ $this->setIndicators( $parserOutput->getIndicators() );
$this->mNewSectionLink = $parserOutput->getNewSection();
$this->mHideNewSectionLink = $parserOutput->getHideNewSection();
@@ -1723,8 +1791,8 @@ class OutputPage extends ContextSource {
// Link flags are ignored for now, but may in the future be
// used to mark individual language links.
$linkFlags = array();
- wfRunHooks( 'LanguageLinks', array( $this->getTitle(), &$this->mLanguageLinks, &$linkFlags ) );
- wfRunHooks( 'OutputPageParserOutput', array( &$this, $parserOutput ) );
+ Hooks::run( 'LanguageLinks', array( $this->getTitle(), &$this->mLanguageLinks, &$linkFlags ) );
+ Hooks::run( 'OutputPageParserOutput', array( &$this, $parserOutput ) );
}
/**
@@ -1753,7 +1821,7 @@ class OutputPage extends ContextSource {
*/
public function addParserOutputText( $parserOutput ) {
$text = $parserOutput->getText();
- wfRunHooks( 'OutputPageBeforeHTML', array( &$this, &$text ) );
+ Hooks::run( 'OutputPageBeforeHTML', array( &$this, &$text ) );
$this->addHTML( $text );
}
@@ -1878,7 +1946,7 @@ class OutputPage extends ContextSource {
),
$config->get( 'CacheVaryCookies' )
);
- wfRunHooks( 'GetCacheVaryCookies', array( $this, &$cookies ) );
+ Hooks::run( 'GetCacheVaryCookies', array( $this, &$cookies ) );
}
return $cookies;
}
@@ -2125,14 +2193,10 @@ class OutputPage extends ContextSource {
* the object, let's actually output it:
*/
public function output() {
- global $wgLanguageCode;
-
if ( $this->mDoNothing ) {
return;
}
- wfProfileIn( __METHOD__ );
-
$response = $this->getRequest()->response();
$config = $this->getConfig();
@@ -2143,7 +2207,7 @@ class OutputPage extends ContextSource {
$redirect = $this->mRedirect;
$code = $this->mRedirectCode;
- if ( wfRunHooks( "BeforePageRedirect", array( $this, &$redirect, &$code ) ) ) {
+ if ( Hooks::run( "BeforePageRedirect", array( $this, &$redirect, &$code ) ) ) {
if ( $code == '301' || $code == '303' ) {
if ( !$config->get( 'DebugRedirects' ) ) {
$message = HttpStatus::getMessage( $code );
@@ -2167,7 +2231,6 @@ class OutputPage extends ContextSource {
}
}
- wfProfileOut( __METHOD__ );
return;
} elseif ( $this->mStatusCode ) {
$message = HttpStatus::getMessage( $this->mStatusCode );
@@ -2180,7 +2243,7 @@ class OutputPage extends ContextSource {
ob_start();
$response->header( 'Content-type: ' . $config->get( 'MimeType' ) . '; charset=UTF-8' );
- $response->header( 'Content-language: ' . $wgLanguageCode );
+ $response->header( 'Content-language: ' . $config->get( 'LanguageCode' ) );
// Avoid Internet Explorer "compatibility view" in IE 8-10, so that
// jQuery etc. can work correctly.
@@ -2220,21 +2283,18 @@ class OutputPage extends ContextSource {
// Hook that allows last minute changes to the output page, e.g.
// adding of CSS or Javascript by extensions.
- wfRunHooks( 'BeforePageDisplay', array( &$this, &$sk ) );
+ Hooks::run( 'BeforePageDisplay', array( &$this, &$sk ) );
- wfProfileIn( 'Output-skin' );
$sk->outputPage();
- wfProfileOut( 'Output-skin' );
}
// This hook allows last minute changes to final overall output by modifying output buffer
- wfRunHooks( 'AfterFinalPageOutput', array( $this ) );
+ Hooks::run( 'AfterFinalPageOutput', array( $this ) );
$this->sendCacheControl();
ob_end_flush();
- wfProfileOut( __METHOD__ );
}
/**
@@ -2454,90 +2514,32 @@ class OutputPage extends ContextSource {
}
/**
- * Display a page stating that the Wiki is in read-only mode,
- * and optionally show the source of the page that the user
- * was trying to edit. Should only be called (for this
- * purpose) after wfReadOnly() has returned true.
+ * Display a page stating that the Wiki is in read-only mode.
+ * Should only be called after wfReadOnly() has returned true.
*
- * For historical reasons, this function is _also_ used to
- * show the error message when a user tries to edit a page
- * they are not allowed to edit. (Unless it's because they're
- * blocked, then we show blockedPage() instead.) In this
- * case, the second parameter should be set to true and a list
- * of reasons supplied as the third parameter.
+ * Historically, this function was used to show the source of the page that the user
+ * was trying to edit and _also_ permissions error messages. The relevant code was
+ * moved into EditPage in 1.19 (r102024 / d83c2a431c2a) and removed here in 1.25.
*
- * @todo Needs to be split into multiple functions.
- *
- * @param string $source Source code to show (or null).
- * @param bool $protected Is this a permissions error?
- * @param array $reasons List of reasons for this error, as returned by
- * Title::getUserPermissionsErrors().
- * @param string $action Action that was denied or null if unknown
+ * @deprecated since 1.25; throw the exception directly
* @throws ReadOnlyError
*/
- public function readOnlyPage( $source = null, $protected = false,
- array $reasons = array(), $action = null
- ) {
- $this->setRobotPolicy( 'noindex,nofollow' );
- $this->setArticleRelated( false );
-
- // If no reason is given, just supply a default "I can't let you do
- // that, Dave" message. Should only occur if called by legacy code.
- if ( $protected && empty( $reasons ) ) {
- $reasons[] = array( 'badaccess-group0' );
- }
-
- if ( !empty( $reasons ) ) {
- // Permissions error
- if ( $source ) {
- $this->setPageTitle( $this->msg( 'viewsource-title', $this->getTitle()->getPrefixedText() ) );
- $this->addBacklinkSubtitle( $this->getTitle() );
- } else {
- $this->setPageTitle( $this->msg( 'badaccess' ) );
- }
- $this->addWikiText( $this->formatPermissionsErrorMessage( $reasons, $action ) );
- } else {
- // Wiki is read only
- throw new ReadOnlyError;
- }
-
- // Show source, if supplied
- if ( is_string( $source ) ) {
- $this->addWikiMsg( 'viewsourcetext' );
-
- $pageLang = $this->getTitle()->getPageLanguage();
- $params = array(
- 'id' => 'wpTextbox1',
- 'name' => 'wpTextbox1',
- 'cols' => $this->getUser()->getOption( 'cols' ),
- 'rows' => $this->getUser()->getOption( 'rows' ),
- 'readonly' => 'readonly',
- 'lang' => $pageLang->getHtmlCode(),
- 'dir' => $pageLang->getDir(),
- );
- $this->addHTML( Html::element( 'textarea', $params, $source ) );
-
- // Show templates used by this article
- $templates = Linker::formatTemplates( $this->getTitle()->getTemplateLinksFrom() );
- $this->addHTML( "<div class='templatesUsed'>
-$templates
-</div>
-" );
+ public function readOnlyPage() {
+ if ( func_num_args() > 0 ) {
+ throw new MWException( __METHOD__ . ' no longer accepts arguments since 1.25.' );
}
- # If the title doesn't exist, it's fairly pointless to print a return
- # link to it. After all, you just tried editing it and couldn't, so
- # what's there to do there?
- if ( $this->getTitle()->exists() ) {
- $this->returnToMain( null, $this->getTitle() );
- }
+ throw new ReadOnlyError;
}
/**
* Turn off regular page output and return an error response
* for when rate limiting has triggered.
+ *
+ * @deprecated since 1.25; throw the exception directly
*/
public function rateLimited() {
+ wfDeprecated( __METHOD__, '1.25' );
throw new ThrottledError;
}
@@ -2713,7 +2715,7 @@ $templates
// Allow skins and extensions to add body attributes they need
$sk->addToBodyAttributes( $this, $bodyAttrs );
- wfRunHooks( 'OutputPageBodyAttributes', array( $this, $sk, &$bodyAttrs ) );
+ Hooks::run( 'OutputPageBodyAttributes', array( $this, $sk, &$bodyAttrs ) );
$ret .= Html::openElement( 'body', $bodyAttrs ) . "\n";
@@ -2824,7 +2826,6 @@ $templates
);
$context = new ResourceLoaderContext( $resourceLoader, new FauxRequest( $query ) );
-
// Extract modules that know they're empty and see if we have one or more
// raw modules
$isRaw = false;
@@ -2905,11 +2906,11 @@ $templates
);
} else {
$link = Html::linkedScript( $url );
- if ( $context->getOnly() === 'scripts' && !$context->getRaw() && !$isRaw ) {
- // Wrap only=script requests in a conditional as browsers not supported
- // by the startup module would unconditionally execute this module.
- // Otherwise users will get "ReferenceError: mw is undefined" or
- // "jQuery is undefined" from e.g. a "site" module.
+ if ( !$context->getRaw() && !$isRaw ) {
+ // Wrap only=script / only=combined requests in a conditional as
+ // browsers not supported by the startup module would unconditionally
+ // execute this module. Otherwise users will get "ReferenceError: mw is
+ // undefined" or "jQuery is undefined" from e.g. a "site" module.
$link = Html::inlineScript(
ResourceLoader::makeLoaderConditionalScript(
Xml::encodeJsCall( 'document.write', array( $link ) )
@@ -3108,7 +3109,7 @@ $templates
// This also enforces $.isReady to be true at </body> which fixes the
// mw.loader bug in Firefox with using document.write between </body>
// and the DOMContentReady event (bug 47457).
- $html = Html::inlineScript( 'window.jQuery && jQuery.ready();' );
+ $html = Html::inlineScript( 'if(window.jQuery)jQuery.ready();' );
if ( !$this->getConfig()->get( 'ResourceLoaderExperimentalAsyncLoading' ) ) {
$html .= $this->getScriptsForBottomQueue( false );
@@ -3223,6 +3224,7 @@ $templates
'wgMonthNames' => $lang->getMonthNamesArray(),
'wgMonthNamesShort' => $lang->getMonthAbbreviationsArray(),
'wgRelevantPageName' => $relevantTitle->getPrefixedDBkey(),
+ 'wgRelevantArticleId' => $relevantTitle->getArticleId(),
);
if ( $user->isLoggedIn() ) {
@@ -3263,7 +3265,7 @@ $templates
// Use the 'ResourceLoaderGetConfigVars' hook if the variable is not
// page-dependant but site-wide (without state).
// Alternatively, you may want to use OutputPage->addJsConfigVars() instead.
- wfRunHooks( 'MakeGlobalVariablesScript', array( &$vars, $this ) );
+ Hooks::run( 'MakeGlobalVariablesScript', array( &$vars, $this ) );
// Merge in variables from addJsConfigVars last
return array_merge( $vars, $this->getJsConfigVars() );
@@ -3313,6 +3315,13 @@ $templates
'content' => "MediaWiki $wgVersion",
) );
+ if ( $config->get( 'ReferrerPolicy' ) !== false ) {
+ $tags['meta-referrer'] = Html::element( 'meta', array(
+ 'name' => 'referrer',
+ 'content' => $config->get( 'ReferrerPolicy' )
+ ) );
+ }
+
$p = "{$this->mIndexPolicy},{$this->mFollowPolicy}";
if ( $p !== 'index,follow' ) {
// http://www.robotstxt.org/wc/meta-user.html
@@ -3615,7 +3624,9 @@ $templates
$moduleStyles[] = 'user.groups';
// Per-user custom styles
- if ( $this->getConfig()->get( 'AllowUserCss' ) && $this->getTitle()->isCssSubpage() && $this->userCanPreview() ) {
+ if ( $this->getConfig()->get( 'AllowUserCss' ) && $this->getTitle()->isCssSubpage()
+ && $this->userCanPreview()
+ ) {
// We're on a preview of a CSS subpage
// Exclude this page from the user module in case it's in there (bug 26283)
$link = $this->makeResourceLoaderLink( 'user', ResourceLoaderModule::TYPE_STYLES, false,
@@ -3813,12 +3824,13 @@ $templates
* This function takes a number of message/argument specifications, wraps them in
* some overall structure, and then parses the result and adds it to the output.
*
- * In the $wrap, $1 is replaced with the first message, $2 with the second, and so
- * on. The subsequent arguments may either be strings, in which case they are the
- * message names, or arrays, in which case the first element is the message name,
- * and subsequent elements are the parameters to that message.
+ * In the $wrap, $1 is replaced with the first message, $2 with the second,
+ * and so on. The subsequent arguments may be either
+ * 1) strings, in which case they are message names, or
+ * 2) arrays, in which case, within each array, the first element is the message
+ * name, and subsequent elements are the parameters to that message.
*
- * Don't use this for messages that are not in users interface language.
+ * Don't use this for messages that are not in the user's interface language.
*
* For example:
*
@@ -3829,7 +3841,7 @@ $templates
* $wgOut->addWikiText( "<div class='error'>\n"
* . wfMessage( 'some-error' )->plain() . "\n</div>" );
*
- * The newline after opening div is needed in some wikitext. See bug 19226.
+ * The newline after the opening div is needed in some wikitext. See bug 19226.
*
* @param string $wrap
*/
@@ -3904,4 +3916,16 @@ $templates
public function sectionEditLinksEnabled() {
return $this->mEnableSectionEditLinks;
}
+
+ /**
+ * Add ResourceLoader module styles for OOUI and set up the PHP implementation of it for use with
+ * MediaWiki and this OutputPage instance.
+ *
+ * @since 1.25
+ */
+ public function enableOOUI() {
+ OOUI\Theme::setSingleton( new OOUI\MediaWikiTheme() );
+ OOUI\Element::setDefaultDir( $this->getLanguage()->getDir() );
+ $this->addModuleStyles( 'oojs-ui.styles' );
+ }
}
diff --git a/includes/PHPVersionCheck.php b/includes/PHPVersionCheck.php
new file mode 100644
index 00000000..eee9aa9c
--- /dev/null
+++ b/includes/PHPVersionCheck.php
@@ -0,0 +1,157 @@
+<?php
+/**
+ * Check PHP Version, as well as for composer dependencies in entry points,
+ * and display something vaguely comprehensible in the event of a totally
+ * unrecoverable error.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Check php version and that external dependencies are installed, and
+ * display an informative error if either condition is not satisfied.
+ */
+function wfEntryPointCheck( $entryPoint ) {
+ if ( !function_exists( 'version_compare' )
+ || version_compare( PHP_VERSION, '5.3.3' ) < 0
+ || !file_exists( dirname( __FILE__ ) . '/../vendor/autoload.php' )
+ ) {
+ wfPHPVersionError( $entryPoint );
+ }
+}
+
+/**
+ * Display something vaguely comprehensible in the event of a totally unrecoverable error.
+ * Does not assume access to *anything*; no globals, no autoloader, no database, no localisation.
+ * Safe for PHP4 (and putting this here means that WebStart.php and GlobalSettings.php
+ * no longer need to be).
+ *
+ * Calling this function kills execution immediately.
+ *
+ * @param string $type Which entry point we are protecting. One of:
+ * - index.php
+ * - load.php
+ * - api.php
+ * - mw-config/index.php
+ * - cli
+ *
+ * @note Since we can't rely on anything, the minimum PHP versions and MW current
+ * version are hardcoded here
+ */
+function wfPHPVersionError( $type ) {
+ $mwVersion = '1.25';
+ $minimumVersionPHP = '5.3.3';
+
+ $phpVersion = PHP_VERSION;
+ $protocol = isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
+ $message = "MediaWiki $mwVersion requires at least "
+ . "PHP version $minimumVersionPHP, you are using PHP $phpVersion. Installing some "
+ . " external dependencies (e.g. via composer) is also required.";
+
+ if ( $type == 'cli' ) {
+ $finalOutput = "Error: You are missing some external dependencies or are using on older PHP version. \n"
+ . "MediaWiki $mwVersion needs PHP $minimumVersionPHP or higher.\n\n"
+ . "Check if you have a newer php executable with a different name, such as php5.\n\n"
+ . "MediaWiki now also has some external dependencies that need to be installed\n"
+ . "via composer or from a separate git repo. Please see\n"
+ . "https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries\n"
+ . "for help on installing the required components.";
+ } elseif ( $type == 'index.php' || $type == 'mw-config/index.php' ) {
+ $pathinfo = pathinfo( $_SERVER['SCRIPT_NAME'] );
+ if ( $type == 'mw-config/index.php' ) {
+ $dirname = dirname( $pathinfo['dirname'] );
+ } else {
+ $dirname = $pathinfo['dirname'];
+ }
+ $encLogo = htmlspecialchars(
+ str_replace( '//', '/', $dirname . '/' ) .
+ 'resources/assets/mediawiki.png'
+ );
+
+ header( "$protocol 500 MediaWiki configuration Error" );
+ header( 'Content-type: text/html; charset=UTF-8' );
+ // Don't cache error pages! They cause no end of trouble...
+ header( 'Cache-control: none' );
+ header( 'Pragma: no-cache' );
+
+ $finalOutput = <<<HTML
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+ <head>
+ <meta charset="UTF-8" />
+ <title>MediaWiki {$mwVersion}</title>
+ <style media='screen'>
+ body {
+ color: #000;
+ background-color: #fff;
+ font-family: sans-serif;
+ padding: 2em;
+ text-align: center;
+ }
+ p, img, h1, h2 {
+ text-align: left;
+ margin: 0.5em 0 1em;
+ }
+ h1 {
+ font-size: 120%;
+ }
+ h2 {
+ font-size: 110%;
+ }
+ </style>
+ </head>
+ <body>
+ <img src="{$encLogo}" alt='The MediaWiki logo' />
+ <h1>MediaWiki {$mwVersion} internal error</h1>
+ <div class='error'>
+ <p>
+ {$message}
+ </p>
+ <h2>Supported PHP versions</h2>
+ <p>
+ Please consider <a href="http://www.php.net/downloads.php">upgrading your copy of PHP</a>.
+ PHP versions less than 5.3.0 are no longer supported by the PHP Group and will not receive
+ security or bugfix updates.
+ </p>
+ <p>
+ If for some reason you are unable to upgrade your PHP version, you will need to
+ <a href="https://www.mediawiki.org/wiki/Download">download</a> an older version
+ of MediaWiki from our website. See our
+ <a href="https://www.mediawiki.org/wiki/Compatibility#PHP">compatibility page</a>
+ for details of which versions are compatible with prior versions of PHP.
+ </p>
+ <h2>External dependencies</h2>
+ <p>
+ MediaWiki now also has some external dependencies that need to be installed via
+ composer or from a separate git repo. Please see
+ <a href="https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries">mediawiki.org</a>
+ for help on installing the required components.
+ </p>
+ </div>
+ </body>
+</html>
+HTML;
+ // Handle everything that's not index.php
+ } else {
+ // So nothing thinks this is JS or CSS
+ $finalOutput = ( $type == 'load.php' ) ? "/* $message */" : $message;
+ header( "$protocol 500 MediaWiki configuration Error" );
+ }
+ echo "$finalOutput\n";
+ die( 1 );
+}
diff --git a/includes/PHPVersionError.php b/includes/PHPVersionError.php
index f481650c..007ea894 100644
--- a/includes/PHPVersionError.php
+++ b/includes/PHPVersionError.php
@@ -1,6 +1,7 @@
<?php
/**
- * Display something vaguely comprehensible in the event of a totally unrecoverable error.
+ * Backwards compatibility. The PHP version error function is now
+ * included in PHPVersionCheck.php.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,110 +18,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
+ * @deprecated 1.25
* @file
*/
-
-/**
- * Display something vaguely comprehensible in the event of a totally unrecoverable error.
- * Does not assume access to *anything*; no globals, no autoloader, no database, no localisation.
- * Safe for PHP4 (and putting this here means that WebStart.php and GlobalSettings.php
- * no longer need to be).
- *
- * Calling this function kills execution immediately.
- *
- * @param string $type Which entry point we are protecting. One of:
- * - index.php
- * - load.php
- * - api.php
- * - mw-config/index.php
- * - cli
- *
- * @note Since we can't rely on anything, the minimum PHP versions and MW current
- * version are hardcoded here
- */
-function wfPHPVersionError( $type ) {
- $mwVersion = '1.24';
- $minimumVersionPHP = '5.3.2';
-
- $phpVersion = PHP_VERSION;
- $protocol = isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
- $message = "MediaWiki $mwVersion requires at least "
- . "PHP version $minimumVersionPHP, you are using PHP $phpVersion.";
-
- if ( $type == 'cli' ) {
- $finalOutput = "You are using PHP version $phpVersion "
- . "but MediaWiki $mwVersion needs PHP $minimumVersionPHP or higher. ABORTING.\n"
- . "Check if you have a newer php executable with a different name, such as php5.\n";
- } elseif ( $type == 'index.php' || $type == 'mw-config/index.php' ) {
- $pathinfo = pathinfo( $_SERVER['SCRIPT_NAME'] );
- if ( $type == 'mw-config/index.php' ) {
- $dirname = dirname( $pathinfo['dirname'] );
- } else {
- $dirname = $pathinfo['dirname'];
- }
- $encLogo = htmlspecialchars(
- str_replace( '//', '/', $dirname . '/' ) .
- 'resources/assets/mediawiki.png'
- );
-
- header( "$protocol 500 MediaWiki configuration Error" );
- header( 'Content-type: text/html; charset=UTF-8' );
- // Don't cache error pages! They cause no end of trouble...
- header( 'Cache-control: none' );
- header( 'Pragma: no-cache' );
-
- $finalOutput = <<<HTML
-<!DOCTYPE html>
-<html lang="en" dir="ltr">
- <head>
- <meta charset="UTF-8" />
- <title>MediaWiki {$mwVersion}</title>
- <style media='screen'>
- body {
- color: #000;
- background-color: #fff;
- font-family: sans-serif;
- padding: 2em;
- text-align: center;
- }
- p, img, h1 {
- text-align: left;
- margin: 0.5em 0;
- }
- h1 {
- font-size: 120%;
- }
- </style>
- </head>
- <body>
- <img src="{$encLogo}" alt='The MediaWiki logo' />
- <h1>MediaWiki {$mwVersion} internal error</h1>
- <div class='error'>
- <p>
- {$message}
- </p>
- <p>
- Please consider <a href="http://www.php.net/downloads.php">upgrading your copy of PHP</a>.
- PHP versions less than 5.3.0 are no longer supported by the PHP Group and will not receive
- security or bugfix updates.
- </p>
- <p>
- If for some reason you are unable to upgrade your PHP version, you will need to
- <a href="https://www.mediawiki.org/wiki/Download">download</a> an older version
- of MediaWiki from our website. See our
- <a href="https://www.mediawiki.org/wiki/Compatibility#PHP">compatibility page</a>
- for details of which versions are compatible with prior versions of PHP.
- </p>
- </div>
- </body>
-</html>
-HTML;
- // Handle everything that's not index.php
- } else {
- // So nothing thinks this is JS or CSS
- $finalOutput = ( $type == 'load.php' ) ? "/* $message */" : $message;
- header( "$protocol 500 MediaWiki configuration Error" );
- }
- echo "$finalOutput\n";
- die( 1 );
-}
+require_once dirname( __FILE__ ) . '/PHPVersionCheck.php';
diff --git a/includes/Preferences.php b/includes/Preferences.php
index 84cf5af0..a5239331 100644
--- a/includes/Preferences.php
+++ b/includes/Preferences.php
@@ -96,7 +96,7 @@ class Preferences {
self::searchPreferences( $user, $context, $defaultPreferences );
self::miscPreferences( $user, $context, $defaultPreferences );
- wfRunHooks( 'GetPreferences', array( $user, &$defaultPreferences ) );
+ Hooks::run( 'GetPreferences', array( $user, &$defaultPreferences ) );
self::loadPreferenceValues( $user, $context, $defaultPreferences );
self::$defaultPreferences = $defaultPreferences;
@@ -130,8 +130,7 @@ class Preferences {
if ( $disable && !in_array( $name, self::$saveBlacklist ) ) {
$info['disabled'] = 'disabled';
}
- $field = HTMLForm::loadInputFromParameters( $name, $info ); // For validation
- $field->mParent = $dummyForm;
+ $field = HTMLForm::loadInputFromParameters( $name, $info, $dummyForm ); // For validation
$defaultOptions = User::getDefaultOptions();
$globalDefault = isset( $defaultOptions[$name] )
? $defaultOptions[$name]
@@ -244,10 +243,9 @@ class Preferences {
'type' => 'info',
'label' => $context->msg( 'prefs-memberingroups' )->numParams(
count( $userGroups ) )->params( $userName )->parse(),
- 'default' => $context->msg( 'prefs-memberingroups-type',
- $lang->commaList( $userGroups ),
- $lang->commaList( $userMembers )
- )->plain(),
+ 'default' => $context->msg( 'prefs-memberingroups-type' )
+ ->rawParams( $lang->commaList( $userGroups ), $lang->commaList( $userMembers ) )
+ ->escaped(),
'raw' => true,
'section' => 'personal/info',
);
@@ -339,11 +337,11 @@ class Preferences {
'type' => 'radio',
'section' => 'personal/i18n',
'options' => array(
- $context->msg( 'parentheses',
- $context->msg( 'gender-unknown' )->text()
- )->text() => 'unknown',
- $context->msg( 'gender-female' )->text() => 'female',
- $context->msg( 'gender-male' )->text() => 'male',
+ $context->msg( 'parentheses' )
+ ->params( $context->msg( 'gender-unknown' )->plain() )
+ ->escaped() => 'unknown',
+ $context->msg( 'gender-female' )->escaped() => 'female',
+ $context->msg( 'gender-male' )->escaped() => 'male',
),
'label-message' => 'yourgender',
'help-message' => 'prefs-help-gender',
@@ -451,8 +449,8 @@ class Preferences {
array( 'returnto' => SpecialPage::getTitleFor( 'Preferences' )->getPrefixedText() ) );
$emailAddress .= $emailAddress == '' ? $link : (
- $context->msg( 'word-separator' )->plain()
- . $context->msg( 'parentheses' )->rawParams( $link )->plain()
+ $context->msg( 'word-separator' )->escaped()
+ . $context->msg( 'parentheses' )->rawParams( $link )->escaped()
);
}
@@ -870,7 +868,7 @@ class Preferences {
'min' => 1,
'max' => ceil( $rcMaxAge / ( 3600 * 24 ) ),
'help' => $context->msg( 'recentchangesdays-max' )->numParams(
- ceil( $rcMaxAge / ( 3600 * 24 ) ) )->text()
+ ceil( $rcMaxAge / ( 3600 * 24 ) ) )->escaped()
);
$defaultPreferences['rclimit'] = array(
'type' => 'int',
@@ -895,6 +893,9 @@ class Preferences {
'section' => 'rc/advancedrc',
'label-message' => 'tog-hidepatrolled',
);
+ }
+
+ if ( $user->useNPPatrol() ) {
$defaultPreferences['newpageshidepatrolled'] = array(
'type' => 'toggle',
'section' => 'rc/advancedrc',
@@ -921,13 +922,37 @@ class Preferences {
$watchlistdaysMax = ceil( $config->get( 'RCMaxAge' ) / ( 3600 * 24 ) );
## Watchlist #####################################
+ if ( $user->isAllowed( 'editmywatchlist' ) ) {
+ $editWatchlistLinks = array();
+ $editWatchlistModes = array(
+ 'edit' => array( 'EditWatchlist', false ),
+ 'raw' => array( 'EditWatchlist', 'raw' ),
+ 'clear' => array( 'EditWatchlist', 'clear' ),
+ );
+ foreach ( $editWatchlistModes as $editWatchlistMode => $mode ) {
+ // Messages: prefs-editwatchlist-edit, prefs-editwatchlist-raw, prefs-editwatchlist-clear
+ $editWatchlistLinks[] = Linker::linkKnown(
+ SpecialPage::getTitleFor( $mode[0], $mode[1] ),
+ $context->msg( "prefs-editwatchlist-{$editWatchlistMode}" )->parse()
+ );
+ }
+
+ $defaultPreferences['editwatchlist'] = array(
+ 'type' => 'info',
+ 'raw' => true,
+ 'default' => $context->getLanguage()->pipeList( $editWatchlistLinks ),
+ 'label-message' => 'prefs-editwatchlist-label',
+ 'section' => 'watchlist/editwatchlist',
+ );
+ }
+
$defaultPreferences['watchlistdays'] = array(
'type' => 'float',
'min' => 0,
'max' => $watchlistdaysMax,
'section' => 'watchlist/displaywatchlist',
'help' => $context->msg( 'prefs-watchlist-days-max' )->numParams(
- $watchlistdaysMax )->text(),
+ $watchlistdaysMax )->escaped(),
'label-message' => 'prefs-watchlist-days',
);
$defaultPreferences['wllimit'] = array(
@@ -969,7 +994,7 @@ class Preferences {
'label-message' => 'tog-watchlisthideliu',
);
- if ( $context->getConfig()->get( 'UseRCPatrol' ) ) {
+ if ( $user->useRCPatrol() ) {
$defaultPreferences['watchlisthidepatrolled'] = array(
'type' => 'toggle',
'section' => 'watchlist/advancedwatchlist',
@@ -1047,7 +1072,7 @@ class Preferences {
$ret = array();
$mptitle = Title::newMainPage();
- $previewtext = $context->msg( 'skin-preview' )->text();
+ $previewtext = $context->msg( 'skin-preview' )->escaped();
# Only show skins that aren't disabled in $wgSkipSkins
$validSkinNames = Skin::getAllowedSkins();
@@ -1072,7 +1097,7 @@ class Preferences {
$linkTools = array();
# Mark the default skin
- if ( $skinkey == $defaultSkin ) {
+ if ( strcasecmp( $skinkey, $defaultSkin ) === 0 ) {
$linkTools[] = $context->msg( 'default' )->escaped();
$foundDefault = true;
}
@@ -1092,10 +1117,9 @@ class Preferences {
$linkTools[] = Linker::link( $jsPage, $context->msg( 'prefs-custom-js' )->escaped() );
}
- $display = $sn . ' ' . $context->msg(
- 'parentheses',
- $context->getLanguage()->pipeList( $linkTools )
- )->text();
+ $display = $sn . ' ' . $context->msg( 'parentheses' )
+ ->rawParams( $context->getLanguage()->pipeList( $linkTools ) )
+ ->escaped();
$ret[$display] = $skinkey;
}
@@ -1433,7 +1457,7 @@ class Preferences {
$user->setOption( $key, $value );
}
- wfRunHooks( 'PreferencesFormPreSave', array( $formData, $form, $user, &$result ) );
+ Hooks::run( 'PreferencesFormPreSave', array( $formData, $form, $user, &$result ) );
$user->saveSettings();
}
@@ -1466,28 +1490,6 @@ class Preferences {
return Status::newGood();
}
-
- /**
- * Try to set a user's email address.
- * This does *not* try to validate the address.
- * Caller is responsible for checking $wgAuth and 'editmyprivateinfo'
- * right.
- *
- * @deprecated since 1.20; use User::setEmailWithConfirmation() instead.
- * @param User $user
- * @param string $newaddr New email address
- * @return array (true on success or Status on failure, info string)
- */
- public static function trySetUserEmail( User $user, $newaddr ) {
- wfDeprecated( __METHOD__, '1.20' );
-
- $result = $user->setEmailWithConfirmation( $newaddr );
- if ( $result->isGood() ) {
- return array( true, $result->value );
- } else {
- return array( $result, 'mailerror' );
- }
- }
}
/** Some tweaks to allow js prefs to work */
@@ -1539,12 +1541,8 @@ class PreferencesForm extends HTMLForm {
* @return string
*/
function getButtons() {
- global $wgUseMediaWikiUIEverywhere;
$attrs = array( 'id' => 'mw-prefs-restoreprefs' );
- if ( $wgUseMediaWikiUIEverywhere ) {
- $attrs['class'] = 'mw-ui-button mw-ui-quiet';
- }
if ( !$this->getModifiedUser()->isAllowedAny( 'editmyprivateinfo', 'editmyoptions' ) ) {
return '';
@@ -1556,7 +1554,7 @@ class PreferencesForm extends HTMLForm {
$t = SpecialPage::getTitleFor( 'Preferences', 'reset' );
$html .= "\n" . Linker::link( $t, $this->msg( 'restoreprefs' )->escaped(),
- $attrs );
+ Html::buttonAttributes( $attrs, array( 'mw-ui-quiet' ) ) );
$html = Xml::tags( 'div', array( 'class' => 'mw-prefs-buttons' ), $html );
}
@@ -1601,7 +1599,7 @@ class PreferencesForm extends HTMLForm {
*/
function getLegend( $key ) {
$legend = parent::getLegend( $key );
- wfRunHooks( 'PreferencesGetLegend', array( $this, $key, &$legend ) );
+ Hooks::run( 'PreferencesGetLegend', array( $this, $key, &$legend ) );
return $legend;
}
}
diff --git a/includes/PrefixSearch.php b/includes/PrefixSearch.php
index 718750f5..55a4f49b 100644
--- a/includes/PrefixSearch.php
+++ b/includes/PrefixSearch.php
@@ -34,11 +34,12 @@ abstract class PrefixSearch {
* @param string $search
* @param int $limit
* @param array $namespaces Used if query is not explicit