summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/Action.php57
-rw-r--r--includes/AjaxDispatcher.php10
-rw-r--r--includes/AjaxResponse.php2
-rw-r--r--includes/ArrayUtils.php2
-rw-r--r--includes/Article.php147
-rw-r--r--includes/AuthPlugin.php13
-rw-r--r--includes/AutoLoader.php147
-rw-r--r--includes/Autopromote.php5
-rw-r--r--includes/Block.php308
-rw-r--r--includes/CallableUpdate.php30
-rw-r--r--includes/Category.php3
-rw-r--r--includes/CategoryPage.php8
-rw-r--r--includes/CategoryViewer.php60
-rw-r--r--includes/Cdb.php4
-rw-r--r--includes/Cdb_PHP.php16
-rw-r--r--includes/ChangeTags.php75
-rw-r--r--includes/ChangesFeed.php37
-rw-r--r--includes/ChangesList.php1304
-rw-r--r--includes/Collation.php22
-rw-r--r--includes/ConfEditor.php55
-rw-r--r--includes/Cookie.php7
-rw-r--r--includes/DataUpdate.php4
-rw-r--r--includes/DefaultSettings.php1309
-rw-r--r--includes/DeferredUpdates.php12
-rw-r--r--includes/Defines.php11
-rw-r--r--includes/EditPage.php290
-rw-r--r--includes/Exception.php275
-rw-r--r--includes/Export.php33
-rw-r--r--includes/ExternalEdit.php132
-rw-r--r--includes/ExternalUser.php309
-rw-r--r--includes/Fallback.php28
-rw-r--r--includes/Feed.php26
-rw-r--r--includes/FeedUtils.php27
-rw-r--r--includes/FileDeleteForm.php36
-rw-r--r--includes/ForkController.php4
-rw-r--r--includes/FormOptions.php163
-rw-r--r--includes/GitInfo.php39
-rw-r--r--includes/GlobalFunctions.php675
-rw-r--r--includes/HTMLForm.php541
-rw-r--r--includes/HashRing.php142
-rw-r--r--includes/HistoryBlob.php35
-rw-r--r--includes/Hooks.php282
-rw-r--r--includes/Html.php179
-rw-r--r--includes/HtmlFormatter.php356
-rw-r--r--includes/HttpFunctions.php118
-rw-r--r--includes/IP.php32
-rw-r--r--includes/ImagePage.php49
-rw-r--r--includes/ImageQueryPage.php25
-rw-r--r--includes/Import.php135
-rw-r--r--includes/Init.php150
-rw-r--r--includes/Licenses.php2
-rw-r--r--includes/LinkFilter.php4
-rw-r--r--includes/Linker.php103
-rw-r--r--includes/LinksUpdate.php154
-rw-r--r--includes/MWCryptRand.php (renamed from includes/CryptRand.php)2
-rw-r--r--includes/MWFunction.php34
-rw-r--r--includes/MagicWord.php23
-rw-r--r--includes/MappedIterator.php88
-rw-r--r--includes/Message.php212
-rw-r--r--includes/Metadata.php28
-rw-r--r--includes/MimeMagic.php88
-rw-r--r--includes/Namespace.php6
-rw-r--r--includes/OutputHandler.php80
-rw-r--r--includes/OutputPage.php476
-rw-r--r--includes/PHPVersionError.php12
-rw-r--r--includes/PageQueryPage.php5
-rw-r--r--includes/Pager.php140
-rw-r--r--includes/PathRouter.php10
-rw-r--r--includes/PoolCounter.php221
-rw-r--r--includes/Preferences.php541
-rw-r--r--includes/PrefixSearch.php40
-rw-r--r--includes/ProtectionForm.php110
-rw-r--r--includes/ProxyTools.php40
-rw-r--r--includes/QueryPage.php92
-rw-r--r--includes/Revision.php265
-rw-r--r--includes/RevisionList.php5
-rw-r--r--includes/Sanitizer.php361
-rw-r--r--includes/ScopedCallback.php41
-rw-r--r--includes/SeleniumWebSettings.php221
-rw-r--r--includes/Setup.php153
-rw-r--r--includes/SiteConfiguration.php58
-rw-r--r--includes/SiteStats.php126
-rw-r--r--includes/Skin.php202
-rw-r--r--includes/SkinLegacy.php882
-rw-r--r--includes/SkinTemplate.php346
-rw-r--r--includes/SpecialPage.php160
-rw-r--r--includes/SpecialPageFactory.php15
-rw-r--r--includes/SqlDataUpdate.php2
-rw-r--r--includes/StatCounter.php150
-rw-r--r--includes/Status.php91
-rw-r--r--includes/StreamFile.php1
-rw-r--r--includes/StringUtils.php135
-rw-r--r--includes/StubObject.php25
-rw-r--r--includes/Timestamp.php267
-rw-r--r--includes/Title.php528
-rw-r--r--includes/UIDGenerator.php35
-rw-r--r--includes/User.php1151
-rw-r--r--includes/UserArray.php2
-rw-r--r--includes/UserMailer.php53
-rw-r--r--includes/UserRightsProxy.php10
-rw-r--r--includes/WatchedItem.php65
-rw-r--r--includes/WebRequest.php187
-rw-r--r--includes/WebResponse.php121
-rw-r--r--includes/WebStart.php52
-rw-r--r--includes/Wiki.php145
-rw-r--r--includes/WikiError.php2
-rw-r--r--includes/WikiFilePage.php4
-rw-r--r--includes/WikiMap.php20
-rw-r--r--includes/WikiPage.php632
-rw-r--r--includes/Xml.php221
-rw-r--r--includes/XmlTypeCheck.php86
-rw-r--r--includes/ZhClient.php6
-rw-r--r--includes/ZipDirectoryReader.php2
-rw-r--r--includes/actions/CreditsAction.php15
-rw-r--r--includes/actions/EditAction.php14
-rw-r--r--includes/actions/HistoryAction.php51
-rw-r--r--includes/actions/InfoAction.php69
-rw-r--r--includes/actions/PurgeAction.php4
-rw-r--r--includes/actions/RawAction.php29
-rw-r--r--includes/actions/WatchAction.php62
-rw-r--r--includes/api/ApiBase.php119
-rw-r--r--includes/api/ApiBlock.php2
-rw-r--r--includes/api/ApiComparePages.php8
-rw-r--r--includes/api/ApiCreateAccount.php52
-rw-r--r--includes/api/ApiDelete.php3
-rw-r--r--includes/api/ApiEditPage.php59
-rw-r--r--includes/api/ApiExpandTemplates.php4
-rw-r--r--includes/api/ApiFeedContributions.php15
-rw-r--r--includes/api/ApiFeedWatchlist.php90
-rw-r--r--includes/api/ApiFormatBase.php8
-rw-r--r--includes/api/ApiFormatJson.php24
-rw-r--r--includes/api/ApiFormatWddx.php75
-rw-r--r--includes/api/ApiFormatXml.php137
-rw-r--r--includes/api/ApiImageRotate.php32
-rw-r--r--includes/api/ApiImport.php6
-rw-r--r--includes/api/ApiMain.php36
-rw-r--r--includes/api/ApiMove.php4
-rw-r--r--includes/api/ApiOpenSearch.php21
-rw-r--r--includes/api/ApiOptions.php8
-rw-r--r--includes/api/ApiPageSet.php18
-rw-r--r--includes/api/ApiParamInfo.php6
-rw-r--r--includes/api/ApiParse.php157
-rw-r--r--includes/api/ApiPatrol.php46
-rw-r--r--includes/api/ApiProtect.php3
-rw-r--r--includes/api/ApiPurge.php29
-rw-r--r--includes/api/ApiQuery.php10
-rw-r--r--includes/api/ApiQueryAllCategories.php4
-rw-r--r--includes/api/ApiQueryAllImages.php4
-rw-r--r--includes/api/ApiQueryAllLinks.php116
-rw-r--r--includes/api/ApiQueryAllMessages.php2
-rw-r--r--includes/api/ApiQueryAllPages.php7
-rw-r--r--includes/api/ApiQueryAllUsers.php4
-rw-r--r--includes/api/ApiQueryBacklinks.php15
-rw-r--r--includes/api/ApiQueryBase.php4
-rw-r--r--includes/api/ApiQueryBlocks.php61
-rw-r--r--includes/api/ApiQueryCategories.php5
-rw-r--r--includes/api/ApiQueryCategoryMembers.php6
-rw-r--r--includes/api/ApiQueryDeletedrevs.php6
-rw-r--r--includes/api/ApiQueryDuplicateFiles.php23
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php6
-rw-r--r--includes/api/ApiQueryExternalLinks.php8
-rw-r--r--includes/api/ApiQueryFileRepoInfo.php115
-rw-r--r--includes/api/ApiQueryFilearchive.php8
-rw-r--r--includes/api/ApiQueryIWBacklinks.php4
-rw-r--r--includes/api/ApiQueryIWLinks.php13
-rw-r--r--includes/api/ApiQueryImageInfo.php80
-rw-r--r--includes/api/ApiQueryInfo.php20
-rw-r--r--includes/api/ApiQueryLangBacklinks.php9
-rw-r--r--includes/api/ApiQueryLangLinks.php17
-rw-r--r--includes/api/ApiQueryLogEvents.php24
-rw-r--r--includes/api/ApiQueryORM.php4
-rw-r--r--includes/api/ApiQueryPagesWithProp.php2
-rw-r--r--includes/api/ApiQueryProtectedTitles.php2
-rw-r--r--includes/api/ApiQueryQueryPage.php19
-rw-r--r--includes/api/ApiQueryRandom.php4
-rw-r--r--includes/api/ApiQueryRecentChanges.php43
-rw-r--r--includes/api/ApiQueryRevisions.php29
-rw-r--r--includes/api/ApiQuerySearch.php39
-rw-r--r--includes/api/ApiQuerySiteinfo.php62
-rw-r--r--includes/api/ApiQueryTags.php7
-rw-r--r--includes/api/ApiQueryUserContributions.php21
-rw-r--r--includes/api/ApiQueryUserInfo.php20
-rw-r--r--includes/api/ApiQueryUsers.php6
-rw-r--r--includes/api/ApiQueryWatchlist.php95
-rw-r--r--includes/api/ApiQueryWatchlistRaw.php4
-rw-r--r--includes/api/ApiRsd.php2
-rw-r--r--includes/api/ApiSetNotificationTimestamp.php7
-rw-r--r--includes/api/ApiUpload.php84
-rw-r--r--includes/api/ApiUserrights.php7
-rw-r--r--includes/api/ApiWatch.php11
-rw-r--r--includes/cache/BacklinkCache.php134
-rw-r--r--includes/cache/CacheDependency.php2
-rw-r--r--includes/cache/FileCacheBase.php6
-rw-r--r--includes/cache/GenderCache.php14
-rw-r--r--includes/cache/HTMLCacheUpdate.php11
-rw-r--r--includes/cache/HTMLFileCache.php4
-rw-r--r--includes/cache/LinkBatch.php4
-rw-r--r--includes/cache/LinkCache.php68
-rw-r--r--includes/cache/LocalisationCache.php96
-rw-r--r--includes/cache/MessageCache.php729
-rw-r--r--includes/cache/ResourceFileCache.php2
-rw-r--r--includes/cache/SquidUpdate.php197
-rw-r--r--includes/cache/UserCache.php13
-rw-r--r--includes/changes/ChangesList.php552
-rw-r--r--includes/changes/EnhancedChangesList.php662
-rw-r--r--includes/changes/OldChangesList.php130
-rw-r--r--includes/changes/RCCacheEntry.php35
-rw-r--r--includes/changes/RecentChange.php (renamed from includes/RecentChange.php)232
-rw-r--r--includes/clientpool/RedisConnectionPool.php90
-rw-r--r--includes/content/Content.php14
-rw-r--r--includes/content/ContentHandler.php19
-rw-r--r--includes/content/CssContent.php2
-rw-r--r--includes/content/JavaScriptContent.php2
-rw-r--r--includes/content/TextContent.php2
-rw-r--r--includes/content/WikitextContent.php3
-rw-r--r--includes/content/WikitextContentHandler.php19
-rw-r--r--includes/context/ContextSource.php2
-rw-r--r--includes/context/DerivativeContext.php25
-rw-r--r--includes/context/IContextSource.php2
-rw-r--r--includes/context/RequestContext.php19
-rw-r--r--includes/db/ChronologyProtector.php106
-rw-r--r--includes/db/CloneDatabase.php18
-rw-r--r--includes/db/Database.php517
-rw-r--r--includes/db/DatabaseError.php202
-rw-r--r--includes/db/DatabaseMssql.php190
-rw-r--r--includes/db/DatabaseMysql.php933
-rw-r--r--includes/db/DatabaseMysqlBase.php1154
-rw-r--r--includes/db/DatabaseMysqli.php194
-rw-r--r--includes/db/DatabaseOracle.php92
-rw-r--r--includes/db/DatabasePostgres.php98
-rw-r--r--includes/db/DatabaseSqlite.php49
-rw-r--r--includes/db/DatabaseUtility.php3
-rw-r--r--includes/db/IORMRow.php24
-rw-r--r--includes/db/LBFactory.php94
-rw-r--r--includes/db/LBFactory_Multi.php6
-rw-r--r--includes/db/LoadBalancer.php104
-rw-r--r--includes/db/LoadMonitor.php27
-rw-r--r--includes/db/ORMRow.php182
-rw-r--r--includes/db/ORMTable.php211
-rw-r--r--includes/debug/Debug.php74
-rw-r--r--includes/diff/DairikiDiff.php40
-rw-r--r--includes/diff/DifferenceEngine.php205
-rw-r--r--includes/extauth/Hardcoded.php84
-rw-r--r--includes/extauth/MediaWiki.php168
-rw-r--r--includes/extauth/vB.php146
-rw-r--r--includes/externalstore/ExternalStore.php57
-rw-r--r--includes/externalstore/ExternalStoreDB.php142
-rw-r--r--includes/externalstore/ExternalStoreMedium.php19
-rw-r--r--includes/externalstore/ExternalStoreMwstore.php23
-rw-r--r--includes/filebackend/FSFile.php29
-rw-r--r--includes/filebackend/FSFileBackend.php117
-rw-r--r--includes/filebackend/FileBackend.php143
-rw-r--r--includes/filebackend/FileBackendGroup.php28
-rw-r--r--includes/filebackend/FileBackendMultiWrite.php179
-rw-r--r--includes/filebackend/FileBackendStore.php576
-rw-r--r--includes/filebackend/FileOp.php193
-rw-r--r--includes/filebackend/FileOpBatch.php6
-rw-r--r--includes/filebackend/README2
-rw-r--r--includes/filebackend/SwiftFileBackend.php262
-rw-r--r--includes/filebackend/TempFSFile.php6
-rw-r--r--includes/filebackend/filejournal/DBFileJournal.php10
-rw-r--r--includes/filebackend/lockmanager/DBLockManager.php37
-rw-r--r--includes/filebackend/lockmanager/LockManager.php125
-rw-r--r--includes/filebackend/lockmanager/LockManagerGroup.php4
-rw-r--r--includes/filebackend/lockmanager/MemcLockManager.php41
-rw-r--r--includes/filebackend/lockmanager/QuorumLockManager.php140
-rw-r--r--includes/filebackend/lockmanager/RedisLockManager.php288
-rw-r--r--includes/filebackend/lockmanager/ScopedLock.php44
-rw-r--r--includes/filerepo/FSRepo.php14
-rw-r--r--includes/filerepo/FileRepo.php108
-rw-r--r--includes/filerepo/ForeignAPIRepo.php226
-rw-r--r--includes/filerepo/ForeignDBRepo.php5
-rw-r--r--includes/filerepo/LocalRepo.php42
-rw-r--r--includes/filerepo/RepoGroup.php12
-rw-r--r--includes/filerepo/file/ArchivedFile.php23
-rw-r--r--includes/filerepo/file/File.php37
-rw-r--r--includes/filerepo/file/ForeignAPIFile.php49
-rw-r--r--includes/filerepo/file/ForeignDBFile.php5
-rw-r--r--includes/filerepo/file/LocalFile.php234
-rw-r--r--includes/filerepo/file/OldLocalFile.php3
-rw-r--r--includes/gallery/ImageGalleryBase.php (renamed from includes/ImageGallery.php)239
-rw-r--r--includes/gallery/NolinesImageGallery.php38
-rw-r--r--includes/gallery/PackedImageGallery.php105
-rw-r--r--includes/gallery/PackedOverlayImageGallery.php60
-rw-r--r--includes/gallery/TraditionalImageGallery.php328
-rw-r--r--includes/installer/CliInstaller.php9
-rw-r--r--includes/installer/DatabaseInstaller.php64
-rw-r--r--includes/installer/DatabaseUpdater.php156
-rw-r--r--includes/installer/InstallDocFormatter.php12
-rw-r--r--includes/installer/Installer.i18n.php1959
-rw-r--r--includes/installer/Installer.php298
-rw-r--r--includes/installer/LocalSettingsGenerator.php59
-rw-r--r--includes/installer/MysqlInstaller.php135
-rw-r--r--includes/installer/MysqlUpdater.php516
-rw-r--r--includes/installer/OracleInstaller.php61
-rw-r--r--includes/installer/OracleUpdater.php72
-rw-r--r--includes/installer/PhpBugTests.php2
-rw-r--r--includes/installer/PostgresInstaller.php62
-rw-r--r--includes/installer/PostgresUpdater.php575
-rw-r--r--includes/installer/SqliteInstaller.php38
-rw-r--r--includes/installer/SqliteUpdater.php121
-rw-r--r--includes/installer/WebInstaller.php208
-rw-r--r--includes/installer/WebInstallerOutput.php112
-rw-r--r--includes/installer/WebInstallerPage.php259
-rw-r--r--includes/job/Job.php56
-rw-r--r--includes/job/JobQueue.php355
-rw-r--r--includes/job/JobQueueDB.php688
-rw-r--r--includes/job/JobQueueFederated.php473
-rw-r--r--includes/job/JobQueueGroup.php90
-rw-r--r--includes/job/JobQueueRedis.php856
-rw-r--r--includes/job/aggregator/JobQueueAggregator.php (renamed from includes/job/JobQueueAggregator.php)17
-rw-r--r--includes/job/aggregator/JobQueueAggregatorMemc.php (renamed from includes/job/JobQueueAggregatorMemc.php)11
-rw-r--r--includes/job/aggregator/JobQueueAggregatorRedis.php (renamed from includes/job/JobQueueAggregatorRedis.php)28
-rw-r--r--includes/job/jobs/AssembleUploadChunksJob.php19
-rw-r--r--includes/job/jobs/DoubleRedirectJob.php29
-rw-r--r--includes/job/jobs/DuplicateJob.php2
-rw-r--r--includes/job/jobs/EnotifNotifyJob.php2
-rw-r--r--includes/job/jobs/HTMLCacheUpdateJob.php25
-rw-r--r--includes/job/jobs/NullJob.php16
-rw-r--r--includes/job/jobs/PublishStashedFileJob.php22
-rw-r--r--includes/job/jobs/RefreshLinksJob.php28
-rw-r--r--includes/job/jobs/UploadFromUrlJob.php7
-rw-r--r--includes/json/FormatJson.php217
-rw-r--r--includes/json/Services_JSON.php882
-rw-r--r--includes/libs/CSSJanus.php72
-rw-r--r--includes/libs/CSSMin.php47
-rw-r--r--includes/libs/HttpStatus.php2
-rw-r--r--includes/libs/lessc.inc.php3742
-rw-r--r--includes/limit.sh9
-rw-r--r--includes/logging/DeleteLogFormatter.php196
-rw-r--r--includes/logging/LogEntry.php55
-rw-r--r--includes/logging/LogEventsList.php82
-rw-r--r--includes/logging/LogFormatter.php412
-rw-r--r--includes/logging/LogPage.php62
-rw-r--r--includes/logging/LogPager.php77
-rw-r--r--includes/logging/MoveLogFormatter.php82
-rw-r--r--includes/logging/NewUsersLogFormatter.php65
-rw-r--r--includes/logging/PatrolLog.php7
-rw-r--r--includes/logging/PatrolLogFormatter.php63
-rw-r--r--includes/logging/RightsLogFormatter.php112
-rw-r--r--includes/media/BMP.php6
-rw-r--r--includes/media/Bitmap.php47
-rw-r--r--includes/media/BitmapMetadataHandler.php24
-rw-r--r--includes/media/DjVu.php21
-rw-r--r--includes/media/DjVuImage.php44
-rw-r--r--includes/media/Exif.php76
-rw-r--r--includes/media/ExifBitmap.php8
-rw-r--r--includes/media/FormatMetadata.php101
-rw-r--r--includes/media/GIF.php8
-rw-r--r--includes/media/GIFMetadataExtractor.php46
-rw-r--r--includes/media/IPTC.php8
-rw-r--r--includes/media/ImageHandler.php23
-rw-r--r--includes/media/Jpeg.php20
-rw-r--r--includes/media/JpegMetadataExtractor.php22
-rw-r--r--includes/media/MediaHandler.php149
-rw-r--r--includes/media/MediaTransformOutput.php53
-rw-r--r--includes/media/PNG.php13
-rw-r--r--includes/media/PNGMetadataExtractor.php30
-rw-r--r--includes/media/SVG.php92
-rw-r--r--includes/media/SVGMetadataExtractor.php64
-rw-r--r--includes/media/Tiff.php2
-rw-r--r--includes/media/XCF.php6
-rw-r--r--includes/media/XMP.php26
-rw-r--r--includes/media/XMPInfo.php6
-rw-r--r--includes/media/XMPValidate.php12
-rw-r--r--includes/mime.info4
-rw-r--r--includes/mime.types5
-rw-r--r--includes/normal/README10
-rw-r--r--includes/normal/RandomTest.php6
-rw-r--r--includes/normal/UtfNormal.php4
-rw-r--r--includes/normal/UtfNormalTest.php8
-rw-r--r--includes/normal/UtfNormalTest2.php2
-rw-r--r--includes/normal/UtfNormalUtil.php8
-rw-r--r--includes/objectcache/BagOStuff.php8
-rw-r--r--includes/objectcache/MemcachedBagOStuff.php2
-rw-r--r--includes/objectcache/MemcachedClient.php24
-rw-r--r--includes/objectcache/MemcachedPeclBagOStuff.php14
-rw-r--r--includes/objectcache/MultiWriteBagOStuff.php2
-rw-r--r--includes/objectcache/ObjectCache.php14
-rw-r--r--includes/objectcache/ObjectCacheSessionHandler.php2
-rw-r--r--includes/objectcache/RedisBagOStuff.php40
-rw-r--r--includes/objectcache/SqlBagOStuff.php22
-rw-r--r--includes/parser/CacheTime.php2
-rw-r--r--includes/parser/CoreLinkFunctions.php92
-rw-r--r--includes/parser/CoreParserFunctions.php197
-rw-r--r--includes/parser/CoreTagHooks.php2
-rw-r--r--includes/parser/DateFormatter.php24
-rw-r--r--includes/parser/LinkHolderArray.php216
-rw-r--r--includes/parser/Parser.php848
-rw-r--r--includes/parser/ParserCache.php20
-rw-r--r--includes/parser/ParserOptions.php21
-rw-r--r--includes/parser/ParserOutput.php111
-rw-r--r--includes/parser/Parser_DiffTest.php2
-rw-r--r--includes/parser/Parser_LinkHooks.php326
-rw-r--r--includes/parser/Preprocessor_DOM.php99
-rw-r--r--includes/parser/Preprocessor_Hash.php61
-rw-r--r--includes/parser/Tidy.php11
-rw-r--r--includes/profiler/Profiler.php264
-rw-r--r--includes/profiler/ProfilerSimple.php5
-rw-r--r--includes/profiler/ProfilerSimpleText.php12
-rw-r--r--includes/profiler/ProfilerSimpleTrace.php2
-rw-r--r--includes/profiler/ProfilerSimpleUDP.php12
-rw-r--r--includes/profiler/ProfilerStub.php2
-rw-r--r--includes/rcfeed/IRCColourfulRCFeedFormatter.php99
-rw-r--r--includes/rcfeed/JSONRCFeedFormatter.php90
-rw-r--r--includes/rcfeed/RCFeedEngine.php12
-rw-r--r--includes/rcfeed/RCFeedFormatter.php13
-rw-r--r--includes/rcfeed/RedisPubSubFeedEngine.php41
-rw-r--r--includes/rcfeed/UDPRCFeedEngine.php10
-rw-r--r--includes/resourceloader/ResourceLoader.php181
-rw-r--r--includes/resourceloader/ResourceLoaderContext.php2
-rw-r--r--includes/resourceloader/ResourceLoaderFileModule.php176
-rw-r--r--includes/resourceloader/ResourceLoaderLESSFunctions.php67
-rw-r--r--includes/resourceloader/ResourceLoaderLanguageDataModule.php31
-rw-r--r--includes/resourceloader/ResourceLoaderModule.php102
-rw-r--r--includes/resourceloader/ResourceLoaderSiteModule.php23
-rw-r--r--includes/resourceloader/ResourceLoaderStartUpModule.php10
-rw-r--r--includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php67
-rw-r--r--includes/resourceloader/ResourceLoaderUserGroupsModule.php15
-rw-r--r--includes/resourceloader/ResourceLoaderUserModule.php21
-rw-r--r--includes/resourceloader/ResourceLoaderUserOptionsModule.php4
-rw-r--r--includes/resourceloader/ResourceLoaderUserTokensModule.php9
-rw-r--r--includes/resourceloader/ResourceLoaderWikiModule.php4
-rw-r--r--includes/revisiondelete/RevisionDelete.php86
-rw-r--r--includes/revisiondelete/RevisionDeleteAbstracts.php50
-rw-r--r--includes/revisiondelete/RevisionDeleteUser.php2
-rw-r--r--includes/revisiondelete/RevisionDeleter.php149
-rw-r--r--includes/search/SearchEngine.php169
-rw-r--r--includes/search/SearchMssql.php6
-rw-r--r--includes/search/SearchMySQL.php66
-rw-r--r--includes/search/SearchOracle.php86
-rw-r--r--includes/search/SearchPostgres.php43
-rw-r--r--includes/search/SearchSqlite.php39
-rw-r--r--includes/search/SearchUpdate.php123
-rw-r--r--includes/site/MediaWikiSite.php8
-rw-r--r--includes/site/SiteSQLStore.php52
-rw-r--r--includes/specials/SpecialActiveusers.php74
-rw-r--r--includes/specials/SpecialAllmessages.php143
-rw-r--r--includes/specials/SpecialAllpages.php174
-rw-r--r--includes/specials/SpecialAncientpages.php22
-rw-r--r--includes/specials/SpecialBlankpage.php1
-rw-r--r--includes/specials/SpecialBlock.php49
-rw-r--r--includes/specials/SpecialBlockList.php52
-rw-r--r--includes/specials/SpecialBlockme.php66
-rw-r--r--includes/specials/SpecialBooksources.php49
-rw-r--r--includes/specials/SpecialBrokenRedirects.php12
-rw-r--r--includes/specials/SpecialCachedPage.php1
-rw-r--r--includes/specials/SpecialCategories.php38
-rw-r--r--includes/specials/SpecialChangeEmail.php67
-rw-r--r--includes/specials/SpecialChangePassword.php134
-rw-r--r--includes/specials/SpecialComparePages.php11
-rw-r--r--includes/specials/SpecialConfirmemail.php101
-rw-r--r--includes/specials/SpecialContributions.php380
-rw-r--r--includes/specials/SpecialDeadendpages.php25
-rw-r--r--includes/specials/SpecialDeletedContributions.php179
-rw-r--r--includes/specials/SpecialDisambiguations.php165
-rw-r--r--includes/specials/SpecialDoubleRedirects.php31
-rw-r--r--includes/specials/SpecialEditWatchlist.php174
-rw-r--r--includes/specials/SpecialEmailuser.php82
-rw-r--r--includes/specials/SpecialExport.php143
-rw-r--r--includes/specials/SpecialFewestrevisions.php53
-rw-r--r--includes/specials/SpecialFileDuplicateSearch.php51
-rw-r--r--includes/specials/SpecialFilepath.php62
-rw-r--r--includes/specials/SpecialImport.php245
-rw-r--r--includes/specials/SpecialJavaScriptTest.php56
-rw-r--r--includes/specials/SpecialLinkSearch.php71
-rw-r--r--includes/specials/SpecialListfiles.php301
-rw-r--r--includes/specials/SpecialListgrouprights.php51
-rw-r--r--includes/specials/SpecialListredirects.php34
-rw-r--r--includes/specials/SpecialListusers.php113
-rw-r--r--includes/specials/SpecialLog.php79
-rw-r--r--includes/specials/SpecialLonelypages.php48
-rw-r--r--includes/specials/SpecialLongpages.php1
-rw-r--r--includes/specials/SpecialMIMEsearch.php97
-rw-r--r--includes/specials/SpecialMergeHistory.php118
-rw-r--r--includes/specials/SpecialMostcategories.php48
-rw-r--r--includes/specials/SpecialMostimages.php19
-rw-r--r--includes/specials/SpecialMostinterwikis.php32
-rw-r--r--includes/specials/SpecialMostlinked.php69
-rw-r--r--includes/specials/SpecialMostlinkedcategories.php32
-rw-r--r--includes/specials/SpecialMostlinkedtemplates.php39
-rw-r--r--includes/specials/SpecialMovepage.php263
-rw-r--r--includes/specials/SpecialNewimages.php40
-rw-r--r--includes/specials/SpecialNewpages.php156
-rw-r--r--includes/specials/SpecialPagesWithProp.php32
-rw-r--r--includes/specials/SpecialPasswordReset.php94
-rw-r--r--includes/specials/SpecialPopularpages.php34
-rw-r--r--includes/specials/SpecialPreferences.php26
-rw-r--r--includes/specials/SpecialPrefixindex.php147
-rw-r--r--includes/specials/SpecialProtectedpages.php169
-rw-r--r--includes/specials/SpecialProtectedtitles.php80
-rw-r--r--includes/specials/SpecialRandomInCategory.php291
-rw-r--r--includes/specials/SpecialRandompage.php31
-rw-r--r--includes/specials/SpecialRandomredirect.php1
-rw-r--r--includes/specials/SpecialRecentchanges.php410
-rw-r--r--includes/specials/SpecialRecentchangeslinked.php63
-rw-r--r--includes/specials/SpecialRedirect.php235
-rw-r--r--includes/specials/SpecialResetTokens.php145
-rw-r--r--includes/specials/SpecialRevisiondelete.php170
-rw-r--r--includes/specials/SpecialSearch.php198
-rw-r--r--includes/specials/SpecialShortpages.php18
-rw-r--r--includes/specials/SpecialSpecialpages.php17
-rw-r--r--includes/specials/SpecialStatistics.php37
-rw-r--r--includes/specials/SpecialTags.php69
-rw-r--r--includes/specials/SpecialUnblock.php22
-rw-r--r--includes/specials/SpecialUncategorizedcategories.php6
-rw-r--r--includes/specials/SpecialUncategorizedimages.php2
-rw-r--r--includes/specials/SpecialUncategorizedpages.php13
-rw-r--r--includes/specials/SpecialUndelete.php680
-rw-r--r--includes/specials/SpecialUnusedcategories.php15
-rw-r--r--includes/specials/SpecialUnusedimages.php16
-rw-r--r--includes/specials/SpecialUnusedtemplates.php16
-rw-r--r--includes/specials/SpecialUnwatchedpages.php16
-rw-r--r--includes/specials/SpecialUpload.php126
-rw-r--r--includes/specials/SpecialUploadStash.php29
-rw-r--r--includes/specials/SpecialUserlogin.php369
-rw-r--r--includes/specials/SpecialUserrights.php162
-rw-r--r--includes/specials/SpecialVersion.php203
-rw-r--r--includes/specials/SpecialWantedcategories.php18
-rw-r--r--includes/specials/SpecialWantedfiles.php16
-rw-r--r--includes/specials/SpecialWantedtemplates.php14
-rw-r--r--includes/specials/SpecialWatchlist.php192
-rw-r--r--includes/specials/SpecialWhatlinkshere.php69
-rw-r--r--includes/specials/SpecialWithoutinterwiki.php28
-rw-r--r--includes/templates/NoLocalSettings.php14
-rw-r--r--includes/templates/Usercreate.php445
-rw-r--r--includes/templates/Userlogin.php278
-rw-r--r--includes/tidy.conf2
-rw-r--r--includes/upload/UploadBase.php202
-rw-r--r--includes/upload/UploadFromChunks.php26
-rw-r--r--includes/upload/UploadFromFile.php2
-rw-r--r--includes/upload/UploadFromStash.php6
-rw-r--r--includes/upload/UploadFromUrl.php49
-rw-r--r--includes/upload/UploadStash.php29
534 files changed, 37258 insertions, 22541 deletions
diff --git a/includes/Action.php b/includes/Action.php
index 2e0c88ba..4b6e4468 100644
--- a/includes/Action.php
+++ b/includes/Action.php
@@ -59,7 +59,7 @@ abstract class Action {
* the action is disabled, or null if it's not recognised
* @param $action String
* @param $overrides Array
- * @return bool|null|string
+ * @return bool|null|string|callable
*/
final private static function getClass( $action, array $overrides ) {
global $wgActions;
@@ -89,12 +89,18 @@ abstract class Action {
* if it is not recognised
*/
final public static function factory( $action, Page $page, IContextSource $context = null ) {
- $class = self::getClass( $action, $page->getActionOverrides() );
- if ( $class ) {
- $obj = new $class( $page, $context );
+ $classOrCallable = self::getClass( $action, $page->getActionOverrides() );
+
+ if ( is_string( $classOrCallable ) ) {
+ $obj = new $classOrCallable( $page, $context );
return $obj;
}
- return $class;
+
+ if ( is_callable( $classOrCallable ) ) {
+ return call_user_func_array( $classOrCallable, array( $page, $context ) );
+ }
+
+ return $classOrCallable;
}
/**
@@ -136,7 +142,7 @@ abstract class Action {
return 'view';
}
- $action = Action::factory( $actionName, $context->getWikiPage() );
+ $action = Action::factory( $actionName, $context->getWikiPage(), $context );
if ( $action instanceof Action ) {
return $action->getName();
}
@@ -161,8 +167,14 @@ abstract class Action {
final public function getContext() {
if ( $this->context instanceof IContextSource ) {
return $this->context;
+ } else if ( $this->page instanceof Article ) {
+ // NOTE: $this->page can be a WikiPage, which does not have a context.
+ wfDebug( __METHOD__ . ': no context known, falling back to Article\'s context.' );
+ return $this->page->getContext();
}
- return $this->page->getContext();
+
+ wfWarn( __METHOD__ . ': no context known, falling back to RequestContext::getMain().' );
+ return RequestContext::getMain();
}
/**
@@ -213,7 +225,7 @@ abstract class Action {
/**
* Shortcut to get the user Language being used for this instance
*
- * @deprecated 1.19 Use getLanguage instead
+ * @deprecated since 1.19 Use getLanguage instead
* @return Language
*/
final public function getLang() {
@@ -241,12 +253,20 @@ abstract class Action {
}
/**
- * Protected constructor: use Action::factory( $action, $page ) to actually build
- * these things in the real world
+ * Constructor.
+ *
+ * Only public since 1.21
+ *
* @param $page Page
* @param $context IContextSource
*/
- protected function __construct( Page $page, IContextSource $context = null ) {
+ public function __construct( Page $page, IContextSource $context = null ) {
+ if ( $context === null ) {
+ wfWarn( __METHOD__ . ' called without providing a Context object.' );
+ // NOTE: We could try to initialize $context using $page->getContext(),
+ // if $page is an Article. That however seems to not work seamlessly.
+ }
+
$this->page = $page;
$this->context = $context;
}
@@ -374,18 +394,23 @@ abstract class FormAction extends Action {
* Add pre- or post-text to the form
* @return String HTML which will be sent to $form->addPreText()
*/
- protected function preText() { return ''; }
+ protected function preText() {
+ return '';
+ }
/**
* @return string
*/
- protected function postText() { return ''; }
+ protected function postText() {
+ return '';
+ }
/**
* Play with the HTMLForm if you need to more substantially
* @param $form HTMLForm
*/
- protected function alterForm( HTMLForm $form ) {}
+ protected function alterForm( HTMLForm $form ) {
+ }
/**
* Get the HTMLForm to control behavior
@@ -464,7 +489,7 @@ abstract class FormAction extends Action {
public function execute( array $data = null, $captureErrors = true ) {
try {
// Set a new context so output doesn't leak.
- $this->context = clone $this->page->getContext();
+ $this->context = clone $this->getContext();
// This will throw exceptions if there's a problem
$this->checkCanExecute( $this->getUser() );
@@ -553,7 +578,7 @@ abstract class FormlessAction extends Action {
public function execute( array $data = null, $captureErrors = true ) {
try {
// Set a new context so output doesn't leak.
- $this->context = clone $this->page->getContext();
+ $this->context = clone $this->getContext();
if ( is_array( $data ) ) {
$this->context->setRequest( new FauxRequest( $data, false ) );
}
diff --git a/includes/AjaxDispatcher.php b/includes/AjaxDispatcher.php
index b00cf309..c9ca1283 100644
--- a/includes/AjaxDispatcher.php
+++ b/includes/AjaxDispatcher.php
@@ -63,7 +63,7 @@ class AjaxDispatcher {
$this->mode = "post";
}
- switch( $this->mode ) {
+ switch ( $this->mode ) {
case 'get':
$this->func_name = isset( $_GET["rs"] ) ? $_GET["rs"] : '';
if ( ! empty( $_GET["rsargs"] ) ) {
@@ -111,15 +111,13 @@ class AjaxDispatcher {
wfHttpError(
400,
'Bad Request',
- "unknown function " . (string) $this->func_name
+ "unknown function " . $this->func_name
);
- } elseif ( !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true )
- && !$wgUser->isAllowed( 'read' ) )
- {
+ } elseif ( !User::isEveryoneAllowed( 'read' ) && !$wgUser->isAllowed( 'read' ) ) {
wfHttpError(
403,
'Forbidden',
- 'You must log in to view pages.' );
+ 'You are not allowed to view pages.' );
} else {
wfDebug( __METHOD__ . ' dispatching ' . $this->func_name . "\n" );
diff --git a/includes/AjaxResponse.php b/includes/AjaxResponse.php
index 138f808a..d5536529 100644
--- a/includes/AjaxResponse.php
+++ b/includes/AjaxResponse.php
@@ -210,7 +210,7 @@ class AjaxResponse {
* @param $timestamp string
* @return bool Returns true if the response code was set to 304 Not Modified.
*/
- function checkLastModified ( $timestamp ) {
+ function checkLastModified( $timestamp ) {
global $wgCachePages, $wgCacheEpoch, $wgUser;
$fname = 'AjaxResponse::checkLastModified';
diff --git a/includes/ArrayUtils.php b/includes/ArrayUtils.php
index 0b74f06a..985271f7 100644
--- a/includes/ArrayUtils.php
+++ b/includes/ArrayUtils.php
@@ -39,7 +39,7 @@ class ArrayUtils {
*
* @return bool|int|string
*/
- public static function pickRandom( $weights ){
+ public static function pickRandom( $weights ) {
if ( !is_array( $weights ) || count( $weights ) == 0 ) {
return false;
}
diff --git a/includes/Article.php b/includes/Article.php
index 9b4afe44..0b18221a 100644
--- a/includes/Article.php
+++ b/includes/Article.php
@@ -25,7 +25,7 @@
*
* This maintains WikiPage functions for backwards compatibility.
*
- * @todo move and rewrite code to an Action class
+ * @todo Move and rewrite code to an Action class
*
* See design.txt for an overview.
* Note: edit user interface and cache support functions have been
@@ -160,7 +160,7 @@ class Article implements Page {
$page = null;
wfRunHooks( 'ArticleFromTitle', array( &$title, &$page ) );
if ( !$page ) {
- switch( $title->getNamespace() ) {
+ switch ( $title->getNamespace() ) {
case NS_FILE:
$page = new ImagePage( $title );
break;
@@ -385,7 +385,8 @@ class Article implements Page {
$content = $this->fetchContentObject();
- $this->mContent = ContentHandler::getContentText( $content ); #@todo: get rid of mContent everywhere!
+ // @todo Get rid of mContent everywhere!
+ $this->mContent = ContentHandler::getContentText( $content );
ContentHandler::runLegacyHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) );
wfProfileOut( __METHOD__ );
@@ -609,7 +610,7 @@ class Article implements Page {
$this->mParserOutput = false;
while ( !$outputDone && ++$pass ) {
- switch( $pass ) {
+ switch ( $pass ) {
case 1:
wfRunHooks( 'ArticleViewHeader', array( &$this, &$outputDone, &$useParserCache ) );
break;
@@ -673,12 +674,12 @@ class Article implements Page {
wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
$this->showCssOrJsPage();
$outputDone = true;
- } elseif( !wfRunHooks( 'ArticleContentViewCustom',
+ } elseif ( !wfRunHooks( 'ArticleContentViewCustom',
array( $this->fetchContentObject(), $this->getTitle(), $outputPage ) ) ) {
# Allow extensions do their own custom view for certain pages
$outputDone = true;
- } elseif( !ContentHandler::runLegacyHooks( 'ArticleViewCustom',
+ } elseif ( !ContentHandler::runLegacyHooks( 'ArticleViewCustom',
array( $this->fetchContentObject(), $this->getTitle(), $outputPage ) ) ) {
# Allow extensions do their own custom view for certain pages
@@ -787,7 +788,7 @@ class Article implements Page {
* Show a diff page according to current request variables. For use within
* Article::view() only, other callers should use the DifferenceEngine class.
*
- * @todo: make protected
+ * @todo Make protected
*/
public function showDiffPage() {
$request = $this->getContext()->getRequest();
@@ -854,11 +855,11 @@ class Article implements Page {
/**
* Get the robot policy to be used for the current view
* @param string $action the action= GET parameter
- * @param $pOutput ParserOutput
+ * @param $pOutput ParserOutput|null
* @return Array the policy that should be set
* TODO: actions other than 'view'
*/
- public function getRobotPolicy( $action, $pOutput ) {
+ public function getRobotPolicy( $action, $pOutput = null ) {
global $wgArticleRobotPolicies, $wgNamespaceRobotPolicies, $wgDefaultRobotPolicy;
$ns = $this->getTitle()->getNamespace();
@@ -875,7 +876,7 @@ class Article implements Page {
}
if ( Block::newFromTarget( $specificTarget, $vagueTarget ) instanceof Block ) {
return array(
- 'index' => 'noindex',
+ 'index' => 'noindex',
'follow' => 'nofollow'
);
}
@@ -884,19 +885,19 @@ class Article implements Page {
if ( $this->mPage->getID() === 0 || $this->getOldID() ) {
# Non-articles (special pages etc), and old revisions
return array(
- 'index' => 'noindex',
+ 'index' => 'noindex',
'follow' => 'nofollow'
);
} elseif ( $this->getContext()->getOutput()->isPrintable() ) {
# Discourage indexing of printable versions, but encourage following
return array(
- 'index' => 'noindex',
+ 'index' => 'noindex',
'follow' => 'follow'
);
} elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) {
# For ?curid=x urls, disallow indexing
return array(
- 'index' => 'noindex',
+ 'index' => 'noindex',
'follow' => 'follow'
);
}
@@ -988,8 +989,9 @@ class Article implements Page {
// Set the fragment if one was specified in the redirect
if ( strval( $this->getTitle()->getFragment() ) != '' ) {
- $fragment = Xml::escapeJsString( $this->getTitle()->getFragmentForURL() );
- $outputPage->addInlineScript( "redirectToFragment(\"$fragment\");" );
+ $outputPage->addInlineScript( Xml::encodeJsCall(
+ 'redirectToFragment', array( $this->getTitle()->getFragmentForURL() )
+ ) );
}
// Add a <link rel="canonical"> tag
@@ -1035,11 +1037,10 @@ class Article implements Page {
$this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' );
}
- # If we have been passed an &rcid= parameter, we want to give the user a
- # chance to mark this new article as patrolled.
- $this->showPatrolFooter();
+ // Show a footer allowing the user to patrol the shown revision or page if possible
+ $patrolFooterShown = $this->showPatrolFooter();
- wfRunHooks( 'ArticleViewFooter', array( $this ) );
+ wfRunHooks( 'ArticleViewFooter', array( $this, $patrolFooterShown ) );
}
@@ -1049,21 +1050,92 @@ class Article implements Page {
* desired, does nothing.
* Side effect: When the patrol link is build, this method will call
* OutputPage::preventClickjacking() and load mediawiki.page.patrol.ajax.
+ *
+ * @return bool
*/
public function showPatrolFooter() {
- $request = $this->getContext()->getRequest();
+ global $wgUseNPPatrol, $wgUseRCPatrol, $wgEnableAPI, $wgEnableWriteAPI;
+
$outputPage = $this->getContext()->getOutput();
$user = $this->getContext()->getUser();
- $rcid = $request->getVal( 'rcid' );
+ $cache = wfGetMainCache();
+ $rc = false;
- if ( !$rcid || !$this->getTitle()->quickUserCan( 'patrol', $user ) ) {
- return;
+ if ( !$this->getTitle()->quickUserCan( 'patrol', $user ) || !( $wgUseRCPatrol || $wgUseNPPatrol ) ) {
+ // Patrolling is disabled or the user isn't allowed to
+ return false;
+ }
+
+ wfProfileIn( __METHOD__ );
+
+ // New page patrol: Get the timestamp of the oldest revison which
+ // the revision table holds for the given page. Then we look
+ // whether it's within the RC lifespan and if it is, we try
+ // to get the recentchanges row belonging to that entry
+ // (with rc_new = 1).
+
+ // Check for cached results
+ if ( $cache->get( wfMemcKey( 'NotPatrollablePage', $this->getTitle()->getArticleID() ) ) ) {
+ wfProfileOut( __METHOD__ );
+ return false;
}
+ if ( $this->mRevision && !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 ) ) {
+ // The current revision is already older than what could be in the RC table
+ // 6h tolerance because the RC might not be cleaned out regularly
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+
+ $dbr = wfGetDB( DB_SLAVE );
+ $oldestRevisionTimestamp = $dbr->selectField(
+ 'revision',
+ 'MIN( rev_timestamp )',
+ array( 'rev_page' => $this->getTitle()->getArticleID() ),
+ __METHOD__
+ );
+
+ if ( $oldestRevisionTimestamp && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 ) ) {
+ // 6h tolerance because the RC might not be cleaned out regularly
+ $rc = RecentChange::newFromConds(
+ array(
+ 'rc_new' => 1,
+ 'rc_timestamp' => $oldestRevisionTimestamp,
+ 'rc_namespace' => $this->getTitle()->getNamespace(),
+ 'rc_cur_id' => $this->getTitle()->getArticleID(),
+ 'rc_patrolled' => 0
+ ),
+ __METHOD__,
+ array( 'USE INDEX' => 'new_name_timestamp' )
+ );
+ }
+
+ if ( !$rc ) {
+ // No RC entry around
+
+ // Cache the information we gathered above in case we can't patrol
+ // Don't cache in case we can patrol as this could change
+ $cache->set( wfMemcKey( 'NotPatrollablePage', $this->getTitle()->getArticleID() ), '1' );
+
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+
+ if ( $rc->getPerformer()->getName() == $user->getName() ) {
+ // Don't show a patrol link for own creations. If the user could
+ // patrol them, they already would be patrolled
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+
+ $rcid = $rc->getAttribute( 'rc_id' );
+
$token = $user->getEditToken( $rcid );
$outputPage->preventClickjacking();
- $outputPage->addModules( 'mediawiki.page.patrol.ajax' );
+ if ( $wgEnableAPI && $wgEnableWriteAPI && $user->isAllowed( 'writeapi' ) ) {
+ $outputPage->addModules( 'mediawiki.page.patrol.ajax' );
+ }
$link = Linker::linkKnown(
$this->getTitle(),
@@ -1081,6 +1153,9 @@ class Article implements Page {
wfMessage( 'markaspatrolledlink' )->rawParams( $link )->escaped() .
'</div>'
);
+
+ wfProfileOut( __METHOD__ );
+ return true;
}
/**
@@ -1090,6 +1165,7 @@ class Article implements Page {
public function showMissingArticle() {
global $wgSend404Code;
$outputPage = $this->getContext()->getOutput();
+ // Whether the page is a root user page of an existing user (but not a subpage)
$validUserPage = false;
# Show info in user (talk) namespace. Does the user exist? Is he blocked?
@@ -1099,7 +1175,7 @@ class Article implements Page {
$user = User::newFromName( $rootPart, false /* allow IP users*/ );
$ip = User::isIP( $rootPart );
- if ( !($user && $user->isLoggedIn()) && !$ip ) { # User does not exist
+ if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist
$outputPage->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
array( 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ) );
} elseif ( $user->isBlocked() ) { # Show log extract if the user is currently blocked
@@ -1117,9 +1193,9 @@ class Article implements Page {
)
)
);
- $validUserPage = true;
+ $validUserPage = !$this->getTitle()->isSubpage();
} else {
- $validUserPage = true;
+ $validUserPage = !$this->getTitle()->isSubpage();
}
}
@@ -1139,6 +1215,13 @@ class Article implements Page {
$this->getContext()->getRequest()->response()->header( "HTTP/1.1 404 Not Found" );
}
+ if ( $validUserPage ) {
+ // Also apply the robot policy for nonexisting user pages (as those aren't served as 404)
+ $policy = $this->getRobotPolicy( 'view' );
+ $outputPage->setIndexPolicy( $policy['index'] );
+ $outputPage->setFollowPolicy( $policy['follow'] );
+ }
+
$hookResult = wfRunHooks( 'BeforeDisplayNoArticleText', array( $this ) );
if ( ! $hookResult ) {
@@ -1188,7 +1271,7 @@ class Article implements Page {
} elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) {
# Give explanation and add a link to view the revision...
$oldid = intval( $this->getOldID() );
- $link = $this->getTitle()->getFullUrl( "oldid={$oldid}&unhide=1" );
+ $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
$msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
$outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
@@ -1470,13 +1553,7 @@ class Article implements Page {
$this->doDelete( $reason, $suppress );
- if ( $user->isLoggedIn() && $request->getCheck( 'wpWatch' ) != $user->isWatched( $title ) ) {
- if ( $request->getCheck( 'wpWatch' ) ) {
- WatchAction::doWatch( $title, $user );
- } else {
- WatchAction::doUnwatch( $title, $user );
- }
- }
+ WatchAction::doWatchOrUnwatch( $request->getCheck( 'wpWatch' ), $title, $user );
return;
}
diff --git a/includes/AuthPlugin.php b/includes/AuthPlugin.php
index a4658176..84cf3d5e 100644
--- a/includes/AuthPlugin.php
+++ b/includes/AuthPlugin.php
@@ -213,6 +213,19 @@ class AuthPlugin {
}
/**
+ * Update user groups in the external authentication database.
+ * Return true if successful.
+ *
+ * @param $user User object.
+ * @param $addgroups Groups to add.
+ * @param $delgroups Groups to remove.
+ * @return Boolean
+ */
+ public function updateExternalDBGroups( $user, $addgroups, $delgroups = array() ) {
+ return true;
+ }
+
+ /**
* Check to see if external accounts can be created.
* Return true if external accounts can be created.
* @return Boolean
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index 3555e2c3..0706fe3f 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -55,7 +55,6 @@ $wgAutoloadLocalClasses = array(
'CdbWriter_DBA' => 'includes/Cdb.php',
'CdbWriter_PHP' => 'includes/Cdb_PHP.php',
'ChangesFeed' => 'includes/ChangesFeed.php',
- 'ChangesList' => 'includes/ChangesList.php',
'ChangeTags' => 'includes/ChangeTags.php',
'ChannelFeed' => 'includes/Feed.php',
'Collation' => 'includes/Collation.php',
@@ -65,10 +64,10 @@ $wgAutoloadLocalClasses = array(
'ConfEditorToken' => 'includes/ConfEditor.php',
'Cookie' => 'includes/Cookie.php',
'CookieJar' => 'includes/Cookie.php',
- 'MWCryptRand' => 'includes/CryptRand.php',
'CurlHttpRequest' => 'includes/HttpFunctions.php',
'DeferrableUpdate' => 'includes/DeferredUpdates.php',
'DeferredUpdates' => 'includes/DeferredUpdates.php',
+ 'MWCallableUpdate' => 'includes/CallableUpdate.php',
'DeprecatedGlobal' => 'includes/DeprecatedGlobal.php',
'DerivativeRequest' => 'includes/WebRequest.php',
'DiffHistoryBlob' => 'includes/HistoryBlob.php',
@@ -87,16 +86,8 @@ $wgAutoloadLocalClasses = array(
'DumpPipeOutput' => 'includes/Export.php',
'EditPage' => 'includes/EditPage.php',
'EmailNotification' => 'includes/UserMailer.php',
- 'EnhancedChangesList' => 'includes/ChangesList.php',
'ErrorPageError' => 'includes/Exception.php',
'ExplodeIterator' => 'includes/StringUtils.php',
- 'ExternalEdit' => 'includes/ExternalEdit.php',
- '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',
- 'ExternalUser' => 'includes/ExternalUser.php',
'FakeTitle' => 'includes/FakeTitle.php',
'Fallback' => 'includes/Fallback.php',
'FatalError' => 'includes/Exception.php',
@@ -111,22 +102,27 @@ $wgAutoloadLocalClasses = array(
'FormOptions' => 'includes/FormOptions.php',
'FormSpecialPage' => 'includes/SpecialPage.php',
'GitInfo' => 'includes/GitInfo.php',
+ 'HashRing' => 'includes/HashRing.php',
'HashtableReplacer' => 'includes/StringUtils.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.php',
+ 'HTMLButtonField' => 'includes/HTMLForm.php',
'HTMLCheckField' => 'includes/HTMLForm.php',
'HTMLCheckMatrix' => 'includes/HTMLForm.php',
'HTMLEditTools' => 'includes/HTMLForm.php',
'HTMLFloatField' => 'includes/HTMLForm.php',
'HTMLForm' => 'includes/HTMLForm.php',
'HTMLFormField' => 'includes/HTMLForm.php',
+ 'HTMLFormFieldRequiredOptionsException' => 'includes/HTMLForm.php',
'HTMLHiddenField' => 'includes/HTMLForm.php',
'HTMLInfoField' => 'includes/HTMLForm.php',
'HTMLIntField' => 'includes/HTMLForm.php',
+ 'HTMLNestedFilterable' => 'includes/HTMLForm.php',
'HTMLMultiSelectField' => 'includes/HTMLForm.php',
'HTMLRadioField' => 'includes/HTMLForm.php',
'HTMLSelectAndOtherField' => 'includes/HTMLForm.php',
@@ -140,7 +136,6 @@ $wgAutoloadLocalClasses = array(
'ICacheHelper' => 'includes/CacheHelper.php',
'IcuCollation' => 'includes/Collation.php',
'IdentityCollation' => 'includes/Collation.php',
- 'ImageGallery' => 'includes/ImageGallery.php',
'ImageHistoryList' => 'includes/ImagePage.php',
'ImageHistoryPseudoPager' => 'includes/ImagePage.php',
'ImagePage' => 'includes/ImagePage.php',
@@ -156,7 +151,6 @@ $wgAutoloadLocalClasses = array(
'LCStore_CDB' => 'includes/cache/LocalisationCache.php',
'LCStore_DB' => 'includes/cache/LocalisationCache.php',
'LCStore_Null' => 'includes/cache/LocalisationCache.php',
- 'LegacyTemplate' => 'includes/SkinLegacy.php',
'License' => 'includes/Licenses.php',
'Licenses' => 'includes/Licenses.php',
'Linker' => 'includes/Linker.php',
@@ -174,6 +168,7 @@ $wgAutoloadLocalClasses = array(
'Message' => 'includes/Message.php',
'MessageBlobStore' => 'includes/MessageBlobStore.php',
'MimeMagic' => 'includes/MimeMagic.php',
+ 'MWCryptRand' => 'includes/MWCryptRand.php',
'MWException' => 'includes/Exception.php',
'MWExceptionHandler' => 'includes/Exception.php',
'MWFunction' => 'includes/MWFunction.php',
@@ -181,7 +176,6 @@ $wgAutoloadLocalClasses = array(
'MWHttpRequest' => 'includes/HttpFunctions.php',
'MWInit' => 'includes/Init.php',
'MWNamespace' => 'includes/Namespace.php',
- 'OldChangesList' => 'includes/ChangesList.php',
'OutputPage' => 'includes/OutputPage.php',
'Page' => 'includes/WikiPage.php',
'PageQueryPage' => 'includes/PageQueryPage.php',
@@ -194,6 +188,7 @@ $wgAutoloadLocalClasses = array(
'PoolCounter' => 'includes/PoolCounter.php',
'PoolCounter_Stub' => 'includes/PoolCounter.php',
'PoolCounterWork' => 'includes/PoolCounter.php',
+ 'PoolCounterWorkViaCallback' => 'includes/PoolCounter.php',
'PoolWorkArticleView' => 'includes/WikiPage.php',
'Preferences' => 'includes/Preferences.php',
'PreferencesForm' => 'includes/Preferences.php',
@@ -202,10 +197,8 @@ $wgAutoloadLocalClasses = array(
'QueryPage' => 'includes/QueryPage.php',
'QuickTemplate' => 'includes/SkinTemplate.php',
'RawMessage' => 'includes/Message.php',
- 'RCCacheEntry' => 'includes/ChangesList.php',
'RdfMetaData' => 'includes/Metadata.php',
'ReadOnlyError' => 'includes/Exception.php',
- 'RecentChange' => 'includes/RecentChange.php',
'RedirectSpecialArticle' => 'includes/SpecialPage.php',
'RedirectSpecialPage' => 'includes/SpecialPage.php',
'RegexlikeReplacer' => 'includes/StringUtils.php',
@@ -228,7 +221,6 @@ $wgAutoloadLocalClasses = array(
'SiteStatsInit' => 'includes/SiteStats.php',
'SiteStatsUpdate' => 'includes/SiteStats.php',
'Skin' => 'includes/Skin.php',
- 'SkinLegacy' => 'includes/SkinLegacy.php',
'SkinTemplate' => 'includes/SkinTemplate.php',
'SpecialCreateAccount' => 'includes/SpecialPage.php',
'SpecialListAdmins' => 'includes/SpecialPage.php',
@@ -237,11 +229,13 @@ $wgAutoloadLocalClasses = array(
'SpecialMypage' => 'includes/SpecialPage.php',
'SpecialMytalk' => 'includes/SpecialPage.php',
'SpecialMyuploads' => 'includes/SpecialPage.php',
+ 'SpecialAllMyUploads' => 'includes/SpecialPage.php',
'SpecialPage' => 'includes/SpecialPage.php',
'SpecialPageFactory' => 'includes/SpecialPageFactory.php',
'SpecialRedirectToSpecial' => 'includes/SpecialPage.php',
'SquidPurgeClient' => 'includes/SquidPurgeClient.php',
'SquidPurgeClientPool' => 'includes/SquidPurgeClient.php',
+ 'StatCounter' => 'includes/StatCounter.php',
'Status' => 'includes/Status.php',
'StreamFile' => 'includes/StreamFile.php',
'StringUtils' => 'includes/StringUtils.php',
@@ -415,6 +409,7 @@ $wgAutoloadLocalClasses = array(
'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',
@@ -457,6 +452,13 @@ $wgAutoloadLocalClasses = array(
'TitleDependency' => 'includes/cache/CacheDependency.php',
'TitleListDependency' => 'includes/cache/CacheDependency.php',
+ # includes/changes
+ 'ChangesList' => 'includes/changes/ChangesList.php',
+ 'EnhancedChangesList' => 'includes/changes/EnhancedChangesList.php',
+ 'OldChangesList' => 'includes/changes/OldChangesList.php',
+ 'RCCacheEntry' => 'includes/changes/RCCacheEntry.php',
+ 'RecentChange' => 'includes/changes/RecentChange.php',
+
# includes/clientpool
'RedisConnectionPool' => 'includes/clientpool/RedisConnectionPool.php',
'RedisConnRef' => 'includes/clientpool/RedisConnectionPool.php',
@@ -473,11 +475,13 @@ $wgAutoloadLocalClasses = array(
# includes/db
'Blob' => 'includes/db/DatabaseUtility.php',
- 'ChronologyProtector' => 'includes/db/LBFactory.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',
@@ -485,8 +489,10 @@ $wgAutoloadLocalClasses = array(
'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',
'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',
@@ -507,8 +513,8 @@ $wgAutoloadLocalClasses = array(
'LoadMonitor_Null' => 'includes/db/LoadMonitor.php',
'MssqlField' => 'includes/db/DatabaseMssql.php',
'MssqlResult' => 'includes/db/DatabaseMssql.php',
- 'MySQLField' => 'includes/db/DatabaseMysql.php',
- 'MySQLMasterPos' => 'includes/db/DatabaseMysql.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',
@@ -543,14 +549,17 @@ $wgAutoloadLocalClasses = array(
'WikiDiff3' => 'includes/diff/WikiDiff3.php',
'WordLevelDiff' => 'includes/diff/DairikiDiff.php',
- # includes/extauth
- 'ExternalUser_Hardcoded' => 'includes/extauth/Hardcoded.php',
- 'ExternalUser_MediaWiki' => 'includes/extauth/MediaWiki.php',
- 'ExternalUser_vB' => 'includes/extauth/vB.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',
'FileBackendStore' => 'includes/filebackend/FileBackendStore.php',
'FileBackendStoreShardListIterator' => 'includes/filebackend/FileBackendStore.php',
'FileBackendStoreShardDirIterator' => 'includes/filebackend/FileBackendStore.php',
@@ -582,6 +591,7 @@ $wgAutoloadLocalClasses = array(
'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',
@@ -657,11 +667,15 @@ $wgAutoloadLocalClasses = array(
# includes/job
'Job' => 'includes/job/Job.php',
'JobQueue' => 'includes/job/JobQueue.php',
- 'JobQueueAggregator' => 'includes/job/JobQueueAggregator.php',
- 'JobQueueAggregatorMemc' => 'includes/job/JobQueueAggregatorMemc.php',
- 'JobQueueAggregatorRedis' => 'includes/job/JobQueueAggregatorRedis.php',
+ 'JobQueueAggregator' => 'includes/job/aggregator/JobQueueAggregator.php',
+ 'JobQueueAggregatorMemc' => 'includes/job/aggregator/JobQueueAggregatorMemc.php',
+ 'JobQueueAggregatorRedis' => 'includes/job/aggregator/JobQueueAggregatorRedis.php',
'JobQueueDB' => 'includes/job/JobQueueDB.php',
+ 'JobQueueConnectionError' => 'includes/job/JobQueue.php',
+ 'JobQueueError' => 'includes/job/JobQueue.php',
'JobQueueGroup' => 'includes/job/JobQueueGroup.php',
+ 'JobQueueFederated' => 'includes/job/JobQueueFederated.php',
+ 'JobQueueRedis' => 'includes/job/JobQueueRedis.php',
# includes/job/jobs
'DoubleRedirectJob' => 'includes/job/jobs/DoubleRedirectJob.php',
@@ -678,8 +692,6 @@ $wgAutoloadLocalClasses = array(
# includes/json
'FormatJson' => 'includes/json/FormatJson.php',
- 'Services_JSON' => 'includes/json/Services_JSON.php',
- 'Services_JSON_Error' => 'includes/json/Services_JSON.php',
# includes/libs
'CSSJanus' => 'includes/libs/CSSJanus.php',
@@ -697,9 +709,16 @@ $wgAutoloadLocalClasses = array(
'JSToken' => 'includes/libs/jsminplus.php',
'JSTokenizer' => 'includes/libs/jsminplus.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/LogFormatter.php',
+ 'DeleteLogFormatter' => 'includes/logging/DeleteLogFormatter.php',
'LegacyLogFormatter' => 'includes/logging/LogFormatter.php',
'LogEntry' => 'includes/logging/LogEntry.php',
'LogEventsList' => 'includes/logging/LogEventsList.php',
@@ -708,12 +727,22 @@ $wgAutoloadLocalClasses = array(
'LogPage' => 'includes/logging/LogPage.php',
'LogPager' => 'includes/logging/LogPager.php',
'ManualLogEntry' => 'includes/logging/LogEntry.php',
- 'MoveLogFormatter' => 'includes/logging/LogFormatter.php',
- 'NewUsersLogFormatter' => 'includes/logging/LogFormatter.php',
+ 'MoveLogFormatter' => 'includes/logging/MoveLogFormatter.php',
+ 'NewUsersLogFormatter' => 'includes/logging/NewUsersLogFormatter.php',
'PatrolLog' => 'includes/logging/PatrolLog.php',
- 'PatrolLogFormatter' => 'includes/logging/LogFormatter.php',
+ 'PatrolLogFormatter' => 'includes/logging/PatrolLogFormatter.php',
'RCDatabaseLogEntry' => 'includes/logging/LogEntry.php',
- 'RightsLogFormatter' => 'includes/logging/LogFormatter.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/media
'BitmapHandler' => 'includes/media/Bitmap.php',
@@ -775,12 +804,10 @@ $wgAutoloadLocalClasses = array(
# includes/parser
'CacheTime' => 'includes/parser/CacheTime.php',
- 'CoreLinkFunctions' => 'includes/parser/CoreLinkFunctions.php',
'CoreParserFunctions' => 'includes/parser/CoreParserFunctions.php',
'CoreTagHooks' => 'includes/parser/CoreTagHooks.php',
'DateFormatter' => 'includes/parser/DateFormatter.php',
'LinkHolderArray' => 'includes/parser/LinkHolderArray.php',
- 'LinkMarkerReplacer' => 'includes/parser/Parser_LinkHooks.php',
'MWTidy' => 'includes/parser/Tidy.php',
'MWTidyWrapper' => 'includes/parser/Tidy.php',
'PPCustomFrame_DOM' => 'includes/parser/Preprocessor_DOM.php',
@@ -808,7 +835,6 @@ $wgAutoloadLocalClasses = array(
'ParserOptions' => 'includes/parser/ParserOptions.php',
'ParserOutput' => 'includes/parser/ParserOutput.php',
'Parser_DiffTest' => 'includes/parser/Parser_DiffTest.php',
- 'Parser_LinkHooks' => 'includes/parser/Parser_LinkHooks.php',
'Preprocessor' => 'includes/parser/Preprocessor.php',
'Preprocessor_DOM' => 'includes/parser/Preprocessor_DOM.php',
'Preprocessor_Hash' => 'includes/parser/Preprocessor_Hash.php',
@@ -821,12 +847,22 @@ $wgAutoloadLocalClasses = array(
'ProfilerSimpleTrace' => 'includes/profiler/ProfilerSimpleTrace.php',
'ProfilerSimpleUDP' => 'includes/profiler/ProfilerSimpleUDP.php',
'ProfilerStub' => 'includes/profiler/ProfilerStub.php',
+ 'ProfileSection' => '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',
# includes/resourceloader
'ResourceLoader' => 'includes/resourceloader/ResourceLoader.php',
'ResourceLoaderContext' => 'includes/resourceloader/ResourceLoaderContext.php',
'ResourceLoaderFileModule' => 'includes/resourceloader/ResourceLoaderFileModule.php',
'ResourceLoaderFilePageModule' => 'includes/resourceloader/ResourceLoaderFilePageModule.php',
+ 'ResourceLoaderLESSFunctions' => 'includes/resourceloader/ResourceLoaderLESSFunctions.php',
'ResourceLoaderModule' => 'includes/resourceloader/ResourceLoaderModule.php',
'ResourceLoaderNoscriptModule' => 'includes/resourceloader/ResourceLoaderNoscriptModule.php',
'ResourceLoaderSiteModule' => 'includes/resourceloader/ResourceLoaderSiteModule.php',
@@ -874,7 +910,6 @@ $wgAutoloadLocalClasses = array(
'SearchResultTooMany' => 'includes/search/SearchEngine.php',
'SearchSqlite' => 'includes/search/SearchSqlite.php',
'SearchUpdate' => 'includes/search/SearchUpdate.php',
- 'SearchUpdateMyISAM' => 'includes/search/SearchUpdate.php',
'SqliteSearchResultSet' => 'includes/search/SearchSqlite.php',
'SqlSearchResultSet' => 'includes/search/SearchEngine.php',
@@ -899,7 +934,6 @@ $wgAutoloadLocalClasses = array(
'DeadendPagesPage' => 'includes/specials/SpecialDeadendpages.php',
'DeletedContribsPager' => 'includes/specials/SpecialDeletedContributions.php',
'DeletedContributionsPage' => 'includes/specials/SpecialDeletedContributions.php',
- 'DisambiguationsPage' => 'includes/specials/SpecialDisambiguations.php',
'DoubleRedirectsPage' => 'includes/specials/SpecialDoubleRedirects.php',
'EditWatchlistCheckboxSeriesField' => 'includes/specials/SpecialEditWatchlist.php',
'EditWatchlistNormalHTMLForm' => 'includes/specials/SpecialEditWatchlist.php',
@@ -940,7 +974,6 @@ $wgAutoloadLocalClasses = array(
'SpecialBlankpage' => 'includes/specials/SpecialBlankpage.php',
'SpecialBlock' => 'includes/specials/SpecialBlock.php',
'SpecialBlockList' => 'includes/specials/SpecialBlockList.php',
- 'SpecialBlockme' => 'includes/specials/SpecialBlockme.php',
'SpecialBookSources' => 'includes/specials/SpecialBooksources.php',
'SpecialCachedPage' => 'includes/specials/SpecialCachedPage.php',
'SpecialCategories' => 'includes/specials/SpecialCategories.php',
@@ -969,9 +1002,12 @@ $wgAutoloadLocalClasses = array(
'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',
'SpecialSearch' => 'includes/specials/SpecialSearch.php',
'SpecialSpecialpages' => 'includes/specials/SpecialSpecialpages.php',
@@ -1021,6 +1057,7 @@ $wgAutoloadLocalClasses = array(
'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',
@@ -1091,17 +1128,10 @@ $wgAutoloadLocalClasses = array(
'CologneBlueTemplate' => 'skins/CologneBlue.php',
'ModernTemplate' => 'skins/Modern.php',
'MonoBookTemplate' => 'skins/MonoBook.php',
- 'NostalgiaTemplate' => 'skins/Nostalgia.php',
- 'SkinChick' => 'skins/Chick.php',
'SkinCologneBlue' => 'skins/CologneBlue.php',
'SkinModern' => 'skins/Modern.php',
'SkinMonoBook' => 'skins/MonoBook.php',
- 'SkinMySkin' => 'skins/MySkin.php',
- 'SkinNostalgia' => 'skins/Nostalgia.php',
- 'SkinSimple' => 'skins/Simple.php',
- 'SkinStandard' => 'skins/Standard.php',
'SkinVector' => 'skins/Vector.php',
- 'StandardTemplate' => 'skins/Standard.php',
'VectorTemplate' => 'skins/Vector.php',
);
@@ -1117,12 +1147,13 @@ class AutoLoader {
static function autoload( $className ) {
global $wgAutoloadClasses, $wgAutoloadLocalClasses;
- // Workaround for PHP bug <https://bugs.php.net/bug.php?id=49143> (5.3.2. is broken, it's fixed in 5.3.6).
- // Strip leading backslashes from class names. When namespaces are used, leading backslashes are used to indicate
- // the top-level namespace, e.g. \foo\Bar. When used like this in the code, the leading backslash isn't passed to
- // the auto-loader ($className would be 'foo\Bar'). However, if a class is accessed using a string instead of a
- // class literal (e.g. $class = '\foo\Bar'; new $class()), then some versions of PHP do not strip the leading
- // backlash in this case, causing autoloading to fail.
+ // Workaround for PHP bug <https://bugs.php.net/bug.php?id=49143> (5.3.2. is broken, it's
+ // fixed in 5.3.6). Strip leading backslashes from class names. When namespaces are used,
+ // leading backslashes are used to indicate the top-level namespace, e.g. \foo\Bar. When
+ // used like this in the code, the leading backslash isn't passed to the auto-loader
+ // ($className would be 'foo\Bar'). However, if a class is accessed using a string instead
+ // of a class literal (e.g. $class = '\foo\Bar'; new $class()), then some versions of PHP
+ // do not strip the leading backlash in this case, causing autoloading to fail.
$className = ltrim( $className, '\\' );
if ( isset( $wgAutoloadLocalClasses[$className] ) ) {
@@ -1157,7 +1188,7 @@ class AutoLoader {
$filename = "$IP/$filename";
}
- require( $filename );
+ require $filename;
return true;
}
@@ -1175,12 +1206,4 @@ class AutoLoader {
}
}
-if ( function_exists( 'spl_autoload_register' ) ) {
- spl_autoload_register( array( 'AutoLoader', 'autoload' ) );
-} else {
- function __autoload( $class ) {
- AutoLoader::autoload( $class );
- }
-
- ini_set( 'unserialize_callback_func', '__autoload' );
-}
+spl_autoload_register( array( 'AutoLoader', 'autoload' ) );
diff --git a/includes/Autopromote.php b/includes/Autopromote.php
index 604b9248..170d7abf 100644
--- a/includes/Autopromote.php
+++ b/includes/Autopromote.php
@@ -126,7 +126,8 @@ class Autopromote {
return false;
} elseif ( $cond[0] == '^' ) { // XOR (exactly one cond passes)
if ( count( $cond ) > 3 ) {
- wfWarn( 'recCheckCondition() given XOR ("^") condition on three or more conditions. Check your $wgAutopromote and $wgAutopromoteOnce settings.' );
+ wfWarn( 'recCheckCondition() given XOR ("^") condition on three or more conditions.' .
+ ' Check your $wgAutopromote and $wgAutopromoteOnce settings.' );
}
return self::recCheckCondition( $cond[1], $user )
xor self::recCheckCondition( $cond[2], $user );
@@ -165,7 +166,7 @@ class Autopromote {
return false;
}
- switch( $cond[0] ) {
+ switch ( $cond[0] ) {
case APCOND_EMAILCONFIRMED:
if ( Sanitizer::validateEmail( $user->getEmail() ) ) {
if ( $wgEmailAuthentication ) {
diff --git a/includes/Block.php b/includes/Block.php
index 7ee36ce9..34b89e73 100644
--- a/includes/Block.php
+++ b/includes/Block.php
@@ -65,11 +65,11 @@ class Block {
$timestamp = 0, $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0,
$hideName = 0, $blockEmail = 0, $allowUsertalk = 0, $byText = '' )
{
- if( $timestamp === 0 ) {
+ if ( $timestamp === 0 ) {
$timestamp = wfTimestampNow();
}
- if( count( func_get_args() ) > 0 ) {
+ if ( count( func_get_args() ) > 0 ) {
# Soon... :D
# wfDeprecated( __METHOD__ . " with arguments" );
}
@@ -206,16 +206,16 @@ class Block {
*/
public function load( $address = '', $user = 0 ) {
wfDeprecated( __METHOD__, '1.18' );
- if( $user ) {
+ if ( $user ) {
$username = User::whoIs( $user );
$block = self::newFromTarget( $username, $address );
} else {
$block = self::newFromTarget( null, $address );
}
- if( $block instanceof Block ) {
+ if ( $block instanceof Block ) {
# This is mildly evil, but hey, it's B/C :D
- foreach( $block as $variable => $value ) {
+ foreach ( $block as $variable => $value ) {
$this->$variable = $value;
}
return true;
@@ -237,7 +237,7 @@ class Block {
protected function newLoad( $vagueTarget = null ) {
$db = wfGetDB( $this->mFromMaster ? DB_MASTER : DB_SLAVE );
- if( $this->type !== null ) {
+ if ( $this->type !== null ) {
$conds = array(
'ipb_address' => array( (string)$this->target ),
);
@@ -247,9 +247,9 @@ class Block {
# Be aware that the != '' check is explicit, since empty values will be
# passed by some callers (bug 29116)
- if( $vagueTarget != '' ) {
+ if ( $vagueTarget != '' ) {
list( $target, $type ) = self::parseTarget( $vagueTarget );
- switch( $type ) {
+ switch ( $type ) {
case self::TYPE_USER:
# Slightly weird, but who are we to argue?
$conds['ipb_address'][] = (string)$target;
@@ -285,20 +285,20 @@ class Block {
# This is begging for $this = $bestBlock, but that's not allowed in PHP :(
$bestBlockPreventsEdit = null;
- foreach( $res as $row ) {
+ foreach ( $res as $row ) {
$block = self::newFromRow( $row );
# Don't use expired blocks
- if( $block->deleteIfExpired() ) {
+ if ( $block->deleteIfExpired() ) {
continue;
}
# Don't use anon only blocks on users
- if( $this->type == self::TYPE_USER && !$block->isHardblock() ) {
+ if ( $this->type == self::TYPE_USER && !$block->isHardblock() ) {
continue;
}
- if( $block->getType() == self::TYPE_RANGE ) {
+ if ( $block->getType() == self::TYPE_RANGE ) {
# This is the number of bits that are allowed to vary in the block, give
# or take some floating point errors
$end = wfBaseconvert( $block->getRangeEnd(), 16, 10 );
@@ -313,14 +313,14 @@ class Block {
$score = $block->getType();
}
- if( $score < $bestBlockScore ) {
+ if ( $score < $bestBlockScore ) {
$bestBlockScore = $score;
$bestRow = $row;
$bestBlockPreventsEdit = $block->prevents( 'edit' );
}
}
- if( $bestRow !== null ) {
+ if ( $bestRow !== null ) {
$this->initFromRow( $bestRow );
$this->prevents( 'edit', $bestBlockPreventsEdit );
return true;
@@ -511,7 +511,7 @@ class Block {
* @return Array
*/
protected function getDatabaseArray( $db = null ) {
- if( !$db ) {
+ if ( !$db ) {
$db = wfGetDB( DB_SLAVE );
}
$expiry = $db->encodeExpiry( $this->mExpiry );
@@ -579,7 +579,7 @@ class Block {
global $wgPutIPinRC;
// No IPs are in recentchanges table, so nothing to select
- if( !$wgPutIPinRC ) {
+ if ( !$wgPutIPinRC ) {
return;
}
@@ -601,7 +601,9 @@ class Block {
foreach ( $res as $row ) {
if ( $row->rc_ip ) {
$id = $block->doAutoblock( $row->rc_ip );
- if ( $id ) $blockIds[] = $id;
+ if ( $id ) {
+ $blockIds[] = $id;
+ }
}
}
}
@@ -681,7 +683,7 @@ class Block {
if ( $ipblock ) {
# Check if the block is an autoblock and would exceed the user block
# if renewed. If so, do nothing, otherwise prolong the block time...
- if ( $ipblock->mAuto && // @TODO: why not compare $ipblock->mExpiry?
+ if ( $ipblock->mAuto && // @todo Why not compare $ipblock->mExpiry?
$this->mExpiry > Block::getAutoblockExpiry( $ipblock->mTimestamp )
) {
# Reset block timestamp to now and its expiry to
@@ -793,7 +795,7 @@ class Block {
* @return String IP in Hex form
*/
public function getRangeStart() {
- switch( $this->type ) {
+ switch ( $this->type ) {
case self::TYPE_USER:
return '';
case self::TYPE_IP:
@@ -801,17 +803,18 @@ class Block {
case self::TYPE_RANGE:
list( $start, /*...*/ ) = IP::parseRange( $this->target );
return $start;
- default: throw new MWException( "Block with invalid type" );
+ default:
+ throw new MWException( "Block with invalid type" );
}
}
/**
- * Get the IP address at the start of the range in Hex form
+ * Get the IP address at the end of the range in Hex form
* @throws MWException
* @return String IP in Hex form
*/
public function getRangeEnd() {
- switch( $this->type ) {
+ switch ( $this->type ) {
case self::TYPE_USER:
return '';
case self::TYPE_IP:
@@ -819,7 +822,8 @@ class Block {
case self::TYPE_RANGE:
list( /*...*/, $end ) = IP::parseRange( $this->target );
return $end;
- default: throw new MWException( "Block with invalid type" );
+ default:
+ throw new MWException( "Block with invalid type" );
}
}
@@ -907,7 +911,7 @@ class Block {
* @return Bool
*/
public function prevents( $action, $x = null ) {
- switch( $action ) {
+ switch ( $action ) {
case 'edit':
# For now... <evil laugh>
return true;
@@ -997,11 +1001,16 @@ class Block {
* Purge expired blocks from the ipblocks table
*/
public static function purgeExpired() {
- if ( !wfReadOnly() ) {
- $dbw = wfGetDB( DB_MASTER );
- $dbw->delete( 'ipblocks',
- array( 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ), __METHOD__ );
+ if ( wfReadOnly() ) {
+ return;
}
+
+ $method = __METHOD__;
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->onTransactionIdle( function() use ( $dbw, $method ) {
+ $dbw->delete( 'ipblocks',
+ array( 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ), $method );
+ } );
}
/**
@@ -1050,30 +1059,216 @@ class Block {
public static function newFromTarget( $specificTarget, $vagueTarget = null, $fromMaster = false ) {
list( $target, $type ) = self::parseTarget( $specificTarget );
- if( $type == Block::TYPE_ID || $type == Block::TYPE_AUTO ) {
+ if ( $type == Block::TYPE_ID || $type == Block::TYPE_AUTO ) {
return Block::newFromID( $target );
- } elseif( $target === null && $vagueTarget == '' ) {
+ } elseif ( $target === null && $vagueTarget == '' ) {
# We're not going to find anything useful here
# Be aware that the == '' check is explicit, since empty values will be
# passed by some callers (bug 29116)
return null;
- } elseif( in_array( $type, array( Block::TYPE_USER, Block::TYPE_IP, Block::TYPE_RANGE, null ) ) ) {
+ } elseif ( in_array( $type, array( Block::TYPE_USER, Block::TYPE_IP, Block::TYPE_RANGE, null ) ) ) {
$block = new Block();
$block->fromMaster( $fromMaster );
- if( $type !== null ) {
+ if ( $type !== null ) {
$block->setTarget( $target );
}
- if( $block->newLoad( $vagueTarget ) ) {
+ if ( $block->newLoad( $vagueTarget ) ) {
return $block;
}
}
return null;
}
+
+ /**
+ * Get all blocks that match any IP from an array of IP addresses
+ *
+ * @param Array $ipChain list of IPs (strings), usually retrieved from the
+ * X-Forwarded-For header of the request
+ * @param Bool $isAnon Exclude anonymous-only blocks if false
+ * @param Bool $fromMaster Whether to query the master or slave database
+ * @return Array of Blocks
+ * @since 1.22
+ */
+ public static function getBlocksForIPList( array $ipChain, $isAnon, $fromMaster = false ) {
+ if ( !count( $ipChain ) ) {
+ 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
+ # necessarily trust the header given to us, make sure that we are only
+ # checking for blocks on well-formatted IP addresses (IPv4 and IPv6).
+ # Do not treat private IP spaces as special as it may be desirable for wikis
+ # to block those IP ranges in order to stop misbehaving proxies that spoof XFF.
+ if ( !IP::isValid( $ipaddr ) ) {
+ continue;
+ }
+ # Don't check trusted IPs (includes local squids which will be in every request)
+ if ( wfIsTrustedProxy( $ipaddr ) ) {
+ continue;
+ }
+ # Check both the original IP (to check against single blocks), as well as build
+ # the clause to check for rangeblocks for the given IP.
+ $conds['ipb_address'][] = $ipaddr;
+ $conds[] = self::getRangeCond( IP::toHex( $ipaddr ) );
+ }
+
+ if ( !count( $conds ) ) {
+ wfProfileOut( __METHOD__ );
+ return array();
+ }
+
+ if ( $fromMaster ) {
+ $db = wfGetDB( DB_MASTER );
+ } else {
+ $db = wfGetDB( DB_SLAVE );
+ }
+ $conds = $db->makeList( $conds, LIST_OR );
+ if ( !$isAnon ) {
+ $conds = array( $conds, 'ipb_anon_only' => 0 );
+ }
+ $selectFields = array_merge(
+ array( 'ipb_range_start', 'ipb_range_end' ),
+ Block::selectFields()
+ );
+ $rows = $db->select( 'ipblocks',
+ $selectFields,
+ $conds,
+ __METHOD__
+ );
+
+ $blocks = array();
+ foreach ( $rows as $row ) {
+ $block = self::newFromRow( $row );
+ if ( !$block->deleteIfExpired() ) {
+ $blocks[] = $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
+ * - Softblocks that prevent account creation are chosen over other softblocks
+ * - Other softblocks are chosen over autoblocks
+ * - If there are multiple exact or range blocks at the same level, the one chosen
+ * is random
+
+ * @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, ...)
+ * @param Array $block Array of blocks
+ * @return Block|null the "best" block from the list
+ */
+ public static function chooseBlock( array $blocks, array $ipChain ) {
+ if ( !count( $blocks ) ) {
+ return null;
+ } elseif ( count( $blocks ) == 1 ) {
+ 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 ) {
+ $aWeight = (int)$a->isHardblock() . (int)$a->prevents( 'createaccount' );
+ $bWeight = (int)$b->isHardblock() . (int)$b->prevents( 'createaccount' );
+ return strcmp( $bWeight, $aWeight ); // highest weight first
+ } );
+
+ $blocksListExact = array(
+ 'hard' => false,
+ 'disable_create' => false,
+ 'other' => false,
+ 'auto' => false
+ );
+ $blocksListRange = array(
+ 'hard' => false,
+ 'disable_create' => false,
+ 'other' => false,
+ 'auto' => false
+ );
+ $ipChain = array_reverse( $ipChain );
+
+ foreach ( $blocks as $block ) {
+ // Stop searching if we have already have a "better" block. This
+ // is why the order of the blocks matters
+ if ( !$block->isHardblock() && $blocksListExact['hard'] ) {
+ break;
+ } elseif ( !$block->prevents( 'createaccount' ) && $blocksListExact['disable_create'] ) {
+ break;
+ }
+
+ foreach ( $ipChain as $checkip ) {
+ $checkipHex = IP::toHex( $checkip );
+ if ( (string)$block->getTarget() === $checkip ) {
+ if ( $block->isHardblock() ) {
+ $blocksListExact['hard'] = $blocksListExact['hard'] ?: $block;
+ } elseif ( $block->prevents( 'createaccount' ) ) {
+ $blocksListExact['disable_create'] = $blocksListExact['disable_create'] ?: $block;
+ } elseif ( $block->mAuto ) {
+ $blocksListExact['auto'] = $blocksListExact['auto'] ?: $block;
+ } else {
+ $blocksListExact['other'] = $blocksListExact['other'] ?: $block;
+ }
+ // We found closest exact match in the ip list, so go to the next Block
+ break;
+ } elseif ( array_filter( $blocksListExact ) == array()
+ && $block->getRangeStart() <= $checkipHex
+ && $block->getRangeEnd() >= $checkipHex
+ ) {
+ if ( $block->isHardblock() ) {
+ $blocksListRange['hard'] = $blocksListRange['hard'] ?: $block;
+ } elseif ( $block->prevents( 'createaccount' ) ) {
+ $blocksListRange['disable_create'] = $blocksListRange['disable_create'] ?: $block;
+ } elseif ( $block->mAuto ) {
+ $blocksListRange['auto'] = $blocksListRange['auto'] ?: $block;
+ } else {
+ $blocksListRange['other'] = $blocksListRange['other'] ?: $block;
+ }
+ break;
+ }
+ }
+ }
+
+ if ( array_filter( $blocksListExact ) == array() ) {
+ $blocksList = &$blocksListRange;
+ } else {
+ $blocksList = &$blocksListExact;
+ }
+
+ $chosenBlock = null;
+ if ( $blocksList['hard'] ) {
+ $chosenBlock = $blocksList['hard'];
+ } elseif ( $blocksList['disable_create'] ) {
+ $chosenBlock = $blocksList['disable_create'];
+ } elseif ( $blocksList['other'] ) {
+ $chosenBlock = $blocksList['other'];
+ } elseif ( $blocksList['auto'] ) {
+ $chosenBlock = $blocksList['auto'];
+ } else {
+ wfProfileOut( __METHOD__ );
+ throw new MWException( "Proxy block found, but couldn't be classified." );
+ }
+
+ wfProfileOut( __METHOD__ );
+ return $chosenBlock;
+ }
+
/**
* From an existing Block, get the target and the type of target.
* Note that, except for null, it is always safe to treat the target
@@ -1085,13 +1280,13 @@ class Block {
*/
public static function parseTarget( $target ) {
# We may have been through this before
- if( $target instanceof User ) {
- if( IP::isValid( $target->getName() ) ) {
+ if ( $target instanceof User ) {
+ if ( IP::isValid( $target->getName() ) ) {
return array( $target, self::TYPE_IP );
} else {
return array( $target, self::TYPE_USER );
}
- } elseif( $target === null ) {
+ } elseif ( $target === null ) {
return array( null, null );
}
@@ -1112,7 +1307,7 @@ class Block {
# Consider the possibility that this is not a username at all
# but actually an old subpage (bug #29797)
- if( strpos( $target, '/' ) !== false ) {
+ if ( strpos( $target, '/' ) !== false ) {
# An old subpage, drill down to the user behind it
$parts = explode( '/', $target );
$target = $parts[0];
@@ -1198,4 +1393,43 @@ class Block {
public function setBlocker( $user ) {
$this->blocker = $user;
}
+
+ /**
+ * Get the key and parameters for the corresponding error message.
+ *
+ * @since 1.22
+ * @param IContextSource $context
+ * @return array
+ */
+ public function getPermissionsError( IContextSource $context ) {
+ $blocker = $this->getBlocker();
+ if ( $blocker instanceof User ) { // local user
+ $blockerUserpage = $blocker->getUserPage();
+ $link = "[[{$blockerUserpage->getPrefixedText()}|{$blockerUserpage->getText()}]]";
+ } else { // foreign user
+ $link = $blocker;
+ }
+
+ $reason = $this->mReason;
+ if ( $reason == '' ) {
+ $reason = $context->msg( 'blockednoreason' )->text();
+ }
+
+ /* $ip returns who *is* being blocked, $intended contains who was meant to be blocked.
+ * This could be a username, an IP range, or a single IP. */
+ $intended = $this->getTarget();
+
+ $lang = $context->getLanguage();
+ return array(
+ $this->mAuto ? 'autoblockedtext' : 'blockedtext',
+ $link,
+ $reason,
+ $context->getRequest()->getIP(),
+ $this->getByName(),
+ $this->getId(),
+ $lang->formatExpiry( $this->mExpiry ),
+ (string)$intended,
+ $lang->timeanddate( wfTimestamp( TS_MW, $this->mTimestamp ), true ),
+ );
+ }
}
diff --git a/includes/CallableUpdate.php b/includes/CallableUpdate.php
new file mode 100644
index 00000000..6eb55413
--- /dev/null
+++ b/includes/CallableUpdate.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * Deferrable Update for closure/callback
+ */
+class MWCallableUpdate implements DeferrableUpdate {
+
+ /**
+ * @var closure/callabck
+ */
+ private $callback;
+
+ /**
+ * @param callable $callback
+ */
+ public function __construct( $callback ) {
+ if ( !is_callable( $callback ) ) {
+ throw new MWException( 'Not a valid callback/closure!' );
+ }
+ $this->callback = $callback;
+ }
+
+ /**
+ * Run the update
+ */
+ public function doUpdate() {
+ call_user_func( $this->callback );
+ }
+
+}
diff --git a/includes/Category.php b/includes/Category.php
index 868d6c46..126b8fee 100644
--- a/includes/Category.php
+++ b/includes/Category.php
@@ -40,7 +40,8 @@ class Category {
/** Counts of membership (cat_pages, cat_subcats, cat_files) */
private $mPages = null, $mSubcats = null, $mFiles = null;
- private function __construct() { }
+ private function __construct() {
+ }
/**
* Set up all member variables using a database query.
diff --git a/includes/CategoryPage.php b/includes/CategoryPage.php
index 43ab4dbd..ba71aa01 100644
--- a/includes/CategoryPage.php
+++ b/includes/CategoryPage.php
@@ -106,7 +106,13 @@ class CategoryPage extends Article {
unset( $reqArray["from"] );
unset( $reqArray["to"] );
- $viewer = new $this->mCategoryViewerClass( $this->getContext()->getTitle(), $this->getContext(), $from, $until, $reqArray );
+ $viewer = new $this->mCategoryViewerClass(
+ $this->getContext()->getTitle(),
+ $this->getContext(),
+ $from,
+ $until,
+ $reqArray
+ );
$this->getContext()->getOutput()->addHTML( $viewer->getHTML() );
}
}
diff --git a/includes/CategoryViewer.php b/includes/CategoryViewer.php
index 970adb53..55d9c1e5 100644
--- a/includes/CategoryViewer.php
+++ b/includes/CategoryViewer.php
@@ -141,8 +141,17 @@ class CategoryViewer extends ContextSource {
$this->children = array();
$this->children_start_char = array();
if ( $this->showGallery ) {
- $this->gallery = new ImageGallery();
+ // Note that null for mode is taken to mean use default.
+ $mode = $this->getRequest()->getVal( 'gallerymode', null );
+ try {
+ $this->gallery = ImageGalleryBase::factory( $mode );
+ } catch ( MWException $e ) {
+ // User specified something invalid, fallback to default.
+ $this->gallery = ImageGalleryBase::factory();
+ }
+
$this->gallery->setHideBadImages();
+ $this->gallery->setContext( $this->getContext() );
} else {
$this->imgsNoGallery = array();
$this->imgsNoGallery_start_char = array();
@@ -526,7 +535,10 @@ class CategoryViewer extends ContextSource {
$first = true;
foreach ( $colContents as $char => $articles ) {
- $ret .= '<h3>' . htmlspecialchars( $char );
+ # 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.
@@ -645,28 +657,23 @@ class CategoryViewer extends ContextSource {
* returned? This function says what. Each type is considered independently
* of the other types.
*
- * Note for grepping: uses the messages category-article-count,
- * category-article-count-limited, category-subcat-count,
- * category-subcat-count-limited, category-file-count,
- * category-file-count-limited.
- *
* @param int $rescnt The number of items returned by our database query.
* @param int $dbcnt The number of items according to the category table.
* @param string $type 'subcat', 'article', or 'file'
- * @return String: A message giving the number of items, to output to HTML.
+ * @return string: A message giving the number of items, to output to HTML.
*/
private function getCountMessage( $rescnt, $dbcnt, $type ) {
- # There are three cases:
- # 1) The category table figure seems sane. It might be wrong, but
- # we can't do anything about it if we don't recalculate it on ev-
- # ery category view.
- # 2) The category table figure isn't sane, like it's smaller than the
- # number of actual results, *but* the number of results is less
- # than $this->limit and there's no offset. In this case we still
- # know the right figure.
- # 3) We have no idea.
-
- # Check if there's a "from" or "until" for anything
+ // There are three cases:
+ // 1) The category table figure seems sane. It might be wrong, but
+ // we can't do anything about it if we don't recalculate it on ev-
+ // ery category view.
+ // 2) The category table figure isn't sane, like it's smaller than the
+ // number of actual results, *but* the number of results is less
+ // than $this->limit and there's no offset. In this case we still
+ // know the right figure.
+ // 3) We have no idea.
+
+ // Check if there's a "from" or "until" for anything
// This is a little ugly, but we seem to use different names
// for the paging types then for the messages.
@@ -686,19 +693,22 @@ class CategoryViewer extends ContextSource {
if ( $dbcnt == $rescnt ||
( ( $rescnt == $this->limit || $fromOrUntil ) && $dbcnt > $rescnt )
) {
- # Case 1: seems sane.
+ // Case 1: seems sane.
$totalcnt = $dbcnt;
} elseif ( $rescnt < $this->limit && !$fromOrUntil ) {
- # Case 2: not sane, but salvageable. Use the number of results.
- # Since there are fewer than 200, we can also take this opportunity
- # to refresh the incorrect category table entry -- which should be
- # quick due to the small number of entries.
+ // Case 2: not sane, but salvageable. Use the number of results.
+ // Since there are fewer than 200, we can also take this opportunity
+ // to refresh the incorrect category table entry -- which should be
+ // quick due to the small number of entries.
$totalcnt = $rescnt;
$this->cat->refreshCounts();
} else {
- # Case 3: hopeless. Don't give a total count at all.
+ // Case 3: hopeless. Don't give a total count at all.
+ // Messages: category-subcat-count-limited, category-article-count-limited,
+ // category-file-count-limited
return $this->msg( "category-$type-count-limited" )->numParams( $rescnt )->parseAsBlock();
}
+ // Messages: category-subcat-count, category-article-count, category-file-count
return $this->msg( "category-$type-count" )->numParams( $rescnt, $totalcnt )->parseAsBlock();
}
}
diff --git a/includes/Cdb.php b/includes/Cdb.php
index a142c7cb..81c0afe1 100644
--- a/includes/Cdb.php
+++ b/includes/Cdb.php
@@ -133,7 +133,7 @@ class CdbReader_DBA {
}
function close() {
- if( isset( $this->handle ) ) {
+ if ( isset( $this->handle ) ) {
dba_close( $this->handle );
}
unset( $this->handle );
@@ -164,7 +164,7 @@ class CdbWriter_DBA {
}
function close() {
- if( isset( $this->handle ) ) {
+ if ( isset( $this->handle ) ) {
dba_close( $this->handle );
}
if ( wfIsWindows() ) {
diff --git a/includes/Cdb_PHP.php b/includes/Cdb_PHP.php
index 71b55f87..a38b9a86 100644
--- a/includes/Cdb_PHP.php
+++ b/includes/Cdb_PHP.php
@@ -73,10 +73,10 @@ class CdbFunctions {
public static function hash( $s ) {
$h = 5381;
for ( $i = 0; $i < strlen( $s ); $i++ ) {
- $h5 = ($h << 5) & 0xffffffff;
+ $h5 = ( $h << 5 ) & 0xffffffff;
// Do a 32-bit sum
// Inlined here for speed
- $sum = ($h & 0x3fffffff) + ($h5 & 0x3fffffff);
+ $sum = ( $h & 0x3fffffff ) + ( $h5 & 0x3fffffff );
$h =
(
( $sum & 0x40000000 ? 1 : 0 )
@@ -138,7 +138,7 @@ class CdbReader_PHP extends CdbReader {
}
function close() {
- if( isset( $this->handle ) ) {
+ if ( isset( $this->handle ) ) {
fclose( $this->handle );
}
unset( $this->handle );
@@ -332,7 +332,7 @@ class CdbWriter_PHP extends CdbWriter {
*/
public function close() {
$this->finish();
- if( isset( $this->handle ) ) {
+ if ( isset( $this->handle ) ) {
fclose( $this->handle );
}
if ( wfIsWindows() && file_exists( $this->realFileName ) ) {
@@ -411,7 +411,7 @@ class CdbWriter_PHP extends CdbWriter {
// Calculate the number of items that will be in each hashtable
$counts = array_fill( 0, 256, 0 );
foreach ( $this->hplist as $item ) {
- ++ $counts[ 255 & $item['h'] ];
+ ++ $counts[255 & $item['h']];
}
// Fill in $starts with the *end* indexes
@@ -450,9 +450,11 @@ class CdbWriter_PHP extends CdbWriter {
$hp = $packedTables[$starts[$i] + $u];
$where = CdbFunctions::unsignedMod(
CdbFunctions::unsignedShiftRight( $hp['h'], 8 ), $len );
- while ( $hashtable[$where]['p'] )
- if ( ++$where == $len )
+ while ( $hashtable[$where]['p'] ) {
+ if ( ++$where == $len ) {
$where = 0;
+ }
+ }
$hashtable[$where] = $hp;
}
diff --git a/includes/ChangeTags.php b/includes/ChangeTags.php
index 3adf58f8..3fc27f9a 100644
--- a/includes/ChangeTags.php
+++ b/includes/ChangeTags.php
@@ -34,17 +34,18 @@ class ChangeTags {
* - classes: Array of strings: CSS classes used in the generated html, one class for each tag
*
*/
- static function formatSummaryRow( $tags, $page ) {
+ public static function formatSummaryRow( $tags, $page ) {
global $wgLang;
- if( !$tags )
+ if ( !$tags ) {
return array( '', array() );
+ }
$classes = array();
$tags = explode( ',', $tags );
$displayTags = array();
- foreach( $tags as $tag ) {
+ foreach ( $tags as $tag ) {
$displayTags[] = Xml::tags(
'span',
array( 'class' => 'mw-tag-marker ' .
@@ -53,7 +54,10 @@ class ChangeTags {
);
$classes[] = Sanitizer::escapeClass( "mw-tag-$tag" );
}
- $markers = wfMessage( 'parentheses' )->rawParams( $wgLang->commaList( $displayTags ) )->text();
+ $markers = wfMessage( 'tag-list-wrapper' )
+ ->numParams( count( $displayTags ) )
+ ->rawParams( $wgLang->commaList( $displayTags ) )
+ ->parse();
$markers = Xml::tags( 'span', array( 'class' => 'mw-tag-markers' ), $markers );
return array( $markers, $classes );
@@ -67,7 +71,7 @@ class ChangeTags {
* @return String: Short description of the tag from "mediawiki:tag-$tag" if this message exists,
* html-escaped version of $tag otherwise
*/
- static function tagDescription( $tag ) {
+ public static function tagDescription( $tag ) {
$msg = wfMessage( "tag-$tag" );
return $msg->exists() ? $msg->parse() : htmlspecialchars( $tag );
}
@@ -86,28 +90,29 @@ class ChangeTags {
*
* @exception MWException when $rc_id, $rev_id and $log_id are all null
*/
- static function addTags( $tags, $rc_id = null, $rev_id = null, $log_id = null, $params = null ) {
+ public static function addTags( $tags, $rc_id = null, $rev_id = null, $log_id = null, $params = null ) {
if ( !is_array( $tags ) ) {
$tags = array( $tags );
}
$tags = array_filter( $tags ); // Make sure we're submitting all tags...
- 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!" );
+ 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!' );
}
$dbr = wfGetDB( DB_SLAVE );
// Might as well look for rcids and so on.
- if( !$rc_id ) {
+ if ( !$rc_id ) {
$dbr = wfGetDB( DB_MASTER ); // Info might be out of date, somewhat fractionally, on slave.
- if( $log_id ) {
+ if ( $log_id ) {
$rc_id = $dbr->selectField( 'recentchanges', 'rc_id', array( 'rc_logid' => $log_id ), __METHOD__ );
- } elseif( $rev_id ) {
+ } elseif ( $rev_id ) {
$rc_id = $dbr->selectField( 'recentchanges', 'rc_id', array( 'rc_this_oldid' => $rev_id ), __METHOD__ );
}
- } elseif( !$log_id && !$rev_id ) {
+ } elseif ( !$log_id && !$rev_id ) {
$dbr = wfGetDB( DB_MASTER ); // Info might be out of date, somewhat fractionally, on slave.
$log_id = $dbr->selectField( 'recentchanges', 'rc_logid', array( 'rc_id' => $rc_id ), __METHOD__ );
$rev_id = $dbr->selectField( 'recentchanges', 'rc_this_oldid', array( 'rc_id' => $rc_id ), __METHOD__ );
@@ -138,7 +143,7 @@ class ChangeTags {
// Insert the tags rows.
$tagsRows = array();
- foreach( $tags as $tag ) { // Filter so we don't insert NULLs as zero accidentally.
+ foreach ( $tags as $tag ) { // Filter so we don't insert NULLs as zero accidentally.
$tagsRows[] = array_filter(
array(
'ct_tag' => $tag,
@@ -169,18 +174,18 @@ class ChangeTags {
*
* @throws MWException When unable to determine appropriate JOIN condition for tagging
*/
- static function modifyDisplayQuery( &$tables, &$fields, &$conds,
+ public static function modifyDisplayQuery( &$tables, &$fields, &$conds,
&$join_conds, &$options, $filter_tag = false ) {
global $wgRequest, $wgUseTagFilter;
- if( $filter_tag === false ) {
+ if ( $filter_tag === false ) {
$filter_tag = $wgRequest->getVal( 'tagfilter' );
}
// Figure out which conditions can be done.
if ( in_array( 'recentchanges', $tables ) ) {
$join_cond = 'rc_id';
- } elseif( in_array( 'logging', $tables ) ) {
+ } elseif ( in_array( 'logging', $tables ) ) {
$join_cond = 'log_id';
} elseif ( in_array( 'revision', $tables ) ) {
$join_cond = 'rev_id';
@@ -193,14 +198,12 @@ class ChangeTags {
$join_conds['tag_summary'] = array( 'LEFT JOIN', "ts_$join_cond=$join_cond" );
$fields[] = 'ts_tags';
- if( $wgUseTagFilter && $filter_tag ) {
+ if ( $wgUseTagFilter && $filter_tag ) {
// Somebody wants to filter on a tag.
// Add an INNER JOIN on change_tag
// FORCE INDEX -- change_tags will almost ALWAYS be the correct query plan.
- global $wgOldChangeTagsIndex;
- $index = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id';
- $options['USE INDEX'] = array( 'change_tag' => $index );
+ $options['USE INDEX'] = array( 'change_tag' => 'change_tag_tag_id' );
unset( $options['FORCE INDEX'] );
$tables[] = 'change_tag';
$join_conds['change_tag'] = array( 'INNER JOIN', "ct_$join_cond=$join_cond" );
@@ -252,7 +255,7 @@ class ChangeTags {
*
* @return Array of strings: tags
*/
- static function listDefinedTags() {
+ public static function listDefinedTags() {
// Caching...
global $wgMemc;
$key = wfMemcKey( 'valid-tags' );
@@ -278,4 +281,34 @@ class ChangeTags {
$wgMemc->set( $key, $emptyTags, 300 );
return $emptyTags;
}
+
+ /**
+ * Returns a map of any tags used on the wiki to number of edits
+ * tagged with them, ordered descending by the hitcount.
+ *
+ * @return array Array of string => int
+ */
+ public static function tagUsageStatistics() {
+ $out = array();
+
+ $dbr = wfGetDB( DB_SLAVE );
+ $res = $dbr->select(
+ 'change_tag',
+ array( 'ct_tag', 'hitcount' => 'count(*)' ),
+ array(),
+ __METHOD__,
+ array( 'GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC' )
+ );
+
+ foreach ( $res as $row ) {
+ $out[$row->ct_tag] = $row->hitcount;
+ }
+ foreach ( self::listDefinedTags() as $tag ) {
+ if ( !isset( $out[$tag] ) ) {
+ $out[$tag] = 0;
+ }
+ }
+
+ return $out;
+ }
}
diff --git a/includes/ChangesFeed.php b/includes/ChangesFeed.php
index 476de5bd..0736c507 100644
--- a/includes/ChangesFeed.php
+++ b/includes/ChangesFeed.php
@@ -54,7 +54,7 @@ class ChangesFeed {
return false;
}
- if( !array_key_exists( $this->format, $wgFeedClasses ) ) {
+ if ( !array_key_exists( $this->format, $wgFeedClasses ) ) {
// falling back to atom
$this->format = 'atom';
}
@@ -92,7 +92,7 @@ class ChangesFeed {
* gets it quick too.
*/
$cachedFeed = $this->loadFromCache( $lastmod, $timekey, $key );
- if( is_string( $cachedFeed ) ) {
+ if ( is_string( $cachedFeed ) ) {
wfDebug( "RC: Outputting cached feed\n" );
$feed->httpHeaders();
echo $cachedFeed;
@@ -134,7 +134,7 @@ class ChangesFeed {
$feedLastmod = $messageMemc->get( $timekey );
- if( ( $wgFeedCacheTimeout > 0 ) && $feedLastmod ) {
+ if ( ( $wgFeedCacheTimeout > 0 ) && $feedLastmod ) {
/**
* If the cached feed was rendered very recently, we may
* go ahead and use it even if there have been edits made
@@ -146,7 +146,7 @@ class ChangesFeed {
$feedLastmodUnix = wfTimestamp( TS_UNIX, $feedLastmod );
$lastmodUnix = wfTimestamp( TS_UNIX, $lastmod );
- if( $feedAge < $wgFeedCacheTimeout || $feedLastmodUnix > $lastmodUnix) {
+ if ( $feedAge < $wgFeedCacheTimeout || $feedLastmodUnix > $lastmodUnix ) {
wfDebug( "RC: loading feed from cache ($key; $feedLastmod; $lastmod)...\n" );
if ( $feedLastmodUnix < $lastmodUnix ) {
$wgOut->setLastModified( $feedLastmod ); // bug 21916
@@ -172,30 +172,32 @@ class ChangesFeed {
# Merge adjacent edits by one user
$sorted = array();
$n = 0;
- foreach( $rows as $obj ) {
- if( $n > 0 &&
+ foreach ( $rows as $obj ) {
+ if ( $n > 0 &&
$obj->rc_type == RC_EDIT &&
$obj->rc_namespace >= 0 &&
- $obj->rc_cur_id == $sorted[$n-1]->rc_cur_id &&
- $obj->rc_user_text == $sorted[$n-1]->rc_user_text ) {
- $sorted[$n-1]->rc_last_oldid = $obj->rc_last_oldid;
+ $obj->rc_cur_id == $sorted[$n - 1]->rc_cur_id &&
+ $obj->rc_user_text == $sorted[$n - 1]->rc_user_text ) {
+ $sorted[$n - 1]->rc_last_oldid = $obj->rc_last_oldid;
} else {
$sorted[$n] = $obj;
$n++;
}
}
- foreach( $sorted as $obj ) {
+ foreach ( $sorted as $obj ) {
$title = Title::makeTitle( $obj->rc_namespace, $obj->rc_title );
- $talkpage = MWNamespace::canTalk( $obj->rc_namespace ) ? $title->getTalkPage()->getFullUrl() : '';
+ $talkpage = MWNamespace::canTalk( $obj->rc_namespace ) ? $title->getTalkPage()->getFullURL() : '';
// Skip items with deleted content (avoids partially complete/inconsistent output)
- if( $obj->rc_deleted ) continue;
+ if ( $obj->rc_deleted ) {
+ continue;
+ }
if ( $obj->rc_this_oldid ) {
- $url = $title->getFullURL(
- 'diff=' . $obj->rc_this_oldid .
- '&oldid=' . $obj->rc_last_oldid
- );
+ $url = $title->getFullURL( array(
+ 'diff' => $obj->rc_this_oldid,
+ 'oldid' => $obj->rc_last_oldid,
+ ) );
} else {
// log entry or something like that.
$url = $title->getFullURL();
@@ -206,7 +208,8 @@ class ChangesFeed {
FeedUtils::formatDiff( $obj ),
$url,
$obj->rc_timestamp,
- ( $obj->rc_deleted & Revision::DELETED_USER ) ? wfMessage( 'rev-deleted-user' )->escaped() : $obj->rc_user_text,
+ ( $obj->rc_deleted & Revision::DELETED_USER )
+ ? wfMessage( 'rev-deleted-user' )->escaped() : $obj->rc_user_text,
$talkpage
);
$feed->outItem( $item );
diff --git a/includes/ChangesList.php b/includes/ChangesList.php
deleted file mode 100644
index 8461001e..00000000
--- a/includes/ChangesList.php
+++ /dev/null
@@ -1,1304 +0,0 @@
-<?php
-/**
- * Classes to show lists of changes.
- *
- * These can be:
- * - watchlist
- * - related changes
- * - recent changes
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- */
-
-/**
- * @todo document
- */
-class RCCacheEntry extends RecentChange {
- var $secureName, $link;
- var $curlink, $difflink, $lastlink, $usertalklink, $versionlink;
- var $userlink, $timestamp, $watched;
-
- /**
- * @param $rc RecentChange
- * @return RCCacheEntry
- */
- static function newFromParent( $rc ) {
- $rc2 = new RCCacheEntry;
- $rc2->mAttribs = $rc->mAttribs;
- $rc2->mExtra = $rc->mExtra;
- return $rc2;
- }
-}
-
-/**
- * Base class for all changes lists
- */
-class ChangesList extends ContextSource {
-
- /**
- * @var Skin
- */
- public $skin;
-
- protected $watchlist = false;
-
- protected $message;
-
- /**
- * Changeslist constructor
- *
- * @param $obj Skin or IContextSource
- */
- public function __construct( $obj ) {
- if ( $obj instanceof IContextSource ) {
- $this->setContext( $obj );
- $this->skin = $obj->getSkin();
- } else {
- $this->setContext( $obj->getContext() );
- $this->skin = $obj;
- }
- $this->preCacheMessages();
- }
-
- /**
- * Fetch an appropriate changes list class for the main context
- * This first argument used to be an User object.
- *
- * @deprecated in 1.18; use newFromContext() instead
- * @param string|User $unused Unused
- * @return ChangesList|EnhancedChangesList|OldChangesList derivative
- */
- public static function newFromUser( $unused ) {
- wfDeprecated( __METHOD__, '1.18' );
- return self::newFromContext( RequestContext::getMain() );
- }
-
- /**
- * Fetch an appropriate changes list class for the specified context
- * Some users might want to use an enhanced list format, for instance
- *
- * @param $context IContextSource to use
- * @return ChangesList|EnhancedChangesList|OldChangesList derivative
- */
- public static function newFromContext( IContextSource $context ) {
- $user = $context->getUser();
- $sk = $context->getSkin();
- $list = null;
- if( wfRunHooks( 'FetchChangesList', array( $user, &$sk, &$list ) ) ) {
- $new = $context->getRequest()->getBool( 'enhanced', $user->getOption( 'usenewrc' ) );
- return $new ? new EnhancedChangesList( $context ) : new OldChangesList( $context );
- } else {
- return $list;
- }
- }
-
- /**
- * Sets the list to use a "<li class='watchlist-(namespace)-(page)'>" tag
- * @param $value Boolean
- */
- public function setWatchlistDivs( $value = true ) {
- $this->watchlist = $value;
- }
-
- /**
- * As we use the same small set of messages in various methods and that
- * they are called often, we call them once and save them in $this->message
- */
- private function preCacheMessages() {
- if( !isset( $this->message ) ) {
- foreach ( explode( ' ', 'cur diff hist last blocklink history ' .
- 'semicolon-separator pipe-separator' ) as $msg ) {
- $this->message[$msg] = $this->msg( $msg )->escaped();
- }
- }
- }
-
- /**
- * Returns the appropriate flags for new page, minor change and patrolling
- * @param array $flags Associative array of 'flag' => Bool
- * @param string $nothing to use for empty space
- * @return String
- */
- protected function recentChangesFlags( $flags, $nothing = '&#160;' ) {
- $f = '';
- foreach( array( 'newpage', 'minor', 'bot', 'unpatrolled' ) as $flag ) {
- $f .= isset( $flags[$flag] ) && $flags[$flag]
- ? self::flag( $flag )
- : $nothing;
- }
- return $f;
- }
-
- /**
- * Provide the "<abbr>" element appropriate to a given abbreviated flag,
- * namely the flag indicating a new page, a minor edit, a bot edit, or an
- * unpatrolled edit. By default in English it will contain "N", "m", "b",
- * "!" respectively, plus it will have an appropriate title and class.
- *
- * @param string $flag 'newpage', 'unpatrolled', 'minor', or 'bot'
- * @return String: Raw HTML
- */
- public static function flag( $flag ) {
- static $messages = null;
- if ( is_null( $messages ) ) {
- $messages = array(
- 'newpage' => array( 'newpageletter', 'recentchanges-label-newpage' ),
- 'minoredit' => array( 'minoreditletter', 'recentchanges-label-minor' ),
- 'botedit' => array( 'boteditletter', 'recentchanges-label-bot' ),
- 'unpatrolled' => array( 'unpatrolledletter', 'recentchanges-label-unpatrolled' ),
- );
- foreach( $messages as &$value ) {
- $value[0] = wfMessage( $value[0] )->escaped();
- $value[1] = wfMessage( $value[1] )->escaped();
- }
- }
-
- # Inconsistent naming, bleh
- $map = array(
- 'newpage' => 'newpage',
- 'minor' => 'minoredit',
- 'bot' => 'botedit',
- 'unpatrolled' => 'unpatrolled',
- 'minoredit' => 'minoredit',
- 'botedit' => 'botedit',
- );
- $flag = $map[$flag];
-
- return "<abbr class='$flag' title='" . $messages[$flag][1] . "'>" . $messages[$flag][0] . '</abbr>';
- }
-
- /**
- * Returns text for the start of the tabular part of RC
- * @return String
- */
- public function beginRecentChangesList() {
- $this->rc_cache = array();
- $this->rcMoveIndex = 0;
- $this->rcCacheIndex = 0;
- $this->lastdate = '';
- $this->rclistOpen = false;
- $this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' );
- return '';
- }
-
- /**
- * Show formatted char difference
- * @param $old Integer: bytes
- * @param $new Integer: bytes
- * @param $context IContextSource context to use
- * @return String
- */
- public static function showCharacterDifference( $old, $new, IContextSource $context = null ) {
- global $wgRCChangedSizeThreshold, $wgMiserMode;
-
- if ( !$context ) {
- $context = RequestContext::getMain();
- }
-
- $new = (int)$new;
- $old = (int)$old;
- $szdiff = $new - $old;
-
- $lang = $context->getLanguage();
- $code = $lang->getCode();
- static $fastCharDiff = array();
- if ( !isset( $fastCharDiff[$code] ) ) {
- $fastCharDiff[$code] = $wgMiserMode || $context->msg( 'rc-change-size' )->plain() === '$1';
- }
-
- $formattedSize = $lang->formatNum( $szdiff );
-
- if ( !$fastCharDiff[$code] ) {
- $formattedSize = $context->msg( 'rc-change-size', $formattedSize )->text();
- }
-
- if( abs( $szdiff ) > abs( $wgRCChangedSizeThreshold ) ) {
- $tag = 'strong';
- } else {
- $tag = 'span';
- }
-
- if ( $szdiff === 0 ) {
- $formattedSizeClass = 'mw-plusminus-null';
- }
- if ( $szdiff > 0 ) {
- $formattedSize = '+' . $formattedSize;
- $formattedSizeClass = 'mw-plusminus-pos';
- }
- if ( $szdiff < 0 ) {
- $formattedSizeClass = 'mw-plusminus-neg';
- }
-
- $formattedTotalSize = $context->msg( 'rc-change-size-new' )->numParams( $new )->text();
-
- return Html::element( $tag,
- array( 'dir' => 'ltr', 'class' => $formattedSizeClass, 'title' => $formattedTotalSize ),
- $context->msg( 'parentheses', $formattedSize )->plain() ) . $lang->getDirMark();
- }
-
- /**
- * Format the character difference of one or several changes.
- *
- * @param $old RecentChange
- * @param $new RecentChange last change to use, if not provided, $old will be used
- * @return string HTML fragment
- */
- public function formatCharacterDifference( RecentChange $old, RecentChange $new = null ) {
- $oldlen = $old->mAttribs['rc_old_len'];
-
- if ( $new ) {
- $newlen = $new->mAttribs['rc_new_len'];
- } else {
- $newlen = $old->mAttribs['rc_new_len'];
- }
-
- if( $oldlen === null || $newlen === null ) {
- return '';
- }
-
- return self::showCharacterDifference( $oldlen, $newlen, $this->getContext() );
- }
-
- /**
- * Returns text for the end of RC
- * @return String
- */
- public function endRecentChangesList() {
- if( $this->rclistOpen ) {
- return "</ul>\n";
- } else {
- return '';
- }
- }
-
- /**
- * @param string $s HTML to update
- * @param $rc_timestamp mixed
- */
- public function insertDateHeader( &$s, $rc_timestamp ) {
- # Make date header if necessary
- $date = $this->getLanguage()->userDate( $rc_timestamp, $this->getUser() );
- if( $date != $this->lastdate ) {
- if( $this->lastdate != '' ) {
- $s .= "</ul>\n";
- }
- $s .= Xml::element( 'h4', null, $date ) . "\n<ul class=\"special\">";
- $this->lastdate = $date;
- $this->rclistOpen = true;
- }
- }
-
- /**
- * @param string $s HTML to update
- * @param $title Title
- * @param $logtype string
- */
- public function insertLog( &$s, $title, $logtype ) {
- $page = new LogPage( $logtype );
- $logname = $page->getName()->escaped();
- $s .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $title, $logname ) )->escaped();
- }
-
- /**
- * @param string $s HTML to update
- * @param $rc RecentChange
- * @param $unpatrolled
- */
- public function insertDiffHist( &$s, &$rc, $unpatrolled ) {
- # Diff link
- if( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG ) {
- $diffLink = $this->message['diff'];
- } elseif ( !self::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) {
- $diffLink = $this->message['diff'];
- } else {
- $query = array(
- 'curid' => $rc->mAttribs['rc_cur_id'],
- 'diff' => $rc->mAttribs['rc_this_oldid'],
- 'oldid' => $rc->mAttribs['rc_last_oldid']
- );
-
- if( $unpatrolled ) {
- $query['rcid'] = $rc->mAttribs['rc_id'];
- };
-
- $diffLink = Linker::linkKnown(
- $rc->getTitle(),
- $this->message['diff'],
- array( 'tabindex' => $rc->counter ),
- $query
- );
- }
- $diffhist = $diffLink . $this->message['pipe-separator'];
- # History link
- $diffhist .= Linker::linkKnown(
- $rc->getTitle(),
- $this->message['hist'],
- array(),
- array(
- 'curid' => $rc->mAttribs['rc_cur_id'],
- 'action' => 'history'
- )
- );
- $s .= $this->msg( 'parentheses' )->rawParams( $diffhist )->escaped() . ' <span class="mw-changeslist-separator">. .</span> ';
- }
-
- /**
- * @param string $s HTML to update
- * @param $rc RecentChange
- * @param $unpatrolled
- * @param $watched
- */
- public function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) {
- # If it's a new article, there is no diff link, but if it hasn't been
- # patrolled yet, we need to give users a way to do so
- $params = array();
-
- if ( $unpatrolled && $rc->mAttribs['rc_type'] == RC_NEW ) {
- $params['rcid'] = $rc->mAttribs['rc_id'];
- }
-
- $articlelink = Linker::linkKnown(
- $rc->getTitle(),
- null,
- array( 'class' => 'mw-changeslist-title' ),
- $params
- );
- if( $this->isDeleted( $rc, Revision::DELETED_TEXT ) ) {
- $articlelink = '<span class="history-deleted">' . $articlelink . '</span>';
- }
- # To allow for boldening pages watched by this user
- $articlelink = "<span class=\"mw-title\">{$articlelink}</span>";
- # RTL/LTR marker
- $articlelink .= $this->getLanguage()->getDirMark();
-
- wfRunHooks( 'ChangesListInsertArticleLink',
- array( &$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched ) );
-
- $s .= " $articlelink";
- }
-
- /**
- * Get the timestamp from $rc formatted with current user's settings
- * and a separator
- *
- * @param $rc RecentChange
- * @return string HTML fragment
- */
- public function getTimestamp( $rc ) {
- return $this->message['semicolon-separator'] . '<span class="mw-changeslist-date">' .
- $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() ) . '</span> <span class="mw-changeslist-separator">. .</span> ';
- }
-
- /**
- * Insert time timestamp string from $rc into $s
- *
- * @param string $s HTML to update
- * @param $rc RecentChange
- */
- public function insertTimestamp( &$s, $rc ) {
- $s .= $this->getTimestamp( $rc );
- }
-
- /**
- * Insert links to user page, user talk page and eventually a blocking link
- *
- * @param &$s String HTML to update
- * @param &$rc RecentChange
- */
- public function insertUserRelatedLinks( &$s, &$rc ) {
- if( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
- $s .= ' <span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
- } else {
- $s .= $this->getLanguage()->getDirMark() . Linker::userLink( $rc->mAttribs['rc_user'],
- $rc->mAttribs['rc_user_text'] );
- $s .= Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
- }
- }
-
- /**
- * Insert a formatted action
- *
- * @param $rc RecentChange
- * @return string
- */
- public function insertLogEntry( $rc ) {
- $formatter = LogFormatter::newFromRow( $rc->mAttribs );
- $formatter->setContext( $this->getContext() );
- $formatter->setShowUserToolLinks( true );
- $mark = $this->getLanguage()->getDirMark();
- return $formatter->getActionText() . " $mark" . $formatter->getComment();
- }
-
- /**
- * Insert a formatted comment
- * @param $rc RecentChange
- * @return string
- */
- public function insertComment( $rc ) {
- if( $rc->mAttribs['rc_type'] != RC_MOVE && $rc->mAttribs['rc_type'] != RC_MOVE_OVER_REDIRECT ) {
- if( $this->isDeleted( $rc, Revision::DELETED_COMMENT ) ) {
- return ' <span class="history-deleted">' . $this->msg( 'rev-deleted-comment' )->escaped() . '</span>';
- } else {
- return Linker::commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() );
- }
- }
- return '';
- }
-
- /**
- * Check whether to enable recent changes patrol features
- * @return Boolean
- */
- public static function usePatrol() {
- global $wgUser;
- return $wgUser->useRCPatrol();
- }
-
- /**
- * Returns the string which indicates the number of watching users
- * @return string
- */
- protected function numberofWatchingusers( $count ) {
- static $cache = array();
- if( $count > 0 ) {
- if( !isset( $cache[$count] ) ) {
- $cache[$count] = $this->msg( 'number_of_watching_users_RCview' )->numParams( $count )->escaped();
- }
- return $cache[$count];
- } else {
- return '';
- }
- }
-
- /**
- * Determine if said field of a revision is hidden
- * @param $rc RCCacheEntry
- * @param $field Integer: one of DELETED_* bitfield constants
- * @return Boolean
- */
- public static function isDeleted( $rc, $field ) {
- return ( $rc->mAttribs['rc_deleted'] & $field ) == $field;
- }
-
- /**
- * Determine if the current user is allowed to view a particular
- * field of this revision, if it's marked as deleted.
- * @param $rc RCCacheEntry
- * @param $field Integer
- * @param $user User object to check, or null to use $wgUser
- * @return Boolean
- */
- public static function userCan( $rc, $field, User $user = null ) {
- if( $rc->mAttribs['rc_type'] == RC_LOG ) {
- return LogEventsList::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user );
- } else {
- return Revision::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user );
- }
- }
-
- /**
- * @param $link string
- * @param $watched bool
- * @return string
- */
- protected function maybeWatchedLink( $link, $watched = false ) {
- if( $watched ) {
- return '<strong class="mw-watched">' . $link . '</strong>';
- } else {
- return '<span class="mw-rc-unwatched">' . $link . '</span>';
- }
- }
-
- /** Inserts a rollback link
- *
- * @param $s string
- * @param $rc RecentChange
- */
- public function insertRollback( &$s, &$rc ) {
- if( $rc->mAttribs['rc_type'] != RC_NEW && $rc->mAttribs['rc_this_oldid'] && $rc->mAttribs['rc_cur_id'] ) {
- $page = $rc->getTitle();
- /** Check for rollback and edit permissions, disallow special pages, and only
- * show a link on the top-most revision */
- if ( $this->getUser()->isAllowed( 'rollback' ) && $rc->mAttribs['page_latest'] == $rc->mAttribs['rc_this_oldid'] )
- {
- $rev = new Revision( array(
- 'title' => $page,
- 'id' => $rc->mAttribs['rc_this_oldid'],
- 'user' => $rc->mAttribs['rc_user'],
- 'user_text' => $rc->mAttribs['rc_user_text'],
- 'deleted' => $rc->mAttribs['rc_deleted']
- ) );
- $s .= ' '.Linker::generateRollback( $rev, $this->getContext() );
- }
- }
- }
-
- /**
- * @param $s string
- * @param $rc RecentChange
- * @param $classes
- */
- public function insertTags( &$s, &$rc, &$classes ) {
- if ( empty( $rc->mAttribs['ts_tags'] ) )
- return;
-
- list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow( $rc->mAttribs['ts_tags'], 'changeslist' );
- $classes = array_merge( $classes, $newClasses );
- $s .= ' ' . $tagSummary;
- }
-
- public function insertExtra( &$s, &$rc, &$classes ) {
- // Empty, used for subclasses to add anything special.
- }
-
- protected function showAsUnpatrolled( RecentChange $rc ) {
- $unpatrolled = false;
- if ( !$rc->mAttribs['rc_patrolled'] ) {
- if ( $this->getUser()->useRCPatrol() ) {
- $unpatrolled = true;
- } elseif ( $this->getUser()->useNPPatrol() && $rc->mAttribs['rc_type'] == RC_NEW ) {
- $unpatrolled = true;
- }
- }
- return $unpatrolled;
- }
-}
-
-/**
- * Generate a list of changes using the good old system (no javascript)
- */
-class OldChangesList extends ChangesList {
- /**
- * Format a line using the old system (aka without any javascript).
- *
- * @param $rc RecentChange, passed by reference
- * @param bool $watched (default false)
- * @param int $linenumber (default null)
- *
- * @return string|bool
- */
- public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) {
- global $wgRCShowChangedSize;
- wfProfileIn( __METHOD__ );
-
- # Should patrol-related stuff be shown?
- $unpatrolled = $this->showAsUnpatrolled( $rc );
-
- $dateheader = ''; // $s now contains only <li>...</li>, for hooks' convenience.
- $this->insertDateHeader( $dateheader, $rc->mAttribs['rc_timestamp'] );
-
- $s = '';
- $classes = array();
- // use mw-line-even/mw-line-odd class only if linenumber is given (feature from bug 14468)
- if( $linenumber ) {
- if( $linenumber & 1 ) {
- $classes[] = 'mw-line-odd';
- }
- else {
- $classes[] = 'mw-line-even';
- }
- }
-
- // Indicate watched status on the line to allow for more
- // comprehensive styling.
- $classes[] = $watched ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
-
- // Moved pages (very very old, not supported anymore)
- if( $rc->mAttribs['rc_type'] == RC_MOVE || $rc->mAttribs['rc_type'] == RC_MOVE_OVER_REDIRECT ) {
- // Log entries
- } elseif( $rc->mAttribs['rc_log_type'] ) {
- $logtitle = SpecialPage::getTitleFor( 'Log', $rc->mAttribs['rc_log_type'] );
- $this->insertLog( $s, $logtitle, $rc->mAttribs['rc_log_type'] );
- // Log entries (old format) or log targets, and special pages
- } elseif( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) {
- list( $name, $subpage ) = SpecialPageFactory::resolveAlias( $rc->mAttribs['rc_title'] );
- if( $name == 'Log' ) {
- $this->insertLog( $s, $rc->getTitle(), $subpage );
- }
- // Regular entries
- } else {
- $this->insertDiffHist( $s, $rc, $unpatrolled );
- # M, N, b and ! (minor, new, bot and unpatrolled)
- $s .= $this->recentChangesFlags(
- array(
- 'newpage' => $rc->mAttribs['rc_type'] == RC_NEW,
- 'minor' => $rc->mAttribs['rc_minor'],
- 'unpatrolled' => $unpatrolled,
- 'bot' => $rc->mAttribs['rc_bot']
- ),
- ''
- );
- $this->insertArticleLink( $s, $rc, $unpatrolled, $watched );
- }
- # Edit/log timestamp
- $this->insertTimestamp( $s, $rc );
- # Bytes added or removed
- if ( $wgRCShowChangedSize ) {
- $cd = $this->formatCharacterDifference( $rc );
- if ( $cd !== '' ) {
- $s .= $cd . ' <span class="mw-changeslist-separator">. .</span> ';
- }
- }
-
- if ( $rc->mAttribs['rc_type'] == RC_LOG ) {
- $s .= $this->insertLogEntry( $rc );
- } else {
- # User tool links
- $this->insertUserRelatedLinks( $s, $rc );
- # LTR/RTL direction mark
- $s .= $this->getLanguage()->getDirMark();
- $s .= $this->insertComment( $rc );
- }
-
- # Tags
- $this->insertTags( $s, $rc, $classes );
- # Rollback
- $this->insertRollback( $s, $rc );
- # For subclasses
- $this->insertExtra( $s, $rc, $classes );
-
- # How many users watch this page
- if( $rc->numberofWatchingusers > 0 ) {
- $s .= ' ' . $this->numberofWatchingusers( $rc->numberofWatchingusers );
- }
-
- if( $this->watchlist ) {
- $classes[] = Sanitizer::escapeClass( 'watchlist-' . $rc->mAttribs['rc_namespace'] . '-' . $rc->mAttribs['rc_title'] );
- }
-
- if ( !wfRunHooks( 'OldChangesListRecentChangesLine', array( &$this, &$s, $rc, &$classes ) ) ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- wfProfileOut( __METHOD__ );
- return "$dateheader<li class=\"" . implode( ' ', $classes ) . "\">" . $s . "</li>\n";
- }
-}
-
-/**
- * Generate a list of changes using an Enhanced system (uses javascript).
- */
-class EnhancedChangesList extends ChangesList {
-
- protected $rc_cache;
-
- /**
- * Add the JavaScript file for enhanced changeslist
- * @return String
- */
- public function beginRecentChangesList() {
- $this->rc_cache = array();
- $this->rcMoveIndex = 0;
- $this->rcCacheIndex = 0;
- $this->lastdate = '';
- $this->rclistOpen = false;
- $this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' );
- return '';
- }
- /**
- * Format a line for enhanced recentchange (aka with javascript and block of lines).
- *
- * @param $baseRC RecentChange
- * @param $watched bool
- *
- * @return string
- */
- public function recentChangesLine( &$baseRC, $watched = false ) {
- wfProfileIn( __METHOD__ );
-
- # Create a specialised object
- $rc = RCCacheEntry::newFromParent( $baseRC );
-
- $curIdEq = array( 'curid' => $rc->mAttribs['rc_cur_id'] );
-
- # If it's a new day, add the headline and flush the cache
- $date = $this->getLanguage()->userDate( $rc->mAttribs['rc_timestamp'], $this->getUser() );
- $ret = '';
- if( $date != $this->lastdate ) {
- # Process current cache
- $ret = $this->recentChangesBlock();
- $this->rc_cache = array();
- $ret .= Xml::element( 'h4', null, $date ) . "\n";
- $this->lastdate = $date;
- }
-
- # Should patrol-related stuff be shown?
- $rc->unpatrolled = $this->showAsUnpatrolled( $rc );
-
- $showdifflinks = true;
- # Make article link
- $type = $rc->mAttribs['rc_type'];
- $logType = $rc->mAttribs['rc_log_type'];
- // Page moves, very old style, not supported anymore
- if( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
- // New unpatrolled pages
- } elseif( $rc->unpatrolled && $type == RC_NEW ) {
- $clink = Linker::linkKnown( $rc->getTitle(), null, array(),
- array( 'rcid' => $rc->mAttribs['rc_id'] ) );
- // Log entries
- } elseif( $type == RC_LOG ) {
- if( $logType ) {
- $logtitle = SpecialPage::getTitleFor( 'Log', $logType );
- $logpage = new LogPage( $logType );
- $logname = $logpage->getName()->escaped();
- $clink = $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $logtitle, $logname ) )->escaped();
- } else {
- $clink = Linker::link( $rc->getTitle() );
- }
- $watched = false;
- // Log entries (old format) and special pages
- } elseif( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) {
- wfDebug( "Unexpected special page in recentchanges\n" );
- $clink = '';
- // Edits
- } else {
- $clink = Linker::linkKnown( $rc->getTitle() );
- }
-
- # Don't show unusable diff links
- if ( !ChangesList::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) {
- $showdifflinks = false;
- }
-
- $time = $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() );
- $rc->watched = $watched;
- $rc->link = $clink;
- $rc->timestamp = $time;
- $rc->numberofWatchingusers = $baseRC->numberofWatchingusers;
-
- # Make "cur" and "diff" links. Do not use link(), it is too slow if
- # called too many times (50% of CPU time on RecentChanges!).
- $thisOldid = $rc->mAttribs['rc_this_oldid'];
- $lastOldid = $rc->mAttribs['rc_last_oldid'];
- if( $rc->unpatrolled ) {
- $rcIdQuery = array( 'rcid' => $rc->mAttribs['rc_id'] );
- } else {
- $rcIdQuery = array();
- }
- $querycur = $curIdEq + array( 'diff' => '0', 'oldid' => $thisOldid );
- $querydiff = $curIdEq + array( 'diff' => $thisOldid, 'oldid' =>
- $lastOldid ) + $rcIdQuery;
-
- if( !$showdifflinks ) {
- $curLink = $this->message['cur'];
- $diffLink = $this->message['diff'];
- } elseif( in_array( $type, array( RC_NEW, RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ) ) ) {
- if ( $type != RC_NEW ) {
- $curLink = $this->message['cur'];
- } else {
- $curUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querycur ) );
- $curLink = "<a href=\"$curUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['cur']}</a>";
- }
- $diffLink = $this->message['diff'];
- } else {
- $diffUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querydiff ) );
- $curUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querycur ) );
- $diffLink = "<a href=\"$diffUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['diff']}</a>";
- $curLink = "<a href=\"$curUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['cur']}</a>";
- }
-
- # Make "last" link
- if( !$showdifflinks || !$lastOldid ) {
- $lastLink = $this->message['last'];
- } elseif( in_array( $type, array( RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ) ) ) {
- $lastLink = $this->message['last'];
- } else {
- $lastLink = Linker::linkKnown( $rc->getTitle(), $this->message['last'],
- array(), $curIdEq + array( 'diff' => $thisOldid, 'oldid' => $lastOldid ) + $rcIdQuery );
- }
-
- # Make user links
- if( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
- $rc->userlink = ' <span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
- } else {
- $rc->userlink = Linker::userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
- $rc->usertalklink = Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
- }
-
- $rc->lastlink = $lastLink;
- $rc->curlink = $curLink;
- $rc->difflink = $diffLink;
-
- # Put accumulated information into the cache, for later display
- # Page moves go on their own line
- $title = $rc->getTitle();
- $secureName = $title->getPrefixedDBkey();
- if( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
- # Use an @ character to prevent collision with page names
- $this->rc_cache['@@' . ($this->rcMoveIndex++)] = array( $rc );
- } else {
- # Logs are grouped by type
- if( $type == RC_LOG ) {
- $secureName = SpecialPage::getTitleFor( 'Log', $logType )->getPrefixedDBkey();
- }
- if( !isset( $this->rc_cache[$secureName] ) ) {
- $this->rc_cache[$secureName] = array();
- }
-
- array_push( $this->rc_cache[$secureName], $rc );
- }
-
- wfProfileOut( __METHOD__ );
-
- return $ret;
- }
-
- /**
- * Enhanced RC group
- * @return string
- */
- protected function recentChangesBlockGroup( $block ) {
- global $wgRCShowChangedSize;
-
- wfProfileIn( __METHOD__ );
-
- # Add the namespace and title of the block as part of the class
- $classes = array( 'mw-collapsible', 'mw-collapsed', 'mw-enhanced-rc' );
- if ( $block[0]->mAttribs['rc_log_type'] ) {
- # Log entry
- $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
- . $block[0]->mAttribs['rc_log_type'] . '-' . $block[0]->mAttribs['rc_title'] );
- } else {
- $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns'
- . $block[0]->mAttribs['rc_namespace'] . '-' . $block[0]->mAttribs['rc_title'] );
- }
- $classes[] = $block[0]->watched ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
- $r = Html::openElement( 'table', array( 'class' => $classes ) ) .
- Html::openElement( 'tr' );
-
- # Collate list of users
- $userlinks = array();
- # Other properties
- $unpatrolled = false;
- $isnew = false;
- $allBots = true;
- $allMinors = true;
- $curId = $currentRevision = 0;
- # Some catalyst variables...
- $namehidden = true;
- $allLogs = true;
- foreach( $block as $rcObj ) {
- $oldid = $rcObj->mAttribs['rc_last_oldid'];
- if( $rcObj->mAttribs['rc_type'] == RC_NEW ) {
- $isnew = true;
- }
- // If all log actions to this page were hidden, then don't
- // give the name of the affected page for this block!
- if( !$this->isDeleted( $rcObj, LogPage::DELETED_ACTION ) ) {
- $namehidden = false;
- }
- $u = $rcObj->userlink;
- if( !isset( $userlinks[$u] ) ) {
- $userlinks[$u] = 0;
- }
- if( $rcObj->unpatrolled ) {
- $unpatrolled = true;
- }
- if( $rcObj->mAttribs['rc_type'] != RC_LOG ) {
- $allLogs = false;
- }
- # Get the latest entry with a page_id and oldid
- # since logs may not have these.
- if( !$curId && $rcObj->mAttribs['rc_cur_id'] ) {
- $curId = $rcObj->mAttribs['rc_cur_id'];
- }
- if( !$currentRevision && $rcObj->mAttribs['rc_this_oldid'] ) {
- $currentRevision = $rcObj->mAttribs['rc_this_oldid'];
- }
-
- if( !$rcObj->mAttribs['rc_bot'] ) {
- $allBots = false;
- }
- if( !$rcObj->mAttribs['rc_minor'] ) {
- $allMinors = false;
- }
-
- $userlinks[$u]++;
- }
-
- # Sort the list and convert to text
- krsort( $userlinks );
- asort( $userlinks );
- $users = array();
- foreach( $userlinks as $userlink => $count) {
- $text = $userlink;
- $text .= $this->getLanguage()->getDirMark();
- if( $count > 1 ) {
- $text .= ' ' . $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->formatNum( $count ) . '×' )->escaped();
- }
- array_push( $users, $text );
- }
-
- $users = ' <span class="changedby">'
- . $this->msg( 'brackets' )->rawParams(
- implode( $this->message['semicolon-separator'], $users )
- )->escaped() . '</span>';
-
- $tl = '<span class="mw-collapsible-toggle mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space"></span>';
- $r .= "<td>$tl</td>";
-
- # Main line
- $r .= '<td class="mw-enhanced-rc">' . $this->recentChangesFlags( array(
- 'newpage' => $isnew, # show, when one have this flag
- 'minor' => $allMinors, # show only, when all have this flag
- 'unpatrolled' => $unpatrolled, # show, when one have this flag
- 'bot' => $allBots, # show only, when all have this flag
- ) );
-
- # Timestamp
- $r .= '&#160;' . $block[0]->timestamp . '&#160;</td><td>';
-
- # Article link
- if( $namehidden ) {
- $r .= ' <span class="history-deleted">' . $this->msg( 'rev-deleted-event' )->escaped() . '</span>';
- } elseif( $allLogs ) {
- $r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
- } else {
- $this->insertArticleLink( $r, $block[0], $block[0]->unpatrolled, $block[0]->watched );
- }
-
- $r .= $this->getLanguage()->getDirMark();
-
- $queryParams['curid'] = $curId;
- # Changes message
- $n = count( $block );
- static $nchanges = array();
- if ( !isset( $nchanges[$n] ) ) {
- $nchanges[$n] = $this->msg( 'nchanges' )->numParams( $n )->escaped();
- }
- # Total change link
- $r .= ' ';
- $logtext = '';
- if( !$allLogs ) {
- if( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) {
- $logtext .= $nchanges[$n];
- } elseif( $isnew ) {
- $logtext .= $nchanges[$n];
- } else {
- $params = $queryParams;
- $params['diff'] = $currentRevision;
- $params['oldid'] = $oldid;
-
- $logtext .= Linker::link(
- $block[0]->getTitle(),
- $nchanges[$n],
- array(),
- $params,
- array( 'known', 'noclasses' )
- );
- }
- }
-
- # History
- if( $allLogs ) {
- // don't show history link for logs
- } elseif( $namehidden || !$block[0]->getTitle()->exists() ) {
- $logtext .= $this->message['pipe-separator'] . $this->message['hist'];
- } else {
- $params = $queryParams;
- $params['action'] = 'history';
-
- $logtext .= $this->message['pipe-separator'] .
- Linker::linkKnown(
- $block[0]->getTitle(),
- $this->message['hist'],
- array(),
- $params
- );
- }
-
- if( $logtext !== '' ) {
- $r .= $this->msg( 'parentheses' )->rawParams( $logtext )->escaped();
- }
-
- $r .= ' <span class="mw-changeslist-separator">. .</span> ';
-
- # Character difference (does not apply if only log items)
- if( $wgRCShowChangedSize && !$allLogs ) {
- $last = 0;
- $first = count( $block ) - 1;
- # Some events (like logs) have an "empty" size, so we need to skip those...
- while( $last < $first && $block[$last]->mAttribs['rc_new_len'] === null ) {
- $last++;
- }
- while( $first > $last && $block[$first]->mAttribs['rc_old_len'] === null ) {
- $first--;
- }
- # Get net change
- $chardiff = $this->formatCharacterDifference( $block[$first], $block[$last] );
-
- if( $chardiff == '' ) {
- $r .= ' ';
- } else {
- $r .= ' ' . $chardiff. ' <span class="mw-changeslist-separator">. .</span> ';
- }
- }
-
- $r .= $users;
- $r .= $this->numberofWatchingusers( $block[0]->numberofWatchingusers );
-
- # Sub-entries
- foreach( $block as $rcObj ) {
- # Classes to apply -- TODO implement
- $classes = array();
- $type = $rcObj->mAttribs['rc_type'];
-
- $r .= '<tr><td></td><td class="mw-enhanced-rc">';
- $r .= $this->recentChangesFlags( array(
- 'newpage' => $type == RC_NEW,
- 'minor' => $rcObj->mAttribs['rc_minor'],
- 'unpatrolled' => $rcObj->unpatrolled,
- 'bot' => $rcObj->mAttribs['rc_bot'],
- ) );
- $r .= '&#160;</td><td class="mw-enhanced-rc-nested"><span class="mw-enhanced-rc-time">';
-
- $params = $queryParams;
-
- if( $rcObj->mAttribs['rc_this_oldid'] != 0 ) {
- $params['oldid'] = $rcObj->mAttribs['rc_this_oldid'];
- }
-
- # Log timestamp
- if( $type == RC_LOG ) {
- $link = $rcObj->timestamp;
- # Revision link
- } elseif( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) {
- $link = '<span class="history-deleted">' . $rcObj->timestamp . '</span> ';
- } else {
- if ( $rcObj->unpatrolled && $type == RC_NEW) {
- $params['rcid'] = $rcObj->mAttribs['rc_id'];
- }
-
- $link = Linker::linkKnown(
- $rcObj->getTitle(),
- $rcObj->timestamp,
- array(),
- $params
- );
- if( $this->isDeleted( $rcObj, Revision::DELETED_TEXT ) ) {
- $link = '<span class="history-deleted">' . $link . '</span> ';
- }
- }
- $r .= $link . '</span>';
-
- if ( !$type == RC_LOG || $type == RC_NEW ) {
- $r .= ' ' . $this->msg( 'parentheses' )->rawParams( $rcObj->curlink . $this->message['pipe-separator'] . $rcObj->lastlink )->escaped();
- }
- $r .= ' <span class="mw-changeslist-separator">. .</span> ';
-
- # Character diff
- if ( $wgRCShowChangedSize ) {
- $cd = $this->formatCharacterDifference( $rcObj );
- if ( $cd !== '' ) {
- $r .= $cd . ' <span class="mw-changeslist-separator">. .</span> ';
- }
- }
-
- if ( $rcObj->mAttribs['rc_type'] == RC_LOG ) {
- $r .= $this->insertLogEntry( $rcObj );
- } else {
- # User links
- $r .= $rcObj->userlink;
- $r .= $rcObj->usertalklink;
- $r .= $this->insertComment( $rcObj );
- }
-
- # Rollback
- $this->insertRollback( $r, $rcObj );
- # Tags
- $this->insertTags( $r, $rcObj, $classes );
-
- $r .= "</td></tr>\n";
- }
- $r .= "</table>\n";
-
- $this->rcCacheIndex++;
-
- wfProfileOut( __METHOD__ );
-
- return $r;
- }
-
- /**
- * Generate HTML for an arrow or placeholder graphic
- * @param string $dir one of '', 'd', 'l', 'r'
- * @param string $alt text
- * @param string $title text
- * @return String: HTML "<img>" tag
- */
- protected function arrow( $dir, $alt = '', $title = '' ) {
- global $wgStylePath;
- $encUrl = htmlspecialchars( $wgStylePath . '/common/images/Arr_' . $dir . '.png' );
- $encAlt = htmlspecialchars( $alt );
- $encTitle = htmlspecialchars( $title );
- return "<img src=\"$encUrl\" width=\"12\" height=\"12\" alt=\"$encAlt\" title=\"$encTitle\" />";
- }
-
- /**
- * Generate HTML for a right- or left-facing arrow,
- * depending on language direction.
- * @return String: HTML "<img>" tag
- */
- protected function sideArrow() {
- $dir = $this->getLanguage()->isRTL() ? 'l' : 'r';
- return $this->arrow( $dir, '+', $this->msg( 'rc-enhanced-expand' )->text() );
- }
-
- /**
- * Generate HTML for a down-facing arrow
- * depending on language direction.
- * @return String: HTML "<img>" tag
- */
- protected function downArrow() {
- return $this->arrow( 'd', '-', $this->msg( 'rc-enhanced-hide' )->text() );
- }
-
- /**
- * Generate HTML for a spacer image
- * @return String: HTML "<img>" tag
- */
- protected function spacerArrow() {
- return $this->arrow( '', codepointToUtf8( 0xa0 ) ); // non-breaking space
- }
-
- /**
- * Enhanced RC ungrouped line.
- *
- * @param $rcObj RecentChange
- * @return String: a HTML formatted line (generated using $r)
- */
- protected function recentChangesBlockLine( $rcObj ) {
- global $wgRCShowChangedSize;
-
- wfProfileIn( __METHOD__ );
- $query['curid'] = $rcObj->mAttribs['rc_cur_id'];
-
- $type = $rcObj->mAttribs['rc_type'];
- $logType = $rcObj->mAttribs['rc_log_type'];
- $classes = array( 'mw-enhanced-rc' );
- if( $logType ) {
- # Log entry
- $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
- . $logType . '-' . $rcObj->mAttribs['rc_title'] );
- } else {
- $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns' .
- $rcObj->mAttribs['rc_namespace'] . '-' . $rcObj->mAttribs['rc_title'] );
- }
- $classes[] = $rcObj->watched ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
- $r = Html::openElement( 'table', array( 'class' => $classes ) ) .
- Html::openElement( 'tr' );
-
- $r .= '<td class="mw-enhanced-rc"><span class="mw-enhancedchanges-arrow-space"></span>';
- # Flag and Timestamp
- if( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
- $r .= '&#160;&#160;&#160;&#160;'; // 4 flags -> 4 spaces
- } else {
- $r .= $this->recentChangesFlags( array(
- 'newpage' => $type == RC_NEW,
- 'minor' => $rcObj->mAttribs['rc_minor'],
- 'unpatrolled' => $rcObj->unpatrolled,
- 'bot' => $rcObj->mAttribs['rc_bot'],
- ) );
- }
- $r .= '&#160;' . $rcObj->timestamp . '&#160;</td><td>';
- # Article or log link
- if( $logType ) {
- $logPage = new LogPage( $logType );
- $logTitle = SpecialPage::getTitleFor( 'Log', $logType );
- $logName = $logPage->getName()->escaped();
- $r .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $logTitle, $logName ) )->escaped();
- } else {
- $this->insertArticleLink( $r, $rcObj, $rcObj->unpatrolled, $rcObj->watched );
- }
- # Diff and hist links
- if ( $type != RC_LOG ) {
- $query['action'] = 'history';
- $r .= ' ' . $this->msg( 'parentheses' )->rawParams( $rcObj->difflink . $this->message['pipe-separator'] . Linker::linkKnown(
- $rcObj->getTitle(),
- $this->message['hist'],
- array(),
- $query
- ) )->escaped();
- }
- $r .= ' <span class="mw-changeslist-separator">. .</span> ';
- # Character diff
- if ( $wgRCShowChangedSize ) {
- $cd = $this->formatCharacterDifference( $rcObj );
- if ( $cd !== '' ) {
- $r .= $cd . ' <span class="mw-changeslist-separator">. .</span> ';
- }
- }
-
- if ( $type == RC_LOG ) {
- $r .= $this->insertLogEntry( $rcObj );
- } else {
- $r .= ' ' . $rcObj->userlink . $rcObj->usertalklink;
- $r .= $this->insertComment( $rcObj );
- $this->insertRollback( $r, $rcObj );
- }
-
- # Tags
- $this->insertTags( $r, $rcObj, $classes );
- # Show how many people are watching this if enabled
- $r .= $this->numberofWatchingusers( $rcObj->numberofWatchingusers );
-
- $r .= "</td></tr></table>\n";
-
- wfProfileOut( __METHOD__ );
-
- return $r;
- }
-
- /**
- * If enhanced RC is in use, this function takes the previously cached
- * RC lines, arranges them, and outputs the HTML
- *
- * @return string
- */
- protected function recentChangesBlock() {
- if( count ( $this->rc_cache ) == 0 ) {
- return '';
- }
-
- wfProfileIn( __METHOD__ );
-
- $blockOut = '';
- foreach( $this->rc_cache as $block ) {
- if( count( $block ) < 2 ) {
- $blockOut .= $this->recentChangesBlockLine( array_shift( $block ) );
- } else {
- $blockOut .= $this->recentChangesBlockGroup( $block );
- }
- }
-
- wfProfileOut( __METHOD__ );
-
- return '<div>' . $blockOut . '</div>';
- }
-
- /**
- * Returns text for the end of RC
- * If enhanced RC is in use, returns pretty much all the text
- * @return string
- */
- public function endRecentChangesList() {
- return $this->recentChangesBlock() . parent::endRecentChangesList();
- }
-
-}
diff --git a/includes/Collation.php b/includes/Collation.php
index 6bba019b..b0252c70 100644
--- a/includes/Collation.php
+++ b/includes/Collation.php
@@ -40,7 +40,7 @@ abstract class Collation {
* @return Collation
*/
static function factory( $collationName ) {
- switch( $collationName ) {
+ switch ( $collationName ) {
case 'uppercase':
return new UppercaseCollation;
case 'identity':
@@ -49,7 +49,7 @@ abstract class Collation {
return new IcuCollation( 'root' );
default:
$match = array();
- if ( preg_match( '/^uca-([a-z-]+)$/', $collationName, $match ) ) {
+ if ( preg_match( '/^uca-([a-z@=-]+)$/', $collationName, $match ) ) {
return new IcuCollation( $match[1] );
}
@@ -62,7 +62,7 @@ abstract class Collation {
}
// If all else fails...
- throw new MWException( __METHOD__.": unknown collation type \"$collationName\"" );
+ throw new MWException( __METHOD__ . ": unknown collation type \"$collationName\"" );
}
}
@@ -214,6 +214,7 @@ class IcuCollation extends Collation {
'pt' => array(),
'ru' => array(),
'sv' => array( "Å", "Ä", "Ö" ),
+ 'sv@collation=standard' => array( "Å", "Ä", "Ö" ),
'uk' => array( "Ґ", "Ь" ),
'vi' => array( "Ă", "Â", "Đ", "Ê", "Ô", "Ơ", "Ư" ),
// Not verified, but likely correct
@@ -235,6 +236,7 @@ class IcuCollation extends Collation {
'es' => array( "Ñ" ),
'et' => array( "Š", "Ž", "Õ", "Ä", "Ö", "Ü" ),
'eu' => array( "Ñ" ),
+ 'fa' => array( "آ", "ء", "ه" ),
'fo' => array( "Á", "Ð", "Í", "Ó", "Ú", "Ý", "Æ", "Ø", "Å" ),
'fr' => array(),
'fur' => array( "À", "Á", "Â", "È", "Ì", "Ò", "Ù" ),
@@ -349,21 +351,21 @@ class IcuCollation extends Collation {
$cacheEntry = $cache->get( $cacheKey );
if ( $cacheEntry && isset( $cacheEntry['version'] )
- && $cacheEntry['version'] == self::FIRST_LETTER_VERSION )
- {
+ && $cacheEntry['version'] == self::FIRST_LETTER_VERSION
+ ) {
$this->firstLetterData = $cacheEntry;
return $this->firstLetterData;
}
// Generate data from serialized data file
- if ( isset ( self::$tailoringFirstLetters[$this->locale] ) ) {
+ if ( isset( self::$tailoringFirstLetters[$this->locale] ) ) {
$letters = wfGetPrecompiledData( "first-letters-root.ser" );
// Append additional characters
$letters = array_merge( $letters, self::$tailoringFirstLetters[$this->locale] );
// Remove unnecessary ones, if any
- if ( isset( self::$tailoringFirstLetters[ '-' . $this->locale ] ) ) {
- $letters = array_diff( $letters, self::$tailoringFirstLetters[ '-' . $this->locale ] );
+ if ( isset( self::$tailoringFirstLetters['-' . $this->locale] ) ) {
+ $letters = array_diff( $letters, self::$tailoringFirstLetters['-' . $this->locale] );
}
} else {
$letters = wfGetPrecompiledData( "first-letters-{$this->locale}.ser" );
@@ -430,7 +432,7 @@ class IcuCollation extends Collation {
$prev = false;
$duplicatePrefixes = array();
- foreach( $letterMap as $key => $value ) {
+ foreach ( $letterMap as $key => $value ) {
// Remove terminator byte. Otherwise the prefix
// comparison will get hung up on that.
$trimmedKey = rtrim( $key, "\0" );
@@ -456,7 +458,7 @@ class IcuCollation extends Collation {
}
$prev = $trimmedKey;
}
- foreach( $duplicatePrefixes as $badKey ) {
+ foreach ( $duplicatePrefixes as $badKey ) {
wfDebug( "Removing '{$letterMap[$badKey]}' from first letters." );
unset( $letterMap[$badKey] );
// This code assumes that unsetting does not change sort order.
diff --git a/includes/ConfEditor.php b/includes/ConfEditor.php
index 1d9ca921..67cb87db 100644
--- a/includes/ConfEditor.php
+++ b/includes/ConfEditor.php
@@ -278,19 +278,23 @@ class ConfEditor {
function getVars() {
$vars = array();
$this->parse();
- foreach( $this->pathInfo as $path => $data ) {
- if ( $path[0] != '$' )
+ foreach ( $this->pathInfo as $path => $data ) {
+ if ( $path[0] != '$' ) {
continue;
+ }
$trimmedPath = substr( $path, 1 );
$name = $data['name'];
- if ( $name[0] == '@' )
+ if ( $name[0] == '@' ) {
continue;
- if ( $name[0] == '$' )
+ }
+ if ( $name[0] == '$' ) {
$name = substr( $name, 1 );
+ }
$parentPath = substr( $trimmedPath, 0,
strlen( $trimmedPath ) - strlen( $name ) );
- if( substr( $parentPath, -1 ) == '/' )
+ if ( substr( $parentPath, -1 ) == '/' ) {
$parentPath = substr( $parentPath, 0, -1 );
+ }
$value = substr( $this->text, $data['valueStartByte'],
$data['valueEndByte'] - $data['valueStartByte']
@@ -315,13 +319,15 @@ class ConfEditor {
$target =& $array;
if ( $path !== '' ) {
foreach ( $pathArr as $p ) {
- if( !isset( $target[$p] ) )
+ if ( !isset( $target[$p] ) ) {
$target[$p] = array();
+ }
$target =& $target[$p];
}
}
- if ( !isset( $target[$key] ) )
+ if ( !isset( $target[$key] ) ) {
$target[$key] = $value;
+ }
}
/**
@@ -329,25 +335,30 @@ class ConfEditor {
* @return mixed Parsed value
*/
function parseScalar( $str ) {
- if ( $str !== '' && $str[0] == '\'' )
+ if ( $str !== '' && $str[0] == '\'' ) {
// Single-quoted string
// @todo FIXME: trim() call is due to mystery bug where whitespace gets
// appended to the token; without it we ended up reading in the
// extra quote on the end!
return strtr( substr( trim( $str ), 1, -1 ),
array( '\\\'' => '\'', '\\\\' => '\\' ) );
- if ( $str !== '' && $str[0] == '"' )
+ }
+ if ( $str !== '' && $str[0] == '"' ) {
// Double-quoted string
// @todo FIXME: trim() call is due to mystery bug where whitespace gets
// appended to the token; without it we ended up reading in the
// extra quote on the end!
return stripcslashes( substr( trim( $str ), 1, -1 ) );
- if ( substr( $str, 0, 4 ) == 'true' )
+ }
+ if ( substr( $str, 0, 4 ) == 'true' ) {
return true;
- if ( substr( $str, 0, 5 ) == 'false' )
+ }
+ if ( substr( $str, 0, 5 ) == 'false' ) {
return false;
- if ( substr( $str, 0, 4 ) == 'null' )
+ }
+ if ( substr( $str, 0, 4 ) == 'null' ) {
return null;
+ }
// Must be some kind of numeric value, so let PHP's weak typing
// be useful for a change
return $str;
@@ -537,7 +548,7 @@ class ConfEditor {
*/
function getIndent( $pos, $key = false, $arrowPos = false ) {
$arrowIndent = ' ';
- if ( $pos == 0 || $this->text[$pos-1] == "\n" ) {
+ if ( $pos == 0 || $this->text[$pos - 1] == "\n" ) {
$indentLength = strspn( $this->text, " \t", $pos );
$indent = substr( $this->text, $pos, $indentLength );
} else {
@@ -559,7 +570,7 @@ class ConfEditor {
public function parse() {
$this->initParse();
$this->pushState( 'file' );
- $this->pushPath( '@extra-' . ($this->serial++) );
+ $this->pushPath( '@extra-' . ( $this->serial++ ) );
$token = $this->firstToken();
while ( !$token->isEnd() ) {
@@ -617,19 +628,21 @@ class ConfEditor {
$this->expect( '=' );
$this->skipSpace();
$this->startPathValue();
- if ( $arrayAssign )
+ if ( $arrayAssign ) {
$this->pushState( 'expression', 'array assign end' );
- else
+ } else {
$this->pushState( 'expression', 'statement end' );
+ }
break;
case 'array assign end':
case 'statement end':
$this->endPathValue();
- if ( $state == 'array assign end' )
+ if ( $state == 'array assign end' ) {
$this->popPath();
+ }
$this->skipSpace();
$this->expect( ';' );
- $this->nextPath( '@extra-' . ($this->serial++) );
+ $this->nextPath( '@extra-' . ( $this->serial++ ) );
break;
case 'expression':
$token = $this->skipSpace();
@@ -649,7 +662,7 @@ class ConfEditor {
$this->skipSpace();
$this->expect( '(' );
$this->skipSpace();
- $this->pushPath( '@extra-' . ($this->serial++) );
+ $this->pushPath( '@extra-' . ( $this->serial++ ) );
if ( $this->isAhead( ')' ) ) {
// Empty array
$this->pushState( 'array end' );
@@ -681,7 +694,7 @@ class ConfEditor {
$this->endPathValue();
$this->markComma();
$this->nextToken();
- $this->nextPath( '@extra-' . ($this->serial++) );
+ $this->nextPath( '@extra-' . ( $this->serial++ ) );
// Look ahead to find ending bracket
if ( $this->isAhead( ")" ) ) {
// Found ending bracket, no continuation
@@ -1056,7 +1069,7 @@ class ConfEditorParseError extends MWException {
$lines = StringUtils::explode( "\n", $text );
foreach ( $lines as $lineNum => $line ) {
if ( $lineNum == $this->lineNum - 1 ) {
- return "$line\n" .str_repeat( ' ', $this->colNum - 1 ) . "^\n";
+ return "$line\n" . str_repeat( ' ', $this->colNum - 1 ) . "^\n";
}
}
return '';
diff --git a/includes/Cookie.php b/includes/Cookie.php
index 27a85072..ecf4667d 100644
--- a/includes/Cookie.php
+++ b/includes/Cookie.php
@@ -82,7 +82,8 @@ class Cookie {
* http://publicsuffix.org/
*
* @todo fixme fails to detect 3-letter top-level domains
- * @todo fixme fails to detect 2-letter top-level domains for single-domain use (probably not a big problem in practice, but there are test cases)
+ * @todo fixme fails to detect 2-letter top-level domains for single-domain use (probably
+ * not a big problem in practice, but there are test cases)
*
* @param string $domain the domain to validate
* @param string $originDomain (optional) the domain the cookie originates from
@@ -197,7 +198,7 @@ class CookieJar {
* Set a cookie in the cookie jar. Make sure only one cookie per-name exists.
* @see Cookie::set()
*/
- public function setCookie ( $name, $value, $attr ) {
+ public function setCookie( $name, $value, $attr ) {
/* cookies: case insensitive, so this should work.
* We'll still send the cookies back in the same case we got them, though.
*/
@@ -235,7 +236,7 @@ class CookieJar {
* @param string $domain cookie's domain
* @return null
*/
- public function parseCookieResponseHeader ( $cookie, $domain ) {
+ public function parseCookieResponseHeader( $cookie, $domain ) {
$len = strlen( 'Set-Cookie:' );
if ( substr_compare( 'Set-Cookie:', $cookie, 0, $len, true ) === 0 ) {
diff --git a/includes/DataUpdate.php b/includes/DataUpdate.php
index c1076b23..7b9ac281 100644
--- a/includes/DataUpdate.php
+++ b/includes/DataUpdate.php
@@ -78,7 +78,9 @@ abstract class DataUpdate implements DeferrableUpdate {
* @throws Exception|null
*/
public static function runUpdates( $updates ) {
- if ( empty( $updates ) ) return; # nothing to do
+ if ( empty( $updates ) ) {
+ return; # nothing to do
+ }
$open_transactions = array();
$exception = null;
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index a56ef849..0fc59fb2 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -63,7 +63,7 @@ $wgConf = new SiteConfiguration;
* MediaWiki version number
* @since 1.2
*/
-$wgVersion = '1.21.3';
+$wgVersion = '1.22.0';
/**
* Name of the site. It must be changed in LocalSettings.php
@@ -172,14 +172,6 @@ $wgScriptExtension = '.php';
$wgScript = false;
/**
- * The URL path to redirect.php. This is a script that is used by the Nostalgia
- * skin.
- *
- * Defaults to "{$wgScriptPath}/redirect{$wgScriptExtension}".
- */
-$wgRedirectScript = false;
-
-/**
* The URL path to load.php.
*
* Defaults to "{$wgScriptPath}/load{$wgScriptExtension}".
@@ -274,7 +266,6 @@ $wgAppleTouchIcon = false;
*
* @see wfTempDir()
* @note Default changed to false in MediaWiki 1.20.
- *
*/
$wgTmpDirectory = false;
@@ -317,7 +308,9 @@ $wgActionPaths = array();
* @{
*/
-/** Uploads have to be specially set up to be secure */
+/**
+ * Uploads have to be specially set up to be secure
+ */
$wgEnableUploads = false;
/**
@@ -325,24 +318,22 @@ $wgEnableUploads = false;
*/
$wgUploadStashMaxAge = 6 * 3600; // 6 hours
-/** Allows to move images and other media files */
+/**
+ * Allows to move images and other media files
+ */
$wgAllowImageMoving = true;
/**
* Enable deferred upload tasks that use the job queue.
* Only enable this if job runners are set up for both the
* 'AssembleUploadChunks' and 'PublishStashedFile' job types.
+ *
+ * @note If you use suhosin, this setting is incompatible with
+ * suhosin.session.encrypt.
*/
$wgEnableAsyncUploads = false;
/**
- * Allow chunked uploads. This should only really be needed if you
- * use the UploadWizard extension or allow huge file uploads.
- * https://www.mediawiki.org/wiki/API:Upload#Chunked_uploading
- */
-$wgAllowChunkedUploads = false;
-
-/**
* These are additional characters that should be replaced with '-' in filenames
*/
$wgIllegalFileChars = ":";
@@ -456,7 +447,9 @@ $wgImgAuthPublicTest = true;
*/
$wgLocalFileRepo = false;
-/** @see $wgLocalFileRepo */
+/**
+ * @see $wgLocalFileRepo
+ */
$wgForeignFileRepos = array();
/**
@@ -512,11 +505,11 @@ $wgFileBackends = array();
$wgLockManagers = array();
/**
- * Show EXIF data, on by default if available.
- * Requires PHP's EXIF extension: http://www.php.net/manual/en/ref.exif.php
+ * Show Exif data, on by default if available.
+ * Requires PHP's Exif extension: http://www.php.net/manual/en/ref.exif.php
*
* @note FOR WINDOWS USERS:
- * To enable EXIF functions, add the following lines to the "Windows
+ * To enable Exif functions, add the following lines to the "Windows
* extensions" section of php.ini:
* @code{.ini}
* extension=extensions/php_mbstring.dll
@@ -547,22 +540,36 @@ $wgUpdateCompatibleMetadata = false;
*/
$wgUseSharedUploads = false;
-/** Full path on the web server where shared uploads can be found */
+/**
+ * Full path on the web server where shared uploads can be found
+ */
$wgSharedUploadPath = "http://commons.wikimedia.org/shared/images";
-/** Fetch commons image description pages and display them on the local wiki? */
+/**
+ * Fetch commons image description pages and display them on the local wiki?
+ */
$wgFetchCommonsDescriptions = false;
-/** Path on the file system where shared uploads can be found. */
+/**
+ * Path on the file system where shared uploads can be found.
+ */
$wgSharedUploadDirectory = "/var/www/wiki3/images";
-/** DB name with metadata about shared directory. Set this to false if the uploads do not come from a wiki. */
+/**
+ * DB name with metadata about shared directory.
+ * Set this to false if the uploads do not come from a wiki.
+ */
$wgSharedUploadDBname = false;
-/** Optional table prefix used in database. */
+/**
+ * Optional table prefix used in database.
+ */
$wgSharedUploadDBprefix = '';
-/** Cache shared metadata in memcached. Don't do this if the commons wiki is in a different memcached domain */
+/**
+ * Cache shared metadata in memcached.
+ * Don't do this if the commons wiki is in a different memcached domain
+ */
$wgCacheSharedUploads = true;
/**
@@ -599,6 +606,27 @@ $wgCopyUploadsFromSpecialUpload = false;
$wgCopyUploadProxy = false;
/**
+ * Different timeout for upload by url
+ * This could be useful since when fetching large files, you may want a
+ * timeout longer than the default $wgHTTPTimeout. False means fallback
+ * to default.
+ *
+ * @since 1.22
+ */
+$wgCopyUploadTimeout = false;
+
+/**
+ * Different timeout for upload by url when run as a background job
+ * This could be useful since when fetching large files via job queue,
+ * you may want a different timeout, especially because there is no
+ * http request being kept alive.
+ *
+ * false means fallback to $wgCopyUploadTimeout.
+ * @since 1.22
+ */
+$wgCopyUploadAsyncTimeout = false;
+
+/**
* Max size for uploads, in bytes. If not set to an array, applies to all
* uploads. If set to an array, per upload type maximums can be set, using the
* file and url keys. If the * key is set this value will be used as maximum
@@ -613,7 +641,6 @@ $wgCopyUploadProxy = false;
* @endcode
* Sets the maximum for all uploads to 250 kB except for upload-by-url, which
* will have a maximum of 500 kB.
- *
*/
$wgMaxUploadSize = 1024 * 1024 * 100; # 100MB
@@ -648,6 +675,7 @@ $wgUploadMissingFileUrl = false;
* @endcode
*/
$wgThumbnailScriptPath = false;
+
/**
* @see $wgThumbnailScriptPath
*/
@@ -699,7 +727,7 @@ $wgFileExtensions = array( 'png', 'gif', 'jpg', 'jpeg' );
* Files with these extensions will never be allowed as uploads.
* An array of file extensions to blacklist. You should append to this array
* if you want to blacklist additional files.
- * */
+ */
$wgFileBlacklist = array(
# HTML may contain cookie-stealing JavaScript and web bugs
'html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht',
@@ -826,15 +854,25 @@ $wgContentHandlers = array(
* Use Image Magick instead of PHP builtin functions.
*/
$wgUseImageMagick = false;
-/** The convert command shipped with ImageMagick */
+
+/**
+ * The convert command shipped with ImageMagick
+ */
$wgImageMagickConvertCommand = '/usr/bin/convert';
-/** The identify command shipped with ImageMagick */
+
+/**
+ * The identify command shipped with ImageMagick
+ */
$wgImageMagickIdentifyCommand = '/usr/bin/identify';
-/** Sharpening parameter to ImageMagick */
+/**
+ * Sharpening parameter to ImageMagick
+ */
$wgSharpenParameter = '0x0.4';
-/** Reduction in linear dimensions below which sharpening will be enabled */
+/**
+ * Reduction in linear dimensions below which sharpening will be enabled
+ */
$wgSharpenReductionThreshold = 0.85;
/**
@@ -857,15 +895,15 @@ $wgImageMagickTempDir = false;
*/
$wgCustomConvertCommand = false;
-/** used for lossless jpeg rotation
+/**
+ * used for lossless jpeg rotation
*
* @since 1.21
- * **/
+ */
$wgJpegTran = '/usr/bin/jpegtran';
-
/**
- * Some tests and extensions use exiv2 to manipulate the EXIF metadata in some
+ * Some tests and extensions use exiv2 to manipulate the Exif metadata in some
* image formats.
*/
$wgExiv2Command = '/usr/bin/exiv2';
@@ -889,16 +927,23 @@ $wgSVGConverters = array(
'ImagickExt' => array( 'SvgHandler::rasterizeImagickExt' ),
);
-/** Pick a converter defined in $wgSVGConverters */
+/**
+ * Pick a converter defined in $wgSVGConverters
+ */
$wgSVGConverter = 'ImageMagick';
-/** If not in the executable PATH, specify the SVG converter path. */
+/**
+ * If not in the executable PATH, specify the SVG converter path.
+ */
$wgSVGConverterPath = '';
-/** Don't scale a SVG larger than this */
+/**
+ * Don't scale a SVG larger than this
+ */
$wgSVGMaxSize = 2048;
-/** Don't read SVG metadata beyond this point.
+/**
+ * Don't read SVG metadata beyond this point.
* Default is 1024*256 bytes
*/
$wgSVGMetadataCutoff = 262144;
@@ -930,6 +975,7 @@ $wgAllowTitlesInSVG = false;
* 12.5 million pixels or 3500x3500.
*/
$wgMaxImageArea = 1.25e7;
+
/**
* Force thumbnailing of animated GIFs above this size to a single
* frame instead of an animated thumbnail. As of MW 1.17 this limit
@@ -937,6 +983,7 @@ $wgMaxImageArea = 1.25e7;
* It probably makes sense to keep this equal to $wgMaxImageArea.
*/
$wgMaxAnimatedGifArea = 1.25e7;
+
/**
* Browsers don't support TIFF inline generally...
* For inline display, we need to convert to PNG or JPEG.
@@ -987,7 +1034,9 @@ $wgGenerateThumbnailOnParse = true;
*/
$wgShowArchiveThumbnails = true;
-/** Obsolete, always true, kept for compatibility with extensions */
+/**
+ * Obsolete, always true, kept for compatibility with extensions
+ */
$wgUseImageResize = true;
/**
@@ -1053,29 +1102,31 @@ $wgAntivirusSetup = array(
),
);
-/** Determines if a failed virus scan (AV_SCAN_FAILED) will cause the file to be rejected. */
+/**
+ * Determines if a failed virus scan (AV_SCAN_FAILED) will cause the file to be rejected.
+ */
$wgAntivirusRequired = true;
-/** Determines if the mime type of uploaded files should be checked */
+/**
+ * Determines if the mime type of uploaded files should be checked
+ */
$wgVerifyMimeType = true;
-/** Sets the mime type definition file to use by MimeMagic.php. */
+/**
+ * Sets the mime type definition file to use by MimeMagic.php.
+ * Set to null, to use built-in defaults only.
+ * example: $wgMimeTypeFile = '/etc/mime.types';
+ */
$wgMimeTypeFile = 'includes/mime.types';
-#$wgMimeTypeFile = '/etc/mime.types';
-#$wgMimeTypeFile = null; #use built-in defaults only.
-
-/** Sets the mime type info file to use by MimeMagic.php. */
-$wgMimeInfoFile = 'includes/mime.info';
-#$wgMimeInfoFile = null; #use built-in defaults only.
/**
- * Switch for loading the FileInfo extension by PECL at runtime.
- * This should be used only if fileinfo is installed as a shared object
- * or a dynamic library.
+ * Sets the mime type info file to use by MimeMagic.php.
+ * Set to null, to use built-in defaults only.
*/
-$wgLoadFileinfoExtension = false;
+$wgMimeInfoFile = 'includes/mime.info';
-/** Sets an external mime detector program. The command must print only
+/**
+ * Sets an external mime detector program. The command must print only
* the mime type to standard output.
* The name of the file to process will be appended to the command given here.
* If not set or NULL, mime_content_type will be used if available.
@@ -1145,6 +1196,7 @@ $wgGalleryOptions = array(
'imageHeight' => 120, // Height of the cells containing images in galleries (in "px")
'captionLength' => 25, // Length of caption to truncate (in characters)
'showBytes' => true, // Show the filesize in bytes in categories
+ 'mode' => 'traditional',
);
/**
@@ -1173,25 +1225,26 @@ $wgResponsiveImages = true;
* @name DJVU settings
* @{
*/
+
/**
* Path of the djvudump executable
* Enable this and $wgDjvuRenderer to enable djvu rendering
+ * example: $wgDjvuDump = 'djvudump';
*/
-# $wgDjvuDump = 'djvudump';
$wgDjvuDump = null;
/**
* Path of the ddjvu DJVU renderer
* Enable this and $wgDjvuDump to enable djvu rendering
+ * example: $wgDjvuRenderer = 'ddjvu';
*/
-# $wgDjvuRenderer = 'ddjvu';
$wgDjvuRenderer = null;
/**
* Path of the djvutxt DJVU text extraction utility
* Enable this and $wgDjvuDump to enable text layer extraction from djvu files
+ * example: $wgDjvuTxt = 'djvutxt';
*/
-# $wgDjvuTxt = 'djvutxt';
$wgDjvuTxt = null;
/**
@@ -1216,10 +1269,12 @@ $wgDjvuToXML = null;
* Set this to false to output the ppm file directly.
*/
$wgDjvuPostProcessor = 'pnmtojpeg';
+
/**
* File extension for the DJVU post processor output
*/
$wgDjvuOutputExtension = 'jpg';
+
/** @} */ # end of DJvu }
/** @} */ # end of file uploads }
@@ -1334,7 +1389,8 @@ $wgAllowHTMLEmail = false;
$wgEnotifFromEditor = false;
// TODO move UPO to preferences probably ?
-# If set to true, users get a corresponding option in their preferences and can choose to enable or disable at their discretion
+# If set to true, users get a corresponding option in their preferences and can choose to
+# enable or disable at their discretion
# If set to false, the corresponding input form on the user preference page is suppressed
# It call this to be a "user-preferences-option (UPO)"
@@ -1400,33 +1456,61 @@ $wgEnotifUseRealName = false;
*/
$wgUsersNotifiedOnAllChanges = array();
-
/** @} */ # end of email settings
/************************************************************************//**
* @name Database settings
* @{
*/
-/** Database host name or IP address */
+
+/**
+ * Database host name or IP address
+ */
$wgDBserver = 'localhost';
-/** Database port number (for PostgreSQL) */
+
+/**
+ * Database port number (for PostgreSQL)
+ */
$wgDBport = 5432;
-/** Name of the database */
+
+/**
+ * Name of the database
+ */
$wgDBname = 'my_wiki';
-/** Database username */
+
+/**
+ * Database username
+ */
$wgDBuser = 'wikiuser';
-/** Database user's password */
+
+/**
+ * Database user's password
+ */
$wgDBpassword = '';
-/** Database type */
+
+/**
+ * Database type
+ */
$wgDBtype = 'mysql';
-/** Whether to use SSL in DB connection. */
+
+/**
+ * Whether to use SSL in DB connection.
+ */
$wgDBssl = false;
-/** Whether to use compression in DB connection. */
+
+/**
+ * Whether to use compression in DB connection.
+ */
$wgDBcompress = false;
-/** Separate username for maintenance tasks. Leave as null to use the default. */
+/**
+ * Separate username for maintenance tasks. Leave as null to use the default.
+ */
$wgDBadminuser = null;
-/** Separate password for maintenance tasks. Leave as null to use the default. */
+
+/**
+ * Separate password for maintenance tasks. Leave as null to use the default.
+ */
$wgDBadminpassword = null;
/**
@@ -1437,9 +1521,23 @@ $wgDBadminpassword = null;
*/
$wgSearchType = null;
-/** Table name prefix */
+/**
+ * Alternative search types
+ * Sometimes you want to support multiple search engines for testing. This
+ * allows users to select their search engine of choice via url parameters
+ * to Special:Search and the action=search API. If using this, there's no
+ * need to add $wgSearchType to it, that is handled automatically.
+ */
+$wgSearchTypeAlternatives = null;
+
+/**
+ * Table name prefix
+ */
$wgDBprefix = '';
-/** MySQL table options to use during installation or update */
+
+/**
+ * MySQL table options to use during installation or update
+ */
$wgDBTableOptions = 'ENGINE=InnoDB';
/**
@@ -1450,10 +1548,14 @@ $wgDBTableOptions = 'ENGINE=InnoDB';
*/
$wgSQLMode = '';
-/** Mediawiki schema */
+/**
+ * Mediawiki schema
+ */
$wgDBmwschema = 'mediawiki';
-/** To override default SQLite data directory ($docroot/../data) */
+/**
+ * To override default SQLite data directory ($docroot/../data)
+ */
$wgSQLiteDataDir = '';
/**
@@ -1487,9 +1589,14 @@ $wgAllDBsAreLocalhost = false;
*/
$wgSharedDB = null;
-/** @see $wgSharedDB */
+/**
+ * @see $wgSharedDB
+ */
$wgSharedPrefix = false;
-/** @see $wgSharedDB */
+
+/**
+ * @see $wgSharedDB
+ */
$wgSharedTables = array( 'user', 'user_properties' );
/**
@@ -1509,11 +1616,11 @@ $wgSharedTables = array( 'user', 'user_properties' );
* - DBO_DEFAULT -- turns on DBO_TRX only if !$wgCommandLineMode (recommended)
* - DBO_DEBUG -- equivalent of $wgDebugDumpSql
* - DBO_TRX -- wrap entire request in a transaction
- * - DBO_IGNORE -- ignore errors (not useful in LocalSettings.php)
* - DBO_NOBUFFER -- turn off buffering (not useful in LocalSettings.php)
* - DBO_PERSISTENT -- enables persistent database connections
* - DBO_SSL -- uses SSL/TLS encryption in database connections, if available
- * - DBO_COMPRESS -- uses internal compression in database connections, if available
+ * - DBO_COMPRESS -- uses internal compression in database connections,
+ * if available
*
* - max lag: (optional) Maximum replication lag before a slave will taken out of rotation
* - max threads: (optional) Maximum number of running threads
@@ -1552,10 +1659,14 @@ $wgDBservers = false;
*/
$wgLBFactoryConf = array( 'class' => 'LBFactory_Simple' );
-/** How long to wait for a slave to catch up to the master */
+/**
+ * How long to wait for a slave to catch up to the master
+ */
$wgMasterWaitTimeout = 10;
-/** File to log database errors to */
+/**
+ * File to log database errors to
+ */
$wgDBerrorLog = false;
/**
@@ -1578,7 +1689,9 @@ $wgDBerrorLog = false;
*/
$wgDBerrorLogTZ = false;
-/** When to give an error message */
+/**
+ * When to give an error message
+ */
$wgDBClusterTimeout = 10;
/**
@@ -1607,6 +1720,35 @@ $wgDBAvgStatusPoll = 2000;
$wgDBmysql5 = false;
/**
+ * Set true to enable Oracle DCRP (supported from 11gR1 onward)
+ *
+ * To use this feature set to true and use a datasource defined as
+ * POOLED (i.e. in tnsnames definition set server=pooled in connect_data
+ * block).
+ *
+ * Starting from 11gR1 you can use DCRP (Database Resident Connection
+ * Pool) that maintains established sessions and reuses them on new
+ * connections.
+ *
+ * Not completely tested, but it should fall back on normal connection
+ * in case the pool is full or the datasource is not configured as
+ * pooled.
+ * And the other way around; using oci_pconnect on a non pooled
+ * datasource should produce a normal connection.
+ *
+ * When it comes to frequent shortlived DB connections like with MW
+ * Oracle tends to s***. The problem is the driver connects to the
+ * database reasonably fast, but establishing a session takes time and
+ * resources. MW does not rely on session state (as it does not use
+ * features such as package variables) so establishing a valid session
+ * is in this case an unwanted overhead that just slows things down.
+ *
+ * @warning EXPERIMENTAL!
+ *
+ */
+$wgDBOracleDRCP = false;
+
+/**
* Other wikis on this site, can be administered from a single developer
* account.
* Array numeric key => database name
@@ -1619,13 +1761,11 @@ $wgLocalDatabases = array();
* show a more obvious warning.
*/
$wgSlaveLagWarning = 10;
-/** @see $wgSlaveLagWarning */
-$wgSlaveLagCritical = 30;
/**
- * Use old names for change_tags indices.
+ * @see $wgSlaveLagWarning
*/
-$wgOldChangeTagsIndex = false;
+$wgSlaveLagCritical = 30;
/**@}*/ # End of DB settings }
@@ -1637,8 +1777,8 @@ $wgOldChangeTagsIndex = false;
/**
* We can also compress text stored in the 'text' table. If this is set on, new
* revisions will be compressed on page save if zlib support is available. Any
- * compressed revisions will be decompressed on load regardless of this setting
- * *but will not be readable at all* if zlib support is not available.
+ * compressed revisions will be decompressed on load regardless of this setting,
+ * but will not be readable at all* if zlib support is not available.
*/
$wgCompressRevisions = false;
@@ -1701,23 +1841,36 @@ $wgRevisionCacheExpiry = 0;
* @name Performance hacks and limits
* @{
*/
-/** Disable database-intensive features */
+
+/**
+ * Disable database-intensive features
+ */
$wgMiserMode = false;
-/** Disable all query pages if miser mode is on, not just some */
+
+/**
+ * Disable all query pages if miser mode is on, not just some
+ */
$wgDisableQueryPages = false;
-/** Number of rows to cache in 'querycache' table when miser mode is on */
+
+/**
+ * Number of rows to cache in 'querycache' table when miser mode is on
+ */
$wgQueryCacheLimit = 1000;
-/** Number of links to a page required before it is deemed "wanted" */
+
+/**
+ * Number of links to a page required before it is deemed "wanted"
+ */
$wgWantedPagesThreshold = 1;
-/** Enable slow parser functions */
+
+/**
+ * Enable slow parser functions
+ */
$wgAllowSlowParserFunctions = false;
-/** Allow schema updates */
-$wgAllowSchemaUpdates = true;
/**
- * Do DELETE/INSERT for link updates instead of incremental
+ * Allow schema updates
*/
-$wgUseDumbLinkUpdate = false;
+$wgAllowSchemaUpdates = true;
/**
* Anti-lock flags - bitfield
@@ -1886,11 +2039,15 @@ $wgObjectCacheSessionExpiry = 3600;
*/
$wgSessionHandler = null;
-/** If enabled, will send MemCached debugging information to $wgDebugLogFile */
+/**
+ * If enabled, will send MemCached debugging information to $wgDebugLogFile
+ */
$wgMemCachedDebug = false;
-/** The list of MemCached servers and port numbers */
-$wgMemCachedServers = array( '127.0.0.1:11000' );
+/**
+ * The list of MemCached servers and port numbers
+ */
+$wgMemCachedServers = array( '127.0.0.1:11211' );
/**
* Use persistent connections to MemCached, which are shared across multiple
@@ -1910,13 +2067,6 @@ $wgMemCachedTimeout = 500000;
$wgUseLocalMessageCache = false;
/**
- * Defines format of local cache.
- * - true: Serialized object
- * - false: PHP source file (Warning - security risk)
- */
-$wgLocalMessageCacheSerialized = true;
-
-/**
* Instead of caching everything, only cache those messages which have
* been customised in the site content language. This means that
* MediaWiki:Foo/ja is ignored if MediaWiki:Foo doesn't exist.
@@ -1952,7 +2102,9 @@ $wgLocalisationCacheConf = array(
'manualRecache' => false,
);
-/** Allow client-side caching of pages */
+/**
+ * Allow client-side caching of pages
+ */
$wgCachePages = true;
/**
@@ -2038,7 +2190,8 @@ $wgUseGzip = false;
*/
$wgUseETag = false;
-/** Clock skew or the one-second resolution of time() can occasionally cause cache
+/**
+ * Clock skew or the one-second resolution of time() can occasionally cause cache
* problems when the user requests two pages within a short period of time. This
* variable adds a given number of seconds to vulnerable timestamps, thereby giving
* a grace period.
@@ -2077,13 +2230,18 @@ $wgInvalidateCacheOnLocalSettingsChange = true;
*/
$wgUseSquid = false;
-/** If you run Squid3 with ESI support, enable this (default:false): */
+/**
+ * If you run Squid3 with ESI support, enable this (default:false):
+ */
$wgUseESI = false;
-/** Send X-Vary-Options header for better caching (requires patched Squid) */
+/**
+ * Send X-Vary-Options header for better caching (requires patched Squid)
+ */
$wgUseXVO = false;
-/** Add X-Forwarded-Proto to the Vary and X-Vary-Options headers for API
+/**
+ * Add X-Forwarded-Proto to the Vary and X-Vary-Options headers for API
* requests and RSS/Atom feeds. Use this if you have an SSL termination setup
* and need to split the cache between HTTP and HTTPS for API requests,
* feed requests and HTTP redirect responses in order to prevent cache
@@ -2131,7 +2289,9 @@ $wgSquidServers = array();
*/
$wgSquidServersNoPurge = array();
-/** Maximum number of titles to purge in any one client operation */
+/**
+ * Maximum number of titles to purge in any one client operation
+ */
$wgMaxSquidPurgeTitles = 400;
/**
@@ -2163,12 +2323,12 @@ $wgSquidPurgeUseHostHeader = true;
* Each key in this array is a regular expression to match against the purged
* URL, or an empty string to match all URLs. The purged URL is matched against
* the regexes in the order specified, and the first rule whose regex matches
- * is used.
+ * is used, all remaining rules will thus be ignored.
*
- * Example configuration to send purges for upload.wikimedia.org to one
+ * @par Example configuration to send purges for upload.wikimedia.org to one
* multicast group and all other purges to another:
* @code
- * $wgHTCPMulticastRouting = array(
+ * $wgHTCPRouting = array(
* '|^https?://upload\.wikimedia\.org|' => array(
* 'host' => '239.128.0.113',
* 'port' => 4827,
@@ -2180,11 +2340,44 @@ $wgSquidPurgeUseHostHeader = true;
* );
* @endcode
*
- * @since 1.20
+ * You can also pass an array of hosts to send purges too. This is useful when
+ * you have several multicast groups or unicast address that should receive a
+ * given purge. Multiple hosts support was introduced in MediaWiki 1.22.
+ *
+ * @par Example of sending purges to multiple hosts:
+ * @code
+ * $wgHTCPRouting = array(
+ * '' => array(
+ * // Purges to text caches using multicast
+ * array( 'host' => '239.128.0.114', 'port' => '4827' ),
+ * // Purges to a hardcoded list of caches
+ * array( 'host' => '10.88.66.1', 'port' => '4827' ),
+ * array( 'host' => '10.88.66.2', 'port' => '4827' ),
+ * array( 'host' => '10.88.66.3', 'port' => '4827' ),
+ * ),
+ * );
+ * @endcode
+ *
+ * @since 1.22
+ *
+ * $wgHTCPRouting replaces $wgHTCPMulticastRouting that was introduced in 1.20.
+ * For back compatibility purposes, whenever its array is empty
+ * $wgHTCPMutlicastRouting will be used as a fallback if it not null.
*
* @see $wgHTCPMulticastTTL
*/
-$wgHTCPMulticastRouting = array();
+$wgHTCPRouting = array();
+
+/**
+ * @deprecated since 1.22, please use $wgHTCPRouting instead.
+ *
+ * Whenever this is set and $wgHTCPRouting evaluates to false, $wgHTCPRouting
+ * will be set to this value.
+ * This is merely for back compatibility.
+ *
+ * @since 1.20
+ */
+$wgHTCPMulticastRouting = null;
/**
* HTCP multicast address. Set this to a multicast IP address to enable HTCP.
@@ -2192,29 +2385,34 @@ $wgHTCPMulticastRouting = array();
* Note that MediaWiki uses the old non-RFC compliant HTCP format, which was
* present in the earliest Squid implementations of the protocol.
*
- * This setting is DEPRECATED in favor of $wgHTCPMulticastRouting , and kept
- * for backwards compatibility only. If $wgHTCPMulticastRouting is set, this
- * setting is ignored. If $wgHTCPMulticastRouting is not set and this setting
- * is, it is used to populate $wgHTCPMulticastRouting.
+ * This setting is DEPRECATED in favor of $wgHTCPRouting , and kept for
+ * backwards compatibility only. If $wgHTCPRouting is set, this setting is
+ * ignored. If $wgHTCPRouting is not set and this setting is, it is used to
+ * populate $wgHTCPRouting.
*
- * @deprecated since 1.20 in favor of $wgHTCPMulticastRouting
+ * @deprecated since 1.20 in favor of $wgHTCPMulticastRouting and since 1.22 in
+ * favor of $wgHTCPRouting.
*/
$wgHTCPMulticastAddress = false;
/**
* HTCP multicast port.
- * @deprecated since 1.20 in favor of $wgHTCPMulticastRouting
+ * @deprecated since 1.20 in favor of $wgHTCPMulticastRouting and since 1.22 in
+ * favor of $wgHTCPRouting.
+ *
* @see $wgHTCPMulticastAddress
*/
$wgHTCPPort = 4827;
/**
* HTCP multicast TTL.
- * @see $wgHTCPMulticastRouting
+ * @see $wgHTCPRouting
*/
$wgHTCPMulticastTTL = 1;
-/** Should forwarded Private IPs be accepted? */
+/**
+ * Should forwarded Private IPs be accepted?
+ */
$wgUsePrivateIPs = false;
/** @} */ # end of HTTP proxy settings
@@ -2258,13 +2456,19 @@ $wgLangObjCacheSize = 10;
*/
$wgGrammarForms = array();
-/** Treat language links as magic connectors, not inline links */
+/**
+ * Treat language links as magic connectors, not inline links
+ */
$wgInterwikiMagic = true;
-/** Hide interlanguage links from the sidebar */
+/**
+ * Hide interlanguage links from the sidebar
+ */
$wgHideInterlanguageLinks = false;
-/** List of language names or overrides for default names in Names.php */
+/**
+ * List of language names or overrides for default names in Names.php
+ */
$wgExtraLanguageNames = array();
/**
@@ -2364,7 +2568,8 @@ $wgBrowserBlackList = array(
'/^Mozilla\/4\.[^ ]+ [^(]*?\((?!compatible).*; [UIN]/',
/**
- * MSIE on Mac OS 9 is teh sux0r, converts þ to <thorn>, ð to <eth>, Þ to <THORN> and Ð to <ETH>
+ * MSIE on Mac OS 9 is teh sux0r, converts þ to <thorn>, ð to <eth>,
+ * Þ to <THORN> and Ð to <ETH>
*
* Known useragents:
* - Mozilla/4.0 (compatible; MSIE 5.0; Mac_PowerPC)
@@ -2372,7 +2577,7 @@ $wgBrowserBlackList = array(
* - Mozilla/4.0 (compatible; MSIE 5.23; Mac_PowerPC)
* - [...]
*
- * @link http://en.wikipedia.org/w/index.php?title=User%3A%C6var_Arnfj%F6r%F0_Bjarmason%2Ftestme&diff=12356041&oldid=12355864
+ * @link http://en.wikipedia.org/w/index.php?diff=12356041&oldid=12355864
* @link http://en.wikipedia.org/wiki/Template%3AOS9
*/
'/^Mozilla\/4\.0 \(compatible; MSIE \d+\.\d+; Mac_PowerPC\)/',
@@ -2403,6 +2608,7 @@ $wgLegacySchemaConversion = false;
* the interface is set to English.
*/
$wgAmericanDates = false;
+
/**
* For Hindi and Arabic use local numerals instead of Western style (0-9)
* numerals in interface.
@@ -2425,16 +2631,24 @@ $wgMsgCacheExpiry = 86400;
*/
$wgMaxMsgCacheEntrySize = 10000;
-/** Whether to enable language variant conversion. */
+/**
+ * Whether to enable language variant conversion.
+ */
$wgDisableLangConversion = false;
-/** Whether to enable language variant conversion for links. */
+/**
+ * Whether to enable language variant conversion for links.
+ */
$wgDisableTitleConversion = false;
-/** Whether to enable canonical language links in meta data. */
+/**
+ * Whether to enable canonical language links in meta data.
+ */
$wgCanonicalLanguageLinks = true;
-/** Default variant code, if false, the default will be the language code */
+/**
+ * Default variant code, if false, the default will be the language code
+ */
$wgDefaultLanguageVariant = false;
/**
@@ -2538,52 +2752,44 @@ $wgLocalTZoffset = null;
* @{
*/
-/** The default Content-Type header. */
-$wgMimeType = 'text/html';
-
-/**
- * The content type used in script tags. This is mostly going to be ignored if
- * $wgHtml5 is true, at least for actual HTML output, since HTML5 doesn't
- * require a MIME type for JavaScript or CSS (those are the default script and
- * style languages).
- */
-$wgJsMimeType = 'text/javascript';
-
/**
- * The HTML document type. Ignored if $wgHtml5 is true, since <!DOCTYPE html>
- * doesn't actually have a doctype part to put this variable's contents in.
+ * The default Content-Type header.
*/
-$wgDocType = '-//W3C//DTD XHTML 1.0 Transitional//EN';
+$wgMimeType = 'text/html';
/**
- * The URL of the document type declaration. Ignored if $wgHtml5 is true,
- * since HTML5 has no DTD, and <!DOCTYPE html> doesn't actually have a DTD part
- * to put this variable's contents in.
+ * Previously used as content type in HTML script tags. This is now ignored since
+ * HTML5 doesn't require a MIME type for script tags (javascript is the default).
+ * It was also previously used by RawAction to determine the ctype query parameter
+ * value that will result in a javascript response.
+ * @deprecated since 1.22
*/
-$wgDTD = 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd';
+$wgJsMimeType = null;
/**
- * The default xmlns attribute. Ignored if $wgHtml5 is true (or it's supposed
- * to be), since we don't currently support XHTML5, and in HTML5 (i.e., served
- * as text/html) the attribute has no effect, so why bother?
+ * The default xmlns attribute. The option to define this has been removed.
+ * The value of this variable is no longer used by core and is set to a fixed
+ * value in Setup.php for compatibility with extensions that depend on the value
+ * of this variable being set. Such a dependency however is deprecated.
+ * @deprecated since 1.22
*/
-$wgXhtmlDefaultNamespace = 'http://www.w3.org/1999/xhtml';
+$wgXhtmlDefaultNamespace = null;
/**
- * Should we output an HTML5 doctype? If false, use XHTML 1.0 Transitional
- * instead, and disable HTML5 features. This may eventually be removed and set
- * to always true. If it's true, a number of other settings will be irrelevant
- * and have no effect.
+ * Previously used to determine if we should output an HTML5 doctype.
+ * This is no longer used as we always output HTML5 now. For compatibility with
+ * extensions that still check the value of this config it's value is now forced
+ * to true by Setup.php.
+ * @deprecated since 1.22
*/
$wgHtml5 = true;
/**
* Defines the value of the version attribute in the &lt;html&gt; tag, if any.
- * This is ignored if $wgHtml5 is false. If $wgAllowRdfaAttributes and
- * $wgHtml5 are both true, and this evaluates to boolean false (like if it's
- * left at the default null value), it will be auto-initialized to the correct
- * value for RDFa+HTML5. As such, you should have no reason to ever actually
- * set this to anything.
+ * If $wgAllowRdfaAttributes is true, and this evaluates to boolean false
+ * (like if it's left at the default null value), it will be auto-initialized
+ * to the correct value for RDFa+HTML5. As such, you should have no reason to
+ * ever actually set this to anything.
*/
$wgHtml5Version = null;
@@ -2594,7 +2800,7 @@ $wgHtml5Version = null;
$wgAllowRdfaAttributes = false;
/**
- * Enabled HTML5 microdata attributes for use in wikitext, if $wgHtml5 is also true.
+ * Enabled HTML5 microdata attributes for use in wikitext.
*/
$wgAllowMicrodataAttributes = false;
@@ -2628,8 +2834,7 @@ $wgWellFormedXml = true;
* Normally we wouldn't have to define this in the root "<html>"
* element, but IE needs it there in some circumstances.
*
- * This is ignored if $wgHtml5 is true, for the same reason as
- * $wgXhtmlDefaultNamespace.
+ * This is ignored if $wgMimeType is set to a non-XML mimetype.
*/
$wgXhtmlNamespaces = array();
@@ -2679,34 +2884,11 @@ $wgDefaultSkin = 'vector';
* remove from the .../skins/ directory
*/
$wgSkipSkin = '';
-/** Array for more like $wgSkipSkin. */
-$wgSkipSkins = array();
/**
- * Optionally, we can specify a stylesheet to use for media="handheld".
- * This is recognized by some, but not all, handheld/mobile/PDA browsers.
- * If left empty, compliant handheld browsers won't pick up the skin
- * stylesheet, which is specified for 'screen' media.
- *
- * Can be a complete URL, base-relative path, or $wgStylePath-relative path.
- * Try 'chick/main.css' to apply the Chick styles to the MonoBook HTML.
- *
- * Will also be switched in when 'handheld=yes' is added to the URL, like
- * the 'printable=yes' mode for print media.
+ * Array for more like $wgSkipSkin.
*/
-$wgHandheldStyle = false;
-
-/**
- * If set, 'screen' and 'handheld' media specifiers for stylesheets are
- * transformed such that they apply to the iPhone/iPod Touch Mobile Safari,
- * which doesn't recognize 'handheld' but does support media queries on its
- * screen size.
- *
- * Consider only using this if you have a *really good* handheld stylesheet,
- * as iPhone users won't have any way to disable it and use the "grown-up"
- * styles instead.
- */
-$wgHandheldForIPhone = false;
+$wgSkipSkins = array();
/**
* Allow user Javascript page?
@@ -2729,10 +2911,14 @@ $wgAllowUserCss = false;
*/
$wgAllowUserCssPrefs = true;
-/** Use the site's Javascript page? */
+/**
+ * Use the site's Javascript page?
+ */
$wgUseSiteJs = true;
-/** Use the site's Cascading Style Sheets (CSS)? */
+/**
+ * Use the site's Cascading Style Sheets (CSS)?
+ */
$wgUseSiteCss = true;
/**
@@ -2771,7 +2957,6 @@ $wgEditPageFrameOptions = 'DENY';
* - 'SAMEORIGIN': Allow framing by pages on the same domain.
* - false: Allow all framing.
*/
-
$wgApiFrameOptions = 'DENY';
/**
@@ -2807,9 +2992,10 @@ $wgExperimentalHtmlIds = false;
* The value should be either a string or an array. If it is a string it will be output
* directly as html, however some skins may choose to ignore it. An array is the preferred format
* for the icon, the following keys are used:
- * - src: An absolute url to the image to use for the icon, this is recommended
+ * - 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
- * - url: The url to use in the a element around the text or icon, if not set an a element will not be outputted
+ * - 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
* skins like Modern or if src is not set, and will otherwise be used as
* the alt="" for the image. This key is required.
@@ -2851,7 +3037,7 @@ $wgVectorUseSimpleSearch = true;
* - true = use an icon watch/unwatch button
* - false = use watch/unwatch text link
*/
-$wgVectorUseIconWatch = false;
+$wgVectorUseIconWatch = true;
/**
* Display user edit counts in various prominent places.
@@ -3052,8 +3238,10 @@ $wgLegacyJavaScriptGlobals = true;
*
* If set to a negative number, ResourceLoader will assume there is no query
* string length limit.
+ *
+ * Defaults to a value based on php configuration.
*/
-$wgResourceLoaderMaxQueryLength = -1;
+$wgResourceLoaderMaxQueryLength = false;
/**
* If set to true, JavaScript modules loaded from wiki pages will be parsed
@@ -3081,6 +3269,59 @@ $wgResourceLoaderValidateStaticJS = false;
*/
$wgResourceLoaderExperimentalAsyncLoading = false;
+/**
+ * Global LESS variables. An associative array binding variable names to CSS
+ * string values.
+ *
+ * Because the hashed contents of this array are used to construct the cache key
+ * that ResourceLoader uses to look up LESS compilation results, updating this
+ * array can be used to deliberately invalidate the set of cached results.
+ *
+ * @par Example:
+ * @code
+ * $wgResourceLoaderLESSVars = array(
+ * 'baseFontSize' => '1em',
+ * 'smallFontSize' => '0.75em',
+ * 'WikimediaBlue' => '#006699',
+ * );
+ * @endcode
+ * @since 1.22
+ */
+$wgResourceLoaderLESSVars = array();
+
+/**
+ * Custom LESS functions. An associative array mapping function name to PHP
+ * callable.
+ *
+ * Changes to LESS functions do not trigger cache invalidation. If you update
+ * the behavior of a LESS function and need to invalidate stale compilation
+ * results, you can touch one of values in $wgResourceLoaderLESSVars, as
+ * documented above.
+ *
+ * @since 1.22
+ */
+$wgResourceLoaderLESSFunctions = array(
+ 'embeddable' => 'ResourceLoaderLESSFunctions::embeddable',
+ 'embed' => 'ResourceLoaderLESSFunctions::embed',
+);
+
+/**
+ * Default import paths for LESS modules. LESS files referenced in @import
+ * statements will be looked up here first, and relative to the importing file
+ * second. To avoid collisions, it's important for the LESS files in these
+ * directories to have a common, predictable file name prefix.
+ *
+ * Extensions need not (and should not) register paths in
+ * $wgResourceLoaderLESSImportPaths. The import path includes the path of the
+ * currently compiling LESS file, which allows each extension to freely import
+ * files from its own tree.
+ *
+ * @since 1.22
+ */
+$wgResourceLoaderLESSImportPaths = array(
+ "$IP/resources/mediawiki.less/",
+);
+
/** @} */ # End of resource loader settings }
/*************************************************************************//**
@@ -3167,7 +3408,8 @@ $wgNamespaceAliases = array();
* - []{}|# Are needed for link syntax, never enable these
* - <> Causes problems with HTML escaping, don't use
* - % Enabled by default, minor problems with path to query rewrite rules, see below
- * - + Enabled by default, but doesn't work with path to query rewrite rules, corrupted by apache
+ * - + Enabled by default, but doesn't work with path to query rewrite rules,
+ * corrupted by apache
* - ? Enabled by default, but doesn't work with path to PATH_INFO rewrites
*
* All three of these punctuation problems can be avoided by using an alias,
@@ -3201,6 +3443,7 @@ $wgInterwikiExpiry = 10800;
* @name Interwiki caching settings.
* @{
*/
+
/**
*$wgInterwikiCache specifies path to constant database file.
*
@@ -3215,6 +3458,7 @@ $wgInterwikiExpiry = 10800;
* data layout.
*/
$wgInterwikiCache = false;
+
/**
* Specify number of domains to check for messages.
* - 1: Just wiki(db)-level
@@ -3222,10 +3466,12 @@ $wgInterwikiCache = false;
* - 3: site levels
*/
$wgInterwikiScopes = 3;
+
/**
- * $wgInterwikiFallbackSite - if unable to resolve from cache
+ * Fallback site, if unable to resolve from cache
*/
$wgInterwikiFallbackSite = 'wiki';
+
/** @} */ # end of Interwiki caching settings.
/**
@@ -3270,7 +3516,8 @@ $wgCapitalLinks = true;
*/
$wgCapitalLinkOverrides = array();
-/** Which namespaces should support subpages?
+/**
+ * Which namespaces should support subpages?
* See Language.php for a list of namespaces.
*/
$wgNamespacesWithSubpages = array(
@@ -3313,7 +3560,7 @@ $wgMaxRedirects = 1;
* As of now, this only checks special pages. Redirects to pages in
* other namespaces cannot be invalidated by this variable.
*/
-$wgInvalidRedirectTargets = array( 'Filepath', 'Mypage', 'Mytalk' );
+$wgInvalidRedirectTargets = array( 'Filepath', 'Mypage', 'Mytalk', 'Redirect' );
/** @} */ # End of title and interwiki settings }
@@ -3349,7 +3596,9 @@ $wgParserConf = array(
#'preprocessorClass' => 'Preprocessor_Hash',
);
-/** Maximum indent level of toc. */
+/**
+ * Maximum indent level of toc.
+ */
$wgMaxTocLevel = 999;
/**
@@ -3377,25 +3626,41 @@ $wgMaxGeneratedPPNodeCount = 1000000;
*/
$wgMaxTemplateDepth = 40;
-/** @see $wgMaxTemplateDepth */
+/**
+ * @see $wgMaxTemplateDepth
+ */
$wgMaxPPExpandDepth = 40;
-/** The external URL protocols */
+/**
+ * The external URL protocols
+ */
$wgUrlProtocols = array(
'http://',
'https://',
'ftp://',
+ 'ftps://', // If we allow ftp:// we should allow the secure version.
+ 'ssh://',
+ 'sftp://', // SFTP > FTP
'irc://',
'ircs://', // @bug 28503
+ 'xmpp:', // Another open communication protocol
+ 'sip:',
+ 'sips:',
'gopher://',
'telnet://', // Well if we're going to support the above.. -ævar
'nntp://', // @bug 3808 RFC 1738
'worldwind://',
'mailto:',
+ 'tel:', // If we can make emails linkable, why not phone numbers?
+ 'sms:', // Likewise this is standardized too
'news:',
'svn://',
'git://',
'mms://',
+ 'bitcoin:', // Even registerProtocolHandler whitelists this along with mailto:
+ 'magnet:', // No reason to reject torrents over magnet: when they're allowed over http://
+ 'urn:', // Allow URNs to be used in Microdata/RDFa <link ... href="urn:...">s
+ 'geo:', // urls define geo locations, they're useful in Microdata/RDFa and for coordinates
'//', // for protocol-relative URLs
);
@@ -3404,7 +3669,9 @@ $wgUrlProtocols = array(
*/
$wgCleanSignatures = true;
-/** Whether to allow inline image pointing to other websites */
+/**
+ * Whether to allow inline image pointing to other websites
+ */
$wgAllowExternalImages = false;
/**
@@ -3421,7 +3688,8 @@ $wgAllowExternalImages = false;
*/
$wgAllowExternalImagesFrom = '';
-/** If $wgAllowExternalImages is false, you can allow an on-wiki
+/**
+ * If $wgAllowExternalImages is false, you can allow an on-wiki
* whitelist of regular expression fragments to match the image URL
* against. If the image matches one of the regular expression fragments,
* The image will be displayed.
@@ -3457,15 +3725,30 @@ $wgAllowImageTag = false;
* 'extension=tidy.so' to php.ini.
*/
$wgUseTidy = false;
-/** @see $wgUseTidy */
+
+/**
+ * @see $wgUseTidy
+ */
$wgAlwaysUseTidy = false;
-/** @see $wgUseTidy */
+
+/**
+ * @see $wgUseTidy
+ */
$wgTidyBin = 'tidy';
-/** @see $wgUseTidy */
+
+/**
+ * @see $wgUseTidy
+ */
$wgTidyConf = $IP . '/includes/tidy.conf';
-/** @see $wgUseTidy */
+
+/**
+ * @see $wgUseTidy
+ */
$wgTidyOpts = '';
-/** @see $wgUseTidy */
+
+/**
+ * @see $wgUseTidy
+ */
$wgTidyInternal = extension_loaded( 'tidy' );
/**
@@ -3474,7 +3757,8 @@ $wgTidyInternal = extension_loaded( 'tidy' );
*/
$wgDebugTidy = false;
-/** Allow raw, unchecked HTML in "<html>...</html>" sections.
+/**
+ * Allow raw, unchecked HTML in "<html>...</html>" sections.
* THIS IS VERY DANGEROUS on a publicly editable site, so USE wgGroupPermissions
* TO RESTRICT EDITING to only those that you trust
*/
@@ -3518,8 +3802,9 @@ $wgNoFollowDomainExceptions = array();
$wgAllowDisplayTitle = true;
/**
- * For consistency, restrict DISPLAYTITLE to titles that normalize to the same
- * canonical DB key.
+ * For consistency, restrict DISPLAYTITLE to text that normalizes to the same
+ * canonical DB key. Also disallow some inline CSS rules like display: none;
+ * which can cause the text to be hidden or unselectable.
*/
$wgRestrictDisplayTitle = true;
@@ -3536,12 +3821,13 @@ $wgExpensiveParserFunctionLimit = 100;
$wgPreprocessorCacheThreshold = 1000;
/**
- * Enable interwiki transcluding. Only when iw_trans=1.
+ * Enable interwiki transcluding. Only when iw_trans=1 in the interwiki table.
*/
$wgEnableScaryTranscluding = false;
/**
- * (see next option $wgGlobalDatabase).
+ * Expiry time for transcluded templates cached in transcache database table.
+ * Only used $wgEnableInterwikiTranscluding is set to true.
*/
$wgTranscludeCacheExpiry = 3600;
@@ -3591,7 +3877,8 @@ $wgHitcounterUpdateFreq = 1;
/**
* How many days user must be idle before he is considered inactive. Will affect
- * the number shown on Special:Statistics and Special:ActiveUsers special page.
+ * the number shown on Special:Statistics, Special:ActiveUsers, and the
+ * {{NUMBEROFACTIVEUSERS}} magic word in wikitext.
* You might want to leave this as the default value, to provide comparable
* numbers between different wikis.
*/
@@ -3604,7 +3891,9 @@ $wgActiveUserDays = 30;
* @{
*/
-/** For compatibility with old installations set to false */
+/**
+ * For compatibility with old installations set to false
+ */
$wgPasswordSalt = true;
/**
@@ -3647,7 +3936,7 @@ $wgReservedUsernames = array(
'ScriptImporter', // Default user name used by maintenance/importSiteScripts.php
'msg:double-redirect-fixer', // Automatic double redirect fix
'msg:usermessage-editor', // Default user for leaving user messages
- 'msg:proxyblocker', // For Special:Blockme
+ 'msg:proxyblocker', // For $wgProxyList and Special:Blockme (removed in 1.22)
);
/**
@@ -3655,7 +3944,6 @@ $wgReservedUsernames = array(
* preferences used by anonymous visitors and newly created accounts.
* For instance, to disable section editing links:
* $wgDefaultUserOptions ['editsection'] = 0;
- *
*/
$wgDefaultUserOptions = array(
'ccmeonemails' => 0,
@@ -3673,8 +3961,6 @@ $wgDefaultUserOptions = array(
'enotifusertalkpages' => 1,
'enotifwatchlistpages' => 0,
'extendwatchlist' => 0,
- 'externaldiff' => 0,
- 'externaleditor' => 0,
'fancysig' => 0,
'forceeditsummary' => 0,
'gender' => 'unknown',
@@ -3691,14 +3977,12 @@ $wgDefaultUserOptions = array(
'numberheadings' => 0,
'previewonfirst' => 0,
'previewontop' => 1,
- 'quickbar' => 5,
'rcdays' => 7,
'rclimit' => 50,
'rememberpassword' => 0,
'rows' => 25,
'searchlimit' => 20,
'showhiddencats' => 0,
- 'showjumplinks' => 1,
'shownumberswatching' => 1,
'showtoc' => 1,
'showtoolbar' => 1,
@@ -3708,6 +3992,7 @@ $wgDefaultUserOptions = array(
'underline' => 2,
'uselivepreview' => 0,
'usenewrc' => 0,
+ 'vector-simplesearch' => 1,
'watchcreations' => 0,
'watchdefault' => 0,
'watchdeletion' => 0,
@@ -3720,9 +4005,13 @@ $wgDefaultUserOptions = array(
'watchlisthidepatrolled' => 0,
'watchmoves' => 0,
'wllimit' => 250,
+ 'useeditwarning' => 1,
+ 'prefershttps' => 1,
);
-/** An array of preferences to not show for the user */
+/**
+ * An array of preferences to not show for the user
+ */
$wgHiddenPrefs = array();
/**
@@ -3744,63 +4033,6 @@ $wgInvalidUsernameCharacters = '@';
$wgUserrightsInterwikiDelimiter = '@';
/**
- * Use some particular type of external authentication. The specific
- * authentication module you use will normally require some extra settings to
- * be specified.
- *
- * null indicates no external authentication is to be used. Otherwise,
- * $wgExternalAuthType must be the name of a non-abstract class that extends
- * ExternalUser.
- *
- * Core authentication modules can be found in includes/extauth/.
- */
-$wgExternalAuthType = null;
-
-/**
- * Configuration for the external authentication. This may include arbitrary
- * keys that depend on the authentication mechanism. For instance,
- * authentication against another web app might require that the database login
- * info be provided. Check the file where your auth mechanism is defined for
- * info on what to put here.
- */
-$wgExternalAuthConf = array();
-
-/**
- * When should we automatically create local accounts when external accounts
- * already exist, if using ExternalAuth? Can have three values: 'never',
- * 'login', 'view'. 'view' requires the external database to support cookies,
- * and implies 'login'.
- *
- * TODO: Implement 'view' (currently behaves like 'login').
- */
-$wgAutocreatePolicy = 'login';
-
-/**
- * Policies for how each preference is allowed to be changed, in the presence
- * of external authentication. The keys are preference keys, e.g., 'password'
- * or 'emailaddress' (see Preferences.php et al.). The value can be one of the
- * following:
- *
- * - local: Allow changes to this pref through the wiki interface but only
- * apply them locally (default).
- * - semiglobal: Allow changes through the wiki interface and try to apply them
- * to the foreign database, but continue on anyway if that fails.
- * - global: Allow changes through the wiki interface, but only let them go
- * through if they successfully update the foreign database.
- * - message: Allow no local changes for linked accounts; replace the change
- * form with a message provided by the auth plugin, telling the user how to
- * change the setting externally (maybe providing a link, etc.). If the auth
- * plugin provides no message for this preference, hide it entirely.
- *
- * Accounts that are not linked to an external account are never affected by
- * this setting. You may want to look at $wgHiddenPrefs instead.
- * $wgHiddenPrefs supersedes this option.
- *
- * TODO: Implement message, global.
- */
-$wgAllowPrefChange = array();
-
-/**
* This is to let user authenticate using https when they come from http.
* Based on an idea by George Herbert on wikitech-l:
* http://lists.wikimedia.org/pipermail/wikitech-l/2010-October/050039.html
@@ -3808,13 +4040,6 @@ $wgAllowPrefChange = array();
*/
$wgSecureLogin = false;
-/**
- * By default, keep users logged in via HTTPS when $wgSecureLogin is also
- * true. Users opt-out of HTTPS when they login by de-selecting the checkbox.
- * @since 1.21
- */
-$wgSecureLoginDefaultHTTPS = true;
-
/** @} */ # end user accounts }
/************************************************************************//**
@@ -3832,7 +4057,9 @@ $wgAutoblockExpiry = 86400;
*/
$wgBlockAllowsUTEdit = false;
-/** Allow sysops to ban users from accessing Emailuser */
+/**
+ * Allow sysops to ban users from accessing Emailuser
+ */
$wgSysopEmailBans = true;
/**
@@ -3950,6 +4177,13 @@ $wgGroupPermissions['*']['edit'] = true;
$wgGroupPermissions['*']['createpage'] = true;
$wgGroupPermissions['*']['createtalk'] = true;
$wgGroupPermissions['*']['writeapi'] = true;
+$wgGroupPermissions['*']['editmyusercss'] = true;
+$wgGroupPermissions['*']['editmyuserjs'] = true;
+$wgGroupPermissions['*']['viewmywatchlist'] = true;
+$wgGroupPermissions['*']['editmywatchlist'] = true;
+$wgGroupPermissions['*']['viewmyprivateinfo'] = true;
+$wgGroupPermissions['*']['editmyprivateinfo'] = true;
+$wgGroupPermissions['*']['editmyoptions'] = true;
#$wgGroupPermissions['*']['patrolmarks'] = false; // let anons see what was patrolled
// Implicit group for all logged-in accounts
@@ -3971,25 +4205,29 @@ $wgGroupPermissions['user']['sendemail'] = true;
// Implicit group for accounts that pass $wgAutoConfirmAge
$wgGroupPermissions['autoconfirmed']['autoconfirmed'] = true;
+$wgGroupPermissions['autoconfirmed']['editsemiprotected'] = true;
// Users with bot privilege can have their edits hidden
// from various log pages by default
$wgGroupPermissions['bot']['bot'] = true;
$wgGroupPermissions['bot']['autoconfirmed'] = true;
+$wgGroupPermissions['bot']['editsemiprotected'] = true;
$wgGroupPermissions['bot']['nominornewtalk'] = true;
$wgGroupPermissions['bot']['autopatrol'] = true;
$wgGroupPermissions['bot']['suppressredirect'] = true;
$wgGroupPermissions['bot']['apihighlimits'] = true;
$wgGroupPermissions['bot']['writeapi'] = true;
-#$wgGroupPermissions['bot']['editprotected'] = true; // can edit all protected pages without cascade protection enabled
// Most extra permission abilities go to this group
$wgGroupPermissions['sysop']['block'] = true;
$wgGroupPermissions['sysop']['createaccount'] = true;
$wgGroupPermissions['sysop']['delete'] = true;
-$wgGroupPermissions['sysop']['bigdelete'] = true; // can be separately configured for pages with > $wgDeleteRevisionsLimit revs
-$wgGroupPermissions['sysop']['deletedhistory'] = true; // can view deleted history entries, but not see or restore the text
-$wgGroupPermissions['sysop']['deletedtext'] = true; // can view deleted revision text
+// can be separately configured for pages with > $wgDeleteRevisionsLimit revs
+$wgGroupPermissions['sysop']['bigdelete'] = true;
+// can view deleted history entries, but not see or restore the text
+$wgGroupPermissions['sysop']['deletedhistory'] = true;
+// can view deleted revision text
+$wgGroupPermissions['sysop']['deletedtext'] = true;
$wgGroupPermissions['sysop']['undelete'] = true;
$wgGroupPermissions['sysop']['editinterface'] = true;
$wgGroupPermissions['sysop']['editusercss'] = true;
@@ -4002,6 +4240,7 @@ $wgGroupPermissions['sysop']['move-rootuserpages'] = true;
$wgGroupPermissions['sysop']['patrol'] = true;
$wgGroupPermissions['sysop']['autopatrol'] = true;
$wgGroupPermissions['sysop']['protect'] = true;
+$wgGroupPermissions['sysop']['editprotected'] = true;
$wgGroupPermissions['sysop']['proxyunbannable'] = true;
$wgGroupPermissions['sysop']['rollback'] = true;
$wgGroupPermissions['sysop']['upload'] = true;
@@ -4009,6 +4248,7 @@ $wgGroupPermissions['sysop']['reupload'] = true;
$wgGroupPermissions['sysop']['reupload-shared'] = true;
$wgGroupPermissions['sysop']['unwatchedpages'] = true;
$wgGroupPermissions['sysop']['autoconfirmed'] = true;
+$wgGroupPermissions['sysop']['editsemiprotected'] = true;
$wgGroupPermissions['sysop']['ipblock-exempt'] = true;
$wgGroupPermissions['sysop']['blockemail'] = true;
$wgGroupPermissions['sysop']['markbotedits'] = true;
@@ -4084,11 +4324,12 @@ $wgImplicitGroups = array( '*', 'user', 'autoconfirmed' );
* @endcode
* This allows users in the '*' group (i.e. any user) to remove themselves from
* any group that they happen to be in.
- *
*/
$wgGroupsAddToSelf = array();
-/** @see $wgGroupsAddToSelf */
+/**
+ * @see $wgGroupsAddToSelf
+ */
$wgGroupsRemoveFromSelf = array();
/**
@@ -4108,11 +4349,37 @@ $wgRestrictionTypes = array( 'create', 'edit', 'move', 'upload' );
* dictates the order on the protection form's lists.
*
* - '' will be ignored (i.e. unprotected)
- * - 'sysop' is quietly rewritten to 'protect' for backwards compatibility
+ * - 'autoconfirmed' is quietly rewritten to 'editsemiprotected' for backwards compatibility
+ * - 'sysop' is quietly rewritten to 'editprotected' for backwards compatibility
*/
$wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' );
/**
+ * Restriction levels that can be used with cascading protection
+ *
+ * A page can only be protected with cascading protection if the
+ * requested restriction level is included in this array.
+ *
+ * 'autoconfirmed' is quietly rewritten to 'editsemiprotected' for backwards compatibility.
+ * 'sysop' is quietly rewritten to 'editprotected' for backwards compatibility.
+ */
+$wgCascadingRestrictionLevels = array( 'sysop' );
+
+/**
+ * Restriction levels that should be considered "semiprotected"
+ *
+ * Certain places in the interface recognize a dichotomy between "protected"
+ * and "semiprotected", without further distinguishing the specific levels. In
+ * general, if anyone can be eligible to edit a protection level merely by
+ * reaching some condition in $wgAutopromote, it should probably be considered
+ * "semiprotected".
+ *
+ * 'autoconfirmed' is quietly rewritten to 'editsemiprotected' for backwards compatibility.
+ * 'sysop' is not changed, since it really shouldn't be here.
+ */
+$wgSemiprotectedRestrictionLevels = array( 'autoconfirmed' );
+
+/**
* Set the minimum permissions required to edit pages in each
* namespace. If you list more than one permission, a user must
* have all of them to edit pages in that namespace.
@@ -4248,7 +4515,10 @@ $wgAutopromoteOnceLogInRC = true;
* @endcode
*/
$wgAddGroups = array();
-/** @see $wgAddGroups */
+
+/**
+ * @see $wgAddGroups
+ */
$wgRemoveGroups = array();
/**
@@ -4283,7 +4553,9 @@ $wgAccountCreationThrottle = 0;
*/
$wgSpamRegex = array();
-/** Same as the above except for edit summaries */
+/**
+ * Same as the above except for edit summaries
+ */
$wgSummarySpamRegex = array();
/**
@@ -4337,6 +4609,13 @@ $wgSorbsUrl = array();
$wgProxyWhitelist = array();
/**
+ * Whether to look at the X-Forwarded-For header's list of (potentially spoofed)
+ * IPs and apply IP blocks to them. This allows for IP blocks to work with correctly-configured
+ * (transparent) proxies without needing to block the proxies themselves.
+ */
+$wgApplyIpBlocksToXff = false;
+
+/**
* Simple rate limiter options to brake edit floods.
*
* Maximum number actions allowed in the given number of seconds; after that
@@ -4362,7 +4641,7 @@ $wgRateLimits = array(
'user' => null, // for each logged-in user
'newbie' => null, // for each recent (autoconfirmed) account; overrides 'user'
'ip' => null, // for each anon and recent account
- 'subnet' => null, // ... with final octet removed
+ 'subnet' => null, // ... within a /24 subnet in IPv4 or /64 in IPv6
),
'move' => array(
'user' => null,
@@ -4370,11 +4649,25 @@ $wgRateLimits = array(
'ip' => null,
'subnet' => null,
),
- 'mailpassword' => array(
+ 'mailpassword' => array( // triggering password resets emails
'anon' => null,
),
- 'emailuser' => array(
+ 'emailuser' => array( // emailing other users using MediaWiki
+ 'user' => null,
+ ),
+ 'linkpurge' => array( // purges of link tables
+ 'anon' => null,
'user' => null,
+ 'newbie' => null,
+ 'ip' => null,
+ 'subnet' => null,
+ ),
+ 'renderfile' => array( // files rendered via thumb.php or thumb_handler.php
+ 'anon' => null,
+ 'user' => null,
+ 'newbie' => null,
+ 'ip' => null,
+ 'subnet' => null,
),
);
@@ -4417,22 +4710,8 @@ $wgPasswordAttemptThrottle = array( 'count' => 5, 'seconds' => 300 );
*/
/**
- * If you enable this, every editor's IP address will be scanned for open HTTP
- * proxies.
- *
- * @warning Don't enable this. Many sysops will report "hostile TCP port scans"
- * to your ISP and ask for your server to be shut down.
- * You have been warned.
- *
+ * This should always be customised in LocalSettings.php
*/
-$wgBlockOpenProxies = false;
-/** Port we want to scan for a proxy */
-$wgProxyPorts = array( 80, 81, 1080, 3128, 6588, 8000, 8080, 8888, 65506 );
-/** Script used to scan */
-$wgProxyScriptPath = "$IP/maintenance/proxyCheck.php";
-/** */
-$wgProxyMemcExpiry = 86400;
-/** This should always be customised in LocalSettings.php */
$wgSecretKey = false;
/**
@@ -4516,10 +4795,14 @@ $wgHttpOnlyBlacklist = array(
'/^Mozilla\/4\.0 \(compatible; MSIE \d+\.\d+; Mac_PowerPC\)/',
);
-/** A list of cookies that vary the cache (for use by extensions) */
+/**
+ * A list of cookies that vary the cache (for use by extensions)
+ */
$wgCacheVaryCookies = array();
-/** Override to customise the session name */
+/**
+ * Override to customise the session name
+ */
$wgSessionName = false;
/** @} */ # end of cookie settings }
@@ -4537,7 +4820,7 @@ $wgSessionName = false;
*/
$wgUseTeX = false;
-/* @} */ # end LaTeX }
+/** @} */ # end LaTeX }
/************************************************************************//**
* @name Profiling, testing and debugging
@@ -4676,10 +4959,14 @@ $wgDevelopmentWarnings = false;
*/
$wgDeprecationReleaseLimit = false;
-/** Only record profiling info for pages that took longer than this */
+/**
+ * Only record profiling info for pages that took longer than this
+ */
$wgProfileLimit = 0.0;
-/** Don't put non-profiling info into log file */
+/**
+ * Don't put non-profiling info into log file
+ */
$wgProfileOnly = false;
/**
@@ -4694,10 +4981,14 @@ $wgProfileOnly = false;
*/
$wgProfileToDatabase = false;
-/** If true, print a raw call tree instead of per-function report */
+/**
+ * If true, print a raw call tree instead of per-function report
+ */
$wgProfileCallTree = false;
-/** Should application server host be put into profiling table */
+/**
+ * Should application server host be put into profiling table
+ */
$wgProfilePerHost = false;
/**
@@ -4714,10 +5005,25 @@ $wgUDPProfilerHost = '127.0.0.1';
*/
$wgUDPProfilerPort = '3811';
-/** Detects non-matching wfProfileIn/wfProfileOut calls */
+/**
+ * Format string for the UDP profiler. The UDP profiler invokes sprintf() with
+ * (profile id, count, cpu, cpu_sq, real, real_sq, entry name) as arguments.
+ * You can use sprintf's argument numbering/swapping capability to repeat,
+ * re-order or omit fields.
+ *
+ * @see $wgStatsFormatString
+ * @since 1.22
+ */
+$wgUDPProfilerFormatString = "%s - %d %f %f %f %f %s\n";
+
+/**
+ * Detects non-matching wfProfileIn/wfProfileOut calls
+ */
$wgDebugProfiling = false;
-/** Output debug message on every wfProfileIn/wfProfileOut */
+/**
+ * Output debug message on every wfProfileIn/wfProfileOut
+ */
$wgDebugFunctionEntry = false;
/**
@@ -4736,7 +5042,21 @@ $wgStatsMethod = 'cache';
*/
$wgAggregateStatsID = false;
-/** Whereas to count the number of time an article is viewed.
+/**
+ * When $wgStatsMethod is 'udp', this variable specifies how stats should be
+ * formatted. Its value should be a format string suitable for a sprintf()
+ * invocation with (id, count, key) arguments, where 'id' is either
+ * $wgAggregateStatsID or the DB name, 'count' is the value by which the metric
+ * is being incremented, and 'key' is the metric name.
+ *
+ * @see $wgUDPProfilerFormatString
+ * @see $wgAggregateStatsID
+ * @since 1.22
+ */
+$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;
@@ -4867,7 +5187,6 @@ $wgCountTotalSearchHits = false;
* PHP wrapper to avoid firing up mediawiki for every keystroke
*
* Placeholders: {searchTerms}
- *
*/
$wgOpenSearchTemplate = false;
@@ -4919,7 +5238,6 @@ $wgNamespacesToBeSearchedHelp = array(
* logged-in users.
* Useful for big wikis to maintain different search profiles for anonymous and
* logged-in users.
- *
*/
$wgSearchEverythingOnlyLoggedIn = false;
@@ -5011,12 +5329,8 @@ $wgPreviewOnOpenNamespaces = array(
);
/**
- * Activate external editor interface for files and pages
- * See http://www.mediawiki.org/wiki/Manual:External_editors
+ * Go button goes straight to the edit screen if the article doesn't exist.
*/
-$wgUseExternalEditor = true;
-
-/** Go button goes straight to the edit screen if the article doesn't exist. */
$wgGoToEdit = false;
/**
@@ -5050,7 +5364,9 @@ if ( !isset( $wgCommandLineMode ) ) {
}
/** @endcond */
-/** For colorized maintenance script output, is your terminal background dark ? */
+/**
+ * For colorized maintenance script output, is your terminal background dark ?
+ */
$wgCommandLineDarkBg = false;
/**
@@ -5091,6 +5407,11 @@ $wgReadOnlyFile = false;
$wgUpgradeKey = false;
/**
+ * Fully specified path to git binary
+ */
+$wgGitBin = '/usr/bin/git';
+
+/**
* Map GIT repository URLs to viewer URLs to provide links in Special:Version
*
* Key is a pattern passed to preg_match() and preg_replace(),
@@ -5098,12 +5419,14 @@ $wgUpgradeKey = false;
* The value is the replacement for the key (it can contain $1, etc.)
* %h will be replaced by the short SHA-1 (7 first chars) and %H by the
* full SHA-1 of the HEAD revision.
+ * %r will be replaced with a URL-encoded version of $1.
*
* @since 1.20
*/
$wgGitRepositoryViewers = array(
- 'https://gerrit.wikimedia.org/r/p/(.*)' => 'https://gerrit.wikimedia.org/r/gitweb?p=$1;h=%H',
- 'ssh://(?:[a-z0-9_]+@)?gerrit.wikimedia.org:29418/(.*)' => 'https://gerrit.wikimedia.org/r/gitweb?p=$1;h=%H',
+ 'https://gerrit.wikimedia.org/r/p/(.*)' => 'https://git.wikimedia.org/commit/%r/%H',
+ 'ssh://(?:[a-z0-9_]+@)?gerrit.wikimedia.org:29418/(.*)'
+ => 'https://git.wikimedia.org/commit/%r/%H',
);
/** @} */ # End of maintenance }
@@ -5139,11 +5462,15 @@ $wgRCLinkDays = array( 1, 3, 7, 14, 30 );
/**
* Send recent changes updates via UDP. The updates will be formatted for IRC.
* Set this to the IP address of the receiver.
+ *
+ * @deprecated since 1.22, use $wgRCFeeds
*/
$wgRC2UDPAddress = false;
/**
* Port number for RC updates
+ *
+ * @deprecated since 1.22, use $wgRCFeeds
*/
$wgRC2UDPPort = false;
@@ -5152,42 +5479,108 @@ $wgRC2UDPPort = false;
* This can be used to identify the wiki. A script is available called
* mxircecho.py which listens on a UDP port, and uses a prefix ending in a
* tab to identify the IRC channel to send the log line to.
+ *
+ * @deprecated since 1.22, use $wgRCFeeds
*/
$wgRC2UDPPrefix = '';
/**
* If this is set to true, $wgLocalInterwiki will be prepended to links in the
* IRC feed. If this is set to a string, that string will be used as the prefix.
+ *
+ * @deprecated since 1.22, use $wgRCFeeds
*/
$wgRC2UDPInterwikiPrefix = false;
/**
* Set to true to omit "bot" edits (by users with the bot permission) from the
* UDP feed.
+ *
+ * @deprecated since 1.22, use $wgRCFeeds
*/
$wgRC2UDPOmitBots = false;
/**
+ * Destinations to which notifications about recent changes
+ * should be sent.
+ *
+ * As of MediaWiki 1.22, the only supported 'engine' parameter option in core
+ * is 'UDPRCFeedEngine', which is used to send recent changes over UDP to the
+ * specified server.
+ * The common options are:
+ * * 'uri' -- the address to which the notices are to be sent.
+ * * 'formatter' -- the class name (implementing RCFeedFormatter) which will
+ * produce the text to send.
+ * * 'omit_bots' -- whether the bot edits should be in the feed
+ * The IRC-specific options are:
+ * * 'add_interwiki_prefix' -- whether the titles should be prefixed with
+ * $wgLocalInterwiki.
+ * The JSON-specific options are:
+ * * 'channel' -- if set, the 'channel' parameter is also set in JSON values.
+ *
+ * To ensure backwards-compatability, whenever $wgRC2UDPAddress is set, a
+ * 'default' feed will be created reusing the deprecated $wgRC2UDP* variables.
+ *
+ * @example $wgRCFeeds['example'] = array(
+ * 'formatter' => 'JSONRCFeedFormatter',
+ * 'uri' => "udp://localhost:1336",
+ * 'add_interwiki_prefix' => false,
+ * 'omit_bots' => true,
+ * );
+ * @example $wgRCFeeds['exampleirc'] = array(
+ * 'formatter' => 'IRCColourfulRCFeedFormatter',
+ * 'uri' => "udp://localhost:1338",
+ * 'add_interwiki_prefix' => false,
+ * 'omit_bots' => true,
+ * );
+ * @since 1.22
+ */
+$wgRCFeeds = array();
+
+/**
+ * Used by RecentChange::getEngine to find the correct engine to use for a given URI scheme.
+ * Keys are scheme names, values are names of engine classes.
+ */
+$wgRCEngines = array(
+ 'redis' => 'RedisPubSubFeedEngine',
+ 'udp' => 'UDPRCFeedEngine',
+);
+
+/**
* Enable user search in Special:Newpages
* This is really a temporary hack around an index install bug on some Wikipedias.
* Kill it once fixed.
*/
$wgEnableNewpagesUserFilter = true;
-/** Use RC Patrolling to check for vandalism */
+/**
+ * Use RC Patrolling to check for vandalism
+ */
$wgUseRCPatrol = true;
-/** Use new page patrolling to check new pages on Special:Newpages */
+/**
+ * Use new page patrolling to check new pages on Special:Newpages
+ */
$wgUseNPPatrol = true;
-/** Provide syndication feeds (RSS, Atom) for, e.g., Recentchanges, Newpages */
+/**
+ * Log autopatrol actions to the log table
+ */
+$wgLogAutopatrol = true;
+
+/**
+ * Provide syndication feeds (RSS, Atom) for, e.g., Recentchanges, Newpages
+ */
$wgFeed = true;
-/** Set maximum number of results to return in syndication feeds (RSS, Atom) for
- * eg Recentchanges, Newpages. */
+/**
+ * Set maximum number of results to return in syndication feeds (RSS, Atom) for
+ * eg Recentchanges, Newpages.
+ */
$wgFeedLimit = 50;
-/** _Minimum_ timeout for cached Recentchanges feed, in seconds.
+/**
+ * _Minimum_ timeout for cached Recentchanges feed, in seconds.
* A cached version will continue to be served out even if changes
* are made, until this many seconds runs out since the last render.
*
@@ -5196,11 +5589,14 @@ $wgFeedLimit = 50;
*/
$wgFeedCacheTimeout = 60;
-/** When generating Recentchanges RSS/Atom feed, diffs will not be generated for
- * pages larger than this size. */
+/**
+ * When generating Recentchanges RSS/Atom feed, diffs will not be generated for
+ * pages larger than this size.
+ */
$wgFeedDiffCutoff = 32768;
-/** Override the site's default RSS/ATOM feed for recentchanges that appears on
+/**
+ * Override the site's default RSS/ATOM feed for recentchanges that appears on
* every page. Some sites might have a different feed they'd like to promote
* instead of the RC feed (maybe like a "Recent New Articles" or "Breaking news" one).
* Should be a format as key (either 'rss' or 'atom') and an URL to the feed
@@ -5229,11 +5625,19 @@ $wgFeedClasses = array(
*/
$wgAdvertisedFeedTypes = array( 'atom' );
-/** Show watching users in recent changes, watchlist and page history views */
+/**
+ * Show watching users in recent changes, watchlist and page history views
+ */
$wgRCShowWatchingUsers = false; # UPO
-/** Show watching users in Page views */
+
+/**
+ * Show watching users in Page views
+ */
$wgPageShowWatchingUsers = false;
-/** Show the amount of changed characters in recent changes */
+
+/**
+ * Show the amount of changed characters in recent changes
+ */
$wgRCShowChangedSize = true;
/**
@@ -5245,7 +5649,8 @@ $wgRCChangedSizeThreshold = 500;
/**
* Show "Updated (since my last visit)" marker in RC view, watchlist and history
- * view for watched pages with new changes */
+ * view for watched pages with new changes
+ */
$wgShowUpdatedMarker = true;
/**
@@ -5274,6 +5679,33 @@ $wgUseTagFilter = true;
*/
$wgUnwatchedPageThreshold = false;
+/**
+ * Flags (letter symbols) shown in recent changes and watchlist to indicate
+ * certain types of edits.
+ *
+ * To register a new one:
+ * @code
+ * $wgRecentChangesFlags['flag'] => array(
+ * 'letter' => 'letter-msg',
+ * 'title' => 'tooltip-msg'
+ * );
+ * @endcode
+ *
+ * Optional 'class' allows to set a css class different than the flag name.
+ *
+ * @since 1.22
+ */
+$wgRecentChangesFlags = array(
+ 'newpage' => array( 'letter' => 'newpageletter',
+ 'title' => 'recentchanges-label-newpage' ),
+ 'minor' => array( 'letter' => 'minoreditletter',
+ 'title' => 'recentchanges-label-minor', 'class' => 'minoredit' ),
+ 'bot' => array( 'letter' => 'boteditletter',
+ 'title' => 'recentchanges-label-bot', 'class' => 'botedit' ),
+ 'unpatrolled' => array( 'letter' => 'unpatrolledletter',
+ 'title' => 'recentchanges-label-unpatrolled' ),
+);
+
/** @} */ # end RC/watchlist }
/************************************************************************//**
@@ -5291,15 +5723,17 @@ $wgUnwatchedPageThreshold = false;
$wgRightsPage = null;
/**
- * Set this to specify an external URL containing details about the content license used on your wiki.
+ * Set this to specify an external URL containing details about the content license used on your
+ * wiki.
* If $wgRightsPage is set then this setting is ignored.
*/
$wgRightsUrl = null;
/**
- * If either $wgRightsUrl or $wgRightsPage is specified then this variable gives the text for the link.
- * If using $wgRightsUrl then this value must be specified. If using $wgRightsPage then the name of the
- * page will also be used as the link if this variable is not set.
+ * If either $wgRightsUrl or $wgRightsPage is specified then this variable gives the text for the
+ * link.
+ * If using $wgRightsUrl then this value must be specified. If using $wgRightsPage then the name
+ * of the page will also be used as the link if this variable is not set.
*/
$wgRightsText = null;
@@ -5319,7 +5753,9 @@ $wgLicenseTerms = false;
*/
$wgCopyrightIcon = null;
-/** Set this to true if you want detailed copyright information forms on Upload. */
+/**
+ * Set this to true if you want detailed copyright information forms on Upload.
+ */
$wgUseCopyrightUpload = false;
/**
@@ -5331,8 +5767,10 @@ $wgUseCopyrightUpload = false;
*/
$wgMaxCredits = 0;
-/** If there are more than $wgMaxCredits authors, show $wgMaxCredits of them.
- * Otherwise, link to a separate credits page. */
+/**
+ * If there are more than $wgMaxCredits authors, show $wgMaxCredits of them.
+ * Otherwise, link to a separate credits page.
+ */
$wgShowCreditsIfMax = true;
/** @} */ # end of copyright and credits settings }
@@ -5435,6 +5873,13 @@ $wgExtensionFunctions = array();
$wgExtensionMessagesFiles = array();
/**
+ * Array of files with list(s) of extension entry points to be used in
+ * maintenance/mergeMessageFileList.php
+ * @since 1.22
+ */
+$wgExtensionEntryPointListFiles = array();
+
+/**
* Parser output hooks.
* This is an associative array where the key is an extension-defined tag
* (typically the extension name), and the value is a PHP callback.
@@ -5451,6 +5896,11 @@ $wgExtensionMessagesFiles = array();
$wgParserOutputHooks = array();
/**
+ * Whether to include the NewPP limit report as a HTML comment
+ */
+$wgEnableParserLimitReporting = true;
+
+/**
* List of valid skin names.
* The key should be the name in all lower case, the value should be a properly
* cased name for the skin. This value will be prefixed with "Skin" to create the
@@ -5521,17 +5971,24 @@ $wgAuth = null;
* @endcode
* - A function with some data:
* @code
- * $wgHooks['event_name'][] = array($function, $data);
+ * $wgHooks['event_name'][] = array( $function, $data );
* @endcode
* - A an object method:
* @code
- * $wgHooks['event_name'][] = array($object, 'method');
+ * $wgHooks['event_name'][] = array( $object, 'method' );
+ * @endcode
+ * - A closure:
+ * @code
+ * $wgHooks['event_name'][] = function ( $hookParam ) {
+ * // Handler code goes here.
+ * };
* @endcode
*
* @warning You should always append to an event array or you will end up
* deleting a previous registered hook.
*
- * @todo Does it support PHP closures?
+ * @warning Hook handlers should be registered at file scope. Registering
+ * handlers after file scope can lead to unexpected results due to caching.
*/
$wgHooks = array();
@@ -5543,7 +6000,6 @@ $wgJobClasses = array(
'refreshLinks' => 'RefreshLinksJob',
'refreshLinks2' => 'RefreshLinksJob2',
'htmlCacheUpdate' => 'HTMLCacheUpdateJob',
- 'html_cache_update' => 'HTMLCacheUpdateJob', // backwards-compatible
'sendMail' => 'EmaillingJob',
'enotifNotify' => 'EnotifNotifyJob',
'fixDoubleRedirect' => 'DoubleRedirectJob',
@@ -5843,12 +6299,13 @@ $wgDisableQueryPageUpdate = false;
* List of special pages, followed by what subtitle they should go under
* at Special:SpecialPages
*
- * @deprecated 1.21 Override SpecialPage::getGroupName instead
+ * @deprecated since 1.21 Override SpecialPage::getGroupName instead
*/
$wgSpecialPageGroups = array();
-/** Whether or not to sort special pages in Special:Specialpages */
-
+/**
+ * Whether or not to sort special pages in Special:Specialpages
+ */
$wgSortSpecialPages = true;
/**
@@ -6066,6 +6523,16 @@ $wgAPIRequestLog = false;
$wgAPICacheHelpTimeout = 60 * 60;
/**
+ * The ApiQueryQueryPages module should skip pages that are redundant to true
+ * API queries.
+ */
+$wgAPIUselessQueryPages = array(
+ 'MIMEsearch', // aiprop=mime
+ 'LinkSearch', // list=exturlusage
+ 'FileDuplicateSearch', // prop=duplicatefiles
+);
+
+/**
* Enable AJAX framework
*/
$wgUseAjax = true;
@@ -6119,7 +6586,6 @@ $wgCrossSiteAJAXdomains = array();
* even if they match one of the domains allowed by $wgCrossSiteAJAXdomains
* Uses the same syntax as $wgCrossSiteAJAXdomains
*/
-
$wgCrossSiteAJAXdomainExceptions = array();
/** @} */ # End AJAX and API }
@@ -6132,7 +6598,7 @@ $wgCrossSiteAJAXdomainExceptions = array();
/**
* Maximum amount of virtual memory available to shell processes under linux, in KB.
*/
-$wgMaxShellMemory = 102400;
+$wgMaxShellMemory = 307200;
/**
* Maximum file size created by shell processes under linux, in KB
@@ -6210,6 +6676,12 @@ $wgAsyncHTTPTimeout = 25;
*/
$wgHTTPProxy = false;
+/**
+ * Timeout for connections done internally (in seconds)
+ * Only works for curl
+ */
+$wgHTTPConnectTimeout = 5e0;
+
/** @} */ # End HTTP client }
/************************************************************************//**
@@ -6248,61 +6720,13 @@ $wgMaxBacklinksInvalidate = false;
/** @} */ # End job queue }
/************************************************************************//**
- * @name HipHop compilation
+ * @name Miscellaneous
* @{
*/
/**
- * The build directory for HipHop compilation.
- * Defaults to '$IP/maintenance/hiphop/build'.
- */
-$wgHipHopBuildDirectory = false;
-
-/**
- * The HipHop build type. Can be either "Debug" or "Release".
+ * Name of the external diff engine to use
*/
-$wgHipHopBuildType = 'Debug';
-
-/**
- * Number of parallel processes to use during HipHop compilation, or "detect"
- * to guess from system properties.
- */
-$wgHipHopCompilerProcs = 'detect';
-
-/**
- * Filesystem extensions directory. Defaults to $IP/../extensions.
- *
- * To compile extensions with HipHop, set $wgExtensionsDirectory correctly,
- * and use code like:
- * @code
- * require( MWInit::extensionSetupPath( 'Extension/Extension.php' ) );
- * @endcode
- *
- * to include the extension setup file from LocalSettings.php. It is not
- * necessary to set this variable unless you use MWInit::extensionSetupPath().
- */
-$wgExtensionsDirectory = false;
-
-/**
- * A list of files that should be compiled into a HipHop build, in addition to
- * those listed in $wgAutoloadClasses. Add to this array in an extension setup
- * file in order to add files to the build.
- *
- * The files listed here must either be either absolute paths under $IP or
- * under $wgExtensionsDirectory, or paths relative to the virtual source root
- * "$IP/..", i.e. starting with "phase3" for core files, and "extensions" for
- * extension files.
- */
-$wgCompiledFiles = array();
-
-/** @} */ # End of HipHop compilation }
-
-/************************************************************************//**
- * @name Miscellaneous
- * @{
- */
-
-/** Name of the external diff engine to use */
$wgExternalDiffEngine = false;
/**
@@ -6372,25 +6796,18 @@ $wgPoolCounterConf = null;
$wgUploadMaintenance = false;
/**
- * Allows running of selenium tests via maintenance/tests/RunSeleniumTests.php
- */
-$wgEnableSelenium = false;
-$wgSeleniumTestConfigs = array();
-$wgSeleniumConfigFile = null;
-$wgDBtestuser = ''; //db user that has permission to create and drop the test databases only
-$wgDBtestpassword = '';
-
-/**
- * Associative array mapping namespace IDs to the name of the content model pages in that namespace should have by
- * default (use the CONTENT_MODEL_XXX constants). If no special content type is defined for a given namespace,
- * pages in that namespace will use the CONTENT_MODEL_WIKITEXT (except for the special case of JS and CS pages).
+ * Associative array mapping namespace IDs to the name of the content model pages in that namespace
+ * should have by default (use the CONTENT_MODEL_XXX constants). If no special content type is
+ * defined for a given namespace, pages in that namespace will use the CONTENT_MODEL_WIKITEXT
+ * (except for the special case of JS and CS pages).
*
* @since 1.21
*/
$wgNamespaceContentModels = array();
/**
- * How to react if a plain text version of a non-text Content object is requested using ContentHandler::getContentText():
+ * How to react if a plain text version of a non-text Content object is requested using
+ * ContentHandler::getContentText():
*
* * 'ignore': return null
* * 'fail': throw an MWException
@@ -6402,14 +6819,14 @@ $wgContentHandlerTextFallback = 'ignore';
/**
* Set to false to disable use of the database fields introduced by the ContentHandler facility.
- * This way, the ContentHandler facility can be used without any additional information in the database.
- * A page's content model is then derived solely from the page's title. This however means that changing
- * a page's default model (e.g. using $wgNamespaceContentModels) will break the page and/or make the content
- * inaccessible. This also means that pages can not be moved to a title that would default to a different
- * content model.
+ * This way, the ContentHandler facility can be used without any additional information in the
+ * database. A page's content model is then derived solely from the page's title. This however
+ * means that changing a page's default model (e.g. using $wgNamespaceContentModels) will break
+ * the page and/or make the content inaccessible. This also means that pages can not be moved to
+ * a title that would default to a different content model.
*
- * Overall, with $wgContentHandlerUseDB = false, no database updates are needed, but content handling
- * is less robust and less flexible.
+ * Overall, with $wgContentHandlerUseDB = false, no database updates are needed, but content
+ * handling is less robust and less flexible.
*
* @since 1.21
*/
@@ -6420,7 +6837,7 @@ $wgContentHandlerUseDB = true;
* of texts are also rendered as wikitext, it only means that links, magic words, etc will have
* the effect on the database they would have on a wikitext page.
*
- * @todo: On the long run, it would be nice to put categories etc into a separate structure,
+ * @todo On the long run, it would be nice to put categories etc into a separate structure,
* or at least parse only the contents of comments in the scripts.
*
* @since 1.21
@@ -6448,6 +6865,12 @@ $wgSiteTypes = array(
);
/**
+ * Formerly a list of files for HipHop compilation
+ * @deprecated since 1.22
+ */
+$wgCompiledFiles = array();
+
+/**
* For really cool vim folding this needs to be at the end:
* vim: foldmarker=@{,@} foldmethod=marker
* @}
diff --git a/includes/DeferredUpdates.php b/includes/DeferredUpdates.php
index 89c4df68..c385f138 100644
--- a/includes/DeferredUpdates.php
+++ b/includes/DeferredUpdates.php
@@ -64,6 +64,16 @@ class DeferredUpdates {
}
/**
+ * Add a callable update. In a lot of cases, we just need a callback/closure,
+ * defining a new DeferrableUpdate object is not necessary
+ * @see MWCallableUpdate::__construct()
+ * @param callable $callable
+ */
+ public static function addCallableUpdate( $callable ) {
+ self::addUpdate( new MWCallableUpdate( $callable ) );
+ }
+
+ /**
* Do any deferred updates and clear the list
*
* @param string $commit set to 'commit' to commit after every update to
@@ -99,7 +109,7 @@ class DeferredUpdates {
// be reported to the user since the output is already sent.
// Instead we just log them.
if ( !$e instanceof ErrorPageError ) {
- wfDebugLog( 'exception', $e->getLogMessage() );
+ MWExceptionHandler::logException( $e );
}
}
}
diff --git a/includes/Defines.php b/includes/Defines.php
index c4a86335..86c5520b 100644
--- a/includes/Defines.php
+++ b/includes/Defines.php
@@ -197,14 +197,14 @@ define( 'EDIT_AUTOSUMMARY', 64 );
define( 'LIST_COMMA', 0 );
define( 'LIST_AND', 1 );
define( 'LIST_SET', 2 );
-define( 'LIST_NAMES', 3);
-define( 'LIST_OR', 4);
+define( 'LIST_NAMES', 3 );
+define( 'LIST_OR', 4 );
/**@}*/
/**
* Unicode and normalisation related
*/
-require_once __DIR__.'/normal/UtfNormalDefines.php';
+require_once __DIR__ . '/normal/UtfNormalDefines.php';
/**@{
* Hook support constants
@@ -237,11 +237,6 @@ define( 'SFH_OBJECT_ARGS', 2 );
/**@}*/
/**
- * Flags for Parser::setLinkHook
- */
-define( 'SLH_PATTERN', 1 );
-
-/**
* Flags for Parser::replaceLinkHolders
*/
define( 'RLH_FOR_UPDATE', 1 );
diff --git a/includes/EditPage.php b/includes/EditPage.php
index 8b2dbb5f..530e2674 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -100,7 +100,7 @@ class EditPage {
/**
* Status: user tried to create this page, but is not allowed to do that
- * ( Title->usercan('create') == false )
+ * ( Title->userCan('create') == false )
*/
const AS_NO_CREATE_PERMISSION = 223;
@@ -487,7 +487,7 @@ class EditPage {
// The edit page was reached via a red link.
// Redirect to the article page and let them click the edit tab if
// they really want a permission error.
- $wgOut->redirect( $this->mTitle->getFullUrl() );
+ $wgOut->redirect( $this->mTitle->getFullURL() );
return;
}
@@ -521,6 +521,8 @@ class EditPage {
$wgOut->addHTML( Html::rawElement( 'div', array( 'class' => 'templatesUsed' ),
Linker::formatTemplates( $this->getTemplates() ) ) );
+ $wgOut->addModules( 'mediawiki.action.edit.collapsibleFooter' );
+
if ( $this->mTitle->exists() ) {
$wgOut->returnToMain( null, $this->mTitle );
}
@@ -540,7 +542,7 @@ class EditPage {
// The edit page was reached via a red link.
// Redirect to the article page and let them click the edit tab if
// they really want a permission error.
- $wgOut->redirect( $this->mTitle->getFullUrl() );
+ $wgOut->redirect( $this->mTitle->getFullURL() );
} else {
$wgOut->readOnlyPage( $source, $protected, $reasons, $action );
}
@@ -621,6 +623,7 @@ class EditPage {
$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' );
}
@@ -663,6 +666,11 @@ class EditPage {
$this->edittime = $request->getVal( 'wpEdittime' );
$this->starttime = $request->getVal( 'wpStarttime' );
+ $undidRev = $request->getInt( 'wpUndidRevision' );
+ if ( $undidRev ) {
+ $this->undidRev = $undidRev;
+ }
+
$this->scrolltop = $request->getIntOrNull( 'wpScrolltop' );
if ( $this->textbox1 === '' && $request->getVal( 'wpTextbox1' ) === null ) {
@@ -832,7 +840,6 @@ class EditPage {
if ( $this->textbox1 === false ) {
return false;
}
- wfProxyCheck();
return true;
}
@@ -897,7 +904,9 @@ class EditPage {
$orig = $this->getOriginalContent();
$content = $orig ? $orig->getSection( $this->section ) : null;
- if ( !$content ) $content = $def_content;
+ if ( !$content ) {
+ $content = $def_content;
+ }
} else {
$undoafter = $wgRequest->getInt( 'undoafter' );
$undo = $wgRequest->getInt( 'undo' );
@@ -932,7 +941,19 @@ class EditPage {
# If we just undid one rev, use an autosummary
$firstrev = $oldrev->getNext();
if ( $firstrev && $firstrev->getId() == $undo ) {
- $undoSummary = wfMessage( 'undo-summary', $undo, $undorev->getUserText() )->inContentLanguage()->text();
+ $userText = $undorev->getUserText();
+ if ( $userText === '' ) {
+ $undoSummary = wfMessage(
+ 'undo-summary-username-hidden',
+ $undo
+ )->inContentLanguage()->text();
+ } else {
+ $undoSummary = wfMessage(
+ 'undo-summary',
+ $undo,
+ $userText
+ )->inContentLanguage()->text();
+ }
if ( $this->summary === '' ) {
$this->summary = $undoSummary;
} else {
@@ -950,6 +971,7 @@ class EditPage {
$undoMsg = 'norev';
}
+ // Messages: undo-success, undo-failure, undo-norev
$class = ( $undoMsg == 'success' ? '' : 'error ' ) . "mw-undo-{$undoMsg}";
$this->editFormPageTop .= $wgOut->parse( "<div class=\"{$class}\">" .
wfMessage( 'undo-' . $undoMsg )->plain() . '</div>', true, /* interface */true );
@@ -985,7 +1007,9 @@ class EditPage {
}
$revision = $this->mArticle->getRevisionFetched();
if ( $revision === null ) {
- if ( !$this->contentModel ) $this->contentModel = $this->getTitle()->getContentModel();
+ if ( !$this->contentModel ) {
+ $this->contentModel = $this->getTitle()->getContentModel();
+ }
$handler = ContentHandler::getForModelID( $this->contentModel );
return $handler->makeEmptyContent();
@@ -1007,7 +1031,9 @@ class EditPage {
$content = $rev ? $rev->getContent( Revision::RAW ) : null;
if ( $content === false || $content === null ) {
- if ( !$this->contentModel ) $this->contentModel = $this->getTitle()->getContentModel();
+ if ( !$this->contentModel ) {
+ $this->contentModel = $this->getTitle()->getContentModel();
+ }
$handler = ContentHandler::getForModelID( $this->contentModel );
return $handler->makeEmptyContent();
@@ -1157,20 +1183,20 @@ class EditPage {
* marked HttpOnly. The JavaScript code converts the cookie to a wgPostEdit config
* variable.
*
- * Since WebResponse::setcookie does not allow forcing HttpOnly for a single
- * cookie, we have to use PHP's setcookie() directly.
- *
* We use a path of '/' since wgCookiePath is not exposed to JS
*
* If the variable were set on the server, it would be cached, which is unwanted
* since the post-edit state should only apply to the load right after the save.
*/
protected function setPostEditCookie() {
- global $wgCookiePrefix, $wgCookieDomain;
$revisionId = $this->mArticle->getLatest();
$postEditKey = self::POST_EDIT_COOKIE_KEY_PREFIX . $revisionId;
- setcookie( $wgCookiePrefix . $postEditKey, '1', time() + self::POST_EDIT_COOKIE_DURATION, '/', $wgCookieDomain );
+ $response = RequestContext::getMain()->getRequest()->response();
+ $response->setcookie( $postEditKey, '1', time() + self::POST_EDIT_COOKIE_DURATION, array(
+ 'path' => '/',
+ 'httpOnly' => false,
+ ) );
}
/**
@@ -1213,7 +1239,7 @@ class EditPage {
case self::AS_SUCCESS_NEW_ARTICLE:
$query = $resultDetails['redirect'] ? 'redirect=no' : '';
- $anchor = isset ( $resultDetails['sectionanchor'] ) ? $resultDetails['sectionanchor'] : '';
+ $anchor = isset( $resultDetails['sectionanchor'] ) ? $resultDetails['sectionanchor'] : '';
$wgOut->redirect( $this->mTitle->getFullURL( $query ) . $anchor );
return false;
@@ -1355,6 +1381,24 @@ class EditPage {
return $status;
}
+ $spam = $wgRequest->getText( 'wpAntispam' );
+ if ( $spam !== '' ) {
+ wfDebugLog(
+ 'SimpleAntiSpam',
+ $wgUser->getName() .
+ ' editing "' .
+ $this->mTitle->getPrefixedText() .
+ '" submitted bogus field "' .
+ $spam .
+ '"'
+ );
+ $status->fatal( 'spamprotectionmatch', false );
+ $status->value = self::AS_SPAM_ERROR;
+ wfProfileOut( __METHOD__ . '-checks' );
+ wfProfileOut( __METHOD__ );
+ return $status;
+ }
+
try {
# Construct Content object
$textbox_content = $this->toEditContent( $this->textbox1 );
@@ -1381,6 +1425,18 @@ class EditPage {
# Check for spam
$match = self::matchSummarySpamRegex( $this->summary );
+ if ( $match === false && $this->section == 'new' ) {
+ # $wgSpamRegex is enforced on this new heading/summary because, unlike
+ # regular summaries, it is added to the actual wikitext.
+ if ( $this->sectiontitle !== '' ) {
+ # This branch is taken when the API is used with the 'sectiontitle' parameter.
+ $match = self::matchSpamRegex( $this->sectiontitle );
+ } else {
+ # This branch is taken when the "Add Topic" user interface is used, or the API
+ # is used with the 'summary' parameter.
+ $match = self::matchSpamRegex( $this->summary );
+ }
+ }
if ( $match === false ) {
$match = self::matchSpamRegex( $this->textbox1 );
}
@@ -1454,7 +1510,7 @@ class EditPage {
wfProfileOut( __METHOD__ );
return $status;
}
- if ( $wgUser->pingLimiter() ) {
+ if ( $wgUser->pingLimiter() || $wgUser->pingLimiter( 'linkpurge', 0 ) ) {
$status->fatal( 'actionthrottledtext' );
$status->value = self::AS_RATE_LIMITED;
wfProfileOut( __METHOD__ . '-checks' );
@@ -1488,8 +1544,17 @@ class EditPage {
return $status;
}
- # Don't save a new article if it's blank.
- if ( $this->textbox1 == '' ) {
+ // Don't save a new page if it's blank or if it's a MediaWiki:
+ // message with content equivalent to default (allow empty pages
+ // in this case to disable messages, see bug 50124)
+ $defaultMessageText = $this->mTitle->getDefaultMessageText();
+ if ( $this->mTitle->getNamespace() === NS_MEDIAWIKI && $defaultMessageText !== false ) {
+ $defaultText = $defaultMessageText;
+ } else {
+ $defaultText = '';
+ }
+
+ if ( $this->textbox1 === $defaultText ) {
$status->setResult( false, self::AS_BLANK_ARTICLE );
wfProfileOut( __METHOD__ );
return $status;
@@ -1566,8 +1631,7 @@ class EditPage {
}
}
- // If sectiontitle is set, use it, otherwise use the summary as the section title (for
- // backwards compatibility with old forms/bots).
+ // If sectiontitle is set, use it, otherwise use the summary as the section title.
if ( $this->sectiontitle !== '' ) {
$sectionTitle = $this->sectiontitle;
} else {
@@ -1724,6 +1788,10 @@ class EditPage {
}
$result['nullEdit'] = $doEditStatus->hasMessage( 'edit-no-change' );
+ if ( $result['nullEdit'] ) {
+ // We don't know if it was a null edit until now, so increment here
+ $wgUser->pingLimiter( 'linkpurge' );
+ }
$result['redirect'] = $content->isRedirect();
$this->updateWatchlist();
wfProfileOut( __METHOD__ );
@@ -1736,7 +1804,9 @@ class EditPage {
protected function updateWatchlist() {
global $wgUser;
- if ( $wgUser->isLoggedIn() && $this->watchthis != $wgUser->isWatched( $this->mTitle ) ) {
+ if ( $wgUser->isLoggedIn()
+ && $this->watchthis != $wgUser->isWatched( $this->mTitle, WatchedItem::IGNORE_USER_RIGHTS )
+ ) {
$fname = __METHOD__;
$title = $this->mTitle;
$watch = $this->watchthis;
@@ -1745,11 +1815,7 @@ class EditPage {
$dbw = wfGetDB( DB_MASTER );
$dbw->onTransactionIdle( function() use ( $dbw, $title, $watch, $wgUser, $fname ) {
$dbw->begin( $fname );
- if ( $watch ) {
- WatchAction::doWatch( $title, $wgUser );
- } else {
- WatchAction::doUnwatch( $title, $wgUser );
- }
+ WatchAction::doWatchOrUnwatch( $watch, $title, $wgUser );
$dbw->commit( $fname );
} );
}
@@ -1854,11 +1920,11 @@ class EditPage {
}
/**
- * Check given input text against $wgSpamRegex, and return the text of the first match.
+ * Check given input text against $wgSummarySpamRegex, and return the text of the first match.
*
* @param $text string
*
- * @return string|bool matching string or false
+ * @return string|bool matching string or false
*/
public static function matchSummarySpamRegex( $text ) {
global $wgSummarySpamRegex;
@@ -1885,10 +1951,16 @@ class EditPage {
global $wgOut, $wgUser;
$wgOut->addModules( 'mediawiki.action.edit' );
+ $wgOut->addModuleStyles( 'mediawiki.action.edit.styles' );
if ( $wgUser->getOption( 'uselivepreview', false ) ) {
$wgOut->addModules( 'mediawiki.action.edit.preview' );
}
+
+ if ( $wgUser->getOption( 'useeditwarning', false ) ) {
+ $wgOut->addModules( 'mediawiki.action.edit.editWarning' );
+ }
+
// Bug #19334: textarea jumps when editing articles in IE8
$wgOut->addStyle( 'common/IE80Fixes.css', 'screen', 'IE 8' );
@@ -1928,15 +2000,15 @@ 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' );
- } else if( $namespace == NS_FILE ) {
+ } elseif ( $namespace == NS_FILE ) {
# Show a hint to shared repo
$file = wfFindFile( $this->mTitle );
- if( $file && !$file->isLocal() ) {
+ if ( $file && !$file->isLocal() ) {
$descUrl = $file->getDescriptionUrl();
# there must be a description url to show a hint to shared repo
- if( $descUrl ) {
- if( !$this->mTitle->exists() ) {
- $wgOut->wrapWikiMsg( "<div class=\"mw-sharedupload-desc-create\">\n$1\n</div>", array (
+ if ( $descUrl ) {
+ if ( !$this->mTitle->exists() ) {
+ $wgOut->wrapWikiMsg( "<div class=\"mw-sharedupload-desc-create\">\n$1\n</div>", array(
'sharedupload-desc-create', $file->getRepo()->getDisplayName(), $descUrl
) );
} else {
@@ -2126,7 +2198,7 @@ class EditPage {
}
}
- //@todo: add EditForm plugin interface and use it here!
+ // @todo add EditForm plugin interface and use it here!
// search for textarea1 and textares2, and allow EditForm to override all uses.
$wgOut->addHTML( Html::openElement( 'form', array( 'id' => self::EDITFORM_ID, 'name' => self::EDITFORM_ID,
'method' => 'post', 'action' => $this->getActionURL( $this->getContextTitle() ),
@@ -2136,6 +2208,14 @@ class EditPage {
call_user_func_array( $formCallback, array( &$wgOut ) );
}
+ // Add an empty field to trip up spambots
+ $wgOut->addHTML(
+ Xml::openElement( 'div', array( 'id' => 'antispam-container', 'style' => 'display: none;' ) )
+ . Html::rawElement( 'label', array( 'for' => 'wpAntiSpam' ), wfMessage( 'simpleantispam-label' )->parse() )
+ . Xml::element( 'input', array( 'type' => 'text', 'name' => 'wpAntispam', 'id' => 'wpAntispam', 'value' => '' ) )
+ . Xml::closeElement( 'div' )
+ );
+
wfRunHooks( 'EditPage::showEditForm:fields', array( &$this, &$wgOut ) );
// Put these up at the top to ensure they aren't lost on early form submission
@@ -2161,7 +2241,7 @@ class EditPage {
}
# When the summary is hidden, also hide them on preview/show changes
- if( $this->nosummary ) {
+ if ( $this->nosummary ) {
$wgOut->addHTML( Html::hidden( 'nosummary', true ) );
}
@@ -2239,6 +2319,11 @@ class EditPage {
$wgOut->addHTML( Html::rawElement( 'div', array( 'class' => 'hiddencats' ),
Linker::formatHiddenCategories( $this->mArticle->getHiddenCategories() ) ) );
+ $wgOut->addHTML( Html::rawElement( 'div', array( 'class' => 'limitreport' ),
+ self::getPreviewLimitReport( $this->mParserOutput ) ) );
+
+ $wgOut->addModules( 'mediawiki.action.edit.collapsibleFooter' );
+
if ( $this->isConflict ) {
try {
$this->showConflict();
@@ -2282,7 +2367,7 @@ class EditPage {
}
// Add edit notices
- $wgOut->addHTML( implode( "\n", $this->mTitle->getEditNotices() ) );
+ $wgOut->addHTML( implode( "\n", $this->mTitle->getEditNotices( $this->oldid ) ) );
if ( $this->isConflict ) {
$wgOut->wrapWikiMsg( "<div class='mw-explainconflict'>\n$1\n</div>", 'explainconflict' );
@@ -2509,7 +2594,7 @@ class EditPage {
global $wgParser;
if ( $isSubjectPreview ) {
- $summary = wfMessage( 'newsectionsummary', $wgParser->stripSectionName( $summary ) )
+ $summary = wfMessage( 'newsectionsummary' )->rawParams( $wgParser->stripSectionName( $summary ) )
->inContentLanguage()->text();
}
@@ -2626,7 +2711,7 @@ HTML
$attribs = $customAttribs + array(
'accesskey' => ',',
- 'id' => $name,
+ 'id' => $name,
'cols' => $wgUser->getIntOption( 'cols' ),
'rows' => $wgUser->getIntOption( 'rows' ),
'style' => '' // avoid php notices when appending preferences (appending allows customAttribs['style'] to still work
@@ -2702,9 +2787,9 @@ HTML
$oldtitlemsg = 'currentrev';
# if message does not exist, show diff against the preloaded default
- if( $this->mTitle->getNamespace() == NS_MEDIAWIKI && !$this->mTitle->exists() ) {
+ if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI && !$this->mTitle->exists() ) {
$oldtext = $this->mTitle->getDefaultMessageText();
- if( $oldtext !== false ) {
+ if ( $oldtext !== false ) {
$oldtitlemsg = 'defaultmessagetext';
$oldContent = $this->toEditContent( $oldtext );
} else {
@@ -2799,7 +2884,15 @@ HTML
return self::getCopyrightWarning( $this->mTitle );
}
- public static function getCopyrightWarning( $title ) {
+ /**
+ * Get the copyright warning, by default returns wikitext
+ *
+ * @param Title $title
+ * @param string $format output format, valid values are any function of
+ * a Message object
+ * @return string
+ */
+ public static function getCopyrightWarning( $title, $format = 'plain' ) {
global $wgRightsText;
if ( $wgRightsText ) {
$copywarnMsg = array( 'copyrightwarning',
@@ -2813,7 +2906,60 @@ HTML
wfRunHooks( 'EditPageCopyrightWarning', array( $title, &$copywarnMsg ) );
return "<div id=\"editpage-copywarn\">\n" .
- call_user_func_array( 'wfMessage', $copywarnMsg )->plain() . "\n</div>";
+ call_user_func_array( 'wfMessage', $copywarnMsg )->$format() . "\n</div>";
+ }
+
+ /**
+ * Get the Limit report for page previews
+ *
+ * @since 1.22
+ * @param ParserOutput $output ParserOutput object from the parse
+ * @return string HTML
+ */
+ public static function getPreviewLimitReport( $output ) {
+ if ( !$output || !$output->getLimitReportData() ) {
+ return '';
+ }
+
+ wfProfileIn( __METHOD__ );
+
+ $limitReport = Html::rawElement( 'div', array( 'class' => 'mw-limitReportExplanation' ),
+ wfMessage( 'limitreport-title' )->parseAsBlock()
+ );
+
+ // Show/hide animation doesn't work correctly on a table, so wrap it in a div.
+ $limitReport .= Html::openElement( 'div', array( 'class' => 'preview-limit-report-wrapper' ) );
+
+ $limitReport .= Html::openElement( 'table', array(
+ 'class' => 'preview-limit-report wikitable'
+ ) ) .
+ Html::openElement( 'tbody' );
+
+ foreach ( $output->getLimitReportData() as $key => $value ) {
+ if ( wfRunHooks( 'ParserLimitReportFormat',
+ array( $key, $value, &$limitReport, true, true )
+ ) ) {
+ $keyMsg = wfMessage( $key );
+ $valueMsg = wfMessage( array( "$key-value-html", "$key-value" ) );
+ if ( !$valueMsg->exists() ) {
+ $valueMsg = new RawMessage( '$1' );
+ }
+ if ( !$keyMsg->isDisabled() && !$valueMsg->isDisabled() ) {
+ $limitReport .= Html::openElement( 'tr' ) .
+ Html::rawElement( 'th', null, $keyMsg->parse() ) .
+ Html::rawElement( 'td', null, $valueMsg->params( $value )->parse() ) .
+ Html::closeElement( 'tr' );
+ }
+ }
+ }
+
+ $limitReport .= Html::closeElement( 'tbody' ) .
+ Html::closeElement( 'table' ) .
+ Html::closeElement( 'div' );
+
+ wfProfileOut( __METHOD__ );
+
+ return $limitReport;
}
protected function showStandardInputs( &$tabindex = 2 ) {
@@ -2838,7 +2984,9 @@ HTML
$cancel = $this->getCancelLink();
if ( $cancel !== '' ) {
- $cancel .= wfMessage( 'pipe-separator' )->text();
+ $cancel .= Html::element( 'span',
+ array( 'class' => 'mw-editButtons-pipe-separator' ),
+ wfMessage( 'pipe-separator' )->text() );
}
$edithelpurl = Skin::makeInternalOrExternalUrl( wfMessage( 'edithelppage' )->inContentLanguage()->text() );
$edithelp = '<a target="helpwindow" href="' . $edithelpurl . '">' .
@@ -3028,9 +3176,9 @@ HTML
# don't parse non-wikitext pages, show message about preview
if ( $this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage() ) {
- if( $this->mTitle->isCssJsSubpage() ) {
+ if ( $this->mTitle->isCssJsSubpage() ) {
$level = 'user';
- } elseif( $this->mTitle->isCssOrJsPage() ) {
+ } elseif ( $this->mTitle->isCssOrJsPage() ) {
$level = 'site';
} else {
$level = false;
@@ -3046,7 +3194,7 @@ HTML
# Used messages to make sure grep find them:
# Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview
- if( $level && $format ) {
+ if ( $level && $format ) {
$note = "<div id='mw-{$level}{$format}preview'>" . wfMessage( "{$level}{$format}preview" )->text() . "</div>";
}
}
@@ -3098,9 +3246,9 @@ HTML
'<h2 id="mw-previewheader">' . wfMessage( 'preview' )->escaped() . "</h2>" .
$wgOut->parse( $note, true, /* interface */true ) . $conflict . "</div>\n";
- $pageLang = $this->mTitle->getPageLanguage();
- $attribs = array( 'lang' => $pageLang->getCode(), 'dir' => $pageLang->getDir(),
- 'class' => 'mw-content-' . $pageLang->getDir() );
+ $pageViewLang = $this->mTitle->getPageViewLanguage();
+ $attribs = array( 'lang' => $pageViewLang->getHtmlCode(), 'dir' => $pageViewLang->getDir(),
+ 'class' => 'mw-content-' . $pageViewLang->getDir() );
$previewHTML = Html::rawElement( 'div', $attribs, $previewHTML );
wfProfileOut( __METHOD__ );
@@ -3314,9 +3462,9 @@ HTML
$minorLabel = wfMessage( 'minoredit' )->parse();
if ( $wgUser->isAllowed( 'minoredit' ) ) {
$attribs = array(
- 'tabindex' => ++$tabindex,
+ 'tabindex' => ++$tabindex,
'accesskey' => wfMessage( 'accesskey-minoredit' )->text(),
- 'id' => 'wpMinoredit',
+ 'id' => 'wpMinoredit',
);
$checkboxes['minor'] =
Xml::check( 'wpMinoredit', $checked['minor'], $attribs ) .
@@ -3330,9 +3478,9 @@ HTML
$checkboxes['watch'] = '';
if ( $wgUser->isLoggedIn() ) {
$attribs = array(
- 'tabindex' => ++$tabindex,
+ 'tabindex' => ++$tabindex,
'accesskey' => wfMessage( 'accesskey-watch' )->text(),
- 'id' => 'wpWatchthis',
+ 'id' => 'wpWatchthis',
);
$checkboxes['watch'] =
Xml::check( 'wpWatchthis', $checked['watch'], $attribs ) .
@@ -3356,37 +3504,37 @@ HTML
$buttons = array();
$temp = array(
- 'id' => 'wpSave',
- 'name' => 'wpSave',
- 'type' => 'submit',
- 'tabindex' => ++$tabindex,
- 'value' => wfMessage( 'savearticle' )->text(),
+ 'id' => 'wpSave',
+ 'name' => 'wpSave',
+ 'type' => 'submit',
+ 'tabindex' => ++$tabindex,
+ 'value' => wfMessage( 'savearticle' )->text(),
'accesskey' => wfMessage( 'accesskey-save' )->text(),
- 'title' => wfMessage( 'tooltip-save' )->text() . ' [' . wfMessage( 'accesskey-save' )->text() . ']',
+ 'title' => wfMessage( 'tooltip-save' )->text() . ' [' . wfMessage( 'accesskey-save' )->text() . ']',
);
$buttons['save'] = Xml::element( 'input', $temp, '' );
++$tabindex; // use the same for preview and live preview
$temp = array(
- 'id' => 'wpPreview',
- 'name' => 'wpPreview',
- 'type' => 'submit',
- 'tabindex' => $tabindex,
- 'value' => wfMessage( 'showpreview' )->text(),
+ 'id' => 'wpPreview',
+ 'name' => 'wpPreview',
+ 'type' => 'submit',
+ 'tabindex' => $tabindex,
+ 'value' => wfMessage( 'showpreview' )->text(),
'accesskey' => wfMessage( 'accesskey-preview' )->text(),
- 'title' => wfMessage( 'tooltip-preview' )->text() . ' [' . wfMessage( 'accesskey-preview' )->text() . ']',
+ 'title' => wfMessage( 'tooltip-preview' )->text() . ' [' . wfMessage( 'accesskey-preview' )->text() . ']',
);
$buttons['preview'] = Xml::element( 'input', $temp, '' );
$buttons['live'] = '';
$temp = array(
- 'id' => 'wpDiff',
- 'name' => 'wpDiff',
- 'type' => 'submit',
- 'tabindex' => ++$tabindex,
- 'value' => wfMessage( 'showdiff' )->text(),
+ 'id' => 'wpDiff',
+ 'name' => 'wpDiff',
+ 'type' => 'submit',
+ 'tabindex' => ++$tabindex,
+ 'value' => wfMessage( 'showdiff' )->text(),
'accesskey' => wfMessage( 'accesskey-diff' )->text(),
- 'title' => wfMessage( 'tooltip-diff' )->text() . ' [' . wfMessage( 'accesskey-diff' )->text() . ']',
+ 'title' => wfMessage( 'tooltip-diff' )->text() . ' [' . wfMessage( 'accesskey-diff' )->text() . ']',
);
$buttons['diff'] = Xml::element( 'input', $temp, '' );
@@ -3506,7 +3654,7 @@ HTML
global $wgOut, $wgLang;
$this->textbox2 = $this->textbox1;
- if( is_array( $match ) ) {
+ if ( is_array( $match ) ) {
$match = $wgLang->listToText( $match );
}
$wgOut->prepareErrorPage( wfMessage( 'spamprotectiontitle' ) );
diff --git a/includes/Exception.php b/includes/Exception.php
index 0bd7a2a7..5bad88c2 100644
--- a/includes/Exception.php
+++ b/includes/Exception.php
@@ -30,7 +30,6 @@
* @ingroup Exception
*/
class MWException extends Exception {
- var $logId;
/**
* Should the exception use $wgOut to output the error?
@@ -45,6 +44,16 @@ class MWException extends Exception {
}
/**
+ * Whether to log this exception in the exception debug log.
+ *
+ * @since 1.23
+ * @return boolean
+ */
+ function isLoggable() {
+ return true;
+ }
+
+ /**
* Can the extension use the Message class/wfMessage to get i18n-ed messages?
*
* @return bool
@@ -75,11 +84,11 @@ class MWException extends Exception {
return null; // Just silently ignore
}
- if ( !array_key_exists( $name, $wgExceptionHooks ) || !is_array( $wgExceptionHooks[ $name ] ) ) {
+ if ( !array_key_exists( $name, $wgExceptionHooks ) || !is_array( $wgExceptionHooks[$name] ) ) {
return null;
}
- $hooks = $wgExceptionHooks[ $name ];
+ $hooks = $wgExceptionHooks[$name];
$callargs = array_merge( array( $this ), $args );
foreach ( $hooks as $hook ) {
@@ -126,13 +135,12 @@ class MWException extends Exception {
global $wgShowExceptionDetails;
if ( $wgShowExceptionDetails ) {
- return '<p>' . nl2br( htmlspecialchars( $this->getMessage() ) ) .
- '</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ) .
+ return '<p>' . nl2br( htmlspecialchars( MWExceptionHandler::getLogMessage( $this ) ) ) .
+ '</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( MWExceptionHandler::getRedactedTraceAsString( $this ) ) ) .
"</p>\n";
} else {
- return
- "<div class=\"errorbox\">" .
- '[' . $this->getLogId() . '] ' .
+ return "<div class=\"errorbox\">" .
+ '[' . MWExceptionHandler::getLogId( $this ) . '] ' .
gmdate( 'Y-m-d H:i:s' ) .
": Fatal exception of type " . get_class( $this ) . "</div>\n" .
"<!-- Set \$wgShowExceptionDetails = true; " .
@@ -152,8 +160,8 @@ class MWException extends Exception {
global $wgShowExceptionDetails;
if ( $wgShowExceptionDetails ) {
- return $this->getMessage() .
- "\nBacktrace:\n" . $this->getTraceAsString() . "\n";
+ return MWExceptionHandler::getLogMessage( $this ) .
+ "\nBacktrace:\n" . MWExceptionHandler::getRedactedTraceAsString( $this ) . "\n";
} else {
return "Set \$wgShowExceptionDetails = true; " .
"in LocalSettings.php to show detailed debugging information.\n";
@@ -170,43 +178,28 @@ class MWException extends Exception {
}
/**
- * Get a random ID for this error.
- * This allows to link the exception to its corresponding log entry when
- * $wgShowExceptionDetails is set to false.
+ * Get a the ID for this error.
*
+ * @since 1.20
+ * @deprecated since 1.22 Use MWExceptionHandler::getLogId instead.
* @return string
*/
function getLogId() {
- if ( $this->logId === null ) {
- $this->logId = wfRandomString( 8 );
- }
- return $this->logId;
+ wfDeprecated( __METHOD__, '1.22' );
+ return MWExceptionHandler::getLogId( $this );
}
/**
* Return the requested URL and point to file and line number from which the
* exception occurred
*
+ * @since 1.8
+ * @deprecated since 1.22 Use MWExceptionHandler::getLogMessage instead.
* @return string
*/
function getLogMessage() {
- global $wgRequest;
-
- $id = $this->getLogId();
- $file = $this->getFile();
- $line = $this->getLine();
- $message = $this->getMessage();
-
- if ( isset( $wgRequest ) && !$wgRequest instanceof FauxRequest ) {
- $url = $wgRequest->getRequestURL();
- if ( !$url ) {
- $url = '[no URL]';
- }
- } else {
- $url = '[no req]';
- }
-
- return "[$id] $url Exception from line $line of $file: $message";
+ wfDeprecated( __METHOD__, '1.22' );
+ return MWExceptionHandler::getLogMessage( $this );
}
/**
@@ -248,16 +241,9 @@ class MWException extends Exception {
* It will be either HTML or plain text based on isCommandLine().
*/
function report() {
- global $wgLogExceptionBacktrace;
- $log = $this->getLogMessage();
+ global $wgMimeType;
- if ( $log ) {
- if ( $wgLogExceptionBacktrace ) {
- wfDebugLog( 'exception', $log . "\n" . $this->getTraceAsString() . "\n" );
- } else {
- wfDebugLog( 'exception', $log );
- }
- }
+ MWExceptionHandler::logException( $this );
if ( defined( 'MW_API' ) ) {
// Unhandled API exception, we can't be sure that format printer is alive
@@ -268,6 +254,7 @@ class MWException extends Exception {
} else {
header( "HTTP/1.1 500 MediaWiki exception" );
header( "Status: 500 MediaWiki exception", true );
+ header( "Content-Type: $wgMimeType; charset=utf-8", true );
$this->reportHTML();
}
@@ -329,11 +316,17 @@ class ErrorPageError extends MWException {
$this->msg = $msg;
$this->params = $params;
- if( $msg instanceof Message ) {
- parent::__construct( $msg );
+ // Bug 44111: Messages in the log files should be in English and not
+ // customized by the local wiki. So get the default English version for
+ // passing to the parent constructor. Our overridden report() below
+ // makes sure that the page shown to the user is not forced to English.
+ if ( $msg instanceof Message ) {
+ $enMsg = clone( $msg );
} else {
- parent::__construct( wfMessage( $msg )->text() );
+ $enMsg = wfMessage( $msg, $params );
}
+ $enMsg->inLanguage( 'en' )->useDatabase( false );
+ parent::__construct( $enMsg->text() );
}
function report() {
@@ -461,39 +454,9 @@ class ThrottledError extends ErrorPageError {
*/
class UserBlockedError extends ErrorPageError {
public function __construct( Block $block ) {
- global $wgLang, $wgRequest;
-
- $blocker = $block->getBlocker();
- if ( $blocker instanceof User ) { // local user
- $blockerUserpage = $block->getBlocker()->getUserPage();
- $link = "[[{$blockerUserpage->getPrefixedText()}|{$blockerUserpage->getText()}]]";
- } else { // foreign user
- $link = $blocker;
- }
-
- $reason = $block->mReason;
- if( $reason == '' ) {
- $reason = wfMessage( 'blockednoreason' )->text();
- }
-
- /* $ip returns who *is* being blocked, $intended contains who was meant to be blocked.
- * This could be a username, an IP range, or a single IP. */
- $intended = $block->getTarget();
-
- parent::__construct(
- 'blockedtitle',
- $block->mAuto ? 'autoblockedtext' : 'blockedtext',
- array(
- $link,
- $reason,
- $wgRequest->getIP(),
- $block->getByName(),
- $block->getId(),
- $wgLang->formatExpiry( $block->mExpiry ),
- $intended,
- $wgLang->timeanddate( wfTimestamp( TS_MW, $block->mTimestamp ), true )
- )
- );
+ // @todo FIXME: Implement a more proper way to get context here.
+ $params = $block->getPermissionsError( RequestContext::getMain() );
+ parent::__construct( 'blockedtitle', array_shift( $params ), $params );
}
}
@@ -611,7 +574,7 @@ class HttpError extends MWException {
$content = htmlspecialchars( $this->content );
}
- return "<!DOCTYPE html>\n".
+ return "<!DOCTYPE html>\n" .
"<html><head><title>$header</title></head>\n" .
"<body><h1>$header</h1><p>$content</p></body></html>\n";
}
@@ -648,8 +611,10 @@ class MWExceptionHandler {
$message = "MediaWiki internal error.\n\n";
if ( $wgShowExceptionDetails ) {
- $message .= 'Original exception: ' . $e->__toString() . "\n\n" .
- 'Exception caught inside exception handler: ' . $e2->__toString();
+ $message .= 'Original exception: ' . self::getLogMessage( $e ) .
+ "\nBacktrace:\n" . self::getRedactedTraceAsString( $e ) .
+ "\n\nException caught inside exception handler: " . self::getLogMessage( $e2 ) .
+ "\nBacktrace:\n" . self::getRedactedTraceAsString( $e2 );
} else {
$message .= "Exception caught inside exception handler.\n\n" .
"Set \$wgShowExceptionDetails = true; at the bottom of LocalSettings.php " .
@@ -665,11 +630,11 @@ class MWExceptionHandler {
}
}
} else {
- $message = "Unexpected non-MediaWiki exception encountered, of type \"" . get_class( $e ) . "\"\n" .
- $e->__toString() . "\n";
+ $message = "Unexpected non-MediaWiki exception encountered, of type \"" . get_class( $e ) . "\"";
if ( $wgShowExceptionDetails ) {
- $message .= "\n" . $e->getTraceAsString() . "\n";
+ $message .= "\n" . MWExceptionHandler::getLogMessage( $e ) . "\nBacktrace:\n" .
+ self::getRedactedTraceAsString( $e ) . "\n";
}
if ( $cmdLine ) {
@@ -692,7 +657,7 @@ class MWExceptionHandler {
if ( defined( 'STDERR' ) ) {
fwrite( STDERR, $message );
} else {
- echo( $message );
+ echo $message;
}
}
@@ -715,11 +680,145 @@ class MWExceptionHandler {
// Final cleanup
if ( $wgFullyInitialised ) {
try {
- wfLogProfilingData(); // uses $wgRequest, hence the $wgFullyInitialised condition
- } catch ( Exception $e ) {}
+ // uses $wgRequest, hence the $wgFullyInitialised condition
+ wfLogProfilingData();
+ } catch ( Exception $e ) {
+ }
}
// Exit value should be nonzero for the benefit of shell jobs
exit( 1 );
}
+
+ /**
+ * Generate a string representation of an exception's stack trace
+ *
+ * Like Exception::getTraceAsString, but replaces argument values with
+ * argument type or class name.
+ *
+ * @param Exception $e
+ * @return string
+ */
+ public static function getRedactedTraceAsString( Exception $e ) {
+ $text = '';
+
+ foreach ( self::getRedactedTrace( $e ) as $level => $frame ) {
+ if ( isset( $frame['file'] ) && isset( $frame['line'] ) ) {
+ $text .= "#{$level} {$frame['file']}({$frame['line']}): ";
+ } else {
+ // 'file' and 'line' are unset for calls via call_user_func (bug 55634)
+ // This matches behaviour of Exception::getTraceAsString to instead
+ // display "[internal function]".
+ $text .= "#{$level} [internal function]: ";
+ }
+
+ if ( isset( $frame['class'] ) ) {
+ $text .= $frame['class'] . $frame['type'] . $frame['function'];
+ } else {
+ $text .= $frame['function'];
+ }
+
+ if ( isset( $frame['args'] ) ) {
+ $text .= '(' . implode( ', ', $frame['args'] ) . ")\n";
+ } else {
+ $text .= "()\n";
+ }
+ }
+
+ $level = $level + 1;
+ $text .= "#{$level} {main}";
+
+ return $text;
+ }
+
+ /**
+ * Return a copy of an exception's backtrace as an array.
+ *
+ * Like Exception::getTrace, but replaces each element in each frame's
+ * argument array with the name of its class (if the element is an object)
+ * or its type (if the element is a PHP primitive).
+ *
+ * @since 1.22
+ * @param Exception $e
+ * @return array
+ */
+ public static function getRedactedTrace( Exception $e ) {
+ return array_map( function ( $frame ) {
+ if ( isset( $frame['args'] ) ) {
+ $frame['args'] = array_map( function ( $arg ) {
+ return is_object( $arg ) ? get_class( $arg ) : gettype( $arg );
+ }, $frame['args'] );
+ }
+ return $frame;
+ }, $e->getTrace() );
+ }
+
+
+ /**
+ * Get the ID for this error.
+ *
+ * The ID is saved so that one can match the one output to the user (when
+ * $wgShowExceptionDetails is set to false), to the entry in the debug log.
+ *
+ * @since 1.22
+ * @param Exception $e
+ * @return string
+ */
+ public static function getLogId( Exception $e ) {
+ if ( !isset( $e->_mwLogId ) ) {
+ $e->_mwLogId = wfRandomString( 8 );
+ }
+ return $e->_mwLogId;
+ }
+
+ /**
+ * Return the requested URL and point to file and line number from which the
+ * exception occurred.
+ *
+ * @since 1.22
+ * @param Exception $e
+ * @return string
+ */
+ public static function getLogMessage( Exception $e ) {
+ global $wgRequest;
+
+ $id = self::getLogId( $e );
+ $file = $e->getFile();
+ $line = $e->getLine();
+ $message = $e->getMessage();
+
+ if ( isset( $wgRequest ) && !$wgRequest instanceof FauxRequest ) {
+ $url = $wgRequest->getRequestURL();
+ if ( !$url ) {
+ $url = '[no URL]';
+ }
+ } else {
+ $url = '[no req]';
+ }
+
+ return "[$id] $url Exception from line $line of $file: $message";
+ }
+
+ /**
+ * Log an exception to the exception log (if enabled).
+ *
+ * This method must not assume the exception is an MWException,
+ * it is also used to handle PHP errors or errors from other libraries.
+ *
+ * @since 1.22
+ * @param Exception $e
+ */
+ public static function logException( Exception $e ) {
+ global $wgLogExceptionBacktrace;
+
+ if ( !( $e instanceof MWException ) || $e->isLoggable() ) {
+ $log = self::getLogMessage( $e );
+ if ( $wgLogExceptionBacktrace ) {
+ wfDebugLog( 'exception', $log . "\n" . $e->getTraceAsString() . "\n" );
+ } else {
+ wfDebugLog( 'exception', $log );
+ }
+ }
+ }
+
}
diff --git a/includes/Export.php b/includes/Export.php
index d8cc0242..98de4c00 100644
--- a/includes/Export.php
+++ b/includes/Export.php
@@ -249,9 +249,13 @@ class WikiExporter {
$where = array( 'user_id = log_user' );
# Hide private logs
$hideLogs = LogEventsList::getExcludeClause( $this->db );
- if ( $hideLogs ) $where[] = $hideLogs;
+ if ( $hideLogs ) {
+ $where[] = $hideLogs;
+ }
# Add on any caller specified conditions
- if ( $cond ) $where[] = $cond;
+ if ( $cond ) {
+ $where[] = $cond;
+ }
# Get logging table name for logging.* clause
$logging = $this->db->tableName( 'logging' );
@@ -296,6 +300,7 @@ class WikiExporter {
}
// Inform caller about problem
+ wfProfileOut( __METHOD__ );
throw $e;
}
# For page dumps...
@@ -541,7 +546,7 @@ class XmlDumpWriter {
* @return string
*/
function homelink() {
- return Xml::element( 'base', array(), Title::newMainPage()->getCanonicalUrl() );
+ return Xml::element( 'base', array(), Title::newMainPage()->getCanonicalURL() );
}
/**
@@ -563,8 +568,9 @@ class XmlDumpWriter {
foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
$spaces .= ' ' .
Xml::element( 'namespace',
- array( 'key' => $ns,
- 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
+ array(
+ 'key' => $ns,
+ 'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
), $title ) . "\n";
}
$spaces .= " </namespaces>";
@@ -593,7 +599,7 @@ class XmlDumpWriter {
$out = " <page>\n";
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
$out .= ' ' . Xml::elementClean( 'title', array(), self::canonicalTitle( $title ) ) . "\n";
- $out .= ' ' . Xml::element( 'ns', array(), strval( $row->page_namespace) ) . "\n";
+ $out .= ' ' . Xml::element( 'ns', array(), strval( $row->page_namespace ) ) . "\n";
$out .= ' ' . Xml::element( 'id', array(), strval( $row->page_id ) ) . "\n";
if ( $row->page_is_redirect ) {
$page = WikiPage::factory( $title );
@@ -636,7 +642,7 @@ class XmlDumpWriter {
$out = " <revision>\n";
$out .= " " . Xml::element( 'id', null, strval( $row->rev_id ) ) . "\n";
- if( isset( $row->rev_parent_id ) && $row->rev_parent_id ) {
+ if ( isset( $row->rev_parent_id ) && $row->rev_parent_id ) {
$out .= " " . Xml::element( 'parentid', null, strval( $row->rev_parent_id ) ) . "\n";
}
@@ -683,7 +689,7 @@ class XmlDumpWriter {
$content_model = strval( $row->rev_content_model );
} else {
// probably using $wgContentHandlerUseDB = false;
- // @todo: test!
+ // @todo test!
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
$content_model = ContentHandler::getDefaultModelFor( $title );
}
@@ -694,7 +700,7 @@ class XmlDumpWriter {
$content_format = strval( $row->rev_content_format );
} else {
// probably using $wgContentHandlerUseDB = false;
- // @todo: test!
+ // @todo test!
$content_handler = ContentHandler::getForModelID( $content_model );
$content_format = $content_handler->getDefaultFormat();
}
@@ -818,10 +824,13 @@ class XmlDumpWriter {
$archiveName = '';
}
if ( $dumpContents ) {
+ $be = $file->getRepo()->getBackend();
# Dump file as base64
# Uses only XML-safe characters, so does not need escaping
+ # @TODO: too bad this loads the contents into memory (script might swap)
$contents = ' <contents encoding="base64">' .
- chunk_split( base64_encode( file_get_contents( $file->getPath() ) ) ) .
+ chunk_split( base64_encode(
+ $be->getFileContents( array( 'src' => $file->getPath() ) ) ) ) .
" </contents>\n";
} else {
$contents = '';
@@ -837,7 +846,7 @@ class XmlDumpWriter {
" " . $comment . "\n" .
" " . Xml::element( 'filename', null, $file->getName() ) . "\n" .
$archiveName .
- " " . Xml::element( 'src', null, $file->getCanonicalUrl() ) . "\n" .
+ " " . Xml::element( 'src', null, $file->getCanonicalURL() ) . "\n" .
" " . Xml::element( 'size', null, $file->getSize() ) . "\n" .
" " . Xml::element( 'sha1base36', null, $file->getSha1() ) . "\n" .
" " . Xml::element( 'rel', null, $file->getRel() ) . "\n" .
@@ -1185,7 +1194,7 @@ class Dump7ZipOutput extends DumpPipeOutput {
// Suppress annoying useless crap from p7zip
// Unfortunately this could suppress real error messages too
$command .= ' >' . wfGetNull() . ' 2>&1';
- return( $command );
+ return $command;
}
/**
diff --git a/includes/ExternalEdit.php b/includes/ExternalEdit.php
deleted file mode 100644
index 11e94230..00000000
--- a/includes/ExternalEdit.php
+++ /dev/null
@@ -1,132 +0,0 @@
-<?php
-/**
- * External editors support
- *
- * License: Public domain
- *
- * @file
- * @author Erik Moeller <moeller@scireview.de>
- */
-
-/**
- * Support for external editors to modify both text and files
- * in external applications. It works as follows: MediaWiki
- * sends a meta-file with the MIME type 'application/x-external-editor'
- * to the client. The user has to associate that MIME type with
- * a helper application (a reference implementation in Perl
- * can be found in extensions/ee), which will launch the editor,
- * and save the modified data back to the server.
- *
- */
-class ExternalEdit extends ContextSource {
-
- /**
- * Array of URLs to link to
- * @var Array
- */
- private $urls;
-
- /**
- * Constructor
- * @param $context IContextSource context to use
- * @param $urls array
- */
- public function __construct( IContextSource $context, array $urls = array() ) {
- $this->setContext( $context );
- $this->urls = $urls;
- }
-
- /**
- * Check whether external edit or diff should be used.
- *
- * @param $context IContextSource context to use
- * @param string $type can be either 'edit' or 'diff'
- * @return Bool
- */
- public static function useExternalEngine( IContextSource $context, $type ) {
- global $wgUseExternalEditor;
-
- if ( !$wgUseExternalEditor ) {
- return false;
- }
-
- $pref = $type == 'diff' ? 'externaldiff' : 'externaleditor';
- $request = $context->getRequest();
-
- return !$request->getVal( 'internaledit' ) &&
- ( $context->getUser()->getOption( $pref ) || $request->getVal( 'externaledit' ) );
- }
-
- /**
- * Output the information for the external editor
- */
- public function execute() {
- global $wgContLang, $wgScript, $wgScriptPath, $wgCanonicalServer;
-
- $this->getOutput()->disable();
-
- $response = $this->getRequest()->response();
- $response->header( 'Content-type: application/x-external-editor; charset=utf-8' );
- $response->header( 'Cache-control: no-cache' );
-
- $special = $wgContLang->getNsText( NS_SPECIAL );
-
- # $type can be "Edit text", "Edit file" or "Diff text" at the moment
- # See the protocol specifications at [[m:Help:External editors/Tech]] for
- # details.
- if ( count( $this->urls ) ) {
- $urls = $this->urls;
- $type = "Diff text";
- } elseif ( $this->getRequest()->getVal( 'mode' ) == 'file' ) {
- $type = "Edit file";
- $image = wfLocalFile( $this->getTitle() );
- if ( $image ) {
- $urls = array(
- 'File' => array(
- 'Extension' => $image->getExtension(),
- 'URL' => $image->getCanonicalURL()
- )
- );
- } else {
- $urls = array();
- }
- } else {
- $type = "Edit text";
- # *.wiki file extension is used by some editors for syntax
- # highlighting, so we follow that convention
- $urls = array( 'File' => array(
- 'Extension' => 'wiki',
- 'URL' => $this->getTitle()->getCanonicalURL(
- array( 'action' => 'edit', 'internaledit' => 'true' ) )
- ) );
- }
-
- $files = '';
- foreach( $urls as $key => $vars ) {
- $files .= "\n[$key]\n";
- foreach( $vars as $varname => $varval ) {
- $files .= "$varname=$varval\n";
- }
- }
-
- $url = $this->getTitle()->getFullURL(
- $this->getRequest()->appendQueryValue( 'internaledit', 1, true ) );
-
- $control = <<<CONTROL
-; You're seeing this file because you're using Mediawiki's External Editor feature.
-; This is probably because you selected use external editor in your preferences.
-; To edit normally, either disable that preference or go to the URL:
-; $url
-; See http://www.mediawiki.org/wiki/Manual:External_editors for details.
-[Process]
-Type=$type
-Engine=MediaWiki
-Script={$wgCanonicalServer}{$wgScript}
-Server={$wgCanonicalServer}
-Path={$wgScriptPath}
-Special namespace=$special
-$files
-CONTROL;
- echo $control;
- }
-}
diff --git a/includes/ExternalUser.php b/includes/ExternalUser.php
deleted file mode 100644
index 580b9896..00000000
--- a/includes/ExternalUser.php
+++ /dev/null
@@ -1,309 +0,0 @@
-<?php
-/**
- * Authentication with a foreign database
- *
- * Copyright © 2009 Aryeh Gregor
- *
- * 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
- */
-
-/**
- * @defgroup ExternalUser ExternalUser
- */
-
-/**
- * A class intended to supplement, and perhaps eventually replace, AuthPlugin.
- * See: http://www.mediawiki.org/wiki/ExternalAuth
- *
- * The class represents a user whose data is in a foreign database. The
- * database may have entirely different conventions from MediaWiki, but it's
- * assumed to at least support the concept of a user id (possibly not an
- * integer), a user name (possibly not meeting MediaWiki's username
- * requirements), and a password.
- *
- * @ingroup ExternalUser
- */
-abstract class ExternalUser {
- protected function __construct() {}
-
- /**
- * Wrappers around initFrom*().
- */
-
- /**
- * @param $name string
- * @return mixed ExternalUser, or false on failure
- */
- public static function newFromName( $name ) {
- global $wgExternalAuthType;
- if ( is_null( $wgExternalAuthType ) ) {
- return false;
- }
- $obj = new $wgExternalAuthType;
- if ( !$obj->initFromName( $name ) ) {
- return false;
- }
- return $obj;
- }
-
- /**
- * @param $id string
- * @return mixed ExternalUser, or false on failure
- */
- public static function newFromId( $id ) {
- global $wgExternalAuthType;
- if ( is_null( $wgExternalAuthType ) ) {
- return false;
- }
- $obj = new $wgExternalAuthType;
- if ( !$obj->initFromId( $id ) ) {
- return false;
- }
- return $obj;
- }
-
- /**
- * @return mixed ExternalUser, or false on failure
- */
- public static function newFromCookie() {
- global $wgExternalAuthType;
- if ( is_null( $wgExternalAuthType ) ) {
- return false;
- }
- $obj = new $wgExternalAuthType;
- if ( !$obj->initFromCookie() ) {
- return false;
- }
- return $obj;
- }
-
- /**
- * Creates the object corresponding to the given User object, assuming the
- * user exists on the wiki and is linked to an external account. If either
- * of these is false, this will return false.
- *
- * This is a wrapper around newFromId().
- *
- * @param $user User
- * @return ExternalUser|bool False on failure
- */
- public static function newFromUser( $user ) {
- global $wgExternalAuthType;
- if ( is_null( $wgExternalAuthType ) ) {
- # Short-circuit to avoid database query in common case so no one
- # kills me
- return false;
- }
-
- $dbr = wfGetDB( DB_SLAVE );
- $id = $dbr->selectField( 'external_user', 'eu_external_id',
- array( 'eu_local_id' => $user->getId() ), __METHOD__ );
- if ( $id === false ) {
- return false;
- }
- return self::newFromId( $id );
- }
-
- /**
- * Given a name, which is a string exactly as input by the user in the
- * login form but with whitespace stripped, initialize this object to be
- * the corresponding ExternalUser. Return true if successful, otherwise
- * false.
- *
- * @param $name string
- * @return bool Success?
- */
- abstract protected function initFromName( $name );
-
- /**
- * Given an id, which was at some previous point in history returned by
- * getId(), initialize this object to be the corresponding ExternalUser.
- * Return true if successful, false otherwise.
- *
- * @param $id string
- * @return bool Success?
- */
- abstract protected function initFromId( $id );
-
- /**
- * Try to magically initialize the user from cookies or similar information
- * so he or she can be logged in on just viewing the wiki. If this is
- * impossible to do, just return false.
- *
- * TODO: Actually use this.
- *
- * @return bool Success?
- */
- protected function initFromCookie() {
- return false;
- }
-
- /**
- * This must return some identifier that stably, uniquely identifies the
- * user. In a typical web application, this could be an integer
- * representing the "user id". In other cases, it might be a string. In
- * any event, the return value should be a string between 1 and 255
- * characters in length; must uniquely identify the user in the foreign
- * database; and, if at all possible, should be permanent.
- *
- * This will only ever be used to reconstruct this ExternalUser object via
- * newFromId(). The resulting object in that case should correspond to the
- * same user, even if details have changed in the interim (e.g., renames or
- * preference changes).
- *
- * @return string
- */
- abstract public function getId();
-
- /**
- * This must return the name that the user would normally use for login to
- * the external database. It is subject to no particular restrictions
- * beyond rudimentary sanity, and in particular may be invalid as a
- * MediaWiki username. It's used to auto-generate an account name that
- * *is* valid for MediaWiki, either with or without user input, but
- * basically is only a hint.
- *
- * @return string
- */
- abstract public function getName();
-
- /**
- * Is the given password valid for the external user? The password is
- * provided in plaintext.
- *
- * @param $password string
- * @return bool
- */
- abstract public function authenticate( $password );
-
- /**
- * Retrieve the value corresponding to the given preference key. The most
- * important values are:
- *
- * - emailaddress
- * - language
- *
- * The value must meet MediaWiki's requirements for values of this type,
- * and will be checked for validity before use. If the preference makes no
- * sense for the backend, or it makes sense but is unset for this user, or
- * is unrecognized, return null.
- *
- * $pref will never equal 'password', since passwords are usually hashed
- * and cannot be directly retrieved. authenticate() is used for this
- * instead.
- *
- * TODO: Currently this is only called for 'emailaddress'; generalize! Add
- * some config option to decide which values are grabbed on user
- * initialization.
- *
- * @param $pref string
- * @return mixed
- */
- public function getPref( $pref ) {
- return null;
- }
-
- /**
- * Return an array of identifiers for all the foreign groups that this user
- * has. The identifiers are opaque objects that only need to be
- * specifiable by the administrator in LocalSettings.php when configuring
- * $wgAutopromote. They may be, for instance, strings or integers.
- *
- * TODO: Support this in $wgAutopromote.
- *
- * @return array
- */
- public function getGroups() {
- return array();
- }
-
- /**
- * Given a preference key (e.g., 'emailaddress'), provide an HTML message
- * telling the user how to change it in the external database. The
- * administrator has specified that this preference cannot be changed on
- * the wiki, and may only be changed in the foreign database. If no
- * message is available, such as for an unrecognized preference, return
- * false.
- *
- * TODO: Use this somewhere.
- *
- * @param $pref string
- * @return mixed String or false
- */
- public static function getPrefMessage( $pref ) {
- return false;
- }
-
- /**
- * Set the given preference key to the given value. Two important
- * preference keys that you might want to implement are 'password' and
- * 'emailaddress'. If the set fails, such as because the preference is
- * unrecognized or because the external database can't be changed right
- * now, return false. If it succeeds, return true.
- *
- * If applicable, you should make sure to validate the new value against
- * any constraints the external database may have, since MediaWiki may have
- * more limited constraints (e.g., on password strength).
- *
- * TODO: Untested.
- *
- * @param $key string
- * @param $value string
- * @return bool Success?
- */
- public static function setPref( $key, $value ) {
- return false;
- }
-
- /**
- * Create a link for future reference between this object and the provided
- * user_id. If the user was already linked, the old link will be
- * overwritten.
- *
- * This is part of the core code and is not overridable by specific
- * plugins. It's in this class only for convenience.
- *
- * @param int $id user_id
- */
- final public function linkToLocal( $id ) {
- $dbw = wfGetDB( DB_MASTER );
- $dbw->replace( 'external_user',
- array( 'eu_local_id', 'eu_external_id' ),
- array( 'eu_local_id' => $id,
- 'eu_external_id' => $this->getId() ),
- __METHOD__ );
- }
-
- /**
- * Check whether this external user id is already linked with
- * a local user.
- * @return Mixed User if the account is linked, Null otherwise.
- */
- final public function getLocalUser() {
- $dbr = wfGetDB( DB_SLAVE );
- $row = $dbr->selectRow(
- 'external_user',
- '*',
- array( 'eu_external_id' => $this->getId() )
- );
- return $row
- ? User::newFromId( $row->eu_local_id )
- : null;
- }
-
-}
diff --git a/includes/Fallback.php b/includes/Fallback.php
index 2e19a095..cdf6c88e 100644
--- a/includes/Fallback.php
+++ b/includes/Fallback.php
@@ -35,13 +35,13 @@ class Fallback {
if ( substr( $to, -8 ) == '//IGNORE' ) {
$to = substr( $to, 0, strlen( $to ) - 8 );
}
- if( strcasecmp( $from, $to ) == 0 ) {
+ if ( strcasecmp( $from, $to ) == 0 ) {
return $string;
}
- if( strcasecmp( $from, 'utf-8' ) == 0 ) {
+ if ( strcasecmp( $from, 'utf-8' ) == 0 ) {
return utf8_decode( $string );
}
- if( strcasecmp( $to, 'utf-8' ) == 0 ) {
+ if ( strcasecmp( $to, 'utf-8' ) == 0 ) {
return utf8_encode( $string );
}
return $string;
@@ -64,12 +64,12 @@ class Fallback {
* @return string
*/
public static function mb_substr( $str, $start, $count = 'end' ) {
- if( $start != 0 ) {
+ if ( $start != 0 ) {
$split = self::mb_substr_split_unicode( $str, intval( $start ) );
$str = substr( $str, $split );
}
- if( $count !== 'end' ) {
+ if ( $count !== 'end' ) {
$split = self::mb_substr_split_unicode( $str, intval( $count ) );
$str = substr( $str, 0, $split );
}
@@ -83,14 +83,14 @@ class Fallback {
* @return int
*/
public static function mb_substr_split_unicode( $str, $splitPos ) {
- if( $splitPos == 0 ) {
+ if ( $splitPos == 0 ) {
return 0;
}
$byteLen = strlen( $str );
- if( $splitPos > 0 ) {
- if( $splitPos > 256 ) {
+ if ( $splitPos > 0 ) {
+ if ( $splitPos > 256 ) {
// Optimize large string offsets by skipping ahead N bytes.
// This will cut out most of our slow time on Latin-based text,
// and 1/2 to 1/3 on East European and Asian scripts.
@@ -104,7 +104,7 @@ class Fallback {
$bytePos = 0;
}
- while( $charPos++ < $splitPos ) {
+ while ( $charPos++ < $splitPos ) {
++$bytePos;
// Move past any tail bytes
while ( $bytePos < $byteLen && $str[$bytePos] >= "\x80" && $str[$bytePos] < "\xc0" ) {
@@ -115,7 +115,7 @@ class Fallback {
$splitPosX = $splitPos + 1;
$charPos = 0; // relative to end of string; we don't care about the actual char position here
$bytePos = $byteLen;
- while( $bytePos > 0 && $charPos-- >= $splitPosX ) {
+ while ( $bytePos > 0 && $charPos-- >= $splitPosX ) {
--$bytePos;
// Move past any tail bytes
while ( $bytePos > 0 && $str[$bytePos] >= "\x80" && $str[$bytePos] < "\xc0" ) {
@@ -138,12 +138,12 @@ class Fallback {
$total = 0;
// Count ASCII bytes
- for( $i = 0; $i < 0x80; $i++ ) {
+ for ( $i = 0; $i < 0x80; $i++ ) {
$total += $counts[$i];
}
// Count multibyte sequence heads
- for( $i = 0xc0; $i < 0xff; $i++ ) {
+ for ( $i = 0xc0; $i < 0xff; $i++ ) {
$total += $counts[$i];
}
return $total;
@@ -163,7 +163,7 @@ class Fallback {
$ar = array();
preg_match( '/' . $needle . '/u', $haystack, $ar, PREG_OFFSET_CAPTURE, $offset );
- if( isset( $ar[0][1] ) ) {
+ if ( isset( $ar[0][1] ) ) {
return $ar[0][1];
} else {
return false;
@@ -184,7 +184,7 @@ class Fallback {
$ar = array();
preg_match_all( '/' . $needle . '/u', $haystack, $ar, PREG_OFFSET_CAPTURE, $offset );
- if( isset( $ar[0] ) && count( $ar[0] ) > 0 &&
+ if ( isset( $ar[0] ) && count( $ar[0] ) > 0 &&
isset( $ar[0][count( $ar[0] ) - 1][1] ) ) {
return $ar[0][count( $ar[0] ) - 1][1];
} else {
diff --git a/includes/Feed.php b/includes/Feed.php
index caf2e571..635b04e4 100644
--- a/includes/Feed.php
+++ b/includes/Feed.php
@@ -245,7 +245,7 @@ abstract class ChannelFeed extends FeedItem {
global $wgRequest;
$ctype = $wgRequest->getVal( 'ctype', 'application/xml' );
$allowedctypes = array( 'application/xml', 'text/xml', 'application/rss+xml', 'application/atom+xml' );
- return (in_array( $ctype, $allowedctypes ) ? $ctype : 'application/xml');
+ return ( in_array( $ctype, $allowedctypes ) ? $ctype : 'application/xml' );
}
/**
@@ -306,13 +306,13 @@ class RSSFeed extends ChannelFeed {
function outItem( $item ) {
?>
<item>
- <title><?php print $item->getTitle() ?></title>
- <link><?php print wfExpandUrl( $item->getUrl(), PROTO_CURRENT ) ?></link>
- <guid<?php if( !$item->rssIsPermalink ) print ' isPermaLink="false"' ?>><?php print $item->getUniqueId() ?></guid>
+ <title><?php print $item->getTitle(); ?></title>
+ <link><?php print wfExpandUrl( $item->getUrl(), PROTO_CURRENT ); ?></link>
+ <guid<?php if ( !$item->rssIsPermalink ) { print ' isPermaLink="false"'; } ?>><?php print $item->getUniqueId(); ?></guid>
<description><?php print $item->getDescription() ?></description>
- <?php if( $item->getDate() ) { ?><pubDate><?php print $this->formatTime( $item->getDate() ) ?></pubDate><?php } ?>
- <?php if( $item->getAuthor() ) { ?><dc:creator><?php print $item->getAuthor() ?></dc:creator><?php }?>
- <?php if( $item->getComments() ) { ?><comments><?php print wfExpandUrl( $item->getComments(), PROTO_CURRENT ) ?></comments><?php }?>
+ <?php if ( $item->getDate() ) { ?><pubDate><?php print $this->formatTime( $item->getDate() ); ?></pubDate><?php } ?>
+ <?php if ( $item->getAuthor() ) { ?><dc:creator><?php print $item->getAuthor(); ?></dc:creator><?php }?>
+ <?php if ( $item->getComments() ) { ?><comments><?php print wfExpandUrl( $item->getComments(), PROTO_CURRENT ); ?></comments><?php }?>
</item>
<?php
}
@@ -392,15 +392,15 @@ class AtomFeed extends ChannelFeed {
global $wgMimeType;
?>
<entry>
- <id><?php print $item->getUniqueId() ?></id>
- <title><?php print $item->getTitle() ?></title>
- <link rel="alternate" type="<?php print $wgMimeType ?>" href="<?php print wfExpandUrl( $item->getUrl(), PROTO_CURRENT ) ?>"/>
- <?php if( $item->getDate() ) { ?>
- <updated><?php print $this->formatTime( $item->getDate() ) ?>Z</updated>
+ <id><?php print $item->getUniqueId(); ?></id>
+ <title><?php print $item->getTitle(); ?></title>
+ <link rel="alternate" type="<?php print $wgMimeType ?>" href="<?php print wfExpandUrl( $item->getUrl(), PROTO_CURRENT ); ?>"/>
+ <?php if ( $item->getDate() ) { ?>
+ <updated><?php print $this->formatTime( $item->getDate() ); ?>Z</updated>
<?php } ?>
<summary type="html"><?php print $item->getDescription() ?></summary>
- <?php if( $item->getAuthor() ) { ?><author><name><?php print $item->getAuthor() ?></name></author><?php }?>
+ <?php if ( $item->getAuthor() ) { ?><author><name><?php print $item->getAuthor(); ?></name></author><?php }?>
</entry>
<?php /* @todo FIXME: Need to add comments
diff --git a/includes/FeedUtils.php b/includes/FeedUtils.php
index adc1f781..22cb52be 100644
--- a/includes/FeedUtils.php
+++ b/includes/FeedUtils.php
@@ -59,7 +59,7 @@ class FeedUtils {
return false;
}
- if( !isset( $wgFeedClasses[$type] ) ) {
+ if ( !isset( $wgFeedClasses[$type] ) ) {
$wgOut->addWikiMsg( 'feed-invalid' );
return false;
}
@@ -77,14 +77,14 @@ class FeedUtils {
$titleObj = Title::makeTitle( $row->rc_namespace, $row->rc_title );
$timestamp = wfTimestamp( TS_MW, $row->rc_timestamp );
$actiontext = '';
- if( $row->rc_type == RC_LOG ) {
+ if ( $row->rc_type == RC_LOG ) {
$rcRow = (array)$row; // newFromRow() only accepts arrays for RC rows
$actiontext = LogFormatter::newFromRow( $rcRow )->getActionText();
}
return self::formatDiffRow( $titleObj,
$row->rc_last_oldid, $row->rc_this_oldid,
$timestamp,
- ($row->rc_deleted & Revision::DELETED_COMMENT)
+ $row->rc_deleted & Revision::DELETED_COMMENT
? wfMessage( 'rev-deleted-comment' )->escaped()
: $row->rc_comment,
$actiontext
@@ -121,13 +121,13 @@ 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 ) {
+ if ( $title->getNamespace() < 0 || $accErrors || !$newid ) {
wfProfileOut( __METHOD__ );
return $completeText;
}
- if( $oldid ) {
- wfProfileIn( __METHOD__."-dodiff" );
+ if ( $oldid ) {
+ wfProfileIn( __METHOD__ . "-dodiff" );
#$diffText = $de->getDiff( wfMessage( 'revisionasof',
# $wgLang->timeanddate( $timestamp ),
@@ -168,10 +168,10 @@ class FeedUtils {
$diffText = UtfNormal::cleanUp( $diffText );
$diffText = self::applyDiffStyle( $diffText );
}
- wfProfileOut( __METHOD__."-dodiff" );
+ wfProfileOut( __METHOD__ . "-dodiff" );
} else {
$rev = Revision::newFromId( $newid );
- if( $wgFeedDiffCutoff <= 0 || is_null( $rev ) ) {
+ if ( $wgFeedDiffCutoff <= 0 || is_null( $rev ) ) {
$newContent = ContentHandler::getForTitle( $title )->makeEmptyContent();
} else {
$newContent = $rev->getContent();
@@ -220,10 +220,11 @@ class FeedUtils {
* @return string
*/
protected static function getDiffLink( Title $title, $newid, $oldid = null ) {
- $queryParameters = ($oldid == null)
- ? "diff={$newid}"
- : "diff={$newid}&oldid={$oldid}";
- $diffUrl = $title->getFullUrl( $queryParameters );
+ $queryParameters = array( 'diff' => $newid );
+ if ( $oldid != null ) {
+ $queryParameters['oldid'] = $oldid;
+ }
+ $diffUrl = $title->getFullURL( $queryParameters );
$diffLink = Html::element( 'a', array( 'href' => $diffUrl ),
wfMessage( 'showdiff' )->inContentLanguage()->text() );
@@ -250,7 +251,7 @@ class FeedUtils {
'diffchange' => 'font-weight: bold; text-decoration: none;',
);
- foreach( $styles as $class => $style ) {
+ foreach ( $styles as $class => $style ) {
$text = preg_replace( "/(<[^>]+)class=(['\"])$class\\2([^>]*>)/",
"\\1style=\"$style\"\\3", $text );
}
diff --git a/includes/FileDeleteForm.php b/includes/FileDeleteForm.php
index 28403cca..65d82b87 100644
--- a/includes/FileDeleteForm.php
+++ b/includes/FileDeleteForm.php
@@ -82,18 +82,18 @@ class FileDeleteForm {
# Flag to hide all contents of the archived revisions
$suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed( 'suppressrevision' );
- if( $this->oldimage ) {
+ if ( $this->oldimage ) {
$this->oldfile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->title, $this->oldimage );
}
- if( !self::haveDeletableFile( $this->file, $this->oldfile, $this->oldimage ) ) {
+ if ( !self::haveDeletableFile( $this->file, $this->oldfile, $this->oldimage ) ) {
$wgOut->addHTML( $this->prepareMessage( 'filedelete-nofile' ) );
$wgOut->addReturnTo( $this->title );
return;
}
// Perform the deletion if appropriate
- if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $token, $this->oldimage ) ) {
+ if ( $wgRequest->wasPosted() && $wgUser->matchEditToken( $token, $this->oldimage ) ) {
$deleteReasonList = $wgRequest->getText( 'wpDeleteReasonList' );
$deleteReason = $wgRequest->getText( 'wpReason' );
@@ -109,24 +109,18 @@ class FileDeleteForm {
$status = self::doDelete( $this->title, $this->file, $this->oldimage, $reason, $suppress, $wgUser );
- if( !$status->isGood() ) {
+ if ( !$status->isGood() ) {
$wgOut->addHTML( '<h2>' . $this->prepareMessage( 'filedeleteerror-short' ) . "</h2>\n" );
$wgOut->addWikiText( '<div class="error">' . $status->getWikiText( 'filedeleteerror-short', 'filedeleteerror-long' ) . '</div>' );
}
- if( $status->ok ) {
+ if ( $status->ok ) {
$wgOut->setPageTitle( wfMessage( 'actioncomplete' ) );
$wgOut->addHTML( $this->prepareMessage( 'filedelete-success' ) );
// Return to the main page if we just deleted all versions of the
// file, otherwise go back to the description page
$wgOut->addReturnTo( $this->oldimage ? $this->title : Title::newMainPage() );
- if ( $wgUser->isLoggedIn() && $wgRequest->getCheck( 'wpWatch' ) != $wgUser->isWatched( $this->title ) ) {
- if ( $wgRequest->getCheck( 'wpWatch' ) ) {
- WatchAction::doWatch( $this->title, $wgUser );
- } else {
- WatchAction::doUnwatch( $this->title, $wgUser );
- }
- }
+ WatchAction::doWatchOrUnwatch( $wgRequest->getCheck( 'wpWatch' ), $this->title, $wgUser );
}
return;
}
@@ -153,13 +147,13 @@ class FileDeleteForm {
$user = $wgUser;
}
- if( $oldimage ) {
+ if ( $oldimage ) {
$page = null;
$status = $file->deleteOld( $oldimage, $reason, $suppress );
- if( $status->ok ) {
+ if ( $status->ok ) {
// Need to do a log item
$logComment = wfMessage( 'deletedrevision', $oldimage )->inContentLanguage()->text();
- if( trim( $reason ) != '' ) {
+ if ( trim( $reason ) != '' ) {
$logComment .= wfMessage( 'colon-separator' )
->inContentLanguage()->text() . $reason;
}
@@ -187,7 +181,7 @@ class FileDeleteForm {
// or revision is missing, so check for isOK() rather than isGood()
if ( $deleteStatus->isOK() ) {
$status = $file->delete( $reason, $suppress );
- if( $status->isOK() ) {
+ if ( $status->isOK() ) {
$dbw->commit( __METHOD__ );
} else {
$dbw->rollback( __METHOD__ );
@@ -213,7 +207,7 @@ class FileDeleteForm {
private function showForm() {
global $wgOut, $wgUser, $wgRequest;
- if( $wgUser->isAllowed( 'suppressrevision' ) ) {
+ if ( $wgUser->isAllowed( 'suppressrevision' ) ) {
$suppress = "<tr id=\"wpDeleteSuppressRow\">
<td></td>
<td class='mw-input'><strong>" .
@@ -258,7 +252,7 @@ class FileDeleteForm {
"</td>
</tr>
{$suppress}";
- if( $wgUser->isLoggedIn() ) {
+ if ( $wgUser->isLoggedIn() ) {
$form .= "
<tr>
<td></td>
@@ -314,7 +308,7 @@ class FileDeleteForm {
*/
private function prepareMessage( $message ) {
global $wgLang;
- if( $this->oldimage ) {
+ if ( $this->oldimage ) {
return wfMessage(
"{$message}-old", # To ensure grep will find them: 'filedelete-intro-old', 'filedelete-nofile-old', 'filedelete-success-old'
wfEscapeWikiText( $this->title->getText() ),
@@ -375,11 +369,11 @@ class FileDeleteForm {
$q = array();
$q['action'] = 'delete';
- if( $this->oldimage ) {
+ if ( $this->oldimage ) {
$q['oldimage'] = $this->oldimage;
}
- return $this->title->getLocalUrl( $q );
+ return $this->title->getLocalURL( $q );
}
/**
diff --git a/includes/ForkController.php b/includes/ForkController.php
index 89ad9553..ced45af6 100644
--- a/includes/ForkController.php
+++ b/includes/ForkController.php
@@ -121,7 +121,9 @@ class ForkController {
if ( function_exists( 'pcntl_signal_dispatch' ) ) {
pcntl_signal_dispatch();
} else {
- declare (ticks=1) { $status = $status; }
+ declare( ticks = 1 ) {
+ $status = $status;
+ }
}
// Respond to TERM signal
if ( $this->termReceived ) {
diff --git a/includes/FormOptions.php b/includes/FormOptions.php
index 8477ed98..54822e32 100644
--- a/includes/FormOptions.php
+++ b/includes/FormOptions.php
@@ -28,7 +28,8 @@
/**
* Helper class to keep track of options when mixing links and form elements.
*
- * @todo This badly need some examples and tests :-)
+ * @todo This badly needs some examples and tests :) The usage in SpecialRecentchanges class is a
+ * good ersatz in the meantime.
*/
class FormOptions implements ArrayAccess {
/** @name Type constants
@@ -50,12 +51,26 @@ class FormOptions implements ArrayAccess {
/* @} */
/**
- * @todo Document!
+ * Map of known option names to information about them.
+ *
+ * Each value is an array with the following keys:
+ * - 'default' - the default value as passed to add()
+ * - 'value' - current value, start with null, can be set by various functions
+ * - 'consumed' - true/false, whether the option was consumed using
+ * consumeValue() or consumeValues()
+ * - 'type' - one of the type constants (but never AUTO)
*/
protected $options = array();
# Setting up
+ /**
+ * Add an option to be handled by this FormOptions instance.
+ *
+ * @param string $name Request parameter name
+ * @param mixed $default Default value when the request parameter is not present
+ * @param int $type One of the type constants (optional, defaults to AUTO)
+ */
public function add( $name, $default, $type = self::AUTO ) {
$option = array();
$option['default'] = $default;
@@ -71,20 +86,25 @@ class FormOptions implements ArrayAccess {
$this->options[$name] = $option;
}
+ /**
+ * Remove an option being handled by this FormOptions instance. This is the inverse of add().
+ *
+ * @param string $name Request parameter name
+ */
public function delete( $name ) {
$this->validateName( $name, true );
unset( $this->options[$name] );
}
/**
- * Used to find out which type the data is.
- * All types are defined in the 'Type constants' section of this class
- * Please note we do not support detection of INTNULL MediaWiki type
- * which will be assumed as INT if the data is an integer.
+ * Used to find out which type the data is. All types are defined in the 'Type constants' section
+ * of this class.
*
- * @param $data Mixed: value to guess type for
- * @throws MWException
- * @exception MWException Unsupported datatype
+ * Detection of the INTNULL type is not supported; INT will be assumed if the data is an integer,
+ * MWException will be thrown if it's null.
+ *
+ * @param mixed $data Value to guess the type for
+ * @throws MWException If unable to guess the type
* @return int Type constant
*/
public static function guessType( $data ) {
@@ -102,12 +122,12 @@ class FormOptions implements ArrayAccess {
# Handling values
/**
- * Verify the given option name exist.
+ * Verify that the given option name exists.
*
- * @param string $name option name
- * @param $strict Boolean: throw an exception when the option does not exist (default false)
+ * @param string $name Option name
+ * @param bool $strict Throw an exception when the option doesn't exist instead of returning false
* @throws MWException
- * @return Boolean: true if option exist, false otherwise
+ * @return bool True if the option exists, false otherwise
*/
public function validateName( $name, $strict = false ) {
if ( !isset( $this->options[$name] ) ) {
@@ -117,16 +137,17 @@ class FormOptions implements ArrayAccess {
return false;
}
}
+
return true;
}
/**
* Use to set the value of an option.
*
- * @param string $name option name
- * @param $value Mixed: value for the option
- * @param $force Boolean: whether to set the value when it is equivalent to the default value for this option (default false).
- * @return null
+ * @param string $name Option name
+ * @param mixed $value Value for the option
+ * @param bool $force Whether to set the value when it is equivalent to the default value for this
+ * option (default false).
*/
public function setValue( $name, $value, $force = false ) {
$this->validateName( $name, true );
@@ -140,11 +161,10 @@ class FormOptions implements ArrayAccess {
}
/**
- * Get the value for the given option name.
- * Internally use getValueReal()
+ * Get the value for the given option name. Uses getValueReal() internally.
*
- * @param string $name option name
- * @return Mixed
+ * @param string $name Option name
+ * @return mixed
*/
public function getValue( $name ) {
$this->validateName( $name, true );
@@ -153,9 +173,10 @@ class FormOptions implements ArrayAccess {
}
/**
- * @todo Document
- * @param array $option array structure describing the option
- * @return Mixed. Value or the default value if it is null
+ * Return current option value, based on a structure taken from $options.
+ *
+ * @param array $option Array structure describing the option
+ * @return mixed Value, or the default value if it is null
*/
protected function getValueReal( $option ) {
if ( $option['value'] !== null ) {
@@ -167,9 +188,8 @@ class FormOptions implements ArrayAccess {
/**
* Delete the option value.
- * This will make future calls to getValue() return the default value.
- * @param string $name option name
- * @return null
+ * This will make future calls to getValue() return the default value.
+ * @param string $name Option name
*/
public function reset( $name ) {
$this->validateName( $name, true );
@@ -177,10 +197,13 @@ class FormOptions implements ArrayAccess {
}
/**
- * @todo Document
+ * Get the value of given option and mark it as 'consumed'. Consumed options are not returned
+ * by getUnconsumedValues().
+ *
+ * @see consumeValues()
+ * @throws MWException If the option does not exist
* @param string $name Option name
- * @throws MWException If option does not exist.
- * @return mixed Value or the default value if it is null.
+ * @return mixed Value, or the default value if it is null
*/
public function consumeValue( $name ) {
$this->validateName( $name, true );
@@ -190,11 +213,15 @@ class FormOptions implements ArrayAccess {
}
/**
- * @todo Document
- * @param array $names array of option names
- * @return null
+ * Get the values of given options and mark them as 'consumed'. Consumed options are not returned
+ * by getUnconsumedValues().
+ *
+ * @see consumeValue()
+ * @throws MWException If any option does not exist
+ * @param array $names Array of option names as strings
+ * @return array Array of option values, or the default values if they are null
*/
- public function consumeValues( /*Array*/ $names ) {
+ public function consumeValues( $names ) {
$out = array();
foreach ( $names as $name ) {
@@ -213,9 +240,7 @@ class FormOptions implements ArrayAccess {
* @param string $name option name
* @param int $min minimum value
* @param int $max maximum value
- * @throws MWException
- * @exception MWException Option is not of type int
- * @return null
+ * @throws MWException If option is not of type INT
*/
public function validateIntBounds( $name, $min, $max ) {
$this->validateName( $name, true );
@@ -231,9 +256,10 @@ class FormOptions implements ArrayAccess {
}
/**
- * Getting the data out for use
- * @param $all Boolean: whether to include unchanged options (default: false)
- * @return Array
+ * Get all remaining values which have not been consumed by consumeValue() or consumeValues().
+ *
+ * @param bool $all Whether to include unchanged options (default: false)
+ * @return array
*/
public function getUnconsumedValues( $all = false ) {
$values = array();
@@ -251,7 +277,7 @@ class FormOptions implements ArrayAccess {
/**
* Return options modified as an array ( name => value )
- * @return Array
+ * @return array
*/
public function getChangedValues() {
$values = array();
@@ -266,8 +292,8 @@ class FormOptions implements ArrayAccess {
}
/**
- * Format options to an array ( name => value)
- * @return Array
+ * Format options to an array ( name => value )
+ * @return array
*/
public function getAllValues() {
$values = array();
@@ -281,24 +307,38 @@ class FormOptions implements ArrayAccess {
# Reading values
- public function fetchValuesFromRequest( WebRequest $r, $values = false ) {
- if ( !$values ) {
- $values = array_keys( $this->options );
+ /**
+ * Fetch values for all options (or selected options) from the given WebRequest, making them
+ * available for accessing with getValue() or consumeValue() etc.
+ *
+ * @param WebRequest $r The request to fetch values from
+ * @param array $optionKeys Which options to fetch the values for (default:
+ * all of them). Note that passing an empty array will also result in
+ * values for all keys being fetched.
+ * @throws MWException If the type of any option is invalid
+ */
+ public function fetchValuesFromRequest( WebRequest $r, $optionKeys = null ) {
+ if ( !$optionKeys ) {
+ $optionKeys = array_keys( $this->options );
}
- foreach ( $values as $name ) {
+ foreach ( $optionKeys as $name ) {
$default = $this->options[$name]['default'];
$type = $this->options[$name]['type'];
- switch( $type ) {
+ switch ( $type ) {
case self::BOOL:
- $value = $r->getBool( $name, $default ); break;
+ $value = $r->getBool( $name, $default );
+ break;
case self::INT:
- $value = $r->getInt( $name, $default ); break;
+ $value = $r->getInt( $name, $default );
+ break;
case self::STRING:
- $value = $r->getText( $name, $default ); break;
+ $value = $r->getText( $name, $default );
+ break;
case self::INTNULL:
- $value = $r->getIntOrNull( $name ); break;
+ $value = $r->getIntOrNull( $name );
+ break;
default:
throw new MWException( 'Unsupported datatype' );
}
@@ -310,29 +350,26 @@ class FormOptions implements ArrayAccess {
}
/** @name ArrayAccess functions
- * Those function implements PHP ArrayAccess interface
+ * These functions implement the ArrayAccess PHP interface.
* @see http://php.net/manual/en/class.arrayaccess.php
*/
/* @{ */
- /**
- * Whether option exist
- * @return bool
- */
+ /** Whether the option exists. */
public function offsetExists( $name ) {
return isset( $this->options[$name] );
}
- /**
- * Retrieve an option value
- * @return Mixed
- */
+
+ /** Retrieve an option value. */
public function offsetGet( $name ) {
return $this->getValue( $name );
}
- /** Set an option to given value */
+
+ /** Set an option to given value. */
public function offsetSet( $name, $value ) {
$this->setValue( $name, $value );
}
- /** Delete the option */
+
+ /** Delete the option. */
public function offsetUnset( $name ) {
$this->delete( $name );
}
diff --git a/includes/GitInfo.php b/includes/GitInfo.php
index 6f7f8020..f49f9be1 100644
--- a/includes/GitInfo.php
+++ b/includes/GitInfo.php
@@ -121,6 +121,32 @@ class GitInfo {
}
/**
+ * Return the commit date of HEAD entry of the git code repository
+ *
+ * @since 1.22
+ * @return int|bool Commit date (UNIX timestamp) or false
+ */
+ public function getHeadCommitDate() {
+ global $wgGitBin;
+
+ if ( !is_file( $wgGitBin ) || !is_executable( $wgGitBin ) ) {
+ return false;
+ }
+
+ $environment = array( "GIT_DIR" => $this->basedir );
+ $cmd = wfEscapeShellArg( $wgGitBin ) . " show -s --format=format:%ct HEAD";
+ $retc = false;
+ $commitDate = wfShellExec( $cmd, $retc, $environment );
+
+ if ( $retc !== 0 ) {
+ return false;
+ } else {
+ return (int)$commitDate;
+ }
+
+ }
+
+ /**
* Return the name of the current branch, or HEAD if not found
* @return string The branch name, HEAD, or false
*/
@@ -136,7 +162,7 @@ class GitInfo {
/**
* Get an URL to a web viewer link to the HEAD revision.
*
- * @return string|bool string if an URL is available or false otherwise.
+ * @return string|bool string if a URL is available or false otherwise.
*/
public function getHeadViewUrl() {
$config = "{$this->basedir}/config";
@@ -151,7 +177,7 @@ class GitInfo {
if ( isset( $configArray['remote origin'] ) ) {
$remote = $configArray['remote origin'];
} else {
- foreach( $configArray as $sectionName => $sectionConf ) {
+ foreach ( $configArray as $sectionName => $sectionConf ) {
if ( substr( $sectionName, 0, 6 ) == 'remote' ) {
$remote = $sectionConf;
}
@@ -166,14 +192,15 @@ class GitInfo {
if ( substr( $url, -4 ) !== '.git' ) {
$url .= '.git';
}
- foreach( self::getViewers() as $repo => $viewer ) {
+ foreach ( self::getViewers() as $repo => $viewer ) {
$pattern = '#^' . $repo . '$#';
- if ( preg_match( $pattern, $url ) ) {
+ if ( preg_match( $pattern, $url, $matches ) ) {
$viewerUrl = preg_replace( $pattern, $viewer, $url );
$headSHA1 = $this->getHeadSHA1();
$replacements = array(
'%h' => substr( $headSHA1, 0, 7 ),
- '%H' => $headSHA1
+ '%H' => $headSHA1,
+ '%r' => urlencode( $matches[1] ),
);
return strtr( $viewerUrl, $replacements );
}
@@ -212,7 +239,7 @@ class GitInfo {
protected static function getViewers() {
global $wgGitRepositoryViewers;
- if( self::$viewers === false ) {
+ if ( self::$viewers === false ) {
self::$viewers = $wgGitRepositoryViewers;
wfRunHooks( 'GitViewers', array( &self::$viewers ) );
}
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index 016736f4..77c09e53 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -35,7 +35,7 @@ if ( !defined( 'MEDIAWIKI' ) ) {
* PHP extensions may be included here.
*/
-if( !function_exists( 'iconv' ) ) {
+if ( !function_exists( 'iconv' ) ) {
/**
* @codeCoverageIgnore
* @return string
@@ -73,7 +73,7 @@ if ( !function_exists( 'mb_strlen' ) ) {
}
}
-if( !function_exists( 'mb_strpos' ) ) {
+if ( !function_exists( 'mb_strpos' ) ) {
/**
* @codeCoverageIgnore
* @return int
@@ -84,7 +84,7 @@ if( !function_exists( 'mb_strpos' ) ) {
}
-if( !function_exists( 'mb_strrpos' ) ) {
+if ( !function_exists( 'mb_strrpos' ) ) {
/**
* @codeCoverageIgnore
* @return int
@@ -94,24 +94,16 @@ if( !function_exists( 'mb_strrpos' ) ) {
}
}
-// Support for Wietse Venema's taint feature
-if ( !function_exists( 'istainted' ) ) {
+// gzdecode function only exists in PHP >= 5.4.0
+// http://php.net/gzdecode
+if ( !function_exists( 'gzdecode' ) ) {
/**
* @codeCoverageIgnore
- * @return int
+ * @return string
*/
- function istainted( $var ) {
- return 0;
+ function gzdecode( $data ) {
+ return gzinflate( substr( $data, 10, -8 ) );
}
- /** @codeCoverageIgnore */
- function taint( $var, $level = 0 ) {}
- /** @codeCoverageIgnore */
- function untaint( $var, $level = 0 ) {}
- define( 'TC_HTML', 1 );
- define( 'TC_SHELL', 1 );
- define( 'TC_MYSQL', 1 );
- define( 'TC_PCRE', 1 );
- define( 'TC_SELF', 1 );
}
/// @endcond
@@ -126,19 +118,19 @@ function wfArrayDiff2( $a, $b ) {
}
/**
- * @param $a
- * @param $b
+ * @param $a array|string
+ * @param $b array|string
* @return int
*/
function wfArrayDiff2_cmp( $a, $b ) {
- if ( !is_array( $a ) ) {
+ if ( is_string( $a ) && is_string( $b ) ) {
return strcmp( $a, $b );
} elseif ( count( $a ) !== count( $b ) ) {
return count( $a ) < count( $b ) ? -1 : 1;
} else {
reset( $a );
reset( $b );
- while( ( list( , $valueA ) = each( $a ) ) && ( list( , $valueB ) = each( $b ) ) ) {
+ while ( ( list( , $valueA ) = each( $a ) ) && ( list( , $valueB ) = each( $b ) ) ) {
$cmp = strcmp( $valueA, $valueB );
if ( $cmp !== 0 ) {
return $cmp;
@@ -150,14 +142,16 @@ function wfArrayDiff2_cmp( $a, $b ) {
/**
* Array lookup
- * Returns an array where the values in the first array are replaced by the
- * values in the second array with the corresponding keys
+ * Returns an array where the values in array $b are replaced by the
+ * values in array $a with the corresponding keys
*
+ * @deprecated since 1.22; use array_intersect_key()
* @param $a Array
* @param $b Array
* @return array
*/
function wfArrayLookup( $a, $b ) {
+ wfDeprecated( __FUNCTION__, '1.22' );
return array_flip( array_intersect( array_flip( $a ), array_keys( $b ) ) );
}
@@ -183,11 +177,13 @@ function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) {
* Backwards array plus for people who haven't bothered to read the PHP manual
* XXX: will not darn your socks for you.
*
+ * @deprecated since 1.22; use array_replace()
* @param $array1 Array
* @param [$array2, [...]] Arrays
* @return Array
*/
function wfArrayMerge( $array1/* ... */ ) {
+ wfDeprecated( __FUNCTION__, '1.22' );
$args = func_get_args();
$args = array_reverse( $args, true );
$out = array();
@@ -262,7 +258,7 @@ function wfArrayInsertAfter( array $array, array $insert, $after ) {
*/
function wfObjectToArray( $objOrArray, $recursive = true ) {
$array = array();
- if( is_object( $objOrArray ) ) {
+ if ( is_object( $objOrArray ) ) {
$objOrArray = get_object_vars( $objOrArray );
}
foreach ( $objOrArray as $key => $value ) {
@@ -277,24 +273,6 @@ function wfObjectToArray( $objOrArray, $recursive = true ) {
}
/**
- * Wrapper around array_map() which also taints variables
- *
- * @param $function Callback
- * @param $input Array
- * @return Array
- */
-function wfArrayMap( $function, $input ) {
- $ret = array_map( $function, $input );
- foreach ( $ret as $key => $value ) {
- $taint = istainted( $input[$key] );
- if ( $taint ) {
- taint( $ret[$key], $taint );
- }
- }
- return $ret;
-}
-
-/**
* Get a random decimal value between 0 and 1, in a way
* not likely to give duplicate values for any realistic
* number of articles.
@@ -322,8 +300,8 @@ function wfRandom() {
*/
function wfRandomString( $length = 32 ) {
$str = '';
- while ( strlen( $str ) < $length ) {
- $str .= dechex( mt_rand() );
+ for ( $n = 0; $n < $length; $n += 7 ) {
+ $str .= sprintf( '%07x', mt_rand() & 0xfffffff );
}
return substr( $str, 0, $length );
}
@@ -480,8 +458,8 @@ function wfAppendQuery( $url, $query ) {
if ( is_array( $query ) ) {
$query = wfArrayToCgi( $query );
}
- if( $query != '' ) {
- if( false === strpos( $url, '?' ) ) {
+ if ( $query != '' ) {
+ if ( false === strpos( $url, '?' ) ) {
$url .= '?';
} else {
$url .= '&';
@@ -913,10 +891,10 @@ function wfMakeUrlIndexes( $url ) {
function wfMatchesDomainList( $url, $domains ) {
$bits = wfParseUrl( $url );
if ( is_array( $bits ) && isset( $bits['host'] ) ) {
+ $host = '.' . $bits['host'];
foreach ( (array)$domains as $domain ) {
- // FIXME: This gives false positives. http://nds-nl.wikipedia.org will match nl.wikipedia.org
- // We should use something that interprets dots instead
- if ( substr( $bits['host'], -strlen( $domain ) ) === $domain ) {
+ $domain = '.' . $domain;
+ if ( substr( $host, -strlen( $domain ) ) === $domain ) {
return true;
}
}
@@ -953,14 +931,12 @@ function wfDebug( $text, $logonly = false ) {
MWDebug::debugMsg( $text );
}
- if ( wfRunHooks( 'Debug', array( $text, null /* no log group */ ) ) ) {
- if ( $wgDebugLogFile != '' && !$wgProfileOnly ) {
- # 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 );
- }
+ if ( $wgDebugLogFile != '' && !$wgProfileOnly ) {
+ # 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 );
}
}
@@ -1011,7 +987,7 @@ function wfDebugTimer() {
*/
function wfDebugMem( $exact = false ) {
$mem = memory_get_usage();
- if( !$exact ) {
+ if ( !$exact ) {
$mem = floor( $mem / 1024 ) . ' kilobytes';
} else {
$mem .= ' bytes';
@@ -1031,15 +1007,13 @@ function wfDebugMem( $exact = false ) {
function wfDebugLog( $logGroup, $text, $public = true ) {
global $wgDebugLogGroups;
$text = trim( $text ) . "\n";
- if( isset( $wgDebugLogGroups[$logGroup] ) ) {
+ if ( isset( $wgDebugLogGroups[$logGroup] ) ) {
$time = wfTimestamp( TS_DB );
$wiki = wfWikiID();
$host = wfHostname();
- if ( wfRunHooks( 'Debug', array( $text, $logGroup ) ) ) {
- wfErrorLog( "$time $host $wiki: $text", $wgDebugLogGroups[$logGroup] );
- }
+ wfErrorLog( "$time $host $wiki: $text", $wgDebugLogGroups[$logGroup] );
} elseif ( $public === true ) {
- wfDebug( "[$logGroup] $text", true );
+ wfDebug( "[$logGroup] $text", false );
}
}
@@ -1093,16 +1067,29 @@ function wfDeprecated( $function, $version = false, $component = false, $callerO
/**
* Send a warning either to the debug log or in a PHP error depending on
- * $wgDevelopmentWarnings
+ * $wgDevelopmentWarnings. To log warnings in production, use wfLogWarning() instead.
*
* @param string $msg message to send
* @param $callerOffset Integer: number of items to go back in the backtrace to
* find the correct caller (1 = function calling wfWarn, ...)
- * @param $level Integer: PHP error level; only used when $wgDevelopmentWarnings
- * is true
+ * @param $level Integer: PHP error level; defaults to E_USER_NOTICE;
+ * only used when $wgDevelopmentWarnings is true
*/
function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
- MWDebug::warning( $msg, $callerOffset + 1, $level );
+ MWDebug::warning( $msg, $callerOffset + 1, $level, 'auto' );
+}
+
+/**
+ * Send a warning as a PHP error and the debug log. This is intended for logging
+ * warnings in production. For logging development warnings, use WfWarn instead.
+ *
+ * @param $msg String: message to send
+ * @param $callerOffset Integer: number of items to go back in the backtrace to
+ * find the correct caller (1 = function calling wfLogWarning, ...)
+ * @param $level Integer: PHP error level; defaults to E_USER_WARNING
+ */
+function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
+ MWDebug::warning( $msg, $callerOffset + 1, $level, 'production' );
}
/**
@@ -1177,6 +1164,8 @@ function wfLogProfilingData() {
global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest;
global $wgProfileLimit, $wgUser;
+ StatCounter::singleton()->flush();
+
$profiler = Profiler::instance();
# Profiling must actually be enabled...
@@ -1240,84 +1229,35 @@ function wfLogProfilingData() {
* @return void
*/
function wfIncrStats( $key, $count = 1 ) {
- global $wgStatsMethod;
-
- $count = intval( $count );
- if ( $count == 0 ) {
- return;
- }
-
- if( $wgStatsMethod == 'udp' ) {
- global $wgUDPProfilerHost, $wgUDPProfilerPort, $wgAggregateStatsID;
- static $socket;
-
- $id = $wgAggregateStatsID !== false ? $wgAggregateStatsID : wfWikiID();
-
- if ( !$socket ) {
- $socket = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
- $statline = "stats/{$id} - 1 1 1 1 1 -total\n";
- socket_sendto(
- $socket,
- $statline,
- strlen( $statline ),
- 0,
- $wgUDPProfilerHost,
- $wgUDPProfilerPort
- );
- }
- $statline = "stats/{$id} - {$count} 1 1 1 1 {$key}\n";
- wfSuppressWarnings();
- socket_sendto(
- $socket,
- $statline,
- strlen( $statline ),
- 0,
- $wgUDPProfilerHost,
- $wgUDPProfilerPort
- );
- wfRestoreWarnings();
- } elseif( $wgStatsMethod == 'cache' ) {
- global $wgMemc;
- $key = wfMemcKey( 'stats', $key );
- if ( is_null( $wgMemc->incr( $key, $count ) ) ) {
- $wgMemc->add( $key, $count );
- }
- } else {
- // Disabled
- }
+ StatCounter::singleton()->incr( $key, $count );
}
/**
- * Check if the wiki read-only lock file is present. This can be used to lock
- * off editing functions, but doesn't guarantee that the database will not be
- * modified.
+ * Check whether the wiki is in read-only mode.
*
* @return bool
*/
function wfReadOnly() {
- global $wgReadOnlyFile, $wgReadOnly;
-
- if ( !is_null( $wgReadOnly ) ) {
- return (bool)$wgReadOnly;
- }
- if ( $wgReadOnlyFile == '' ) {
- return false;
- }
- // Set $wgReadOnly for faster access next time
- if ( is_file( $wgReadOnlyFile ) ) {
- $wgReadOnly = file_get_contents( $wgReadOnlyFile );
- } else {
- $wgReadOnly = false;
- }
- return (bool)$wgReadOnly;
+ return wfReadOnlyReason() !== false;
}
/**
- * @return bool
+ * Get the value of $wgReadOnly or the contents of $wgReadOnlyFile.
+ *
+ * @return string|bool: String when in read-only mode; false otherwise
*/
function wfReadOnlyReason() {
- global $wgReadOnly;
- wfReadOnly();
+ global $wgReadOnly, $wgReadOnlyFile;
+
+ if ( $wgReadOnly === null ) {
+ // Set $wgReadOnly for faster access next time
+ if ( is_file( $wgReadOnlyFile ) && filesize( $wgReadOnlyFile ) > 0 ) {
+ $wgReadOnly = file_get_contents( $wgReadOnlyFile );
+ } else {
+ $wgReadOnly = false;
+ }
+ }
+
return $wgReadOnly;
}
@@ -1339,27 +1279,27 @@ function wfReadOnlyReason() {
function wfGetLangObj( $langcode = false ) {
# Identify which language to get or create a language object for.
# Using is_object here due to Stub objects.
- if( is_object( $langcode ) ) {
+ if ( is_object( $langcode ) ) {
# Great, we already have the object (hopefully)!
return $langcode;
}
global $wgContLang, $wgLanguageCode;
- if( $langcode === true || $langcode === $wgLanguageCode ) {
+ if ( $langcode === true || $langcode === $wgLanguageCode ) {
# $langcode is the language code of the wikis content language object.
# or it is a boolean and value is true
return $wgContLang;
}
global $wgLang;
- if( $langcode === false || $langcode === $wgLang->getCode() ) {
+ if ( $langcode === false || $langcode === $wgLang->getCode() ) {
# $langcode is the language code of user language object.
# or it was a boolean and value is false
return $wgLang;
}
$validCodes = array_keys( Language::fetchLanguageNames() );
- if( in_array( $langcode, $validCodes ) ) {
+ if ( in_array( $langcode, $validCodes ) ) {
# $langcode corresponds to a valid language.
return Language::factory( $langcode );
}
@@ -1414,7 +1354,7 @@ function wfMessage( $key /*...*/) {
*/
function wfMessageFallback( /*...*/ ) {
$args = func_get_args();
- return MWFunction::callArray( 'Message::newFallbackSequence', $args );
+ return call_user_func_array( 'Message::newFallbackSequence', $args );
}
/**
@@ -1492,7 +1432,7 @@ function wfMsgForContent( $key ) {
$args = func_get_args();
array_shift( $args );
$forcontent = true;
- if( is_array( $wgForceUIMsgAsContentMsg ) &&
+ if ( is_array( $wgForceUIMsgAsContentMsg ) &&
in_array( $key, $wgForceUIMsgAsContentMsg ) )
{
$forcontent = false;
@@ -1515,7 +1455,7 @@ function wfMsgForContentNoTrans( $key ) {
$args = func_get_args();
array_shift( $args );
$forcontent = true;
- if( is_array( $wgForceUIMsgAsContentMsg ) &&
+ if ( is_array( $wgForceUIMsgAsContentMsg ) &&
in_array( $key, $wgForceUIMsgAsContentMsg ) )
{
$forcontent = false;
@@ -1564,7 +1504,7 @@ function wfMsgGetKey( $key, $useDB = true, $langCode = false, $transform = true
$cache = MessageCache::singleton();
$message = $cache->get( $key, $useDB, $langCode );
- if( $message === false ) {
+ if ( $message === false ) {
$message = '&lt;' . htmlspecialchars( $key ) . '&gt;';
} elseif ( $transform ) {
$message = $cache->transform( $message );
@@ -1591,7 +1531,7 @@ function wfMsgReplaceArgs( $message, $args ) {
$args = array_values( $args[0] );
}
$replacementKeys = array();
- foreach( $args as $n => $param ) {
+ foreach ( $args as $n => $param ) {
$replacementKeys['$' . ( $n + 1 )] = $param;
}
$message = strtr( $message, $replacementKeys );
@@ -1675,11 +1615,11 @@ function wfMsgExt( $key, $options ) {
array_shift( $args );
$options = (array)$options;
- foreach( $options as $arrayKey => $option ) {
- if( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) {
+ foreach ( $options as $arrayKey => $option ) {
+ if ( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) {
# 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,
+ } elseif ( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option,
array( 'parse', 'parseinline', 'escape', 'escapenoentities',
'replaceafter', 'parsemag', 'content' ) ) ) {
# A numeric index with unknown value
@@ -1687,11 +1627,11 @@ function wfMsgExt( $key, $options ) {
}
}
- if( in_array( 'content', $options, true ) ) {
+ if ( in_array( 'content', $options, true ) ) {
$forContent = true;
$langCode = true;
$langCodeObj = null;
- } elseif( array_key_exists( 'language', $options ) ) {
+ } elseif ( array_key_exists( 'language', $options ) ) {
$forContent = false;
$langCode = wfGetLangObj( $options['language'] );
$langCodeObj = $langCode;
@@ -1703,13 +1643,13 @@ function wfMsgExt( $key, $options ) {
$string = wfMsgGetKey( $key, /*DB*/true, $langCode, /*Transform*/false );
- if( !in_array( 'replaceafter', $options, true ) ) {
+ if ( !in_array( 'replaceafter', $options, true ) ) {
$string = wfMsgReplaceArgs( $string, $args );
}
$messageCache = MessageCache::singleton();
$parseInline = in_array( 'parseinline', $options, true );
- if( in_array( 'parse', $options, true ) || $parseInline ) {
+ if ( in_array( 'parse', $options, true ) || $parseInline ) {
$string = $messageCache->parse( $string, null, true, !$forContent, $langCodeObj );
if ( $string instanceof ParserOutput ) {
$string = $string->getText();
@@ -1717,7 +1657,7 @@ function wfMsgExt( $key, $options ) {
if ( $parseInline ) {
$m = array();
- if( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) {
+ if ( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) {
$string = $m[1];
}
}
@@ -1732,7 +1672,7 @@ function wfMsgExt( $key, $options ) {
$string = Sanitizer::escapeHtmlAllowEntities( $string );
}
- if( in_array( 'replaceafter', $options, true ) ) {
+ if ( in_array( 'replaceafter', $options, true ) ) {
$string = wfMsgReplaceArgs( $string, $args );
}
@@ -1741,7 +1681,7 @@ function wfMsgExt( $key, $options ) {
/**
* Since wfMsg() and co suck, they don't return false if the message key they
- * looked up didn't exist but a XHTML string, this function checks for the
+ * looked up didn't exist but instead the key wrapped in <>'s, this function checks for the
* nonexistence of messages by checking the MessageCache::get() result directly.
*
* @deprecated since 1.18. Use Message::isDisabled().
@@ -1759,10 +1699,12 @@ function wfEmptyMsg( $key ) {
* Throw a debugging exception. This function previously once exited the process,
* but now throws an exception instead, with similar results.
*
+ * @deprecated since 1.22; just throw an MWException yourself
* @param string $msg message shown when dying.
* @throws MWException
*/
function wfDebugDieBacktrace( $msg = '' ) {
+ wfDeprecated( __FUNCTION__, '1.22' );
throw new MWException( $msg );
}
@@ -1779,7 +1721,7 @@ function wfHostname() {
# Hostname overriding
global $wgOverrideHostname;
- if( $wgOverrideHostname !== false ) {
+ if ( $wgOverrideHostname !== false ) {
# Set static and skip any detection
$host = $wgOverrideHostname;
return $host;
@@ -1791,7 +1733,7 @@ function wfHostname() {
} else {
$uname = false;
}
- if( is_array( $uname ) && isset( $uname['nodename'] ) ) {
+ if ( is_array( $uname ) && isset( $uname['nodename'] ) ) {
$host = $uname['nodename'];
} elseif ( getenv( 'COMPUTERNAME' ) ) {
# Windows computer name
@@ -1838,7 +1780,7 @@ function wfReportTime() {
function wfDebugBacktrace( $limit = 0 ) {
static $disabled = null;
- if( extension_loaded( 'Zend Optimizer' ) ) {
+ if ( extension_loaded( 'Zend Optimizer' ) ) {
wfDebug( "Zend Optimizer detected; skipping debug_backtrace for safety.\n" );
return array();
}
@@ -1878,14 +1820,14 @@ function wfBacktrace() {
$msg = "<ul>\n";
}
$backtrace = wfDebugBacktrace();
- foreach( $backtrace as $call ) {
- if( isset( $call['file'] ) ) {
+ 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'] ) ) {
+ if ( isset( $call['line'] ) ) {
$line = $call['line'];
} else {
$line = '-';
@@ -1895,7 +1837,7 @@ function wfBacktrace() {
} else {
$msg .= '<li>' . $file . ' line ' . $line . ' calls ';
}
- if( !empty( $call['class'] ) ) {
+ if ( !empty( $call['class'] ) ) {
$msg .= $call['class'] . $call['type'];
}
$msg .= $call['function'] . '()';
@@ -1993,11 +1935,11 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) {
$query = wfCgiToArray( $query );
- if( is_object( $link ) ) {
+ if ( is_object( $link ) ) {
$title = $link;
} else {
$title = Title::newFromText( $link );
- if( is_null( $title ) ) {
+ if ( is_null( $title ) ) {
return false;
}
}
@@ -2006,23 +1948,6 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) {
}
/**
- * Make a list item, used by various special pages
- *
- * @param string $page Page link
- * @param string $details Text between brackets
- * @param $oppositedm Boolean Add the direction mark opposite to your
- * language, to display text properly
- * @return String
- * @deprecated since 1.19; use Language::specialList() instead
- */
-function wfSpecialList( $page, $details, $oppositedm = true ) {
- wfDeprecated( __METHOD__, '1.19' );
-
- global $wgLang;
- return $wgLang->specialList( $page, $details, $oppositedm );
-}
-
-/**
* @todo document
* @todo FIXME: We may want to blacklist some broken browsers
*
@@ -2033,16 +1958,16 @@ function wfClientAcceptsGzip( $force = false ) {
static $result = null;
if ( $result === null || $force ) {
$result = false;
- if( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) {
+ if ( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) {
# @todo FIXME: We may want to blacklist some broken browsers
$m = array();
- if( preg_match(
+ if ( preg_match(
'/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
$_SERVER['HTTP_ACCEPT_ENCODING'],
$m )
)
{
- if( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) {
+ if ( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) {
$result = false;
return $result;
}
@@ -2078,22 +2003,54 @@ function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) {
* @return String
*/
function wfEscapeWikiText( $text ) {
- $text = strtr( "\n$text", array(
- '"' => '&#34;', '&' => '&#38;', "'" => '&#39;', '<' => '&#60;',
- '=' => '&#61;', '>' => '&#62;', '[' => '&#91;', ']' => '&#93;',
- '{' => '&#123;', '|' => '&#124;', '}' => '&#125;',
- "\n#" => "\n&#35;", "\n*" => "\n&#42;",
- "\n:" => "\n&#58;", "\n;" => "\n&#59;",
- '://' => '&#58;//', 'ISBN ' => 'ISBN&#32;', 'RFC ' => 'RFC&#32;',
- ) );
- return substr( $text, 1 );
+ static $repl = null, $repl2 = null;
+ if ( $repl === null ) {
+ $repl = array(
+ '"' => '&#34;', '&' => '&#38;', "'" => '&#39;', '<' => '&#60;',
+ '=' => '&#61;', '>' => '&#62;', '[' => '&#91;', ']' => '&#93;',
+ '{' => '&#123;', '|' => '&#124;', '}' => '&#125;', ';' => '&#59;',
+ "\n#" => "\n&#35;", "\r#" => "\r&#35;",
+ "\n*" => "\n&#42;", "\r*" => "\r&#42;",
+ "\n:" => "\n&#58;", "\r:" => "\r&#58;",
+ "\n " => "\n&#32;", "\r " => "\r&#32;",
+ "\n\n" => "\n&#10;", "\r\n" => "&#13;\n",
+ "\n\r" => "\n&#13;", "\r\r" => "\r&#13;",
+ "\n\t" => "\n&#9;", "\r\t" => "\r&#9;", // "\n\t\n" is treated like "\n\n"
+ "\n----" => "\n&#45;---", "\r----" => "\r&#45;---",
+ '__' => '_&#95;', '://' => '&#58;//',
+ );
+
+ // We have to catch everything "\s" matches in PCRE
+ foreach ( array( 'ISBN', 'RFC', 'PMID' ) as $magic ) {
+ $repl["$magic "] = "$magic&#32;";
+ $repl["$magic\t"] = "$magic&#9;";
+ $repl["$magic\r"] = "$magic&#13;";
+ $repl["$magic\n"] = "$magic&#10;";
+ $repl["$magic\f"] = "$magic&#12;";
+ }
+
+ // And handle protocols that don't use "://"
+ global $wgUrlProtocols;
+ $repl2 = array();
+ foreach ( $wgUrlProtocols as $prot ) {
+ if ( substr( $prot, -1 ) === ':' ) {
+ $repl2[] = preg_quote( substr( $prot, 0, -1 ), '/' );
+ }
+ }
+ $repl2 = $repl2 ? '/\b(' . join( '|', $repl2 ) . '):/i' : '/^(?!)/';
+ }
+ $text = substr( strtr( "\n$text", $repl ), 1 );
+ $text = preg_replace( $repl2, '$1&#58;', $text );
+ return $text;
}
/**
* Get the current unix timestamp with microseconds. Useful for profiling
+ * @deprecated since 1.22; call microtime() directly
* @return Float
*/
function wfTime() {
+ wfDeprecated( __FUNCTION__, '1.22' );
return microtime( true );
}
@@ -2195,14 +2152,14 @@ function wfHttpError( $code, $label, $desc ) {
* @param $resetGzipEncoding Bool
*/
function wfResetOutputBuffers( $resetGzipEncoding = true ) {
- if( $resetGzipEncoding ) {
+ if ( $resetGzipEncoding ) {
// Suppress Content-Encoding and Content-Length
// headers from 1.10+s wfOutputHandler
global $wgDisableOutputCompression;
$wgDisableOutputCompression = true;
}
- while( $status = ob_get_status() ) {
- if( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) {
+ while ( $status = ob_get_status() ) {
+ if ( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) {
// Probably from zlib.output_compression or other
// PHP-internal setting which can't be removed.
//
@@ -2210,13 +2167,13 @@ function wfResetOutputBuffers( $resetGzipEncoding = true ) {
// output behavior.
break;
}
- if( !ob_end_clean() ) {
+ if ( !ob_end_clean() ) {
// Could not remove output buffer handler; abort now
// to avoid getting in some kind of infinite loop.
break;
}
- if( $resetGzipEncoding ) {
- if( $status['name'] == 'ob_gzhandler' ) {
+ if ( $resetGzipEncoding ) {
+ if ( $status['name'] == 'ob_gzhandler' ) {
// Reset the 'Content-Encoding' field set by this handler
// so we can start fresh.
header_remove( 'Content-Encoding' );
@@ -2252,7 +2209,7 @@ function wfClearOutputBuffers() {
*/
function wfAcceptToPrefs( $accept, $def = '*/*' ) {
# No arg means accept anything (per HTTP spec)
- if( !$accept ) {
+ if ( !$accept ) {
return array( $def => 1.0 );
}
@@ -2260,7 +2217,7 @@ function wfAcceptToPrefs( $accept, $def = '*/*' ) {
$parts = explode( ',', $accept );
- foreach( $parts as $part ) {
+ foreach ( $parts as $part ) {
# @todo FIXME: Doesn't deal with params like 'text/html; level=1'
$values = explode( ';', trim( $part ) );
$match = array();
@@ -2287,13 +2244,13 @@ function wfAcceptToPrefs( $accept, $def = '*/*' ) {
* @private
*/
function mimeTypeMatch( $type, $avail ) {
- if( array_key_exists( $type, $avail ) ) {
+ if ( array_key_exists( $type, $avail ) ) {
return $type;
} else {
$parts = explode( '/', $type );
- if( array_key_exists( $parts[0] . '/*', $avail ) ) {
+ if ( array_key_exists( $parts[0] . '/*', $avail ) ) {
return $parts[0] . '/*';
- } elseif( array_key_exists( '*/*', $avail ) ) {
+ } elseif ( array_key_exists( '*/*', $avail ) ) {
return '*/*';
} else {
return null;
@@ -2317,21 +2274,21 @@ function mimeTypeMatch( $type, $avail ) {
function wfNegotiateType( $cprefs, $sprefs ) {
$combine = array();
- foreach( array_keys( $sprefs ) as $type ) {
+ foreach ( array_keys( $sprefs ) as $type ) {
$parts = explode( '/', $type );
- if( $parts[1] != '*' ) {
+ if ( $parts[1] != '*' ) {
$ckey = mimeTypeMatch( $type, $cprefs );
- if( $ckey ) {
+ if ( $ckey ) {
$combine[$type] = $sprefs[$type] * $cprefs[$ckey];
}
}
}
- foreach( array_keys( $cprefs ) as $type ) {
+ foreach ( array_keys( $cprefs ) as $type ) {
$parts = explode( '/', $type );
- if( $parts[1] != '*' && !array_key_exists( $type, $sprefs ) ) {
+ if ( $parts[1] != '*' && !array_key_exists( $type, $sprefs ) ) {
$skey = mimeTypeMatch( $type, $sprefs );
- if( $skey ) {
+ if ( $skey ) {
$combine[$type] = $sprefs[$skey] * $cprefs[$type];
}
}
@@ -2340,8 +2297,8 @@ function wfNegotiateType( $cprefs, $sprefs ) {
$bestq = 0;
$besttype = null;
- foreach( array_keys( $combine ) as $type ) {
- if( $combine[$type] > $bestq ) {
+ foreach ( array_keys( $combine ) as $type ) {
+ if ( $combine[$type] > $bestq ) {
$besttype = $type;
$bestq = $combine[$type];
}
@@ -2447,7 +2404,7 @@ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) {
try {
$timestamp = new MWTimestamp( $ts );
return $timestamp->getTimestamp( $outputtype );
- } catch( TimestampException $e ) {
+ } catch ( TimestampException $e ) {
wfDebug( "wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n" );
return false;
}
@@ -2462,7 +2419,7 @@ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) {
* @return String
*/
function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) {
- if( is_null( $ts ) ) {
+ if ( is_null( $ts ) ) {
return null;
} else {
return wfTimestamp( $outputtype, $ts );
@@ -2498,7 +2455,7 @@ function wfIsWindows() {
* @return Bool
*/
function wfIsHipHop() {
- return function_exists( 'hphp_thread_set_warmup_enabled' );
+ return defined( 'HPHP_VERSION' );
}
/**
@@ -2533,8 +2490,8 @@ function wfTempDir() {
$tmpDir = array_map( "getenv", array( 'TMPDIR', 'TMP', 'TEMP' ) );
- foreach( $tmpDir as $tmp ) {
- if( $tmp && file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) {
+ foreach ( $tmpDir as $tmp ) {
+ if ( $tmp && file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) {
return $tmp;
}
}
@@ -2561,7 +2518,7 @@ function wfMkdirParents( $dir, $mode = null, $caller = null ) {
wfDebug( "$caller: called wfMkdirParents($dir)\n" );
}
- if( strval( $dir ) === '' || file_exists( $dir ) ) {
+ if ( strval( $dir ) === '' || ( file_exists( $dir ) && is_dir( $dir ) ) ) {
return true;
}
@@ -2576,10 +2533,14 @@ function wfMkdirParents( $dir, $mode = null, $caller = null ) {
$ok = mkdir( $dir, $mode, true ); // PHP5 <3
wfRestoreWarnings();
- if( !$ok ) {
+ if ( !$ok ) {
+ //directory may have been created on another request since we last checked
+ if ( is_dir( $dir ) ) {
+ return true;
+ }
+
// PHP doesn't report the path in its warning message, so add our own to aid in diagnosis.
- trigger_error( sprintf( "%s: failed to mkdir \"%s\" mode 0%o", __FUNCTION__, $dir, $mode ),
- E_USER_WARNING );
+ wfLogWarning( sprintf( "failed to mkdir \"%s\" mode 0%o", $dir, $mode ) );
}
return $ok;
}
@@ -2630,7 +2591,9 @@ function wfPercent( $nr, $acc = 2, $round = true ) {
function in_string( $needle, $str, $insensitive = false ) {
wfDeprecated( __METHOD__, '1.21' );
$func = 'strpos';
- if( $insensitive ) $func = 'stripos';
+ if ( $insensitive ) {
+ $func = 'stripos';
+ }
return $func( $str, $needle ) !== false;
}
@@ -2659,47 +2622,15 @@ function in_string( $needle, $str, $insensitive = false ) {
* @return Bool
*/
function wfIniGetBool( $setting ) {
- $val = ini_get( $setting );
+ $val = strtolower( ini_get( $setting ) );
// 'on' and 'true' can't have whitespace around them, but '1' can.
- return strtolower( $val ) == 'on'
- || strtolower( $val ) == 'true'
- || strtolower( $val ) == 'yes'
+ return $val == 'on'
+ || $val == 'true'
+ || $val == 'yes'
|| preg_match( "/^\s*[+-]?0*[1-9]/", $val ); // approx C atoi() function
}
/**
- * Wrapper function for PHP's dl(). This doesn't work in most situations from
- * PHP 5.3 onward, and is usually disabled in shared environments anyway.
- *
- * @param string $extension A PHP extension. The file suffix (.so or .dll)
- * should be omitted
- * @param string $fileName Name of the library, if not $extension.suffix
- * @return Bool - Whether or not the extension is loaded
- */
-function wfDl( $extension, $fileName = null ) {
- if( extension_loaded( $extension ) ) {
- return true;
- }
-
- $canDl = false;
- if( PHP_SAPI == 'cli' || PHP_SAPI == 'cgi' || PHP_SAPI == 'embed' ) {
- $canDl = ( function_exists( 'dl' ) && is_callable( 'dl' )
- && wfIniGetBool( 'enable_dl' ) && !wfIniGetBool( 'safe_mode' ) );
- }
-
- if( $canDl ) {
- $fileName = $fileName ? $fileName : $extension;
- if( wfIsWindows() ) {
- $fileName = 'php_' . $fileName;
- }
- wfSuppressWarnings();
- dl( $fileName . '.' . PHP_SHLIB_SUFFIX );
- wfRestoreWarnings();
- }
- return extension_loaded( $extension );
-}
-
-/**
* Windows-compatible version of escapeshellarg()
* Windows doesn't recognise single-quotes in the shell, but the escapeshellarg()
* function puts single quotes in regardless of OS.
@@ -2764,25 +2695,15 @@ function wfEscapeShellArg() {
}
/**
- * Execute a shell command, with time and memory limits mirrored from the PHP
- * configuration if supported.
- * @param string $cmd Command line, properly escaped for shell.
- * @param &$retval null|Mixed optional, will receive the program's exit code.
- * (non-zero is usually failure)
- * @param array $environ optional environment variables which should be
- * added to the executed command environment.
- * @param array $limits optional array with limits(filesize, memory, time, walltime)
- * this overwrites the global wgShellMax* limits.
- * @return string collected stdout as a string (trailing newlines stripped)
+ * Check if wfShellExec() is effectively disabled via php.ini config
+ * @return bool|string False or one of (safemode,disabled)
+ * @since 1.22
*/
-function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array() ) {
- global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime,
- $wgMaxShellWallClockTime, $wgShellCgroup;
-
- static $disabled;
+function wfShellExecDisabled() {
+ static $disabled = null;
if ( is_null( $disabled ) ) {
$disabled = false;
- if( wfIniGetBool( 'safe_mode' ) ) {
+ if ( wfIniGetBool( 'safe_mode' ) ) {
wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" );
$disabled = 'safemode';
} else {
@@ -2795,6 +2716,28 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array
}
}
}
+ return $disabled;
+}
+
+/**
+ * Execute a shell command, with time and memory limits mirrored from the PHP
+ * configuration if supported.
+ * @param string $cmd Command line, properly escaped for shell.
+ * @param &$retval null|Mixed optional, will receive the program's exit code.
+ * (non-zero is usually failure)
+ * @param array $environ optional environment variables which should be
+ * added to the executed command environment.
+ * @param array $limits optional array with limits(filesize, memory, time, walltime)
+ * this overwrites the global wgShellMax* limits.
+ * @param array $options Array of options. Only one is "duplicateStderr" => true, which
+ * Which duplicates stderr to stdout, including errors from limit.sh
+ * @return string collected stdout as a string
+ */
+function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array(), $options = array() ) {
+ global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime,
+ $wgMaxShellWallClockTime, $wgShellCgroup;
+
+ $disabled = wfShellExecDisabled();
if ( $disabled ) {
$retval = 1;
return $disabled == 'safemode' ?
@@ -2802,10 +2745,12 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array
'Unable to run external programs, passthru() is disabled.';
}
+ $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
+
wfInitShellLocale();
$envcmd = '';
- foreach( $environ as $k => $v ) {
+ foreach ( $environ as $k => $v ) {
if ( wfIsWindows() ) {
/* Surrounding a set in quotes (method used by wfEscapeShellArg) makes the quotes themselves
* appear in the environment variable, so we must use carat escaping as documented in
@@ -2839,17 +2784,25 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array
$cmd = '/bin/bash ' . escapeshellarg( "$IP/includes/limit.sh" ) . ' ' .
escapeshellarg( $cmd ) . ' ' .
escapeshellarg(
+ "MW_INCLUDE_STDERR=" . ( $includeStderr ? '1' : '' ) . ';' .
"MW_CPU_LIMIT=$time; " .
'MW_CGROUP=' . escapeshellarg( $wgShellCgroup ) . '; ' .
"MW_MEM_LIMIT=$mem; " .
"MW_FILE_SIZE_LIMIT=$filesize; " .
"MW_WALL_CLOCK_LIMIT=$wallTime"
);
+ } elseif ( $includeStderr ) {
+ $cmd .= ' 2>&1';
}
+ } elseif ( $includeStderr ) {
+ $cmd .= ' 2>&1';
}
wfDebug( "wfShellExec: $cmd\n" );
- $retval = 1; // error by default?
+ // Default to an unusual value that shouldn't happen naturally,
+ // so in the unlikely event of a weird php bug, it would be
+ // more obvious what happened.
+ $retval = 200;
ob_start();
passthru( $cmd, $retval );
$output = ob_get_contents();
@@ -2862,6 +2815,24 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array
}
/**
+ * Execute a shell command, returning both stdout and stderr. Convenience
+ * 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 &$retval null|Mixed optional, will receive the program's exit code.
+ * (non-zero is usually failure)
+ * @param array $environ optional environment variables which should be
+ * added to the executed command environment.
+ * @param array $limits optional array with limits(filesize, memory, time, walltime)
+ * this overwrites the global wgShellMax* limits.
+ * @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 ) );
+}
+
+/**
* Workaround for http://bugs.php.net/bug.php?id=45132
* escapeshellarg() destroys non-ASCII characters if LANG is not a UTF-8 locale
*/
@@ -2930,7 +2901,7 @@ function wfMerge( $old, $mine, $yours, &$result ) {
$haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
wfRestoreWarnings();
- if( !$haveDiff3 ) {
+ if ( !$haveDiff3 ) {
wfDebug( "diff3 not found\n" );
return false;
}
@@ -2959,7 +2930,7 @@ function wfMerge( $old, $mine, $yours, &$result ) {
wfEscapeShellArg( $yourtextName );
$handle = popen( $cmd, 'r' );
- if( fgets( $handle, 1024 ) ) {
+ if ( fgets( $handle, 1024 ) ) {
$conflict = true;
} else {
$conflict = false;
@@ -3011,7 +2982,7 @@ function wfDiff( $before, $after, $params = '-u' ) {
# This check may also protect against code injection in
# case of broken installations.
- if( !$haveDiff ) {
+ if ( !$haveDiff ) {
wfDebug( "diff executable not found\n" );
$diffs = new Diff( explode( "\n", $before ), explode( "\n", $after ) );
$format = new UnifiedDiffFormatter();
@@ -3125,7 +3096,7 @@ function wfBaseName( $path, $suffix = '' ) {
? ''
: ( '(?:' . preg_quote( $suffix, '#' ) . ')?' );
$matches = array();
- if( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) {
+ if ( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) {
return $matches[1];
} else {
return '';
@@ -3153,21 +3124,21 @@ function wfRelativePath( $path, $from ) {
$pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
$against = explode( DIRECTORY_SEPARATOR, $from );
- if( $pieces[0] !== $against[0] ) {
+ if ( $pieces[0] !== $against[0] ) {
// Non-matching Windows drive letters?
// Return a full path.
return $path;
}
// Trim off common prefix
- while( count( $pieces ) && count( $against )
+ while ( count( $pieces ) && count( $against )
&& $pieces[0] == $against[0] ) {
array_shift( $pieces );
array_shift( $against );
}
// relative dots to bump us to the parent
- while( count( $against ) ) {
+ while ( count( $against ) ) {
array_unshift( $pieces, '..' );
array_shift( $against );
}
@@ -3206,20 +3177,20 @@ function wfDoUpdates( $commit = '' ) {
*/
function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, $lowercase = true, $engine = 'auto' ) {
$input = (string)$input;
- if(
+ if (
$sourceBase < 2 ||
$sourceBase > 36 ||
$destBase < 2 ||
$destBase > 36 ||
- $sourceBase != (int) $sourceBase ||
- $destBase != (int) $destBase ||
- $pad != (int) $pad ||
+ $sourceBase != (int)$sourceBase ||
+ $destBase != (int)$destBase ||
+ $pad != (int)$pad ||
!preg_match( "/^[" . substr( '0123456789abcdefghijklmnopqrstuvwxyz', 0, $sourceBase ) . "]+$/i", $input )
) {
return false;
}
- static $baseChars = array (
+ static $baseChars = array(
10 => 'a', 11 => 'b', 12 => 'c', 13 => 'd', 14 => 'e', 15 => 'f',
16 => 'g', 17 => 'h', 18 => 'i', 19 => 'j', 20 => 'k', 21 => 'l',
22 => 'm', 23 => 'n', 24 => 'o', 25 => 'p', 26 => 'q', 27 => 'r',
@@ -3234,40 +3205,40 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, $lowercase = t
'u' => 30, 'v' => 31, 'w' => 32, 'x' => 33, 'y' => 34, 'z' => 35
);
- if( extension_loaded( 'gmp' ) && ( $engine == 'auto' || $engine == 'gmp' ) ) {
+ if ( extension_loaded( 'gmp' ) && ( $engine == 'auto' || $engine == 'gmp' ) ) {
$result = gmp_strval( gmp_init( $input, $sourceBase ), $destBase );
- } elseif( extension_loaded( 'bcmath' ) && ( $engine == 'auto' || $engine == 'bcmath' ) ) {
+ } elseif ( extension_loaded( 'bcmath' ) && ( $engine == 'auto' || $engine == 'bcmath' ) ) {
$decimal = '0';
- foreach( str_split( strtolower( $input ) ) as $char ) {
+ foreach ( str_split( strtolower( $input ) ) as $char ) {
$decimal = bcmul( $decimal, $sourceBase );
$decimal = bcadd( $decimal, $baseChars[$char] );
}
- for( $result = ''; bccomp( $decimal, 0 ); $decimal = bcdiv( $decimal, $destBase, 0 ) ) {
+ for ( $result = ''; bccomp( $decimal, 0 ); $decimal = bcdiv( $decimal, $destBase, 0 ) ) {
$result .= $baseChars[bcmod( $decimal, $destBase )];
}
$result = strrev( $result );
} else {
$inDigits = array();
- foreach( str_split( strtolower( $input ) ) as $char ) {
+ foreach ( str_split( strtolower( $input ) ) as $char ) {
$inDigits[] = $baseChars[$char];
}
// Iterate over the input, modulo-ing out an output digit
// at a time until input is gone.
$result = '';
- while( $inDigits ) {
+ while ( $inDigits ) {
$work = 0;
$workDigits = array();
// Long division...
- foreach( $inDigits as $digit ) {
+ foreach ( $inDigits as $digit ) {
$work *= $sourceBase;
$work += $digit;
- if( $workDigits || $work >= $destBase ) {
- $workDigits[] = (int) ( $work / $destBase );
+ if ( $workDigits || $work >= $destBase ) {
+ $workDigits[] = (int)( $work / $destBase );
}
$work %= $destBase;
}
@@ -3283,7 +3254,7 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, $lowercase = t
$result = strrev( $result );
}
- if( !$lowercase ) {
+ if ( !$lowercase ) {
$result = strtoupper( $result );
}
@@ -3309,9 +3280,9 @@ function wfCreateObject( $name, $p ) {
function wfHttpOnlySafe() {
global $wgHttpOnlyBlacklist;
- if( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
- foreach( $wgHttpOnlyBlacklist as $regex ) {
- if( preg_match( $regex, $_SERVER['HTTP_USER_AGENT'] ) ) {
+ if ( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ foreach ( $wgHttpOnlyBlacklist as $regex ) {
+ if ( preg_match( $regex, $_SERVER['HTTP_USER_AGENT'] ) ) {
return false;
}
}
@@ -3338,7 +3309,7 @@ function wfCheckEntropy() {
*/
function wfFixSessionID() {
// If the cookie or session id is already set we already have a session and should abort
- if ( isset( $_COOKIE[ session_name() ] ) || session_id() ) {
+ if ( isset( $_COOKIE[session_name()] ) || session_id() ) {
return;
}
@@ -3356,6 +3327,27 @@ function wfFixSessionID() {
}
/**
+ * Reset the session_id
+ * @since 1.22
+ */
+function wfResetSessionID() {
+ global $wgCookieSecure;
+ $oldSessionId = session_id();
+ $cookieParams = session_get_cookie_params();
+ if ( wfCheckEntropy() && $wgCookieSecure == $cookieParams['secure'] ) {
+ session_regenerate_id( false );
+ } else {
+ $tmp = $_SESSION;
+ session_destroy();
+ wfSetupSession( MWCryptRand::generateHex( 32 ) );
+ $_SESSION = $tmp;
+ }
+ $newSessionId = session_id();
+ wfRunHooks( 'ResetSessionID', array( $oldSessionId, $newSessionId ) );
+}
+
+
+/**
* Initialise php session
*
* @param $sessionId Bool
@@ -3363,9 +3355,9 @@ function wfFixSessionID() {
function wfSetupSession( $sessionId = false ) {
global $wgSessionsInMemcached, $wgSessionsInObjectCache, $wgCookiePath, $wgCookieDomain,
$wgCookieSecure, $wgCookieHttpOnly, $wgSessionHandler;
- if( $wgSessionsInObjectCache || $wgSessionsInMemcached ) {
+ if ( $wgSessionsInObjectCache || $wgSessionsInMemcached ) {
ObjectCacheSessionHandler::install();
- } elseif( $wgSessionHandler && $wgSessionHandler != ini_get( 'session.save_handler' ) ) {
+ } elseif ( $wgSessionHandler && $wgSessionHandler != ini_get( 'session.save_handler' ) ) {
# Only set this if $wgSessionHandler isn't null and session.save_handler
# hasn't already been set to the desired value (that causes errors)
ini_set( 'session.save_handler', $wgSessionHandler );
@@ -3440,7 +3432,7 @@ function wfForeignMemcKey( $db, $prefix /*, ... */ ) {
} else {
$key = $db . ':' . implode( ':', $args );
}
- return $key;
+ return str_replace( ' ', '_', $key );
}
/**
@@ -3588,7 +3580,7 @@ function wfScript( $script = 'index' ) {
global $wgScriptPath, $wgScriptExtension, $wgScript, $wgLoadScript;
if ( $script === 'index' ) {
return $wgScript;
- } else if ( $script === 'load' ) {
+ } elseif ( $script === 'load' ) {
return $wgLoadScript;
} else {
return "{$wgScriptPath}/{$script}{$wgScriptExtension}";
@@ -3601,7 +3593,7 @@ function wfScript( $script = 'index' ) {
* @return string script URL
*/
function wfGetScriptUrl() {
- if( isset( $_SERVER['SCRIPT_NAME'] ) ) {
+ if ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
#
# as it was called, minus the query string.
#
@@ -3649,15 +3641,22 @@ function wfGetNull() {
*
* @param $maxLag Integer (deprecated)
* @param $wiki mixed Wiki identifier accepted by wfGetLB
+ * @param $cluster string cluster name accepted by LBFactory
*/
-function wfWaitForSlaves( $maxLag = false, $wiki = false ) {
- $lb = wfGetLB( $wiki );
+function wfWaitForSlaves( $maxLag = false, $wiki = false, $cluster = false ) {
+ $lb = ( $cluster !== false )
+ ? wfGetLBFactory()->getExternalLB( $cluster )
+ : wfGetLB( $wiki );
// bug 27975 - Don't try to wait for slaves if there are none
// Prevents permission error when getting master position
if ( $lb->getServerCount() > 1 ) {
- $dbw = $lb->getConnection( DB_MASTER );
+ $dbw = $lb->getConnection( DB_MASTER, array(), $wiki );
$pos = $dbw->getMasterPos();
- $lb->waitForAll( $pos );
+ // The DBMS may not support getMasterPos() or the whole
+ // load balancer might be fake (e.g. $wgAllDBsAreLocalhost).
+ if ( $pos !== false ) {
+ $lb->waitForAll( $pos );
+ }
}
}
@@ -3739,9 +3738,9 @@ function wfStripIllegalFilenameChars( $name ) {
function wfMemoryLimit() {
global $wgMemoryLimit;
$memlimit = wfShorthandToInteger( ini_get( 'memory_limit' ) );
- if( $memlimit != -1 ) {
+ if ( $memlimit != -1 ) {
$conflimit = wfShorthandToInteger( $wgMemoryLimit );
- if( $conflimit == -1 ) {
+ if ( $conflimit == -1 ) {
wfDebug( "Removing PHP's memory limit\n" );
wfSuppressWarnings();
ini_set( 'memory_limit', $conflimit );
@@ -3766,12 +3765,12 @@ function wfMemoryLimit() {
*/
function wfShorthandToInteger( $string = '' ) {
$string = trim( $string );
- if( $string === '' ) {
+ if ( $string === '' ) {
return -1;
}
$last = $string[strlen( $string ) - 1];
$val = intval( $string );
- switch( $last ) {
+ switch ( $last ) {
case 'g':
case 'G':
$val *= 1024;
@@ -3799,22 +3798,17 @@ function wfBCP47( $code ) {
$codeSegment = explode( '-', $code );
$codeBCP = array();
foreach ( $codeSegment as $segNo => $seg ) {
- if ( count( $codeSegment ) > 0 ) {
- // when previous segment is x, it is a private segment and should be lc
- if( $segNo > 0 && strtolower( $codeSegment[( $segNo - 1 )] ) == 'x' ) {
- $codeBCP[$segNo] = strtolower( $seg );
- // ISO 3166 country code
- } elseif ( ( strlen( $seg ) == 2 ) && ( $segNo > 0 ) ) {
- $codeBCP[$segNo] = strtoupper( $seg );
- // ISO 15924 script code
- } elseif ( ( strlen( $seg ) == 4 ) && ( $segNo > 0 ) ) {
- $codeBCP[$segNo] = ucfirst( strtolower( $seg ) );
- // Use lowercase for other cases
- } else {
- $codeBCP[$segNo] = strtolower( $seg );
- }
+ // when previous segment is x, it is a private segment and should be lc
+ if ( $segNo > 0 && strtolower( $codeSegment[( $segNo - 1 )] ) == 'x' ) {
+ $codeBCP[$segNo] = strtolower( $seg );
+ // ISO 3166 country code
+ } elseif ( ( strlen( $seg ) == 2 ) && ( $segNo > 0 ) ) {
+ $codeBCP[$segNo] = strtoupper( $seg );
+ // ISO 15924 script code
+ } elseif ( ( strlen( $seg ) == 4 ) && ( $segNo > 0 ) ) {
+ $codeBCP[$segNo] = ucfirst( strtolower( $seg ) );
+ // Use lowercase for other cases
} else {
- // Use lowercase for single segment
$codeBCP[$segNo] = strtolower( $seg );
}
}
@@ -3879,7 +3873,7 @@ function wfGetLangConverterCacheStorage() {
* @param array $args parameters passed to hook functions
* @return Boolean True if no handler aborted the hook
*/
-function wfRunHooks( $event, $args = array() ) {
+function wfRunHooks( $event, array $args = array() ) {
return Hooks::run( $event, $args );
}
@@ -3897,7 +3891,7 @@ function wfRunHooks( $event, $args = array() ) {
* @throws MWException if $data not long enough, or if unpack fails
* @return array Associative array of the extracted data
*/
-function wfUnpack( $format, $data, $length=false ) {
+function wfUnpack( $format, $data, $length = false ) {
if ( $length !== false ) {
$realLen = strlen( $data );
if ( $realLen < $length ) {
@@ -3939,19 +3933,19 @@ function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
# Handle redirects
$redirectTitle = RepoGroup::singleton()->checkRedirect( Title::makeTitle( NS_FILE, $name ) );
- if( $redirectTitle ) {
- $name = $redirectTitle->getDbKey();
+ if ( $redirectTitle ) {
+ $name = $redirectTitle->getDBkey();
}
# Run the extension hook
$bad = false;
- if( !wfRunHooks( 'BadImage', array( $name, &$bad ) ) ) {
+ if ( !wfRunHooks( 'BadImage', array( $name, &$bad ) ) ) {
wfProfileOut( __METHOD__ );
return $bad;
}
$cacheable = ( $blacklist === null );
- if( $cacheable && $badImageCache !== null ) {
+ if ( $cacheable && $badImageCache !== null ) {
$badImages = $badImageCache;
} else { // cache miss
if ( $blacklist === null ) {
@@ -3960,7 +3954,7 @@ function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
# Build the list now
$badImages = array();
$lines = explode( "\n", $blacklist );
- foreach( $lines as $line ) {
+ foreach ( $lines as $line ) {
# List items only
if ( substr( $line, 0, 1 ) !== '*' ) {
continue;
@@ -3999,3 +3993,16 @@ function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
wfProfileOut( __METHOD__ );
return $bad;
}
+
+/**
+ * Determine whether the client at a given source IP is likely to be able to
+ * access the wiki via HTTPS.
+ *
+ * @param string $ip The IPv4/6 address in the normal human-readable form
+ * @return boolean
+ */
+function wfCanIPUseHTTPS( $ip ) {
+ $canDo = true;
+ wfRunHooks( 'CanIPUseHTTPS', array( $ip, &$canDo ) );
+ return !!$canDo;
+}
diff --git a/includes/HTMLForm.php b/includes/HTMLForm.php
index 68639739..d260862c 100644
--- a/includes/HTMLForm.php
+++ b/includes/HTMLForm.php
@@ -83,9 +83,8 @@
* $form = new HTMLForm( $someFields );
* $form->setMethod( 'get' )
* ->setWrapperLegendMsg( 'message-key' )
- * ->suppressReset()
* ->prepareForm()
- * ->displayForm();
+ * ->displayForm( '' );
* @endcode
* Note that you will have prepareForm and displayForm at the end. Other
* methods call done after that would simply not be part of the form :(
@@ -95,7 +94,7 @@
class HTMLForm extends ContextSource {
// A mapping of 'type' inputs onto standard HTMLFormField subclasses
- static $typeMappings = array(
+ public static $typeMappings = array(
'api' => 'HTMLApiField',
'text' => 'HTMLTextField',
'textarea' => 'HTMLTextAreaField',
@@ -128,6 +127,7 @@ class HTMLForm extends ContextSource {
protected $mFieldTree;
protected $mShowReset = false;
+ protected $mShowSubmit = true;
public $mFieldData;
protected $mSubmitCallback;
@@ -140,6 +140,7 @@ class HTMLForm extends ContextSource {
protected $mSectionFooters = array();
protected $mPost = '';
protected $mId;
+ protected $mTableId = '';
protected $mSubmitID;
protected $mSubmitName;
@@ -186,6 +187,7 @@ class HTMLForm extends ContextSource {
'table',
'div',
'raw',
+ 'vform',
);
/**
@@ -200,12 +202,12 @@ class HTMLForm extends ContextSource {
$this->setContext( $context );
$this->mTitle = false; // We don't need them to set a title
$this->mMessagePrefix = $messagePrefix;
- } else {
+ } elseif ( is_null( $context ) && $messagePrefix !== '' ) {
+ $this->mMessagePrefix = $messagePrefix;
+ } elseif ( is_string( $context ) && $messagePrefix === '' ) {
// B/C since 1.18
- if ( is_string( $context ) && $messagePrefix === '' ) {
- // it's actually $messagePrefix
- $this->mMessagePrefix = $context;
- }
+ // it's actually $messagePrefix
+ $this->mMessagePrefix = $context;
}
// Expand out into a tree.
@@ -222,8 +224,15 @@ class HTMLForm extends ContextSource {
}
$field = self::loadInputFromParameters( $fieldname, $info );
+ // FIXME During field's construct, the parent form isn't available!
+ // could add a 'parent' name-value to $info, could add a third parameter.
$field->mParent = $this;
+ // vform gets too much space if empty labels generate HTML.
+ if ( $this->isVForm() ) {
+ $field->setShowEmptyLabel( false );
+ }
+
$setSection =& $loadedDescriptor;
if ( $section ) {
$sectionParts = explode( '/', $section );
@@ -256,7 +265,7 @@ class HTMLForm extends ContextSource {
*/
public function setDisplayFormat( $format ) {
if ( !in_array( $format, $this->availableDisplayFormats ) ) {
- throw new MWException ( 'Display format must be one of ' . print_r( $this->availableDisplayFormats, true ) );
+ throw new MWException( 'Display format must be one of ' . print_r( $this->availableDisplayFormats, true ) );
}
$this->displayFormat = $format;
return $this;
@@ -272,11 +281,22 @@ class HTMLForm extends ContextSource {
}
/**
+ * Test if displayFormat is 'vform'
+ * @since 1.22
+ * @return Bool
+ */
+ public function isVForm() {
+ return $this->displayFormat === 'vform';
+ }
+
+ /**
* Add the HTMLForm-specific JavaScript, if it hasn't been
* done already.
* @deprecated since 1.18 load modules with ResourceLoader instead
*/
- static function addJS() { wfDeprecated( __METHOD__, '1.18' ); }
+ static function addJS() {
+ wfDeprecated( __METHOD__, '1.18' );
+ }
/**
* Initialise a new Object for the field
@@ -572,6 +592,21 @@ class HTMLForm extends ContextSource {
}
/**
+ * Add an array of hidden fields to the output
+ *
+ * @since 1.22
+ * @param array $fields Associative array of fields to add;
+ * mapping names to their values
+ * @return HTMLForm $this for chaining calls
+ */
+ public function addHiddenFields( array $fields ) {
+ foreach ( $fields as $name => $value ) {
+ $this->mHiddenFields[] = array( $value, array( 'name' => $name ) );
+ }
+ return $this;
+ }
+
+ /**
* Add a button to the form
* @param string $name field name.
* @param string $value field value
@@ -585,8 +620,8 @@ class HTMLForm extends ContextSource {
}
/**
- * Display the form (sending to $wgOut), with an appropriate error
- * message or stack of messages, and any validation errors, etc.
+ * Display the form (sending to the context's OutputPage object), with an
+ * appropriate error message or stack of messages, and any validation errors, etc.
*
* @attention You should call prepareForm() before calling this function.
* Moreover, when doing method chaining this should be the very last method
@@ -608,6 +643,11 @@ class HTMLForm extends ContextSource {
# For good measure (it is the default)
$this->getOutput()->preventClickjacking();
$this->getOutput()->addModules( 'mediawiki.htmlform' );
+ if ( $this->isVForm() ) {
+ $this->getOutput()->addModuleStyles( 'mediawiki.ui' );
+ // TODO should vertical form set setWrapperLegend( false )
+ // to hide ugly fieldsets?
+ }
$html = ''
. $this->getErrors( $submitResult )
@@ -640,15 +680,18 @@ class HTMLForm extends ContextSource {
: 'application/x-www-form-urlencoded';
# Attributes
$attribs = array(
- 'action' => $this->mAction === false ? $this->getTitle()->getFullURL() : $this->mAction,
- 'method' => $this->mMethod,
- 'class' => 'visualClear',
+ 'action' => $this->getAction(),
+ 'method' => $this->getMethod(),
+ 'class' => array( 'visualClear' ),
'enctype' => $encType,
);
if ( !empty( $this->mId ) ) {
$attribs['id'] = $this->mId;
}
+ if ( $this->isVForm() ) {
+ array_push( $attribs['class'], 'mw-ui-vform', 'mw-ui-container' );
+ }
return Html::rawElement( 'form', $attribs, $html );
}
@@ -682,24 +725,40 @@ class HTMLForm extends ContextSource {
* @return String HTML.
*/
function getButtons() {
- $html = '';
- $attribs = array();
+ $html = '<span class="mw-htmlform-submit-buttons">';
- if ( isset( $this->mSubmitID ) ) {
- $attribs['id'] = $this->mSubmitID;
- }
+ if ( $this->mShowSubmit ) {
+ $attribs = array();
- if ( isset( $this->mSubmitName ) ) {
- $attribs['name'] = $this->mSubmitName;
- }
+ if ( isset( $this->mSubmitID ) ) {
+ $attribs['id'] = $this->mSubmitID;
+ }
- if ( isset( $this->mSubmitTooltip ) ) {
- $attribs += Linker::tooltipAndAccesskeyAttribs( $this->mSubmitTooltip );
- }
+ if ( isset( $this->mSubmitName ) ) {
+ $attribs['name'] = $this->mSubmitName;
+ }
- $attribs['class'] = 'mw-htmlform-submit';
+ if ( isset( $this->mSubmitTooltip ) ) {
+ $attribs += Linker::tooltipAndAccesskeyAttribs( $this->mSubmitTooltip );
+ }
- $html .= Xml::submitButton( $this->getSubmitText(), $attribs ) . "\n";
+ $attribs['class'] = array( 'mw-htmlform-submit' );
+
+ if ( $this->isVForm() ) {
+ // mw-ui-block is necessary because the buttons aren't necessarily in an
+ // immediate child div of the vform.
+ array_push( $attribs['class'], 'mw-ui-button', 'mw-ui-big', 'mw-ui-primary', 'mw-ui-block' );
+ }
+
+ $html .= Xml::submitButton( $this->getSubmitText(), $attribs ) . "\n";
+
+ // Buttons are top-level form elements in table and div layouts,
+ // but vform wants all elements inside divs to get spaced-out block
+ // styling.
+ if ( $this->isVForm() ) {
+ $html = Html::rawElement( 'div', null, "\n$html\n" );
+ }
+ }
if ( $this->mShowReset ) {
$html .= Html::element(
@@ -729,6 +788,8 @@ class HTMLForm extends ContextSource {
$html .= Html::element( 'input', $attrs );
}
+ $html .= '</span>';
+
return $html;
}
@@ -737,7 +798,7 @@ class HTMLForm extends ContextSource {
* @return String
*/
function getBody() {
- return $this->displaySection( $this->mFieldTree );
+ return $this->displaySection( $this->mFieldTree, $this->mTableId );
}
/**
@@ -852,6 +913,33 @@ class HTMLForm extends ContextSource {
}
/**
+ * Stop a default submit button being shown for this form. This implies that an
+ * alternate submit method must be provided manually.
+ *
+ * @since 1.22
+ *
+ * @param bool $suppressSubmit Set to false to re-enable the button again
+ *
+ * @return HTMLForm $this for chaining calls
+ */
+ function suppressDefaultSubmit( $suppressSubmit = true ) {
+ $this->mShowSubmit = !$suppressSubmit;
+ return $this;
+ }
+
+ /**
+ * Set the id of the \<table\> or outermost \<div\> element.
+ *
+ * @since 1.22
+ * @param string $id new value of the id attribute, or "" to remove
+ * @return HTMLForm $this for chaining calls
+ */
+ public function setTableId( $id ) {
+ $this->mTableId = $id;
+ return $this;
+ }
+
+ /**
* @param string $id DOM id for the form
* @return HTMLForm $this for chaining calls (since 1.20)
*/
@@ -859,10 +947,12 @@ class HTMLForm extends ContextSource {
$this->mId = $id;
return $this;
}
+
/**
* Prompt the whole form to be wrapped in a "<fieldset>", with
* this text as its "<legend>" element.
- * @param string $legend HTML to go inside the "<legend>" element.
+ * @param string|false $legend HTML to go inside the "<legend>" element, or
+ * false for no <legend>
* Will be escaped
* @return HTMLForm $this for chaining calls (since 1.20)
*/
@@ -931,19 +1021,30 @@ class HTMLForm extends ContextSource {
/**
* @todo Document
- * @param $fields array[]|HTMLFormField[] array of fields (either arrays or objects)
+ * @param array[]|HTMLFormField[] $fields array of fields (either arrays or objects)
* @param string $sectionName ID attribute of the "<table>" tag for this section, ignored if empty
* @param string $fieldsetIDPrefix ID prefix for the "<fieldset>" tag of each subsection, ignored if empty
+ * @param boolean &$hasUserVisibleFields Whether the section had user-visible fields
* @return String
*/
- public function displaySection( $fields, $sectionName = '', $fieldsetIDPrefix = '' ) {
+ public function displaySection( $fields, $sectionName = '', $fieldsetIDPrefix = '', &$hasUserVisibleFields = false ) {
$displayFormat = $this->getDisplayFormat();
$html = '';
$subsectionHtml = '';
$hasLabel = false;
- $getFieldHtmlMethod = ( $displayFormat == 'table' ) ? 'getTableRow' : 'get' . ucfirst( $displayFormat );
+ switch( $displayFormat ) {
+ case 'table':
+ $getFieldHtmlMethod = 'getTableRow';
+ break;
+ case 'vform':
+ // Close enough to a div.
+ $getFieldHtmlMethod = 'getDiv';
+ break;
+ default:
+ $getFieldHtmlMethod = 'get' . ucfirst( $displayFormat );
+ }
foreach ( $fields as $key => $value ) {
if ( $value instanceof HTMLFormField ) {
@@ -956,20 +1057,38 @@ class HTMLForm extends ContextSource {
if ( $labelValue != '&#160;' && $labelValue !== '' ) {
$hasLabel = true;
}
- } elseif ( is_array( $value ) ) {
- $section = $this->displaySection( $value, $key, "$fieldsetIDPrefix$key-" );
- $legend = $this->getLegend( $key );
- if ( isset( $this->mSectionHeaders[$key] ) ) {
- $section = $this->mSectionHeaders[$key] . $section;
- }
- if ( isset( $this->mSectionFooters[$key] ) ) {
- $section .= $this->mSectionFooters[$key];
+
+ if ( get_class( $value ) !== 'HTMLHiddenField' &&
+ get_class( $value ) !== 'HTMLApiField' ) {
+ $hasUserVisibleFields = true;
}
- $attributes = array();
- if ( $fieldsetIDPrefix ) {
- $attributes['id'] = Sanitizer::escapeId( "$fieldsetIDPrefix$key" );
+ } elseif ( is_array( $value ) ) {
+ $subsectionHasVisibleFields = false;
+ $section = $this->displaySection( $value, "mw-htmlform-$key", "$fieldsetIDPrefix$key-", $subsectionHasVisibleFields );
+ $legend = null;
+
+ if ( $subsectionHasVisibleFields === true ) {
+ // Display the section with various niceties.
+ $hasUserVisibleFields = true;
+
+ $legend = $this->getLegend( $key );
+
+ if ( isset( $this->mSectionHeaders[$key] ) ) {
+ $section = $this->mSectionHeaders[$key] . $section;
+ }
+ if ( isset( $this->mSectionFooters[$key] ) ) {
+ $section .= $this->mSectionFooters[$key];
+ }
+
+ $attributes = array();
+ if ( $fieldsetIDPrefix ) {
+ $attributes['id'] = Sanitizer::escapeId( "$fieldsetIDPrefix$key" );
+ }
+ $subsectionHtml .= Xml::fieldset( $legend, $section, $attributes ) . "\n";
+ } else {
+ // Just return the inputs, nothing fancy.
+ $subsectionHtml .= $section;
}
- $subsectionHtml .= Xml::fieldset( $legend, $section, $attributes ) . "\n";
}
}
@@ -985,13 +1104,13 @@ class HTMLForm extends ContextSource {
);
if ( $sectionName ) {
- $attribs['id'] = Sanitizer::escapeId( "mw-htmlform-$sectionName" );
+ $attribs['id'] = Sanitizer::escapeId( $sectionName );
}
if ( $displayFormat === 'table' ) {
$html = Html::rawElement( 'table', $attribs,
Html::rawElement( 'tbody', array(), "\n$html\n" ) ) . "\n";
- } elseif ( $displayFormat === 'div' ) {
+ } elseif ( $displayFormat === 'div' || $displayFormat === 'vform' ) {
$html = Html::rawElement( 'div', $attribs, "\n$html\n" );
}
}
@@ -1074,6 +1193,32 @@ class HTMLForm extends ContextSource {
return $this;
}
+ /**
+ * Get the value for the action attribute of the form.
+ *
+ * @since 1.22
+ *
+ * @return string
+ */
+ public function getAction() {
+ global $wgScript, $wgArticlePath;
+
+ // If an action is alredy provided, return it
+ if ( $this->mAction !== false ) {
+ return $this->mAction;
+ }
+
+ // Check whether we are in GET mode and $wgArticlePath contains a "?"
+ // meaning that getLocalURL() would return something like "index.php?title=...".
+ // As browser remove the query string before submitting GET forms,
+ // it means that the title would be lost. In such case use $wgScript instead
+ // and put title in an hidden field (see getHiddenFields()).
+ if ( strpos( $wgArticlePath, '?' ) !== false && $this->getMethod() === 'get' ) {
+ return $wgScript;
+ }
+
+ return $this->getTitle()->getLocalURL();
+ }
}
/**
@@ -1092,6 +1237,12 @@ abstract class HTMLFormField {
protected $mDefault;
/**
+ * @var bool If true will generate an empty div element with no label
+ * @since 1.22
+ */
+ protected $mShowEmptyLabels = true;
+
+ /**
* @var HTMLForm
*/
public $mParent;
@@ -1167,6 +1318,18 @@ abstract class HTMLFormField {
}
/**
+ * Tell the field whether to generate a separate label element if its label
+ * is blank.
+ *
+ * @since 1.22
+ * @param bool $show Set to false to not generate a label.
+ * @return void
+ */
+ public function setShowEmptyLabel( $show ) {
+ $this->mShowEmptyLabels = $show;
+ }
+
+ /**
* Get the value that this input has been set to from a posted form,
* or the input's default value if it has not been set.
* @param $request WebRequest
@@ -1183,6 +1346,8 @@ abstract class HTMLFormField {
/**
* Initialise the object
* @param array $params Associative Array. See HTMLForm doc for syntax.
+ *
+ * @since 1.22 The 'label' attribute no longer accepts raw HTML, use 'label-raw' instead
* @throws MWException
*/
function __construct( $params ) {
@@ -1201,7 +1366,14 @@ abstract class HTMLFormField {
$this->mLabel = wfMessage( $msg, $msgInfo )->parse();
} elseif ( isset( $params['label'] ) ) {
- $this->mLabel = $params['label'];
+ if ( $params['label'] === '&#160;' ) {
+ // Apparently some things set &nbsp directly and in an odd format
+ $this->mLabel = '&#160;';
+ } else {
+ $this->mLabel = htmlspecialchars( $params['label'] );
+ }
+ } elseif ( isset( $params['label-raw'] ) ) {
+ $this->mLabel = $params['label-raw'];
}
$this->mName = "wp{$params['fieldname']}";
@@ -1246,6 +1418,10 @@ abstract class HTMLFormField {
if ( isset( $params['flatlist'] ) ) {
$this->mClass .= ' mw-htmlform-flatlist';
}
+
+ if ( isset( $params['hidelabel'] ) ) {
+ $this->mShowEmptyLabels = false;
+ }
}
/**
@@ -1306,13 +1482,22 @@ abstract class HTMLFormField {
$cellAttributes = array();
$label = $this->getLabelHtml( $cellAttributes );
+ $outerDivClass = array(
+ 'mw-input',
+ 'mw-htmlform-nolabel' => ( $label === '' )
+ );
+
$field = Html::rawElement(
'div',
- array( 'class' => 'mw-input' ) + $cellAttributes,
+ array( 'class' => $outerDivClass ) + $cellAttributes,
$inputHtml . "\n$errors"
);
+ $divCssClasses = array( "mw-htmlform-field-$fieldType", $this->mClass, $errorClass );
+ if ( $this->mParent->isVForm() ) {
+ $divCssClasses[] = 'mw-ui-vform-div';
+ }
$html = Html::rawElement( 'div',
- array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass" ),
+ array( 'class' => $divCssClasses ),
$label . $field );
$html .= $helptext;
return $html;
@@ -1437,7 +1622,7 @@ abstract class HTMLFormField {
}
function getLabel() {
- return $this->mLabel;
+ return is_null( $this->mLabel ) ? '' : $this->mLabel;
}
function getLabelHtml( $cellAttributes = array() ) {
@@ -1449,20 +1634,32 @@ abstract class HTMLFormField {
$for['for'] = $this->mID;
}
+ $labelValue = trim( $this->getLabel() );
+ $hasLabel = false;
+ if ( $labelValue !== '&#160;' && $labelValue !== '' ) {
+ $hasLabel = true;
+ }
+
$displayFormat = $this->mParent->getDisplayFormat();
- $labelElement = Html::rawElement( 'label', $for, $this->getLabel() );
+ $html = '';
- if ( $displayFormat == 'table' ) {
- return Html::rawElement( 'td', array( 'class' => 'mw-label' ) + $cellAttributes,
- Html::rawElement( 'label', $for, $this->getLabel() )
+ if ( $displayFormat === 'table' ) {
+ $html = Html::rawElement( 'td', array( 'class' => 'mw-label' ) + $cellAttributes,
+ Html::rawElement( 'label', $for, $labelValue )
);
- } elseif ( $displayFormat == 'div' ) {
- return Html::rawElement( 'div', array( 'class' => 'mw-label' ) + $cellAttributes,
- Html::rawElement( 'label', $for, $this->getLabel() )
- );
- } else {
- return $labelElement;
+ } elseif ( $hasLabel || $this->mShowEmptyLabels ) {
+ if ( $displayFormat === 'div' ) {
+ $html = Html::rawElement(
+ 'div',
+ array( 'class' => 'mw-label' ) + $cellAttributes,
+ Html::rawElement( 'label', $for, $labelValue )
+ );
+ } else {
+ $html = Html::rawElement( 'label', $for, $labelValue );
+ }
}
+
+ return $html;
}
function getDefault() {
@@ -1602,16 +1799,19 @@ class HTMLTextField extends HTMLFormField {
}
}
class HTMLTextAreaField extends HTMLFormField {
+ const DEFAULT_COLS = 80;
+ const DEFAULT_ROWS = 25;
+
function getCols() {
return isset( $this->mParams['cols'] )
? $this->mParams['cols']
- : 80;
+ : static::DEFAULT_COLS;
}
function getRows() {
return isset( $this->mParams['rows'] )
? $this->mParams['rows']
- : 25;
+ : static::DEFAULT_ROWS;
}
function getInputHTML( $value ) {
@@ -1741,8 +1941,25 @@ class HTMLCheckField extends HTMLFormField {
$attr['class'] = $this->mClass;
}
- return Xml::check( $this->mName, $value, $attr ) . '&#160;' .
- Html::rawElement( 'label', array( 'for' => $this->mID ), $this->mLabel );
+ if ( $this->mParent->isVForm() ) {
+ // Nest checkbox inside label.
+ return Html::rawElement(
+ 'label',
+ array(
+ 'class' => 'mw-ui-checkbox-label'
+ ),
+ Xml::check(
+ $this->mName,
+ $value,
+ $attr
+ ) .
+ // Html:rawElement doesn't escape contents.
+ htmlspecialchars( $this->mLabel )
+ );
+ } else {
+ return Xml::check( $this->mName, $value, $attr ) . '&#160;' .
+ Html::rawElement( 'label', array( 'for' => $this->mID ), $this->mLabel );
+ }
}
/**
@@ -1755,6 +1972,13 @@ class HTMLCheckField extends HTMLFormField {
}
/**
+ * checkboxes don't need a label.
+ */
+ protected function needsLabel() {
+ return false;
+ }
+
+ /**
* @param $request WebRequest
* @return String
*/
@@ -1786,9 +2010,39 @@ class HTMLCheckField extends HTMLFormField {
* A checkbox matrix
* Operates similarly to HTMLMultiSelectField, but instead of using an array of
* options, uses an array of rows and an array of columns to dynamically
- * construct a matrix of options.
+ * construct a matrix of options. The tags used to identify a particular cell
+ * are of the form "columnName-rowName"
+ *
+ * Options:
+ * - columns
+ * - Required list of columns in the matrix.
+ * - rows
+ * - Required list of rows in the matrix.
+ * - force-options-on
+ * - Accepts array of column-row tags to be displayed as enabled but unavailable to change
+ * - force-options-off
+ * - Accepts array of column-row tags to be displayed as disabled but unavailable to change.
+ * - tooltips
+ * - Optional array mapping row label to tooltip content
+ * - tooltip-class
+ * - Optional CSS class used on tooltip container span. Defaults to mw-icon-question.
*/
-class HTMLCheckMatrix extends HTMLFormField {
+class HTMLCheckMatrix extends HTMLFormField implements HTMLNestedFilterable {
+
+ static private $requiredParams = array(
+ // Required by underlying HTMLFormField
+ 'fieldname',
+ // Required by HTMLCheckMatrix
+ 'rows', 'columns'
+ );
+
+ public function __construct( $params ) {
+ $missing = array_diff( self::$requiredParams, array_keys( $params ) );
+ if ( $missing ) {
+ throw new HTMLFormFieldRequiredOptionsException( $this, $missing );
+ }
+ parent::__construct( $params );
+ }
function validate( $value, $alldata ) {
$rows = $this->mParams['rows'];
@@ -1848,27 +2102,42 @@ class HTMLCheckMatrix extends HTMLFormField {
}
$tableContents .= Html::rawElement( 'tr', array(), "\n$headerContents\n" );
+ $tooltipClass = 'mw-icon-question';
+ if ( isset( $this->mParams['tooltip-class'] ) ) {
+ $tooltipClass = $this->mParams['tooltip-class'];
+ }
+
// Build the options matrix
foreach ( $rows as $rowLabel => $rowTag ) {
+ // Append tooltip if configured
+ if ( isset( $this->mParams['tooltips'][$rowLabel] ) ) {
+ $tooltipAttribs = array(
+ 'class' => "mw-htmlform-tooltip $tooltipClass",
+ 'title' => $this->mParams['tooltips'][$rowLabel],
+ );
+ $rowLabel .= ' ' . Html::element( 'span', $tooltipAttribs, '' );
+ }
$rowContents = Html::rawElement( 'td', array(), $rowLabel );
foreach ( $columns as $columnTag ) {
- // Knock out any options that are not wanted
- if ( isset( $this->mParams['remove-options'] )
- && in_array( "$columnTag-$rowTag", $this->mParams['remove-options'] ) )
- {
- $rowContents .= Html::rawElement( 'td', array(), '&#160;' );
- } else {
- // Construct the checkbox
- $thisAttribs = array(
- 'id' => "{$this->mID}-$columnTag-$rowTag",
- 'value' => $columnTag . '-' . $rowTag
- );
- $checkbox = Xml::check(
- $this->mName . '[]',
- in_array( $columnTag . '-' . $rowTag, (array)$value, true ),
- $attribs + $thisAttribs );
- $rowContents .= Html::rawElement( 'td', array(), $checkbox );
+ $thisTag = "$columnTag-$rowTag";
+ // Construct the checkbox
+ $thisAttribs = array(
+ 'id' => "{$this->mID}-$thisTag",
+ 'value' => $thisTag,
+ );
+ $checked = in_array( $thisTag, (array)$value, true );
+ if ( $this->isTagForcedOff( $thisTag ) ) {
+ $checked = false;
+ $thisAttribs['disabled'] = 1;
+ } elseif ( $this->isTagForcedOn( $thisTag ) ) {
+ $checked = true;
+ $thisAttribs['disabled'] = 1;
}
+ $rowContents .= Html::rawElement(
+ 'td',
+ array(),
+ Xml::check( "{$this->mName}[]", $checked, $attribs + $thisAttribs )
+ );
}
$tableContents .= Html::rawElement( 'tr', array(), "\n$rowContents\n" );
}
@@ -1880,6 +2149,16 @@ class HTMLCheckMatrix extends HTMLFormField {
return $html;
}
+ protected function isTagForcedOff( $tag ) {
+ return isset( $this->mParams['force-options-off'] )
+ && in_array( $tag, $this->mParams['force-options-off'] );
+ }
+
+ protected function isTagForcedOn( $tag ) {
+ return isset( $this->mParams['force-options-on'] )
+ && in_array( $tag, $this->mParams['force-options-on'] );
+ }
+
/**
* Get the complete table row for the input, including help text,
* labels, and whatever.
@@ -1944,6 +2223,27 @@ class HTMLCheckMatrix extends HTMLFormField {
return array();
}
}
+
+ function filterDataForSubmit( $data ) {
+ $columns = HTMLFormField::flattenOptions( $this->mParams['columns'] );
+ $rows = HTMLFormField::flattenOptions( $this->mParams['rows'] );
+ $res = array();
+ foreach ( $columns as $column ) {
+ foreach ( $rows as $row ) {
+ // Make sure option hasn't been forced
+ $thisTag = "$column-$row";
+ if ( $this->isTagForcedOff( $thisTag ) ) {
+ $res[$thisTag] = false;
+ } elseif ( $this->isTagForcedOn( $thisTag ) ) {
+ $res[$thisTag] = true;
+ } else {
+ $res[$thisTag] = in_array( $thisTag, $data );
+ }
+ }
+ }
+
+ return $res;
+ }
}
/**
@@ -1959,10 +2259,11 @@ class HTMLSelectField extends HTMLFormField {
$validOptions = HTMLFormField::flattenOptions( $this->mParams['options'] );
- if ( in_array( $value, $validOptions ) )
+ if ( in_array( $value, $validOptions ) ) {
return true;
- else
+ } else {
return $this->msg( 'htmlform-select-badoption' )->parse();
+ }
}
function getInputHTML( $value ) {
@@ -1996,7 +2297,6 @@ class HTMLSelectField extends HTMLFormField {
* Select dropdown field, with an additional "other" textbox.
*/
class HTMLSelectOrOtherField extends HTMLTextField {
- static $jsAdded = false;
function __construct( $params ) {
if ( !in_array( 'other', $params['options'], true ) ) {
@@ -2085,7 +2385,7 @@ class HTMLSelectOrOtherField extends HTMLTextField {
/**
* Multi-select field
*/
-class HTMLMultiSelectField extends HTMLFormField {
+class HTMLMultiSelectField extends HTMLFormField implements HTMLNestedFilterable {
function validate( $value, $alldata ) {
$p = parent::validate( $value, $alldata );
@@ -2178,6 +2478,17 @@ class HTMLMultiSelectField extends HTMLFormField {
}
}
+ function filterDataForSubmit( $data ) {
+ $options = HTMLFormField::flattenOptions( $this->mParams['options'] );
+
+ $res = array();
+ foreach ( $options as $opt ) {
+ $res["$opt"] = in_array( $opt, $data );
+ }
+
+ return $res;
+ }
+
protected function needsLabel() {
return false;
}
@@ -2512,14 +2823,28 @@ class HTMLHiddenField extends HTMLFormField {
return $this->getTableRow( $value );
}
- public function getInputHTML( $value ) { return ''; }
+ public function getInputHTML( $value ) {
+ return '';
+ }
}
/**
* Add a submit button inline in the form (as opposed to
* HTMLForm::addButton(), which will add it at the end).
*/
-class HTMLSubmitField extends HTMLFormField {
+class HTMLSubmitField extends HTMLButtonField {
+ protected $buttonType = 'submit';
+}
+
+/**
+ * Adds a generic button inline to the form. Does not do anything, you must add
+ * click handling code in JavaScript. Use a HTMLSubmitField if you merely
+ * wish to add a submit button to a form.
+ *
+ * @since 1.22
+ */
+class HTMLButtonField extends HTMLFormField {
+ protected $buttonType = 'button';
public function __construct( $info ) {
$info['nodata'] = true;
@@ -2527,13 +2852,20 @@ class HTMLSubmitField extends HTMLFormField {
}
public function getInputHTML( $value ) {
- return Xml::submitButton(
+ $attr = array(
+ 'class' => 'mw-htmlform-submit ' . $this->mClass,
+ 'id' => $this->mID,
+ );
+
+ if ( !empty( $this->mParams['disabled'] ) ) {
+ $attr['disabled'] = 'disabled';
+ }
+
+ return Html::input(
+ $this->mName,
$value,
- array(
- 'class' => 'mw-htmlform-submit ' . $this->mClass,
- 'name' => $this->mName,
- 'id' => $this->mID,
- )
+ $this->buttonType,
+ $attr
);
}
@@ -2612,3 +2944,22 @@ class HTMLApiField extends HTMLFormField {
return '';
}
}
+
+interface HTMLNestedFilterable {
+ /**
+ * Support for seperating multi-option preferences into multiple preferences
+ * Due to lack of array support.
+ * @param $data array
+ */
+ function filterDataForSubmit( $data );
+}
+
+class HTMLFormFieldRequiredOptionsException extends MWException {
+ public function __construct( HTMLFormField $field, array $missing ) {
+ parent::__construct( sprintf(
+ "Form type `%s` expected the following parameters to be set: %s",
+ get_class( $field ),
+ implode( ', ', $missing )
+ ) );
+ }
+}
diff --git a/includes/HashRing.php b/includes/HashRing.php
new file mode 100644
index 00000000..930f8c0a
--- /dev/null
+++ b/includes/HashRing.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * Convenience class for weighted consistent hash rings.
+ *
+ * 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
+ * @author Aaron Schulz
+ */
+
+/**
+ * Convenience class for weighted consistent hash rings
+ *
+ * @since 1.22
+ */
+class HashRing {
+ /** @var Array (location => weight) */
+ protected $sourceMap = array();
+ /** @var Array (location => (start, end)) */
+ protected $ring = array();
+
+ const RING_SIZE = 268435456; // 2^28
+
+ /**
+ * @param array $map (location => weight)
+ */
+ public function __construct( array $map ) {
+ $map = array_filter( $map, function( $w ) { return $w > 0; } );
+ if ( !count( $map ) ) {
+ throw new MWException( "Ring is empty or all weights are zero." );
+ }
+ $this->sourceMap = $map;
+ // Sort the locations based on the hash of their names
+ $hashes = array();
+ foreach ( $map as $location => $weight ) {
+ $hashes[$location] = sha1( $location );
+ }
+ uksort( $map, function ( $a, $b ) use ( $hashes ) {
+ return strcmp( $hashes[$a], $hashes[$b] );
+ } );
+ // Fit the map to weight-proportionate one with a space of size RING_SIZE
+ $sum = array_sum( $map );
+ $standardMap = array();
+ foreach ( $map as $location => $weight ) {
+ $standardMap[$location] = (int)floor( $weight / $sum * self::RING_SIZE );
+ }
+ // Build a ring of RING_SIZE spots, with each location at a spot in location hash order
+ $index = 0;
+ foreach ( $standardMap as $location => $weight ) {
+ // Location covers half-closed interval [$index,$index + $weight)
+ $this->ring[$location] = array( $index, $index + $weight );
+ $index += $weight;
+ }
+ // Make sure the last location covers what is left
+ end( $this->ring );
+ $this->ring[key( $this->ring )][1] = self::RING_SIZE;
+ }
+
+ /**
+ * Get the location of an item on the ring
+ *
+ * @param string $item
+ * @return string Location
+ */
+ public function getLocation( $item ) {
+ $locations = $this->getLocations( $item, 1 );
+ return $locations[0];
+ }
+
+ /**
+ * Get the location of an item on the ring, as well as the next clockwise locations
+ *
+ * @param string $item
+ * @param integer $limit Maximum number of locations to return
+ * @return array List of locations
+ */
+ public function getLocations( $item, $limit ) {
+ $locations = array();
+ $primaryLocation = null;
+ $spot = hexdec( substr( sha1( $item ), 0, 7 ) ); // first 28 bits
+ foreach ( $this->ring as $location => $range ) {
+ if ( count( $locations ) >= $limit ) {
+ break;
+ }
+ // The $primaryLocation is the location the item spot is in.
+ // After that is reached, keep appending the next locations.
+ if ( ( $range[0] <= $spot && $spot < $range[1] ) || $primaryLocation !== null ) {
+ if ( $primaryLocation === null ) {
+ $primaryLocation = $location;
+ }
+ $locations[] = $location;
+ }
+ }
+ // If more locations are requested, wrap-around and keep adding them
+ reset( $this->ring );
+ while ( count( $locations ) < $limit ) {
+ list( $location, ) = each( $this->ring );
+ if ( $location === $primaryLocation ) {
+ break; // don't go in circles
+ }
+ $locations[] = $location;
+ }
+ return $locations;
+ }
+
+ /**
+ * Get the map of locations to weight (ignores 0-weight items)
+ *
+ * @return array
+ */
+ public function getLocationWeights() {
+ return $this->sourceMap;
+ }
+
+ /**
+ * Get a new hash ring with a location removed from the ring
+ *
+ * @param string $location
+ * @return HashRing|bool Returns false if no non-zero weighted spots are left
+ */
+ public function newWithoutLocation( $location ) {
+ $map = $this->sourceMap;
+ unset( $map[$location] );
+ if ( count( $map ) ) {
+ return new self( $map );
+ }
+ return false;
+ }
+}
diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php
index 1af733a2..31aa0f87 100644
--- a/includes/HistoryBlob.php
+++ b/includes/HistoryBlob.php
@@ -143,7 +143,7 @@ class ConcatenatedGzipHistoryBlob implements HistoryBlob
* Compress the bulk data in the object
*/
public function compress() {
- if ( !$this->mCompressed ) {
+ if ( !$this->mCompressed ) {
$this->mItems = gzdeflate( serialize( $this->mItems ) );
$this->mCompressed = true;
}
@@ -231,16 +231,16 @@ class HistoryBlobStub {
* @return string
*/
function getText() {
- if( isset( self::$blobCache[$this->mOldId] ) ) {
+ if ( isset( self::$blobCache[$this->mOldId] ) ) {
$obj = self::$blobCache[$this->mOldId];
} else {
$dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow( 'text', array( 'old_flags', 'old_text' ), array( 'old_id' => $this->mOldId ) );
- if( !$row ) {
+ if ( !$row ) {
return false;
}
$flags = explode( ',', $row->old_flags );
- if( in_array( 'external', $flags ) ) {
+ if ( in_array( 'external', $flags ) ) {
$url = $row->old_text;
$parts = explode( '://', $url, 2 );
if ( !isset( $parts[1] ) || $parts[1] == '' ) {
@@ -249,11 +249,11 @@ class HistoryBlobStub {
$row->old_text = ExternalStore::fetchFromUrl( $url );
}
- if( !in_array( 'object', $flags ) ) {
+ if ( !in_array( 'object', $flags ) ) {
return false;
}
- if( in_array( 'gzip', $flags ) ) {
+ if ( in_array( 'gzip', $flags ) ) {
// This shouldn't happen, but a bug in the compress script
// may at times gzip-compress a HistoryBlob object row.
$obj = unserialize( gzinflate( $row->old_text ) );
@@ -261,7 +261,7 @@ class HistoryBlobStub {
$obj = unserialize( $row->old_text );
}
- if( !is_object( $obj ) ) {
+ if ( !is_object( $obj ) ) {
// Correct for old double-serialization bug.
$obj = unserialize( $obj );
}
@@ -318,7 +318,7 @@ class HistoryBlobCurStub {
function getText() {
$dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow( 'cur', array( 'cur_text' ), array( 'cur_id' => $this->mCurId ) );
- if( !$row ) {
+ if ( !$row ) {
return false;
}
return $row->cur_text;
@@ -530,7 +530,7 @@ class DiffHistoryBlob implements HistoryBlob {
$header = unpack( 'Vofp/Vcsize', substr( $diff, 0, 8 ) );
- # Check the checksum if hash/mhash is available
+ # Check the checksum if hash extension is available
$ofp = $this->xdiffAdler32( $base );
if ( $ofp !== false && $ofp !== substr( $diff, 0, 4 ) ) {
wfDebug( __METHOD__ . ": incorrect base checksum\n" );
@@ -577,24 +577,23 @@ class DiffHistoryBlob implements HistoryBlob {
* Compute a binary "Adler-32" checksum as defined by LibXDiff, i.e. with
* the bytes backwards and initialised with 0 instead of 1. See bug 34428.
*
- * Returns false if no hashing library is available
+ * @param string $s
+ * @return string|bool: false if the hash extension is not available
*/
function xdiffAdler32( $s ) {
+ if ( !function_exists( 'hash' ) ) {
+ return false;
+ }
+
static $init;
if ( $init === null ) {
$init = str_repeat( "\xf0", 205 ) . "\xee" . str_repeat( "\xf0", 67 ) . "\x02";
}
+
// The real Adler-32 checksum of $init is zero, so it initialises the
// state to zero, as it is at the start of LibXDiff's checksum
// algorithm. Appending the subject string then simulates LibXDiff.
- if ( function_exists( 'hash' ) ) {
- $hash = hash( 'adler32', $init . $s, true );
- } elseif ( function_exists( 'mhash' ) ) {
- $hash = mhash( MHASH_ADLER32, $init . $s );
- } else {
- return false;
- }
- return strrev( $hash );
+ return strrev( hash( 'adler32', $init . $s, true ) );
}
function uncompress() {
diff --git a/includes/Hooks.php b/includes/Hooks.php
index 8cc7acea..396e360d 100644
--- a/includes/Hooks.php
+++ b/includes/Hooks.php
@@ -1,4 +1,5 @@
<?php
+
/**
* A tool for running hook functions.
*
@@ -37,40 +38,43 @@ class MWHookException extends MWException {}
*/
class Hooks {
+ /**
+ * Array of events mapped to an array of callbacks to be run
+ * when that event is triggered.
+ */
protected static $handlers = array();
/**
- * Clears hooks registered via Hooks::register(). Does not touch $wgHooks.
- * This is intended for use while testing and will fail if MW_PHPUNIT_TEST is not defined.
+ * Attach an event handler to a given hook.
*
- * @since 1.21
- *
- * @param string $name the name of the hook to clear.
+ * @param string $name Name of hook
+ * @param mixed $callback Callback function to attach
*
- * @throws MWException if not in testing mode.
+ * @since 1.18
*/
- public static function clear( $name ) {
- if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
- throw new MWException( 'can not reset hooks in operation.' );
+ public static function register( $name, $callback ) {
+ if ( !isset( self::$handlers[$name] ) ) {
+ self::$handlers[$name] = array();
}
- unset( self::$handlers[$name] );
+ self::$handlers[$name][] = $callback;
}
/**
- * Attach an event handler to a given hook
+ * Clears hooks registered via Hooks::register(). Does not touch $wgHooks.
+ * This is intended for use while testing and will fail if MW_PHPUNIT_TEST is not defined.
*
- * @since 1.18
+ * @param string $name the name of the hook to clear.
*
- * @param string $name name of hook
- * @param $callback Mixed: callback function to attach
+ * @since 1.21
+ * @throws MWException if not in testing mode.
*/
- public static function register( $name, $callback ) {
- if( !isset( self::$handlers[$name] ) ) {
- self::$handlers[$name] = array();
+ public static function clear( $name ) {
+ if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
+ throw new MWException( 'Cannot reset hooks in operation.' );
}
- self::$handlers[$name][] = $callback;
+ unset( self::$handlers[$name] );
}
/**
@@ -79,216 +83,139 @@ class Hooks {
*
* @since 1.18
*
- * @param string $name name of hook
- * @return Boolean: true if the hook has a function registered to it
+ * @param string $name Name of hook
+ * @return bool True if the hook has a function registered to it
*/
public static function isRegistered( $name ) {
global $wgHooks;
-
return !empty( $wgHooks[$name] ) || !empty( self::$handlers[$name] );
}
/**
* Returns an array of all the event functions attached to a hook
* This combines functions registered via Hooks::register and with $wgHooks.
- * @since 1.18
*
- * @throws MWException
- * @throws FatalError
- * @param string $name name of the hook
+ * @since 1.18
*
+ * @param string $name Name of the hook
* @return array
*/
public static function getHandlers( $name ) {
global $wgHooks;
- // Return quickly in the most common case
- if ( empty( self::$handlers[$name] ) && empty( $wgHooks[$name] ) ) {
+ if ( !self::isRegistered( $name ) ) {
return array();
- }
-
- if ( !is_array( self::$handlers ) ) {
- throw new MWException( "Local hooks array is not an array!\n" );
- }
-
- if ( !is_array( $wgHooks ) ) {
- throw new MWException( "Global hooks array is not an array!\n" );
- }
-
- if ( empty( Hooks::$handlers[$name] ) ) {
- $hooks = $wgHooks[$name];
- } elseif ( empty( $wgHooks[$name] ) ) {
- $hooks = Hooks::$handlers[$name];
+ } elseif ( !isset( self::$handlers[$name] ) ) {
+ return $wgHooks[$name];
+ } elseif ( !isset( $wgHooks[$name] ) ) {
+ return self::$handlers[$name];
} else {
- // so they are both not empty...
- $hooks = array_merge( Hooks::$handlers[$name], $wgHooks[$name] );
+ return array_merge( self::$handlers[$name], $wgHooks[$name] );
}
-
- if ( !is_array( $hooks ) ) {
- throw new MWException( "Hooks array for event '$name' is not an array!\n" );
- }
-
- return $hooks;
}
/**
- * Call hook functions defined in Hooks::register
+ * Call hook functions defined in Hooks::register and $wgHooks.
*
- * @param string $event event name
- * @param $args Array: parameters passed to hook functions
+ * For a certain hook event, fetch the array of hook events and
+ * process them. Determine the proper callback for each hook and
+ * then call the actual hook using the appropriate arguments.
+ * Finally, process the return value and return/throw accordingly.
*
+ * @param string $event Event name
+ * @param array $args Array of parameters passed to hook functions
+ * @return bool True if no handler aborted the hook
+ *
+ * @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
- * @return Boolean True if no handler aborted the hook
*/
- public static function run( $event, $args = array() ) {
- global $wgHooks;
-
- // Return quickly in the most common case
- if ( empty( self::$handlers[$event] ) && empty( $wgHooks[$event] ) ) {
- return true;
- }
-
+ public static function run( $event, array $args = array() ) {
wfProfileIn( 'hook: ' . $event );
- $hooks = self::getHandlers( $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 ) ) {
+ $hook = array( $hook );
+ }
- foreach ( $hooks as $hook ) {
- $object = null;
- $method = null;
- $func = null;
- $data = null;
- $have_data = false;
- $closure = false;
- $badhookmsg = false;
+ if ( !array_filter( $hook ) ) {
+ // Either array is empty or it's an array filled with null/false/empty.
+ continue;
+ } elseif ( is_array( $hook[0] ) ) {
+ // First element is an array, meaning the developer intended
+ // the first element to be a callback. Merge it in so that
+ // processing can be uniform.
+ $hook = array_merge( $hook[0], array_slice( $hook, 1 ) );
+ }
/**
* $hook can be: a function, an object, an array of $function and
* $data, an array of just a function, an array of object and
* method, or an array of object, method, and data.
*/
- if ( is_array( $hook ) ) {
- if ( count( $hook ) < 1 ) {
- throw new MWException( 'Empty array in hooks for ' . $event . "\n" );
- } elseif ( is_object( $hook[0] ) ) {
- $object = $hook[0];
- if ( $object instanceof Closure ) {
- $closure = true;
- if ( count( $hook ) > 1 ) {
- $data = $hook[1];
- $have_data = true;
- }
- } else {
- if ( count( $hook ) < 2 ) {
- $method = 'on' . $event;
- } else {
- $method = $hook[1];
- if ( count( $hook ) > 2 ) {
- $data = $hook[2];
- $have_data = true;
- }
- }
- }
- } elseif ( is_string( $hook[0] ) ) {
- $func = $hook[0];
- if ( count( $hook ) > 1) {
- $data = $hook[1];
- $have_data = true;
- }
- } else {
- throw new MWException( 'Unknown datatype in hooks for ' . $event . "\n" );
- }
- } elseif ( is_string( $hook ) ) { # functions look like strings, too
- $func = $hook;
- } elseif ( is_object( $hook ) ) {
- $object = $hook;
- if ( $object instanceof Closure ) {
- $closure = true;
- } else {
- $method = "on" . $event;
+ if ( $hook[0] instanceof Closure ) {
+ $func = "hook-$event-closure";
+ $callback = array_shift( $hook );
+ } elseif ( is_object( $hook[0] ) ) {
+ $object = array_shift( $hook );
+ $method = array_shift( $hook );
+
+ // If no method was specified, default to on$event.
+ if ( $method === null ) {
+ $method = "on$event";
}
- } else {
- throw new MWException( 'Unknown datatype in hooks for ' . $event . "\n" );
- }
-
- /* We put the first data element on, if needed. */
- if ( $have_data ) {
- $hook_args = array_merge( array( $data ), $args );
- } else {
- $hook_args = $args;
- }
- if ( $closure ) {
- $callback = $object;
- $func = "hook-$event-closure";
- } elseif ( isset( $object ) ) {
$func = get_class( $object ) . '::' . $method;
$callback = array( $object, $method );
+ } elseif ( is_string( $hook[0] ) ) {
+ $func = $callback = array_shift( $hook );
} else {
- $callback = $func;
+ throw new MWException( 'Unknown datatype in hooks for ' . $event . "\n" );
}
// Run autoloader (workaround for call_user_func_array bug)
- is_callable( $callback );
+ // and throw error if not callable.
+ if ( !is_callable( $callback ) ) {
+ throw new MWException( 'Invalid callback in hooks for ' . $event . "\n" );
+ }
- /**
- * Call the hook. The documentation of call_user_func_array clearly
- * states that FALSE is returned on failure. However this is not
- * case always. In some version of PHP if the function signature
- * does not match the call signature, PHP will issue an warning:
- * Param y in x expected to be a reference, value given.
- *
- * In that case the call will also return null. The following code
- * catches that warning and provides better error message. The
- * function documentation also says that:
- * In other words, it does not depend on the function signature
- * whether the parameter is passed by a value or by a reference.
- * There is also PHP bug http://bugs.php.net/bug.php?id=47554 which
- * is unsurprisingly marked as bogus. In short handling of failures
- * with call_user_func_array is a failure, the documentation for that
- * function is wrong and misleading and PHP developers don't see any
- * problem here.
+ /*
+ * Call the hook. The documentation of call_user_func_array says
+ * false is returned on failure. However, if the function signature
+ * does not match the call signature, PHP will issue an warning and
+ * return null instead. The following code catches that warning and
+ * provides better error message.
*/
$retval = null;
- set_error_handler( 'Hooks::hookErrorHandler' );
+ $badhookmsg = null;
+ $hook_args = array_merge( $hook, $args );
+
+ // Profile first in case the Profiler causes errors.
wfProfileIn( $func );
+ set_error_handler( 'Hooks::hookErrorHandler' );
try {
$retval = call_user_func_array( $callback, $hook_args );
} catch ( MWHookException $e ) {
$badhookmsg = $e->getMessage();
}
- wfProfileOut( $func );
restore_error_handler();
+ wfProfileOut( $func );
- /* String return is an error; false return means stop processing. */
+ // Process the return value.
if ( is_string( $retval ) ) {
+ // String returned means error.
throw new FatalError( $retval );
- } elseif( $retval === null ) {
- if ( $closure ) {
- $prettyFunc = "$event closure";
- } elseif( is_array( $callback ) ) {
- if( is_object( $callback[0] ) ) {
- $prettyClass = get_class( $callback[0] );
- } else {
- $prettyClass = strval( $callback[0] );
- }
- $prettyFunc = $prettyClass . '::' . strval( $callback[1] );
- } else {
- $prettyFunc = strval( $callback );
- }
- if ( $badhookmsg ) {
- throw new MWException(
- 'Detected bug in an extension! ' .
- "Hook $prettyFunc has invalid call signature; " . $badhookmsg
- );
- } else {
- throw new MWException(
- 'Detected bug in an extension! ' .
- "Hook $prettyFunc failed to return a value; " .
- 'should return true to continue hook processing or false to abort.'
- );
- }
- } elseif ( !$retval ) {
+ } elseif ( $badhookmsg !== null ) {
+ // Exception was thrown from Hooks::hookErrorHandler.
+ throw new MWException(
+ 'Detected bug in an extension! ' .
+ "Hook $func has invalid call signature; " . $badhookmsg
+ );
+ } elseif ( $retval === false ) {
wfProfileOut( 'hook: ' . $event );
+ // False was returned. Stop processing, but no error.
return false;
}
}
@@ -298,18 +225,21 @@ class Hooks {
}
/**
+ * Handle PHP errors issued inside a hook. Catch errors that have to do with
+ * a function expecting a reference, and let all others pass through.
+ *
* This REALLY should be protected... but it's public for compatibility
*
* @since 1.18
*
- * @param int $errno Unused
- * @param string $errstr error message
- * @throws MWHookException
- * @return Boolean: false
+ * @param int $errno Error number (unused)
+ * @param string $errstr Error message
+ * @throws MWHookException If the error has to do with the function signature
+ * @return bool Always returns false
*/
public static function hookErrorHandler( $errno, $errstr ) {
if ( strpos( $errstr, 'expected to be a reference, value given' ) !== false ) {
- throw new MWHookException( $errstr );
+ throw new MWHookException( $errstr, $errno );
}
return false;
}
diff --git a/includes/Html.php b/includes/Html.php
index af4b4bbf..932f753e 100644
--- a/includes/Html.php
+++ b/includes/Html.php
@@ -36,8 +36,8 @@
*
* There are two important configuration options this class uses:
*
- * $wgHtml5: If this is set to false, then all output should be valid XHTML 1.0
- * Transitional.
+ * $wgMimeType: If this is set to an xml mimetype then output should be
+ * valid XHTML5.
* $wgWellFormedXml: If this is set to true, then all output should be
* well-formed XML (quotes on attributes, self-closing tags, etc.).
*
@@ -101,19 +101,6 @@ class Html {
'itemscope',
);
- private static $HTMLFiveOnlyAttribs = array(
- 'autocomplete',
- 'autofocus',
- 'max',
- 'min',
- 'multiple',
- 'pattern',
- 'placeholder',
- 'required',
- 'step',
- 'spellcheck',
- );
-
/**
* Returns an HTML element in a string. The major advantage here over
* manually typing out the HTML is that it will escape all attribute
@@ -177,7 +164,7 @@ class Html {
* @return string
*/
public static function openElement( $element, $attribs = array() ) {
- global $wgHtml5, $wgWellFormedXml;
+ global $wgWellFormedXml;
$attribs = (array)$attribs;
// This is not required in HTML5, but let's do it anyway, for
// consistency and better compression.
@@ -185,7 +172,7 @@ class Html {
// 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/specs/web-apps/current-work/multipage/syntax.html#optional-tags>
+ // <http://www.whatwg.org/html/syntax.html#optional-tags>
if ( !$wgWellFormedXml && !$attribs
&& in_array( $element, array( 'html', 'head' ) ) ) {
return '';
@@ -204,36 +191,28 @@ class Html {
'image',
'reset',
'button',
- );
- // Allow more input types in HTML5 mode
- if( $wgHtml5 ) {
- $validTypes = array_merge( $validTypes, array(
- 'datetime',
- 'datetime-local',
- 'date',
- 'month',
- 'time',
- 'week',
- 'number',
- 'range',
- 'email',
- 'url',
- 'search',
- 'tel',
- 'color',
- ) );
- }
+ // HTML input types
+ 'datetime',
+ 'datetime-local',
+ 'date',
+ 'month',
+ 'time',
+ 'week',
+ 'number',
+ 'range',
+ 'email',
+ 'url',
+ 'search',
+ 'tel',
+ 'color',
+ );
if ( isset( $attribs['type'] )
&& !in_array( $attribs['type'], $validTypes ) ) {
unset( $attribs['type'] );
}
}
- if ( !$wgHtml5 && $element == 'textarea' && isset( $attribs['maxlength'] ) ) {
- unset( $attribs['maxlength'] );
- }
-
// According to standard the default type for <button> elements is "submit".
// Depending on compatibility mode IE might use "button", instead.
// We enforce the standard "submit".
@@ -259,7 +238,7 @@ class Html {
$element = strtolower( $element );
// Reference:
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags
+ // http://www.whatwg.org/html/syntax.html#optional-tags
if ( !$wgWellFormedXml && in_array( $element, array(
'html',
'head',
@@ -294,12 +273,6 @@ class Html {
* @return array An array of attributes functionally identical to $attribs
*/
private static function dropDefaults( $element, $attribs ) {
- // Don't bother doing anything if we aren't outputting HTML5; it's too
- // much of a pain to maintain two sets of defaults.
- global $wgHtml5;
- if ( !$wgHtml5 ) {
- return $attribs;
- }
// Whenever altering this array, please provide a covering test case
// in HtmlTest::provideElementsWithAttributesHavingDefaultValues
@@ -340,7 +313,7 @@ class Html {
foreach ( $attribs as $attrib => $value ) {
$lcattrib = strtolower( $attrib );
- if( is_array( $value ) ) {
+ if ( is_array( $value ) ) {
$value = implode( ' ', $value );
} else {
$value = strval( $value );
@@ -444,11 +417,12 @@ class Html {
* (starting with a space if at least one attribute is output)
*/
public static function expandAttributes( $attribs ) {
- global $wgHtml5, $wgWellFormedXml;
+ global $wgWellFormedXml;
$ret = '';
$attribs = (array)$attribs;
foreach ( $attribs as $key => $value ) {
+ // Support intuitive array( 'checked' => true/false ) form
if ( $value === false || is_null( $value ) ) {
continue;
}
@@ -460,15 +434,10 @@ class Html {
$key = $value;
}
- // Not technically required in HTML5, but required in XHTML 1.0,
- // and we'd like consistency and better compression anyway.
+ // Not technically required in HTML5 but we'd like consistency
+ // and better compression anyway.
$key = strtolower( $key );
- // Here we're blacklisting some HTML5-only attributes...
- if ( !$wgHtml5 && in_array( $key, self::$HTMLFiveOnlyAttribs ) ) {
- continue;
- }
-
// Bug 23769: Blacklist all form validation attributes for now. Current
// (June 2010) WebKit has no UI, so the form just refuses to submit
// without telling the user why, which is much worse than failing
@@ -552,15 +521,12 @@ class Html {
}
if ( in_array( $key, self::$boolAttribs ) ) {
- // In XHTML 1.0 Transitional, the value needs to be equal to the
- // key. In HTML5, we can leave the value empty instead. If we
- // don't need well-formed XML, we can omit the = entirely.
+ // In HTML5, we can leave the value empty. If we don't need
+ // well-formed XML, we can omit the = entirely.
if ( !$wgWellFormedXml ) {
$ret .= " $key";
- } elseif ( $wgHtml5 ) {
- $ret .= " $key=\"\"";
} else {
- $ret .= " $key=\"$key\"";
+ $ret .= " $key=\"\"";
}
} else {
// Apparently we need to entity-encode \n, \r, \t, although the
@@ -602,14 +568,10 @@ class Html {
* @return string Raw HTML
*/
public static function inlineScript( $contents ) {
- global $wgHtml5, $wgJsMimeType, $wgWellFormedXml;
+ global $wgWellFormedXml;
$attrs = array();
- if ( !$wgHtml5 ) {
- $attrs['type'] = $wgJsMimeType;
- }
-
if ( $wgWellFormedXml && preg_match( '/[<&]/', $contents ) ) {
$contents = "/*<![CDATA[*/$contents/*]]>*/";
}
@@ -625,14 +587,8 @@ class Html {
* @return string Raw HTML
*/
public static function linkedScript( $url ) {
- global $wgHtml5, $wgJsMimeType;
-
$attrs = array( 'src' => $url );
- if ( !$wgHtml5 ) {
- $attrs['type'] = $wgJsMimeType;
- }
-
return self::element( 'script', $attrs );
}
@@ -677,8 +633,7 @@ class Html {
/**
* Convenience function to produce an "<input>" element. This supports the
- * new HTML5 input types and attributes, and will silently strip them if
- * $wgHtml5 is false.
+ * new HTML5 input types and attributes.
*
* @param $name string name attribute
* @param $value mixed value attribute
@@ -712,9 +667,7 @@ class Html {
* Convenience function to produce an "<input>" element.
*
* This supports leaving out the cols= and rows= which Xml requires and are
- * required by HTML4/XHTML but not required by HTML5 and will silently set
- * cols="" and rows="" if $wgHtml5 is false and cols and rows are omitted
- * (HTML4 validates present but empty cols="" and rows="" as valid).
+ * required by HTML4/XHTML but not required by HTML5.
*
* @param $name string name attribute
* @param $value string value attribute
@@ -723,20 +676,8 @@ class Html {
* @return string Raw HTML
*/
public static function textarea( $name, $value = '', $attribs = array() ) {
- global $wgHtml5;
-
$attribs['name'] = $name;
- if ( !$wgHtml5 ) {
- if ( !isset( $attribs['cols'] ) ) {
- $attribs['cols'] = "";
- }
-
- if ( !isset( $attribs['rows'] ) ) {
- $attribs['rows'] = "";
- }
- }
-
if ( substr( $value, 0, 1 ) == "\n" ) {
// Workaround for bug 12130: browsers eat the initial newline
// assuming that it's just for show, but they do keep the later
@@ -859,28 +800,29 @@ class Html {
public static function htmlHeader( $attribs = array() ) {
$ret = '';
- global $wgMimeType;
-
- if ( self::isXmlMimeType( $wgMimeType ) ) {
- $ret .= "<?xml version=\"1.0\" encoding=\"UTF-8\" ?" . ">\n";
- }
+ global $wgHtml5Version, $wgMimeType, $wgXhtmlNamespaces;
- global $wgHtml5, $wgHtml5Version, $wgDocType, $wgDTD;
- global $wgXhtmlNamespaces, $wgXhtmlDefaultNamespace;
+ $isXHTML = self::isXmlMimeType( $wgMimeType );
- if ( $wgHtml5 ) {
- $ret .= "<!DOCTYPE html>\n";
+ if ( $isXHTML ) { // XHTML5
+ // XML mimetyped markup should have an xml header.
+ // However a DOCTYPE is not needed.
+ $ret .= "<?xml version=\"1.0\" encoding=\"UTF-8\" ?" . ">\n";
- if ( $wgHtml5Version ) {
- $attribs['version'] = $wgHtml5Version;
- }
- } else {
- $ret .= "<!DOCTYPE html PUBLIC \"$wgDocType\" \"$wgDTD\">\n";
- $attribs['xmlns'] = $wgXhtmlDefaultNamespace;
+ // Add the standard xmlns
+ $attribs['xmlns'] = 'http://www.w3.org/1999/xhtml';
+ // And support custom namespaces
foreach ( $wgXhtmlNamespaces as $tag => $ns ) {
$attribs["xmlns:$tag"] = $ns;
}
+ } else { // HTML5
+ // DOCTYPE
+ $ret .= "<!DOCTYPE html>\n";
+ }
+
+ if ( $wgHtml5Version ) {
+ $attribs['version'] = $wgHtml5Version;
}
$html = Html::openElement( 'html', $attribs );
@@ -901,14 +843,11 @@ class Html {
* @return Boolean
*/
public static function isXmlMimeType( $mimetype ) {
- switch ( $mimetype ) {
- case 'text/xml':
- case 'application/xhtml+xml':
- case 'application/xml':
- return true;
- default:
- return false;
- }
+ # http://www.whatwg.org/html/infrastructure.html#xml-mime-type
+ # * text/xml
+ # * application/xml
+ # * Any mimetype with a subtype ending in +xml (this implicitly includes application/xhtml+xml)
+ return (bool)preg_match( '!^(text|application)/xml$|^.+/.+\+xml$!', $mimetype );
}
/**
@@ -926,22 +865,22 @@ class Html {
global $wgStylePath;
if ( $useStylePath ) {
- $icon = $wgStylePath.'/common/images/'.$icon;
+ $icon = $wgStylePath . '/common/images/' . $icon;
}
$s = Html::openElement( 'div', array( 'class' => "mw-infobox $class" ) );
- $s .= Html::openElement( 'div', array( 'class' => 'mw-infobox-left' ) ).
+ $s .= Html::openElement( 'div', array( 'class' => 'mw-infobox-left' ) ) .
Html::element( 'img',
array(
'src' => $icon,
'alt' => $alt,
)
- ).
+ ) .
Html::closeElement( 'div' );
- $s .= Html::openElement( 'div', array( 'class' => 'mw-infobox-right' ) ).
- $text.
+ $s .= Html::openElement( 'div', array( 'class' => 'mw-infobox-right' ) ) .
+ $text .
Html::closeElement( 'div' );
$s .= Html::element( 'div', array( 'style' => 'clear: left;' ), ' ' );
@@ -962,9 +901,9 @@ class Html {
*/
static function srcSet( $urls ) {
$candidates = array();
- foreach( $urls as $density => $url ) {
+ foreach ( $urls as $density => $url ) {
// Image candidate syntax per current whatwg live spec, 2012-09-23:
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content-1.html#attr-img-srcset
+ // http://www.whatwg.org/html/embedded-content-1.html#attr-img-srcset
$candidates[] = "{$url} {$density}x";
}
return implode( ", ", $candidates );
diff --git a/includes/HtmlFormatter.php b/includes/HtmlFormatter.php
new file mode 100644
index 00000000..248a76fe
--- /dev/null
+++ b/includes/HtmlFormatter.php
@@ -0,0 +1,356 @@
+<?php
+/**
+ * Performs transformations of HTML by wrapping around libxml2 and working
+ * around its countless bugs.
+ *
+ * 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
+ */
+class HtmlFormatter {
+ /**
+ * @var DOMDocument
+ */
+ private $doc;
+
+ private $html;
+ private $itemsToRemove = array();
+ private $elementsToFlatten = array();
+ protected $removeMedia = false;
+
+ /**
+ * Constructor
+ *
+ * @param string $html: Text to process
+ */
+ public function __construct( $html ) {
+ $this->html = $html;
+ }
+
+ /**
+ * Turns a chunk of HTML into a proper document
+ * @param string $html
+ * @return string
+ */
+ public static function wrapHTML( $html ) {
+ return '<!doctype html><html><head></head><body>' . $html . '</body></html>';
+ }
+
+ /**
+ * Override this in descendant class to modify HTML after it has been converted from DOM tree
+ * @param string $html: HTML to process
+ * @return string: Processed HTML
+ */
+ protected function onHtmlReady( $html ) {
+ return $html;
+ }
+
+ /**
+ * @return DOMDocument: DOM to manipulate
+ */
+ public function getDoc() {
+ if ( !$this->doc ) {
+ $html = mb_convert_encoding( $this->html, 'HTML-ENTITIES', 'UTF-8' );
+
+ // Workaround for bug that caused spaces before references
+ // to disappear during processing:
+ // https://bugzilla.wikimedia.org/show_bug.cgi?id=53086
+ //
+ // Please replace with a better fix if one can be found.
+ $html = str_replace( ' <', '&#32;<', $html );
+
+ libxml_use_internal_errors( true );
+ $loader = libxml_disable_entity_loader();
+ $this->doc = new DOMDocument();
+ $this->doc->strictErrorChecking = false;
+ $this->doc->loadHTML( $html );
+ libxml_disable_entity_loader( $loader );
+ libxml_use_internal_errors( false );
+ $this->doc->encoding = 'UTF-8';
+ }
+ return $this->doc;
+ }
+
+ /**
+ * Sets whether images/videos/sounds should be removed from output
+ * @param bool $flag
+ */
+ public function setRemoveMedia( $flag = true ) {
+ $this->removeMedia = $flag;
+ }
+
+ /**
+ * Adds one or more selector of content to remove. A subset of CSS selector
+ * syntax is supported:
+ *
+ * <tag>
+ * <tag>.class
+ * .<class>
+ * #<id>
+ *
+ * @param Array|string $selectors: Selector(s) of stuff to remove
+ */
+ public function remove( $selectors ) {
+ $this->itemsToRemove = array_merge( $this->itemsToRemove, (array)$selectors );
+ }
+
+ /**
+ * Adds one or more element name to the list to flatten (remove tag, but not its content)
+ * Can accept undelimited regexes
+ *
+ * Note this interface may fail in surprising unexpected ways due to usage of regexes,
+ * so should not be relied on for HTML markup security measures.
+ *
+ * @param Array|string $elements: Name(s) of tag(s) to flatten
+ */
+ public function flatten( $elements ) {
+ $this->elementsToFlatten = array_merge( $this->elementsToFlatten, (array)$elements );
+ }
+
+ /**
+ * Instructs the formatter to flatten all tags
+ */
+ public function flattenAllTags() {
+ $this->flatten( '[?!]?[a-z0-9]+' );
+ }
+
+ /**
+ * Removes content we've chosen to remove
+ */
+ public function filterContent() {
+ wfProfileIn( __METHOD__ );
+ $removals = $this->parseItemsToRemove();
+
+ if ( !$removals ) {
+ wfProfileOut( __METHOD__ );
+ return;
+ }
+
+ $doc = $this->getDoc();
+
+ // Remove tags
+
+ // You can't remove DOMNodes from a DOMNodeList as you're iterating
+ // over them in a foreach loop. It will seemingly leave the internal
+ // iterator on the foreach out of wack and results will be quite
+ // strange. Though, making a queue of items to remove seems to work.
+ $domElemsToRemove = array();
+ foreach ( $removals['TAG'] as $tagToRemove ) {
+ $tagToRemoveNodes = $doc->getElementsByTagName( $tagToRemove );
+ foreach ( $tagToRemoveNodes as $tagToRemoveNode ) {
+ if ( $tagToRemoveNode ) {
+ $domElemsToRemove[] = $tagToRemoveNode;
+ }
+ }
+ }
+
+ $this->removeElements( $domElemsToRemove );
+
+ // Elements with named IDs
+ $domElemsToRemove = array();
+ foreach ( $removals['ID'] as $itemToRemove ) {
+ $itemToRemoveNode = $doc->getElementById( $itemToRemove );
+ if ( $itemToRemoveNode ) {
+ $domElemsToRemove[] = $itemToRemoveNode;
+ }
+ }
+ $this->removeElements( $domElemsToRemove );
+
+ // CSS Classes
+ $domElemsToRemove = array();
+ $xpath = new DOMXpath( $doc );
+ foreach ( $removals['CLASS'] as $classToRemove ) {
+ $elements = $xpath->query( '//*[contains(@class, "' . $classToRemove . '")]' );
+
+ /** @var $element DOMElement */
+ foreach ( $elements as $element ) {
+ $classes = $element->getAttribute( 'class' );
+ if ( preg_match( "/\b$classToRemove\b/", $classes ) && $element->parentNode ) {
+ $domElemsToRemove[] = $element;
+ }
+ }
+ }
+ $this->removeElements( $domElemsToRemove );
+
+ // Tags with CSS Classes
+ foreach ( $removals['TAG_CLASS'] as $classToRemove ) {
+ $parts = explode( '.', $classToRemove );
+
+ $elements = $xpath->query(
+ '//' . $parts[0] . '[@class="' . $parts[1] . '"]'
+ );
+
+ $this->removeElements( $elements );
+ }
+
+ wfProfileOut( __METHOD__ );
+ }
+
+ /**
+ * Removes a list of elelments from DOMDocument
+ * @param array|DOMNodeList $elements
+ */
+ private function removeElements( $elements ) {
+ $list = $elements;
+ if ( $elements instanceof DOMNodeList ) {
+ $list = array();
+ foreach ( $elements as $element ) {
+ $list[] = $element;
+ }
+ }
+ /** @var $element DOMElement */
+ foreach ( $list as $element ) {
+ if ( $element->parentNode ) {
+ $element->parentNode->removeChild( $element );
+ }
+ }
+ }
+
+ /**
+ * libxml in its usual pointlessness converts many chars to entities - this function
+ * perfoms a reverse conversion
+ * @param string $html
+ * @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
+ // normalized by libxml. Using this function with input not sanitized by libxml is UNSAFE!
+ $replacements = new ReplacementArray( array(
+ '&quot;' => '&amp;quot;',
+ '&amp;' => '&amp;amp;',
+ '&lt;' => '&amp;lt;',
+ '&gt;' => '&amp;gt;',
+ ) );
+ }
+ $html = $replacements->replace( $html );
+ $html = mb_convert_encoding( $html, 'UTF-8', 'HTML-ENTITIES' );
+ wfProfileOut( __METHOD__ );
+ return $html;
+ }
+
+ /**
+ * Performs final transformations and returns resulting HTML
+ *
+ * @param DOMElement|string|null $element: ID of element to get HTML from or false to get it from the whole tree
+ * @return string: Processed HTML
+ */
+ public function getText( $element = null ) {
+ wfProfileIn( __METHOD__ );
+
+ if ( $this->doc ) {
+ if ( $element !== null && !( $element instanceof DOMElement ) ) {
+ $element = $this->doc->getElementById( $element );
+ }
+ if ( $element ) {
+ $body = $this->doc->getElementsByTagName( 'body' )->item( 0 );
+ $nodesArray = array();
+ foreach ( $body->childNodes as $node ) {
+ $nodesArray[] = $node;
+ }
+ foreach ( $nodesArray as $nodeArray ) {
+ $body->removeChild( $nodeArray );
+ }
+ $body->appendChild( $element );
+ }
+ $html = $this->doc->saveHTML();
+ $html = $this->fixLibXml( $html );
+ } else {
+ $html = $this->html;
+ }
+ if ( wfIsWindows() ) {
+ // Appears to be cleanup for CRLF misprocessing of unknown origin
+ // when running server on Windows platform.
+ //
+ // If this error continues in the future, please track it down in the
+ // XML code paths if possible and fix there.
+ $html = str_replace( '&#13;', '', $html );
+ }
+ $html = preg_replace( '/<!--.*?-->|^.*?<body>|<\/body>.*$/s', '', $html );
+ $html = $this->onHtmlReady( $html );
+
+ if ( $this->elementsToFlatten ) {
+ $elements = implode( '|', $this->elementsToFlatten );
+ $html = preg_replace( "#</?($elements)\\b[^>]*>#is", '', $html );
+ }
+
+ wfProfileOut( __METHOD__ );
+ return $html;
+ }
+
+ /**
+ * @param $selector: CSS selector to parse
+ * @param $type
+ * @param $rawName
+ * @return bool: Whether the selector was successfully recognised
+ */
+ protected function parseSelector( $selector, &$type, &$rawName ) {
+ if ( strpos( $selector, '.' ) === 0 ) {
+ $type = 'CLASS';
+ $rawName = substr( $selector, 1 );
+ } elseif ( strpos( $selector, '#' ) === 0 ) {
+ $type = 'ID';
+ $rawName = substr( $selector, 1 );
+ } elseif ( strpos( $selector, '.' ) !== 0 &&
+ strpos( $selector, '.' ) !== false )
+ {
+ $type = 'TAG_CLASS';
+ $rawName = $selector;
+ } elseif ( strpos( $selector, '[' ) === false
+ && strpos( $selector, ']' ) === false )
+ {
+ $type = 'TAG';
+ $rawName = $selector;
+ } else {
+ throw new MWException( __METHOD__ . "(): unrecognized selector '$selector'" );
+ }
+
+ return true;
+ }
+
+ /**
+ * Transforms CSS selectors into an internal representation suitable for processing
+ * @return array
+ */
+ protected function parseItemsToRemove() {
+ wfProfileIn( __METHOD__ );
+ $removals = array(
+ 'ID' => array(),
+ 'TAG' => array(),
+ 'CLASS' => array(),
+ 'TAG_CLASS' => array(),
+ );
+
+ foreach ( $this->itemsToRemove as $itemToRemove ) {
+ $type = '';
+ $rawName = '';
+ if ( $this->parseSelector( $itemToRemove, $type, $rawName ) ) {
+ $removals[$type][] = $rawName;
+ }
+ }
+
+ if ( $this->removeMedia ) {
+ $removals['TAG'][] = 'img';
+ $removals['TAG'][] = 'audio';
+ $removals['TAG'][] = 'video';
+ }
+
+ wfProfileOut( __METHOD__ );
+ return $removals;
+ }
+}
diff --git a/includes/HttpFunctions.php b/includes/HttpFunctions.php
index dc65c67e..78c2ac7a 100644
--- a/includes/HttpFunctions.php
+++ b/includes/HttpFunctions.php
@@ -40,14 +40,15 @@ class Http {
* @param array $options options to pass to MWHttpRequest object.
* Possible keys for the array:
* - timeout Timeout length in seconds
+ * - connectTimeout Timeout for connection, in seconds (curl only)
* - postData An array of key-value pairs or a url-encoded form data
* - proxy The proxy to use.
* Otherwise it will use $wgHTTPProxy (if set)
* Otherwise it will use the environment variable "http_proxy" (if set)
* - noProxy Don't use any proxy at all. Takes precedence over proxy value(s).
- * - sslVerifyHost (curl only) Verify hostname against certificate
- * - sslVerifyCert (curl only) Verify SSL certificate
- * - caInfo (curl only) Provide CA information
+ * - sslVerifyHost Verify hostname against certificate
+ * - sslVerifyCert Verify SSL certificate
+ * - caInfo Provide CA information
* - maxRedirects Maximum number of redirects to follow (defaults to 5)
* - followRedirects Whether to follow redirects (defaults to false).
* Note: this should only be used when the target URL is trusted,
@@ -58,20 +59,26 @@ class Http {
*/
public static function request( $method, $url, $options = array() ) {
wfDebug( "HTTP: $method: $url\n" );
+ wfProfileIn( __METHOD__ . "-$method" );
+
$options['method'] = strtoupper( $method );
if ( !isset( $options['timeout'] ) ) {
$options['timeout'] = 'default';
}
+ if ( !isset( $options['connectTimeout'] ) ) {
+ $options['connectTimeout'] = 'default';
+ }
$req = MWHttpRequest::factory( $url, $options );
$status = $req->execute();
+ $content = false;
if ( $status->isOK() ) {
- return $req->getContent();
- } else {
- return false;
+ $content = $req->getContent();
}
+ wfProfileOut( __METHOD__ . "-$method" );
+ return $content;
}
/**
@@ -213,7 +220,7 @@ class MWHttpRequest {
* @param array $options (optional) extra params to pass (see Http::request())
*/
protected function __construct( $url, $options = array() ) {
- global $wgHTTPTimeout;
+ global $wgHTTPTimeout, $wgHTTPConnectTimeout;
$this->url = wfExpandUrl( $url, PROTO_HTTP );
$this->parsedUrl = wfParseUrl( $this->url );
@@ -229,7 +236,12 @@ class MWHttpRequest {
} else {
$this->timeout = $wgHTTPTimeout;
}
- if( isset( $options['userAgent'] ) ) {
+ if ( isset( $options['connectTimeout'] ) && $options['connectTimeout'] != 'default' ) {
+ $this->connectTimeout = $options['connectTimeout'];
+ } else {
+ $this->connectTimeout = $wgHTTPConnectTimeout;
+ }
+ if ( isset( $options['userAgent'] ) ) {
$this->setUserAgent( $options['userAgent'] );
}
@@ -277,7 +289,7 @@ class MWHttpRequest {
' Http::$httpEngine is set to "curl"' );
}
- switch( Http::$httpEngine ) {
+ switch ( Http::$httpEngine ) {
case 'curl':
return new CurlHttpRequest( $url, $options );
case 'php':
@@ -302,7 +314,7 @@ class MWHttpRequest {
/**
* Set the parameters of the request
-
+ *
* @param $args Array
* @todo overload the args param
*/
@@ -427,6 +439,8 @@ class MWHttpRequest {
public function execute() {
global $wgTitle;
+ wfProfileIn( __METHOD__ );
+
$this->content = "";
if ( strtoupper( $this->method ) == "HEAD" ) {
@@ -446,6 +460,8 @@ class MWHttpRequest {
if ( !isset( $this->reqHeaders['User-Agent'] ) ) {
$this->setUserAgent( Http::userAgent() );
}
+
+ wfProfileOut( __METHOD__ );
}
/**
@@ -454,6 +470,8 @@ class MWHttpRequest {
* found in an array in the member variable headerList.
*/
protected function parseHeader() {
+ wfProfileIn( __METHOD__ );
+
$lastname = "";
foreach ( $this->headerList as $header ) {
@@ -470,6 +488,8 @@ class MWHttpRequest {
}
$this->parseCookies();
+
+ wfProfileOut( __METHOD__ );
}
/**
@@ -552,8 +572,8 @@ class MWHttpRequest {
$this->parseHeader();
}
- if ( isset( $this->respHeaders[strtolower ( $header ) ] ) ) {
- $v = $this->respHeaders[strtolower ( $header ) ];
+ if ( isset( $this->respHeaders[strtolower( $header )] ) ) {
+ $v = $this->respHeaders[strtolower( $header )];
return $v[count( $v ) - 1];
}
@@ -603,6 +623,8 @@ 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;
}
@@ -613,6 +635,8 @@ class MWHttpRequest {
$this->cookieJar->parseCookieResponseHeader( $cookie, $url['host'] );
}
}
+
+ wfProfileOut( __METHOD__ );
}
/**
@@ -631,17 +655,17 @@ class MWHttpRequest {
$headers = $this->getResponseHeaders();
//return full url (fix for incorrect but handled relative location)
- if ( isset( $headers[ 'location' ] ) ) {
- $locations = $headers[ 'location' ];
+ if ( isset( $headers['location'] ) ) {
+ $locations = $headers['location'];
$domain = '';
$foundRelativeURI = false;
$countLocations = count( $locations );
for ( $i = $countLocations - 1; $i >= 0; $i-- ) {
- $url = parse_url( $locations[ $i ] );
+ $url = parse_url( $locations[$i] );
if ( isset( $url['host'] ) ) {
- $domain = $url[ 'scheme' ] . '://' . $url[ 'host' ];
+ $domain = $url['scheme'] . '://' . $url['host'];
break; //found correct URI (with host)
} else {
$foundRelativeURI = true;
@@ -650,15 +674,15 @@ class MWHttpRequest {
if ( $foundRelativeURI ) {
if ( $domain ) {
- return $domain . $locations[ $countLocations - 1 ];
+ return $domain . $locations[$countLocations - 1];
} else {
$url = parse_url( $this->url );
- if ( isset($url[ 'host' ]) ) {
- return $url[ 'scheme' ] . '://' . $url[ 'host' ] . $locations[ $countLocations - 1 ];
+ if ( isset( $url['host'] ) ) {
+ return $url['scheme'] . '://' . $url['host'] . $locations[$countLocations - 1];
}
}
} else {
- return $locations[ $countLocations - 1 ];
+ return $locations[$countLocations - 1];
}
}
@@ -681,11 +705,6 @@ class MWHttpRequest {
class CurlHttpRequest extends MWHttpRequest {
const SUPPORTS_FILE_POSTS = true;
- static $curlMessageMap = array(
- 6 => 'http-host-unreachable',
- 28 => 'http-timed-out'
- );
-
protected $curlOptions = array();
protected $headerText = "";
@@ -700,14 +719,18 @@ class CurlHttpRequest extends MWHttpRequest {
}
public function execute() {
+ wfProfileIn( __METHOD__ );
+
parent::execute();
if ( !$this->status->isOK() ) {
+ wfProfileOut( __METHOD__ );
return $this->status;
}
$this->curlOptions[CURLOPT_PROXY] = $this->proxy;
$this->curlOptions[CURLOPT_TIMEOUT] = $this->timeout;
+ $this->curlOptions[CURLOPT_CONNECTTIMEOUT_MS] = $this->connectTimeout * 1000;
$this->curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
$this->curlOptions[CURLOPT_WRITEFUNCTION] = $this->callback;
$this->curlOptions[CURLOPT_HEADERFUNCTION] = array( $this, "readHeader" );
@@ -746,6 +769,7 @@ 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." );
}
@@ -760,14 +784,11 @@ class CurlHttpRequest extends MWHttpRequest {
wfRestoreWarnings();
}
- if ( false === curl_exec( $curlHandle ) ) {
- $code = curl_error( $curlHandle );
-
- if ( isset( self::$curlMessageMap[$code] ) ) {
- $this->status->fatal( self::$curlMessageMap[$code] );
- } else {
- $this->status->fatal( 'http-curl-error', curl_error( $curlHandle ) );
- }
+ $curlRes = curl_exec( $curlHandle );
+ if ( curl_errno( $curlHandle ) == CURLE_OPERATION_TIMEOUTED ) {
+ $this->status->fatal( 'http-timed-out', $this->url );
+ } elseif ( $curlRes === false ) {
+ $this->status->fatal( 'http-curl-error', curl_error( $curlHandle ) );
} else {
$this->headerList = explode( "\r\n", $this->headerText );
}
@@ -777,6 +798,8 @@ class CurlHttpRequest extends MWHttpRequest {
$this->parseHeader();
$this->setStatus();
+ wfProfileOut( __METHOD__ );
+
return $this->status;
}
@@ -811,6 +834,8 @@ class PhpHttpRequest extends MWHttpRequest {
}
public function execute() {
+ wfProfileIn( __METHOD__ );
+
parent::execute();
if ( is_array( $this->postData ) ) {
@@ -826,7 +851,7 @@ class PhpHttpRequest extends MWHttpRequest {
if ( $this->method == 'POST' ) {
// Required for HTTP 1.0 POSTs
$this->reqHeaders['Content-Length'] = strlen( $this->postData );
- if( !isset( $this->reqHeaders['Content-Type'] ) ) {
+ if ( !isset( $this->reqHeaders['Content-Type'] ) ) {
$this->reqHeaders['Content-Type'] = "application/x-www-form-urlencoded";
}
}
@@ -860,7 +885,23 @@ class PhpHttpRequest extends MWHttpRequest {
$options['timeout'] = $this->timeout;
- $context = stream_context_create( array( 'http' => $options ) );
+ if ( $this->sslVerifyHost ) {
+ $options['CN_match'] = $this->parsedUrl['host'];
+ }
+ if ( $this->sslVerifyCert ) {
+ $options['verify_peer'] = true;
+ }
+
+ if ( is_dir( $this->caInfo ) ) {
+ $options['capath'] = $this->caInfo;
+ } elseif ( is_file( $this->caInfo ) ) {
+ $options['cafile'] = $this->caInfo;
+ } elseif ( $this->caInfo ) {
+ throw new MWException( "Invalid CA info passed: {$this->caInfo}" );
+ }
+
+ $scheme = $this->parsedUrl['scheme'];
+ $context = stream_context_create( array( "$scheme" => $options ) );
$this->headerList = array();
$reqCount = 0;
@@ -903,18 +944,19 @@ class PhpHttpRequest extends MWHttpRequest {
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;
}
// If everything went OK, or we received some error code
// get the response body content.
- if ( $this->status->isOK()
- || (int)$this->respStatus >= 300) {
+ if ( $this->status->isOK() || (int)$this->respStatus >= 300 ) {
while ( !feof( $fh ) ) {
$buf = fread( $fh, 8192 );
@@ -930,6 +972,8 @@ class PhpHttpRequest extends MWHttpRequest {
}
fclose( $fh );
+ wfProfileOut( __METHOD__ );
+
return $this->status;
}
}
diff --git a/includes/IP.php b/includes/IP.php
index 72b9a52c..73834a59 100644
--- a/includes/IP.php
+++ b/includes/IP.php
@@ -33,24 +33,17 @@ define( 'RE_IP_BLOCK', RE_IP_ADD . '\/' . RE_IP_PREFIX );
// An IPv6 address is made up of 8 words (each x0000 to xFFFF).
// However, the "::" abbreviation can be used on consecutive x0000 words.
define( 'RE_IPV6_WORD', '([0-9A-Fa-f]{1,4})' );
-define( 'RE_IPV6_PREFIX', '(12[0-8]|1[01][0-9]|[1-9]?\d)');
+define( 'RE_IPV6_PREFIX', '(12[0-8]|1[01][0-9]|[1-9]?\d)' );
define( 'RE_IPV6_ADD',
'(?:' . // starts with "::" (including "::")
':(?::|(?::' . RE_IPV6_WORD . '){1,7})' .
'|' . // ends with "::" (except "::")
RE_IPV6_WORD . '(?::' . RE_IPV6_WORD . '){0,6}::' .
- '|' . // contains one "::" in the middle, ending in "::WORD"
- RE_IPV6_WORD . '(?::' . RE_IPV6_WORD . '){0,5}' . '::' . RE_IPV6_WORD .
- '|' . // contains one "::" in the middle, not ending in "::WORD" (regex for PCRE 4.0+)
- RE_IPV6_WORD . '(?::(?P<abn>:(?P<iabn>))?' . RE_IPV6_WORD . '(?!:(?P=abn))){1,5}' .
- ':' . RE_IPV6_WORD . '(?P=iabn)' .
- // NOTE: (?!(?P=abn)) fails iff "::" used twice; (?P=iabn) passes iff a "::" was found.
+ '|' . // contains one "::" in the middle (the ^ makes the test fail if none found)
+ RE_IPV6_WORD . '(?::((?(-1)|:))?' . RE_IPV6_WORD . '){1,6}(?(-2)|^)' .
'|' . // contains no "::"
RE_IPV6_WORD . '(?::' . RE_IPV6_WORD . '){7}' .
')'
- // NOTE: With PCRE 7.2+, we can combine the two '"::" in the middle' cases into:
- // RE_IPV6_WORD . '(?::((?(-1)|:))?' . RE_IPV6_WORD . '){1,6}(?(-2)|^)'
- // This also improves regex concatenation by using relative references.
);
// An IPv6 block is an IP address and a prefix (d1 to d128)
define( 'RE_IPV6_BLOCK', RE_IPV6_ADD . '\/' . RE_IPV6_PREFIX );
@@ -212,7 +205,7 @@ class IP {
$longest = $match;
$longestPos = $pos;
}
- $offset += ( $pos + strlen( $match ) ); // advance
+ $offset = ( $pos + strlen( $match ) ); // advance
}
if ( $longest !== false ) {
// Replace this portion of the string with the '::' abbreviation
@@ -392,11 +385,11 @@ class IP {
static $privateRanges = false;
if ( !$privateRanges ) {
$privateRanges = array(
- array( '10.0.0.0', '10.255.255.255' ), # RFC 1918 (private)
- array( '172.16.0.0', '172.31.255.255' ), # "
- array( '192.168.0.0', '192.168.255.255' ), # "
- array( '0.0.0.0', '0.255.255.255' ), # this network
- array( '127.0.0.0', '127.255.255.255' ), # loopback
+ array( '10.0.0.0', '10.255.255.255' ), # RFC 1918 (private)
+ array( '172.16.0.0', '172.31.255.255' ), # RFC 1918 (private)
+ array( '192.168.0.0', '192.168.255.255' ), # RFC 1918 (private)
+ array( '0.0.0.0', '0.255.255.255' ), # this network
+ array( '127.0.0.0', '127.255.255.255' ), # loopback
);
}
@@ -492,6 +485,11 @@ class IP {
$n = ip2long( $ip );
if ( $n < 0 ) {
$n += pow( 2, 32 );
+ # On 32-bit platforms (and on Windows), 2^32 does not fit into an int,
+ # so $n becomes a float. We convert it to string instead.
+ if ( is_float( $n ) ) {
+ $n = (string)$n;
+ }
}
}
return $n;
@@ -526,7 +524,7 @@ class IP {
if ( $bits == 0 ) {
$network = 0;
} else {
- $network &= ~( ( 1 << ( 32 - $bits ) ) - 1);
+ $network &= ~( ( 1 << ( 32 - $bits ) ) - 1 );
}
# Convert to unsigned
if ( $network < 0 ) {
diff --git a/includes/ImagePage.php b/includes/ImagePage.php
index b3a485aa..7ea06b0e 100644
--- a/includes/ImagePage.php
+++ b/includes/ImagePage.php
@@ -206,7 +206,7 @@ class ImagePage extends Article {
}
// Add remote Filepage.css
- if( !$this->repo->isLocal() ) {
+ if ( !$this->repo->isLocal() ) {
$css = $this->repo->getDescriptionStylesheetUrl();
if ( $css ) {
$out->addStyle( $css );
@@ -250,7 +250,7 @@ class ImagePage extends Article {
*
* @todo FIXME: Bad interface, see note on MediaHandler::formatMetadata().
*
- * @param array $metadata the array containing the EXIF data
+ * @param array $metadata the array containing the Exif data
* @return String The metadata table. This is treated as Wikitext (!)
*/
protected function makeMetadataTable( $metadata ) {
@@ -311,6 +311,12 @@ class ImagePage extends Article {
} else {
$params = array( 'page' => $page );
}
+
+ $renderLang = $request->getVal( 'lang' );
+ if ( !is_null( $renderLang ) ) {
+ $params['lang'] = $renderLang;
+ }
+
$width_orig = $this->displayImg->getWidth( $page );
$width = $width_orig;
$height_orig = $this->displayImg->getHeight( $page );
@@ -395,6 +401,7 @@ class ImagePage extends Article {
$isMulti = $this->displayImg->isMultipage() && $this->displayImg->pageCount() > 1;
if ( $isMulti ) {
+ $out->addModules( 'mediawiki.page.image.pagination' );
$out->addHTML( '<table class="multipageimage"><tr><td>' );
}
@@ -444,7 +451,6 @@ class ImagePage extends Article {
$formParams = array(
'name' => 'pageselector',
'action' => $wgScript,
- 'onchange' => 'document.pageselector.submit();',
);
$options = array();
for ( $i = 1; $i <= $count; $i++ ) {
@@ -597,7 +603,7 @@ EOT
$descText = $this->mPage->getFile()->getDescriptionText();
/* Add canonical to head if there is no local page for this shared file */
- if( $descUrl && $this->mPage->getID() == 0 ) {
+ if ( $descUrl && $this->mPage->getID() == 0 ) {
$out->setCanonicalUrl( $descUrl );
}
@@ -631,7 +637,7 @@ EOT
* external editing (and instructions link) etc.
*/
protected function uploadLinksBox() {
- global $wgEnableUploads, $wgUseExternalEditor;
+ global $wgEnableUploads;
if ( !$wgEnableUploads ) {
return;
@@ -654,25 +660,6 @@ EOT
$out->addHTML( "<li id=\"mw-imagepage-upload-disallowed\">" . $this->getContext()->msg( 'upload-disallowed-here' )->escaped() . "</li>\n" );
}
- # External editing link
- if ( $wgUseExternalEditor ) {
- $elink = Linker::linkKnown(
- $this->getTitle(),
- wfMessage( 'edit-externally' )->escaped(),
- array(),
- array(
- 'action' => 'edit',
- 'externaledit' => 'true',
- 'mode' => 'file'
- )
- );
- $out->addHTML(
- '<li id="mw-imagepage-edit-external">' . $elink . ' <small>' .
- wfMessage( 'edit-externally-help' )->parse() .
- "</small></li>\n"
- );
- }
-
$out->addHTML( "</ul>\n" );
}
@@ -719,7 +706,7 @@ EOT
$limit = 100;
$out = $this->getContext()->getOutput();
- $res = $this->queryImageLinks( $this->getTitle()->getDbKey(), $limit + 1);
+ $res = $this->queryImageLinks( $this->getTitle()->getDBkey(), $limit + 1 );
$rows = array();
$redirects = array();
foreach ( $res as $row ) {
@@ -772,13 +759,21 @@ EOT
// Create links for every element
$currentCount = 0;
- foreach( $rows as $element ) {
+ foreach ( $rows as $element ) {
$currentCount++;
if ( $currentCount > $limit ) {
break;
}
- $link = Linker::linkKnown( Title::makeTitle( $element->page_namespace, $element->page_title ) );
+ $query = array();
+ # Add a redirect=no to make redirect pages reachable
+ if ( isset( $redirects[$element->page_title] ) ) {
+ $query['redirect'] = 'no';
+ }
+ $link = Linker::linkKnown(
+ Title::makeTitle( $element->page_namespace, $element->page_title ),
+ null, array(), $query
+ );
if ( !isset( $redirects[$element->page_title] ) ) {
# No redirects
$liContents = $link;
diff --git a/includes/ImageQueryPage.php b/includes/ImageQueryPage.php
index f9f6ceed..75f7ba64 100644
--- a/includes/ImageQueryPage.php
+++ b/includes/ImageQueryPage.php
@@ -29,25 +29,25 @@
* @author Rob Church <robchur@gmail.com>
*/
abstract class ImageQueryPage extends QueryPage {
-
/**
* Format and output report results using the given information plus
* OutputPage
*
- * @param $out OutputPage to print to
- * @param $skin Skin: user skin to use [unused]
- * @param $dbr DatabaseBase (read) connection to use
- * @param $res Integer: result pointer
- * @param $num Integer: number of available result rows
- * @param $offset Integer: paging offset
+ * @param OutputPage $out OutputPage to print to
+ * @param Skin $skin User skin to use [unused]
+ * @param DatabaseBase $dbr (read) connection to use
+ * @param int $res Result pointer
+ * @param int $num Number of available result rows
+ * @param int $offset Paging offset
*/
protected function outputResults( $out, $skin, $dbr, $res, $num, $offset ) {
- if( $num > 0 ) {
- $gallery = new ImageGallery();
+ if ( $num > 0 ) {
+ $gallery = ImageGalleryBase::factory();
+ $gallery->setContext( $this->getContext() );
# $res might contain the whole 1,000 rows, so we read up to
# $num [should update this to use a Pager]
- for( $i = 0; $i < $num && $row = $dbr->fetchObject( $res ); $i++ ) {
+ for ( $i = 0; $i < $num && $row = $dbr->fetchObject( $res ); $i++ ) {
$namespace = isset( $row->namespace ) ? $row->namespace : NS_FILE;
$title = Title::makeTitleSafe( $namespace, $row->title );
if ( $title instanceof Title && $title->getNamespace() == NS_FILE ) {
@@ -65,11 +65,10 @@ abstract class ImageQueryPage extends QueryPage {
/**
* Get additional HTML to be shown in a results' cell
*
- * @param $row Object: result row
- * @return String
+ * @param object $row Result row
+ * @return string
*/
protected function getCellHtml( $row ) {
return '';
}
-
}
diff --git a/includes/Import.php b/includes/Import.php
index bb5d6349..8b7af02a 100644
--- a/includes/Import.php
+++ b/includes/Import.php
@@ -47,7 +47,7 @@ class WikiImporter {
stream_wrapper_register( 'uploadsource', 'UploadSourceAdapter' );
$id = UploadSourceAdapter::registerSource( $source );
- if (defined( 'LIBXML_PARSEHUGE' ) ) {
+ if ( defined( 'LIBXML_PARSEHUGE' ) ) {
$this->reader->open( "uploadsource://$id", null, LIBXML_PARSEHUGE );
} else {
$this->reader->open( "uploadsource://$id" );
@@ -66,7 +66,7 @@ class WikiImporter {
}
private function debug( $data ) {
- if( $this->mDebug ) {
+ if ( $this->mDebug ) {
wfDebug( "IMPORT: $data\n" );
}
}
@@ -188,10 +188,10 @@ class WikiImporter {
* @return bool
*/
public function setTargetNamespace( $namespace ) {
- if( is_null( $namespace ) ) {
+ if ( is_null( $namespace ) ) {
// Don't override namespaces
$this->mTargetNamespace = null;
- } elseif( $namespace >= 0 ) {
+ } elseif ( $namespace >= 0 ) {
// @todo FIXME: Check for validity
$this->mTargetNamespace = intval( $namespace );
} else {
@@ -206,16 +206,16 @@ class WikiImporter {
*/
public function setTargetRootPage( $rootpage ) {
$status = Status::newGood();
- if( is_null( $rootpage ) ) {
+ if ( is_null( $rootpage ) ) {
// No rootpage
$this->mTargetRootPage = null;
- } elseif( $rootpage !== '' ) {
+ } elseif ( $rootpage !== '' ) {
$rootpage = rtrim( $rootpage, '/' ); //avoid double slashes
$title = Title::newFromText( $rootpage, !is_null( $this->mTargetNamespace ) ? $this->mTargetNamespace : NS_MAIN );
- if( !$title || $title->isExternal() ) {
+ if ( !$title || $title->isExternal() ) {
$status->fatal( 'import-rootpage-invalid' );
} else {
- if( !MWNamespace::hasSubpages( $title->getNamespace() ) ) {
+ if ( !MWNamespace::hasSubpages( $title->getNamespace() ) ) {
global $wgContLang;
$displayNSText = $title->getNamespace() == NS_MAIN
@@ -304,7 +304,7 @@ class WikiImporter {
*/
public function debugRevisionHandler( &$revision ) {
$this->debug( "Got revision:" );
- if( is_object( $revision->title ) ) {
+ if ( is_object( $revision->title ) ) {
$this->debug( "-- Title: " . $revision->title->getPrefixedText() );
} else {
$this->debug( "-- Title: <invalid>" );
@@ -320,7 +320,7 @@ class WikiImporter {
* @param $title Title
*/
function pageCallback( $title ) {
- if( isset( $this->mPageCallback ) ) {
+ if ( isset( $this->mPageCallback ) ) {
call_user_func( $this->mPageCallback, $title );
}
}
@@ -334,7 +334,7 @@ class WikiImporter {
* @param array $pageInfo associative array of page information
*/
private function pageOutCallback( $title, $origTitle, $revCount, $sucCount, $pageInfo ) {
- if( isset( $this->mPageOutCallback ) ) {
+ if ( isset( $this->mPageOutCallback ) ) {
$args = func_get_args();
call_user_func_array( $this->mPageOutCallback, $args );
}
@@ -376,12 +376,12 @@ class WikiImporter {
* @access private
*/
private function nodeContents() {
- if( $this->reader->isEmptyElement ) {
+ if ( $this->reader->isEmptyElement ) {
return "";
}
$buffer = "";
- while( $this->reader->read() ) {
- switch( $this->reader->nodeType ) {
+ while ( $this->reader->read() ) {
+ switch ( $this->reader->nodeType ) {
case XmlReader::TEXT:
case XmlReader::SIGNIFICANT_WHITESPACE:
$buffer .= $this->reader->value;
@@ -420,19 +420,19 @@ class WikiImporter {
"END_ELEMENT",
"END_ENTITY",
"XML_DECLARATION",
- );
+ );
$lookup = array();
- foreach( $xmlReaderConstants as $name ) {
- $lookup[constant("XmlReader::$name")] = $name;
+ foreach ( $xmlReaderConstants as $name ) {
+ $lookup[constant( "XmlReader::$name" )] = $name;
}
}
- print( var_dump(
+ print var_dump(
$lookup[$this->reader->nodeType],
$this->reader->name,
$this->reader->value
- )."\n\n" );
+ ) . "\n\n";
}
/**
@@ -450,7 +450,7 @@ class WikiImporter {
if ( $this->reader->name != 'mediawiki' ) {
libxml_disable_entity_loader( $oldDisable );
- throw new MWException( "Expected <mediawiki> tag, got ".
+ throw new MWException( "Expected <mediawiki> tag, got " .
$this->reader->name );
}
$this->debug( "<mediawiki> tag is correct." );
@@ -463,7 +463,7 @@ class WikiImporter {
$tag = $this->reader->name;
$type = $this->reader->nodeType;
- if ( !wfRunHooks( 'ImportHandleToplevelXMLTag', $this ) ) {
+ if ( !wfRunHooks( 'ImportHandleToplevelXMLTag', array( $this ) ) ) {
// Do nothing
} elseif ( $tag == 'mediawiki' && $type == XmlReader::END_ELEMENT ) {
break;
@@ -522,8 +522,9 @@ class WikiImporter {
$tag = $this->reader->name;
- if ( !wfRunHooks( 'ImportHandleLogItemXMLTag',
- $this, $logInfo ) ) {
+ if ( !wfRunHooks( 'ImportHandleLogItemXMLTag', array(
+ $this, $logInfo
+ ) ) ) {
// Do nothing
} elseif ( in_array( $tag, $normalFields ) ) {
$logInfo[$tag] = $this->nodeContents();
@@ -639,8 +640,9 @@ class WikiImporter {
$tag = $this->reader->name;
- if ( !wfRunHooks( 'ImportHandleRevisionXMLTag', $this,
- $pageInfo, $revisionInfo ) ) {
+ if ( !wfRunHooks( 'ImportHandleRevisionXMLTag', array(
+ $this, $pageInfo, $revisionInfo
+ ) ) ) {
// Do nothing
} elseif ( in_array( $tag, $normalFields ) ) {
$revisionInfo[$tag] = $this->nodeContents();
@@ -666,7 +668,7 @@ class WikiImporter {
private function processRevision( $pageInfo, $revisionInfo ) {
$revision = new WikiRevision;
- if( isset( $revisionInfo['id'] ) ) {
+ if ( isset( $revisionInfo['id'] ) ) {
$revision->setID( $revisionInfo['id'] );
}
if ( isset( $revisionInfo['text'] ) ) {
@@ -725,8 +727,9 @@ class WikiImporter {
$tag = $this->reader->name;
- if ( !wfRunHooks( 'ImportHandleUploadXMLTag', $this,
- $pageInfo ) ) {
+ if ( !wfRunHooks( 'ImportHandleUploadXMLTag', array(
+ $this, $pageInfo
+ ) ) ) {
// Do nothing
} elseif ( in_array( $tag, $normalFields ) ) {
$uploadInfo[$tag] = $this->nodeContents();
@@ -840,33 +843,33 @@ class WikiImporter {
$workTitle = $text;
$origTitle = Title::newFromText( $workTitle );
- if( !is_null( $this->mTargetNamespace ) && !is_null( $origTitle ) ) {
+ 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() );
} else {
- if( !is_null( $this->mTargetRootPage ) ) {
+ if ( !is_null( $this->mTargetRootPage ) ) {
$workTitle = $this->mTargetRootPage . '/' . $workTitle;
}
$title = Title::newFromText( $workTitle );
}
- if( is_null( $title ) ) {
+ if ( is_null( $title ) ) {
# Invalid page title? Ignore the page
$this->notice( 'import-error-invalid', $workTitle );
return false;
- } elseif( $title->isExternal() ) {
+ } elseif ( $title->isExternal() ) {
$this->notice( 'import-error-interwiki', $title->getPrefixedText() );
return false;
- } elseif( !$title->canExist() ) {
+ } elseif ( !$title->canExist() ) {
$this->notice( 'import-error-special', $title->getPrefixedText() );
return false;
- } elseif( !$title->userCan( 'edit' ) && !$wgCommandLineMode ) {
+ } elseif ( !$title->userCan( 'edit' ) && !$wgCommandLineMode ) {
# 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' ) && !$wgCommandLineMode ) {
# Do not import if the importing wiki user cannot create this page
$this->notice( 'import-error-create', $title->getPrefixedText() );
return false;
@@ -997,12 +1000,12 @@ class XMLReader2 extends XMLReader {
* @return bool|string
*/
function nodeContents() {
- if( $this->isEmptyElement ) {
+ if ( $this->isEmptyElement ) {
return "";
}
$buffer = "";
- while( $this->read() ) {
- switch( $this->nodeType ) {
+ while ( $this->read() ) {
+ switch ( $this->nodeType ) {
case XmlReader::TEXT:
case XmlReader::SIGNIFICANT_WHITESPACE:
$buffer .= $this->value;
@@ -1051,9 +1054,9 @@ class WikiRevision {
* @throws MWException
*/
function setTitle( $title ) {
- if( is_object( $title ) ) {
+ if ( is_object( $title ) ) {
$this->title = $title;
- } elseif( is_null( $title ) ) {
+ } elseif ( is_null( $title ) ) {
throw new MWException( "WikiRevision given a null title in import. You may need to adjust \$wgLegalTitleChars." );
} else {
throw new MWException( "WikiRevision given non-object title in import." );
@@ -1369,7 +1372,7 @@ class WikiRevision {
# Sneak a single revision into place
$user = User::newFromName( $this->getUser() );
- if( $user ) {
+ if ( $user ) {
$userId = intval( $user->getId() );
$userText = $user->getName();
$userObj = $user;
@@ -1384,7 +1387,7 @@ class WikiRevision {
$linkCache->clear();
$page = WikiPage::factory( $this->title );
- if( !$page->exists() ) {
+ if ( !$page->exists() ) {
# must create the page...
$pageId = $page->insertOn( $dbw );
$created = true;
@@ -1400,7 +1403,7 @@ class WikiRevision {
'rev_comment' => $this->getComment() ),
__METHOD__
);
- if( $prior ) {
+ if ( $prior ) {
// @todo FIXME: This could fail slightly for multiple matches :P
wfDebug( __METHOD__ . ": skipping existing revision for [[" .
$this->title->getPrefixedText() . "]], timestamp " . $this->timestamp . "\n" );
@@ -1440,7 +1443,7 @@ class WikiRevision {
function importLogItem() {
$dbw = wfGetDB( DB_MASTER );
# @todo FIXME: This will not record autoblocks
- if( !$this->getTitle() ) {
+ if ( !$this->getTitle() ) {
wfDebug( __METHOD__ . ": skipping invalid {$this->type}/{$this->action} log time, timestamp " .
$this->timestamp . "\n" );
return;
@@ -1459,7 +1462,7 @@ class WikiRevision {
__METHOD__
);
// @todo FIXME: This could fail slightly for multiple matches :P
- if( $prior ) {
+ if ( $prior ) {
wfDebug( __METHOD__ . ": skipping existing item for Log:{$this->type}/{$this->action}, timestamp " .
$this->timestamp . "\n" );
return;
@@ -1500,7 +1503,7 @@ class WikiRevision {
wfDebug( __METHOD__ . "File already exists; importing as $archiveName\n" );
}
}
- if( !$file ) {
+ if ( !$file ) {
wfDebug( __METHOD__ . ': Bad file for ' . $this->getTitle() . "\n" );
return false;
}
@@ -1512,7 +1515,7 @@ class WikiRevision {
$source = $this->downloadSource();
$flags |= File::DELETE_SOURCE;
}
- if( !$source ) {
+ if ( !$source ) {
wfDebug( __METHOD__ . ": Could not fetch remote file.\n" );
return false;
}
@@ -1551,13 +1554,13 @@ class WikiRevision {
*/
function downloadSource() {
global $wgEnableUploads;
- if( !$wgEnableUploads ) {
+ if ( !$wgEnableUploads ) {
return false;
}
$tempo = tempnam( wfTempDir(), 'download' );
$f = fopen( $tempo, 'wb' );
- if( !$f ) {
+ if ( !$f ) {
wfDebug( "IMPORT: couldn't write to temp file $tempo\n" );
return false;
}
@@ -1565,7 +1568,7 @@ class WikiRevision {
// @todo FIXME!
$src = $this->getSrc();
$data = Http::get( $src );
- if( !$data ) {
+ if ( !$data ) {
wfDebug( "IMPORT: couldn't fetch source $src\n" );
fclose( $f );
unlink( $tempo );
@@ -1601,7 +1604,7 @@ class ImportStringSource {
* @return bool|string
*/
function readChunk() {
- if( $this->atEnd() ) {
+ if ( $this->atEnd() ) {
return false;
}
$this->mRead = true;
@@ -1640,7 +1643,7 @@ class ImportStreamSource {
wfSuppressWarnings();
$file = fopen( $filename, 'rt' );
wfRestoreWarnings();
- if( !$file ) {
+ if ( !$file ) {
return Status::newFatal( "importcantopen" );
}
return Status::newGood( new ImportStreamSource( $file ) );
@@ -1653,11 +1656,11 @@ class ImportStreamSource {
static function newFromUpload( $fieldname = "xmlimport" ) {
$upload =& $_FILES[$fieldname];
- if( $upload === null || !$upload['name'] ) {
+ if ( $upload === null || !$upload['name'] ) {
return Status::newFatal( 'importnofile' );
}
- if( !empty( $upload['error'] ) ) {
- switch( $upload['error'] ) {
+ if ( !empty( $upload['error'] ) ) {
+ switch ( $upload['error'] ) {
case 1: # The uploaded file exceeds the upload_max_filesize directive in php.ini.
return Status::newFatal( 'importuploaderrorsize' );
case 2: # The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
@@ -1671,7 +1674,7 @@ class ImportStreamSource {
}
$fname = $upload['tmp_name'];
- if( is_uploaded_file( $fname ) ) {
+ if ( is_uploaded_file( $fname ) ) {
return ImportStreamSource::newFromFile( $fname );
} else {
return Status::newFatal( 'importnofile' );
@@ -1690,7 +1693,7 @@ class ImportStreamSource {
# otherwise prevent importing from large sites, such
# as the Wikimedia cluster, etc.
$data = Http::request( $method, $url, array( 'followRedirects' => true ) );
- if( $data !== false ) {
+ if ( $data !== false ) {
$file = tmpfile();
fwrite( $file, $data );
fflush( $file );
@@ -1710,18 +1713,24 @@ class ImportStreamSource {
* @return Status
*/
public static function newFromInterwiki( $interwiki, $page, $history = false, $templates = false, $pageLinkDepth = 0 ) {
- if( $page == '' ) {
+ if ( $page == '' ) {
return Status::newFatal( 'import-noarticle' );
}
$link = Title::newFromText( "$interwiki:Special:Export/$page" );
- if( is_null( $link ) || $link->getInterwiki() == '' ) {
+ if ( is_null( $link ) || $link->getInterwiki() == '' ) {
return Status::newFatal( 'importbadinterwiki' );
} else {
$params = array();
- if ( $history ) $params['history'] = 1;
- if ( $templates ) $params['templates'] = 1;
- if ( $pageLinkDepth ) $params['pagelink-depth'] = $pageLinkDepth;
- $url = $link->getFullUrl( $params );
+ if ( $history ) {
+ $params['history'] = 1;
+ }
+ if ( $templates ) {
+ $params['templates'] = 1;
+ }
+ if ( $pageLinkDepth ) {
+ $params['pagelink-depth'] = $pageLinkDepth;
+ }
+ $url = $link->getFullURL( $params );
# For interwikis, use POST to avoid redirects.
return ImportStreamSource::newFromURL( $url, "POST" );
}
diff --git a/includes/Init.php b/includes/Init.php
index 66f9544d..64431f09 100644
--- a/includes/Init.php
+++ b/includes/Init.php
@@ -2,6 +2,9 @@
/**
* Some functions that are useful during startup.
*
+ * This class previously contained some functionality related to a PHP compiler
+ * called hphpc. That compiler has now been discontinued.
+ *
* 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
@@ -22,40 +25,35 @@
/**
* Some functions that are useful during startup.
+ *
+ * This class previously contained some functionality related to a PHP compiler
+ * called hphpc. That compiler has now been discontinued. All methods are now
+ * deprecated.
*/
class MWInit {
static $compilerVersion;
/**
- * Get the version of HipHop used to compile, or false if MediaWiki was not
- * compiled. This works by having our build script insert a special function
- * into the compiled code.
+ * @deprecated since 1.22
*/
static function getCompilerVersion() {
- if ( self::$compilerVersion === null ) {
- if ( self::functionExists( 'wfHipHopCompilerVersion' ) ) {
- self::$compilerVersion = wfHipHopCompilerVersion();
- } else {
- self::$compilerVersion = false;
- }
- }
- return self::$compilerVersion;
+ return false;
}
/**
* Returns true if we are running under HipHop, whether in compiled or
* interpreted mode.
*
+ * @deprecated since 1.22
* @return bool
*/
static function isHipHop() {
- return function_exists( 'hphp_thread_set_warmup_enabled' );
+ return defined( 'HPHP_VERSION' );
}
/**
- * Get a fully-qualified path for a source file relative to $IP. Including
- * such a path under HipHop will force the file to be interpreted. This is
- * useful for configuration files.
+ * Get a fully-qualified path for a source file relative to $IP.
+ * @deprecated since 1.22
*
* @param $file string
*
@@ -67,117 +65,39 @@ class MWInit {
}
/**
- * If we are running code compiled by HipHop, this will pass through the
- * input path, assumed to be relative to $IP. If the code is interpreted,
- * it will converted to a fully qualified path. It is necessary to use a
- * path which is relative to $IP in order to make HipHop use its compiled
- * code.
- *
+ * @deprecated since 1.22
* @param $file string
- *
* @return string
*/
static function compiledPath( $file ) {
global $IP;
-
- if ( defined( 'MW_COMPILED' ) ) {
- return "phase3/$file";
- } else {
- return "$IP/$file";
- }
- }
-
- /**
- * The equivalent of MWInit::interpretedPath() but for files relative to the
- * extensions directory.
- *
- * @param $file string
- * @return string
- */
- static function extInterpretedPath( $file ) {
- return self::getExtensionsDirectory() . '/' . $file;
+ return "$IP/$file";
}
/**
- * The equivalent of MWInit::compiledPath() but for file