summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/Action.php467
-rw-r--r--includes/AjaxDispatcher.php15
-rw-r--r--includes/AjaxFunctions.php101
-rw-r--r--includes/AjaxResponse.php4
-rw-r--r--includes/Article.php3667
-rw-r--r--includes/AuthPlugin.php13
-rw-r--r--includes/AutoLoader.php522
-rw-r--r--includes/Autopromote.php72
-rw-r--r--includes/BacklinkCache.php131
-rw-r--r--includes/BagOStuff.php906
-rw-r--r--includes/Block.php1156
-rw-r--r--includes/Category.php11
-rw-r--r--includes/CategoryPage.php197
-rw-r--r--includes/Categoryfinder.php7
-rw-r--r--includes/Cdb.php12
-rw-r--r--includes/Cdb_PHP.php76
-rw-r--r--includes/ChangeTags.php36
-rw-r--r--includes/ChangesFeed.php54
-rw-r--r--includes/ChangesList.php564
-rw-r--r--includes/Collation.php52
-rw-r--r--includes/ConfEditor.php78
-rw-r--r--includes/Cookie.php245
-rw-r--r--includes/DefaultSettings.php833
-rw-r--r--includes/Defines.php41
-rw-r--r--includes/DjVuImage.php12
-rw-r--r--includes/EditPage.php905
-rw-r--r--includes/Exception.php351
-rw-r--r--includes/Exif.php1150
-rw-r--r--includes/Export.php448
-rw-r--r--includes/ExternalEdit.php63
-rw-r--r--includes/ExternalStore.php16
-rw-r--r--includes/ExternalStoreDB.php16
-rw-r--r--includes/ExternalUser.php4
-rw-r--r--includes/FakeTitle.php6
-rw-r--r--includes/Fallback.php200
-rw-r--r--includes/Feed.php52
-rw-r--r--includes/FeedUtils.php2
-rw-r--r--includes/FileDeleteForm.php36
-rw-r--r--includes/FileRevertForm.php180
-rw-r--r--includes/ForkController.php6
-rw-r--r--includes/FormOptions.php121
-rw-r--r--includes/GenderCache.php135
-rw-r--r--includes/GlobalFunctions.php2092
-rw-r--r--includes/HTMLForm.php507
-rw-r--r--includes/HistoryBlob.php88
-rw-r--r--includes/HistoryPage.php50
-rw-r--r--includes/Hooks.php384
-rw-r--r--includes/Html.php102
-rw-r--r--includes/HttpFunctions.old.php1
-rw-r--r--includes/HttpFunctions.php306
-rw-r--r--includes/IP.php86
-rw-r--r--includes/ImageFunctions.php2
-rw-r--r--includes/ImageGallery.php147
-rw-r--r--includes/ImagePage.php660
-rw-r--r--includes/ImageQueryPage.php29
-rw-r--r--includes/Import.php290
-rw-r--r--includes/Init.php184
-rw-r--r--includes/Licenses.php49
-rw-r--r--includes/LinkFilter.php36
-rw-r--r--includes/Linker.php971
-rw-r--r--includes/LinksUpdate.php44
-rw-r--r--includes/LocalisationCache.php130
-rw-r--r--includes/LogEventsList.php277
-rw-r--r--includes/LogPage.php135
-rw-r--r--includes/MWFunction.php64
-rw-r--r--includes/MacBinary.php272
-rw-r--r--includes/MagicWord.php169
-rw-r--r--includes/Math.php341
-rw-r--r--includes/Message.php194
-rw-r--r--includes/MessageBlobStore.php56
-rw-r--r--includes/Metadata.php146
-rw-r--r--includes/MimeMagic.php475
-rw-r--r--includes/Namespace.php106
-rw-r--r--includes/ObjectCache.php123
-rw-r--r--includes/OutputHandler.php22
-rw-r--r--includes/OutputPage.php1537
-rw-r--r--includes/PHPVersionError.php91
-rw-r--r--includes/PageQueryPage.php2
-rw-r--r--includes/Pager.php40
-rw-r--r--includes/PatrolLog.php12
-rw-r--r--includes/PoolCounter.php115
-rw-r--r--includes/Preferences.php244
-rw-r--r--includes/PrefixSearch.php40
-rw-r--r--includes/ProfilerSimpleText.php39
-rw-r--r--includes/ProfilerStub.php52
-rw-r--r--includes/ProtectionForm.php80
-rw-r--r--includes/ProxyTools.php107
-rw-r--r--includes/QueryPage.php393
-rw-r--r--includes/RawPage.php38
-rw-r--r--includes/RecentChange.php157
-rw-r--r--includes/RequestContext.php399
-rw-r--r--includes/Revision.php124
-rw-r--r--includes/RevisionList.php370
-rw-r--r--includes/Sanitizer.php760
-rw-r--r--includes/SeleniumWebSettings.php197
-rw-r--r--includes/Setup.php463
-rw-r--r--includes/SiteConfiguration.php43
-rw-r--r--includes/SiteStats.php148
-rw-r--r--includes/Skin.php1761
-rw-r--r--includes/SkinLegacy.php942
-rw-r--r--includes/SkinTemplate.php1275
-rw-r--r--includes/SpecialPage.php1164
-rw-r--r--includes/SpecialPageFactory.php554
-rw-r--r--includes/SquidPurgeClient.php28
-rw-r--r--includes/Status.php54
-rw-r--r--includes/StreamFile.php15
-rw-r--r--includes/StringUtils.php22
-rw-r--r--includes/StubObject.php22
-rw-r--r--includes/Title.php1394
-rw-r--r--includes/TitleArray.php22
-rw-r--r--includes/User.php1740
-rw-r--r--includes/UserArray.php35
-rw-r--r--includes/UserMailer.php183
-rw-r--r--includes/ViewCountUpdate.php111
-rw-r--r--includes/WatchedItem.php5
-rw-r--r--includes/WatchlistEditor.php528
-rw-r--r--includes/WebRequest.php375
-rw-r--r--includes/WebResponse.php130
-rw-r--r--includes/WebStart.php82
-rw-r--r--includes/Wiki.php639
-rw-r--r--includes/WikiCategoryPage.php37
-rw-r--r--includes/WikiError.php12
-rw-r--r--includes/WikiFilePage.php139
-rw-r--r--includes/WikiMap.php21
-rw-r--r--includes/WikiPage.php2677
-rw-r--r--includes/Xml.php202
-rw-r--r--includes/XmlTypeCheck.php17
-rw-r--r--includes/ZhClient.php88
-rw-r--r--includes/ZhConversion.php230
-rw-r--r--includes/ZipDirectoryReader.php684
-rw-r--r--includes/actions/CreditsAction.php (renamed from includes/Credits.php)109
-rw-r--r--includes/actions/DeletetrackbackAction.php54
-rw-r--r--includes/actions/InfoAction.php151
-rw-r--r--includes/actions/MarkpatrolledAction.php86
-rw-r--r--includes/actions/PurgeAction.php100
-rw-r--r--includes/actions/RevertAction.php140
-rw-r--r--includes/actions/RevisiondeleteAction.php53
-rw-r--r--includes/actions/RollbackAction.php122
-rw-r--r--includes/actions/WatchAction.php183
-rw-r--r--includes/api/ApiBase.php276
-rw-r--r--includes/api/ApiBlock.php85
-rw-r--r--includes/api/ApiComparePages.php130
-rw-r--r--includes/api/ApiDelete.php42
-rw-r--r--includes/api/ApiDisabled.php6
-rw-r--r--includes/api/ApiEditPage.php105
-rw-r--r--includes/api/ApiEmailUser.php8
-rw-r--r--includes/api/ApiExpandTemplates.php21
-rw-r--r--includes/api/ApiFeedContributions.php207
-rw-r--r--includes/api/ApiFeedWatchlist.php41
-rw-r--r--includes/api/ApiFileRevert.php189
-rw-r--r--includes/api/ApiFormatBase.php22
-rw-r--r--includes/api/ApiFormatDbg.php6
-rw-r--r--includes/api/ApiFormatDump.php4
-rw-r--r--includes/api/ApiFormatJson.php4
-rw-r--r--includes/api/ApiFormatPhp.php4
-rw-r--r--includes/api/ApiFormatRaw.php8
-rw-r--r--includes/api/ApiFormatTxt.php6
-rw-r--r--includes/api/ApiFormatWddx.php4
-rw-r--r--includes/api/ApiFormatXml.php24
-rw-r--r--includes/api/ApiFormatYaml.php14
-rw-r--r--includes/api/ApiHelp.php17
-rw-r--r--includes/api/ApiImport.php38
-rw-r--r--includes/api/ApiLogin.php25
-rw-r--r--includes/api/ApiLogout.php10
-rw-r--r--includes/api/ApiMain.php256
-rw-r--r--includes/api/ApiMove.php49
-rw-r--r--includes/api/ApiOpenSearch.php12
-rw-r--r--includes/api/ApiPageSet.php127
-rw-r--r--includes/api/ApiParamInfo.php83
-rw-r--r--includes/api/ApiParse.php209
-rw-r--r--includes/api/ApiPatrol.php10
-rw-r--r--includes/api/ApiProtect.php24
-rw-r--r--includes/api/ApiPurge.php47
-rw-r--r--includes/api/ApiQuery.php74
-rw-r--r--includes/api/ApiQueryAllCategories.php28
-rw-r--r--includes/api/ApiQueryAllLinks.php31
-rw-r--r--includes/api/ApiQueryAllUsers.php189
-rw-r--r--includes/api/ApiQueryAllimages.php68
-rw-r--r--includes/api/ApiQueryAllmessages.php135
-rw-r--r--includes/api/ApiQueryAllpages.php74
-rw-r--r--includes/api/ApiQueryBacklinks.php80
-rw-r--r--includes/api/ApiQueryBase.php104
-rw-r--r--includes/api/ApiQueryBlocks.php86
-rw-r--r--includes/api/ApiQueryCategories.php14
-rw-r--r--includes/api/ApiQueryCategoryInfo.php8
-rw-r--r--includes/api/ApiQueryCategoryMembers.php88
-rw-r--r--includes/api/ApiQueryDeletedrevs.php117
-rw-r--r--includes/api/ApiQueryDisabled.php6
-rw-r--r--includes/api/ApiQueryDuplicateFiles.php14
-rw-r--r--includes/api/ApiQueryExtLinksUsage.php109
-rw-r--r--includes/api/ApiQueryExternalLinks.php52
-rw-r--r--includes/api/ApiQueryFilearchive.php113
-rw-r--r--includes/api/ApiQueryIWBacklinks.php13
-rw-r--r--includes/api/ApiQueryIWLinks.php33
-rw-r--r--includes/api/ApiQueryImageInfo.php227
-rw-r--r--includes/api/ApiQueryImages.php28
-rw-r--r--includes/api/ApiQueryInfo.php50
-rw-r--r--includes/api/ApiQueryLangBacklinks.php220
-rw-r--r--includes/api/ApiQueryLangLinks.php39
-rw-r--r--includes/api/ApiQueryLinks.php18
-rw-r--r--includes/api/ApiQueryLogEvents.php82
-rw-r--r--includes/api/ApiQueryPageProps.php62
-rw-r--r--includes/api/ApiQueryProtectedTitles.php32
-rw-r--r--includes/api/ApiQueryQueryPage.php198
-rw-r--r--includes/api/ApiQueryRandom.php22
-rw-r--r--includes/api/ApiQueryRecentChanges.php132
-rw-r--r--includes/api/ApiQueryRevisions.php57
-rw-r--r--includes/api/ApiQuerySearch.php38
-rw-r--r--includes/api/ApiQuerySiteinfo.php140
-rw-r--r--includes/api/ApiQueryStashImageInfo.php93
-rw-r--r--includes/api/ApiQueryTags.php20
-rw-r--r--includes/api/ApiQueryUserContributions.php30
-rw-r--r--includes/api/ApiQueryUserInfo.php41
-rw-r--r--includes/api/ApiQueryUsers.php97
-rw-r--r--includes/api/ApiQueryWatchlist.php94
-rw-r--r--includes/api/ApiQueryWatchlistRaw.php14
-rw-r--r--includes/api/ApiResult.php35
-rw-r--r--includes/api/ApiRollback.php24
-rw-r--r--includes/api/ApiRsd.php15
-rw-r--r--includes/api/ApiUnblock.php46
-rw-r--r--includes/api/ApiUndelete.php19
-rw-r--r--includes/api/ApiUpload.php291
-rw-r--r--includes/api/ApiUserrights.php17
-rw-r--r--includes/api/ApiWatch.php33
-rw-r--r--includes/cache/CacheDependency.php (renamed from includes/CacheDependency.php)29
-rw-r--r--includes/cache/HTMLCacheUpdate.php (renamed from includes/HTMLCacheUpdate.php)28
-rw-r--r--includes/cache/HTMLFileCache.php (renamed from includes/HTMLFileCache.php)51
-rw-r--r--includes/cache/LinkBatch.php (renamed from includes/LinkBatch.php)37
-rw-r--r--includes/cache/LinkCache.php (renamed from includes/LinkCache.php)30
-rw-r--r--includes/cache/MemcachedSessions.php (renamed from includes/MemcachedSessions.php)5
-rw-r--r--includes/cache/MessageCache.php (renamed from includes/MessageCache.php)539
-rw-r--r--includes/cache/SquidUpdate.php (renamed from includes/SquidUpdate.php)50
-rw-r--r--includes/db/CloneDatabase.php158
-rw-r--r--includes/db/Database.php1763
-rw-r--r--includes/db/DatabaseError.php314
-rw-r--r--includes/db/DatabaseIbm_db2.php292
-rw-r--r--includes/db/DatabaseMssql.php484
-rw-r--r--includes/db/DatabaseMysql.php219
-rw-r--r--includes/db/DatabaseOracle.php290
-rw-r--r--includes/db/DatabasePostgres.php178
-rw-r--r--includes/db/DatabaseSqlite.php299
-rw-r--r--includes/db/DatabaseUtility.php268
-rw-r--r--includes/db/LBFactory.php47
-rw-r--r--includes/db/LBFactory_Multi.php22
-rw-r--r--includes/db/LBFactory_Single.php42
-rw-r--r--includes/db/LoadBalancer.php203
-rw-r--r--includes/db/LoadMonitor.php67
-rw-r--r--includes/diff/DairikiDiff.php (renamed from includes/diff/WikiDiff.php)450
-rw-r--r--includes/diff/DifferenceEngine.php259
-rw-r--r--includes/diff/WikiDiff3.php14
-rw-r--r--includes/extauth/MediaWiki.php35
-rw-r--r--includes/extauth/vB.php6
-rw-r--r--includes/filerepo/ArchivedFile.php69
-rw-r--r--includes/filerepo/FSRepo.php122
-rw-r--r--includes/filerepo/File.php541
-rw-r--r--includes/filerepo/FileRepo.php122
-rw-r--r--includes/filerepo/FileRepoStatus.php12
-rw-r--r--includes/filerepo/ForeignAPIFile.php29
-rw-r--r--includes/filerepo/ForeignAPIRepo.php51
-rw-r--r--includes/filerepo/ForeignDBFile.php23
-rw-r--r--includes/filerepo/ForeignDBRepo.php4
-rw-r--r--includes/filerepo/Image.php80
-rw-r--r--includes/filerepo/LocalFile.php273
-rw-r--r--includes/filerepo/LocalRepo.php37
-rw-r--r--includes/filerepo/NullRepo.php3
-rw-r--r--includes/filerepo/OldLocalFile.php100
-rw-r--r--includes/filerepo/README18
-rw-r--r--includes/filerepo/RepoGroup.php56
-rw-r--r--includes/filerepo/UnregisteredLocalFile.php24
-rw-r--r--includes/installer/CliInstaller.php21
-rw-r--r--includes/installer/DatabaseInstaller.php27
-rw-r--r--includes/installer/DatabaseUpdater.php82
-rw-r--r--includes/installer/Ibm_db2Installer.php251
-rw-r--r--includes/installer/Ibm_db2Updater.php69
-rw-r--r--includes/installer/InstallDocFormatter.php42
-rw-r--r--includes/installer/Installer.i18n.php6105
-rw-r--r--includes/installer/Installer.php115
-rw-r--r--includes/installer/LocalSettingsGenerator.php19
-rw-r--r--includes/installer/MysqlInstaller.php62
-rw-r--r--includes/installer/MysqlUpdater.php36
-rw-r--r--includes/installer/OracleInstaller.php6
-rw-r--r--includes/installer/OracleUpdater.php35
-rw-r--r--includes/installer/PhpBugTests.php2
-rw-r--r--includes/installer/PostgresInstaller.php54
-rw-r--r--includes/installer/PostgresUpdater.php2
-rw-r--r--includes/installer/SqliteInstaller.php43
-rw-r--r--includes/installer/SqliteUpdater.php7
-rw-r--r--includes/installer/WebInstaller.php89
-rw-r--r--includes/installer/WebInstallerOutput.php27
-rw-r--r--includes/installer/WebInstallerPage.php114
-rw-r--r--includes/interwiki/Interwiki.php (renamed from includes/Interwiki.php)32
-rw-r--r--includes/job/DoubleRedirectJob.php10
-rw-r--r--includes/job/JobQueue.php74
-rw-r--r--includes/job/RefreshLinksJob.php2
-rw-r--r--includes/job/UploadFromUrlJob.php61
-rw-r--r--includes/json/FormatJson.php7
-rw-r--r--includes/json/Services_JSON.php80
-rw-r--r--includes/libs/CSSMin.php52
-rw-r--r--includes/libs/HttpStatus.php68
-rw-r--r--includes/libs/jsminplus.php2094
-rw-r--r--includes/libs/spyc.php248
-rw-r--r--includes/media/BMP.php35
-rw-r--r--includes/media/Bitmap.php363
-rw-r--r--includes/media/BitmapMetadataHandler.php269
-rw-r--r--includes/media/Bitmap_ClientOnly.php14
-rw-r--r--includes/media/DjVu.php65
-rw-r--r--includes/media/Exif.php836
-rw-r--r--includes/media/ExifBitmap.php210
-rw-r--r--includes/media/FormatMetadata.php1354
-rw-r--r--includes/media/GIF.php103
-rw-r--r--includes/media/GIFMetadataExtractor.php236
-rw-r--r--includes/media/Generic.php302
-rw-r--r--includes/media/IPTC.php576
-rw-r--r--includes/media/Jpeg.php46
-rw-r--r--includes/media/JpegMetadataExtractor.php252
-rw-r--r--includes/media/MediaTransformOutput.php26
-rw-r--r--includes/media/PNG.php88
-rw-r--r--includes/media/PNGMetadataExtractor.php359
-rw-r--r--includes/media/SVG.php92
-rw-r--r--includes/media/SVGMetadataExtractor.php27
-rw-r--r--includes/media/Tiff.php51
-rw-r--r--includes/media/XMP.php1174
-rw-r--r--includes/media/XMPInfo.php1139
-rw-r--r--includes/media/XMPValidate.php323
-rw-r--r--includes/mime.info3
-rw-r--r--includes/mime.types2
-rw-r--r--includes/normal/CleanUpTest.php425
-rw-r--r--includes/normal/Makefile10
-rw-r--r--includes/normal/Utf8Case.php30
-rw-r--r--includes/normal/Utf8CaseGenerate.php1
-rw-r--r--includes/normal/Utf8Test.php2
-rw-r--r--includes/normal/UtfNormal.php42
-rw-r--r--includes/normal/UtfNormalBench.php1
-rw-r--r--includes/normal/UtfNormalData.inc10
-rw-r--r--includes/normal/UtfNormalDataK.inc2
-rw-r--r--includes/normal/UtfNormalDefines.php6
-rw-r--r--includes/normal/UtfNormalGenerate.php1
-rw-r--r--includes/normal/UtfNormalMemStress.php110
-rw-r--r--includes/normal/UtfNormalTest.php1
-rw-r--r--includes/normal/UtfNormalTest2.php6
-rw-r--r--includes/normal/UtfNormalUtil.php6
-rw-r--r--includes/objectcache/APCBagOStuff.php43
-rw-r--r--includes/objectcache/BagOStuff.php164
-rw-r--r--includes/objectcache/DBABagOStuff.php194
-rw-r--r--includes/objectcache/EhcacheBagOStuff.php230
-rw-r--r--includes/objectcache/EmptyBagOStuff.php27
-rw-r--r--includes/objectcache/HashBagOStuff.php58
-rw-r--r--includes/objectcache/MemcachedClient.php (renamed from includes/memcached-client.php)41
-rw-r--r--includes/objectcache/MemcachedPhpBagOStuff.php178
-rw-r--r--includes/objectcache/MultiWriteBagOStuff.php113
-rw-r--r--includes/objectcache/ObjectCache.php119
-rw-r--r--includes/objectcache/SqlBagOStuff.php432
-rw-r--r--includes/objectcache/WinCacheBagOStuff.php71
-rw-r--r--includes/objectcache/XCacheBagOStuff.php51
-rw-r--r--includes/objectcache/eAccelBagOStuff.php46
-rw-r--r--includes/parser/CoreLinkFunctions.php30
-rw-r--r--includes/parser/CoreParserFunctions.php173
-rw-r--r--includes/parser/CoreTagHooks.php65
-rw-r--r--includes/parser/DateFormatter.php8
-rw-r--r--includes/parser/LinkHolderArray.php252
-rw-r--r--includes/parser/Parser.php1224
-rw-r--r--includes/parser/ParserCache.php44
-rw-r--r--includes/parser/ParserOptions.php192
-rw-r--r--includes/parser/ParserOutput.php133
-rw-r--r--includes/parser/Parser_DiffTest.php4
-rw-r--r--includes/parser/Parser_LinkHooks.php21
-rw-r--r--includes/parser/Preprocessor.php48
-rw-r--r--includes/parser/Preprocessor_DOM.php249
-rw-r--r--includes/parser/Preprocessor_Hash.php214
-rw-r--r--includes/parser/Preprocessor_HipHop.hphp1941
-rw-r--r--includes/parser/StripState.php175
-rw-r--r--includes/parser/Tidy.php163
-rw-r--r--includes/profiler/Profiler.php (renamed from includes/Profiler.php)311
-rw-r--r--includes/profiler/ProfilerSimple.php (renamed from includes/ProfilerSimple.php)47
-rw-r--r--includes/profiler/ProfilerSimpleText.php54
-rw-r--r--includes/profiler/ProfilerSimpleTrace.php (renamed from includes/ProfilerSimpleTrace.php)10
-rw-r--r--includes/profiler/ProfilerSimpleUDP.php (renamed from includes/ProfilerSimpleUDP.php)8
-rw-r--r--includes/profiler/ProfilerStub.php15
-rw-r--r--includes/proxy_check.php54
-rw-r--r--includes/resourceloader/ResourceLoader.php297
-rw-r--r--includes/resourceloader/ResourceLoaderContext.php50
-rw-r--r--includes/resourceloader/ResourceLoaderFileModule.php270
-rw-r--r--includes/resourceloader/ResourceLoaderFilePageModule.php11
-rw-r--r--includes/resourceloader/ResourceLoaderModule.php189
-rw-r--r--includes/resourceloader/ResourceLoaderNoscriptModule.php52
-rw-r--r--includes/resourceloader/ResourceLoaderSiteModule.php4
-rw-r--r--includes/resourceloader/ResourceLoaderStartUpModule.php143
-rw-r--r--includes/resourceloader/ResourceLoaderUserGroupsModule.php59
-rw-r--r--includes/resourceloader/ResourceLoaderUserModule.php12
-rw-r--r--includes/resourceloader/ResourceLoaderUserOptionsModule.php29
-rw-r--r--includes/resourceloader/ResourceLoaderUserTokensModule.php63
-rw-r--r--includes/resourceloader/ResourceLoaderWikiModule.php44
-rw-r--r--includes/revisiondelete/RevisionDelete.php374
-rw-r--r--includes/revisiondelete/RevisionDeleteAbstracts.php235
-rw-r--r--includes/revisiondelete/RevisionDeleteUser.php130
-rw-r--r--includes/revisiondelete/RevisionDeleter.php191
-rw-r--r--includes/search/SearchEngine.php124
-rw-r--r--includes/search/SearchIBM_DB2.php4
-rw-r--r--includes/search/SearchMssql.php8
-rw-r--r--includes/search/SearchMySQL.php190
-rw-r--r--includes/search/SearchOracle.php14
-rw-r--r--includes/search/SearchPostgres.php36
-rw-r--r--includes/search/SearchSqlite.php12
-rw-r--r--includes/search/SearchUpdate.php4
-rw-r--r--includes/specials/SpecialActiveusers.php14
-rw-r--r--includes/specials/SpecialAllmessages.php214
-rw-r--r--includes/specials/SpecialAllpages.php80
-rw-r--r--includes/specials/SpecialAncientpages.php40
-rw-r--r--includes/specials/SpecialBlankpage.php3
-rw-r--r--includes/specials/SpecialBlock.php855
-rw-r--r--includes/specials/SpecialBlockList.php437
-rw-r--r--includes/specials/SpecialBlockip.php892
-rw-r--r--includes/specials/SpecialBlockme.php8
-rw-r--r--includes/specials/SpecialBooksources.php41
-rw-r--r--includes/specials/SpecialBrokenRedirects.php75
-rw-r--r--includes/specials/SpecialCategories.php19
-rw-r--r--includes/specials/SpecialChangePassword.php (renamed from includes/specials/SpecialResetpass.php)64
-rw-r--r--includes/specials/SpecialComparePages.php170
-rw-r--r--includes/specials/SpecialConfirmemail.php79
-rw-r--r--includes/specials/SpecialContributions.php322
-rw-r--r--includes/specials/SpecialDeadendpages.php58
-rw-r--r--includes/specials/SpecialDeletedContributions.php31
-rw-r--r--includes/specials/SpecialDisambiguations.php109
-rw-r--r--includes/specials/SpecialDoubleRedirects.php86
-rw-r--r--includes/specials/SpecialEditWatchlist.php596
-rw-r--r--includes/specials/SpecialEmailuser.php112
-rw-r--r--includes/specials/SpecialExport.php127
-rw-r--r--includes/specials/SpecialFewestrevisions.php41
-rw-r--r--includes/specials/SpecialFileDuplicateSearch.php226
-rw-r--r--includes/specials/SpecialFilepath.php11
-rw-r--r--includes/specials/SpecialImport.php50
-rw-r--r--includes/specials/SpecialIpblocklist.php581
-rw-r--r--includes/specials/SpecialLinkSearch.php202
-rw-r--r--includes/specials/SpecialListfiles.php148
-rw-r--r--includes/specials/SpecialListgrouprights.php62
-rw-r--r--includes/specials/SpecialListredirects.php96
-rw-r--r--includes/specials/SpecialListusers.php86
-rw-r--r--includes/specials/SpecialLockdb.php24
-rw-r--r--includes/specials/SpecialLog.php9
-rw-r--r--includes/specials/SpecialLonelypages.php64
-rw-r--r--includes/specials/SpecialLongpages.php15
-rw-r--r--includes/specials/SpecialMIMEsearch.php151
-rw-r--r--includes/specials/SpecialMergeHistory.php210
-rw-r--r--includes/specials/SpecialMostcategories.php49
-rw-r--r--includes/specials/SpecialMostimages.php39
-rw-r--r--includes/specials/SpecialMostlinked.php62
-rw-r--r--includes/specials/SpecialMostlinkedcategories.php57
-rw-r--r--includes/specials/SpecialMostlinkedtemplates.php50
-rw-r--r--includes/specials/SpecialMostrevisions.php62
-rw-r--r--includes/specials/SpecialMovepage.php36
-rw-r--r--includes/specials/SpecialNewimages.php308
-rw-r--r--includes/specials/SpecialNewpages.php286
-rw-r--r--includes/specials/SpecialPasswordReset.php273
-rw-r--r--includes/specials/SpecialPopularpages.php51
-rw-r--r--includes/specials/SpecialPreferences.php5
-rw-r--r--includes/specials/SpecialPrefixindex.php58
-rw-r--r--includes/specials/SpecialProtectedpages.php39
-rw-r--r--includes/specials/SpecialProtectedtitles.php26
-rw-r--r--includes/specials/SpecialRandompage.php90
-rw-r--r--includes/specials/SpecialRecentchanges.php417
-rw-r--r--includes/specials/SpecialRecentchangeslinked.php52
-rw-r--r--includes/specials/SpecialRevisiondelete.php238
-rw-r--r--includes/specials/SpecialSearch.php412
-rw-r--r--includes/specials/SpecialShortpages.php60
-rw-r--r--includes/specials/SpecialSpecialpages.php44
-rw-r--r--includes/specials/SpecialStatistics.php121
-rw-r--r--includes/specials/SpecialTags.php16
-rw-r--r--includes/specials/SpecialUnblock.php209
-rw-r--r--includes/specials/SpecialUncategorizedcategories.php18
-rw-r--r--includes/specials/SpecialUncategorizedimages.php32
-rw-r--r--includes/specials/SpecialUncategorizedpages.php54
-rw-r--r--includes/specials/SpecialUncategorizedtemplates.php18
-rw-r--r--includes/specials/SpecialUndelete.php703
-rw-r--r--includes/specials/SpecialUnlockdb.php16
-rw-r--r--includes/specials/SpecialUnusedcategories.php34
-rw-r--r--includes/specials/SpecialUnusedimages.php70
-rw-r--r--includes/specials/SpecialUnusedtemplates.php39
-rw-r--r--includes/specials/SpecialUnwatchedpages.php66
-rw-r--r--includes/specials/SpecialUpload.php150
-rw-r--r--includes/specials/SpecialUploadStash.php168
-rw-r--r--includes/specials/SpecialUserlogin.php405
-rw-r--r--includes/specials/SpecialUserrights.php29
-rw-r--r--includes/specials/SpecialVersion.php303
-rw-r--r--includes/specials/SpecialWantedcategories.php48
-rw-r--r--includes/specials/SpecialWantedfiles.php45
-rw-r--r--includes/specials/SpecialWantedpages.php102
-rw-r--r--includes/specials/SpecialWantedtemplates.php44
-rw-r--r--includes/specials/SpecialWatchlist.php825
-rw-r--r--includes/specials/SpecialWhatlinkshere.php86
-rw-r--r--includes/specials/SpecialWithoutinterwiki.php66
-rw-r--r--includes/templates/PHP4.php102
-rw-r--r--includes/templates/Userlogin.php48
-rw-r--r--includes/upload/UploadBase.php314
-rw-r--r--includes/upload/UploadFromFile.php33
-rw-r--r--includes/upload/UploadFromStash.php130
-rw-r--r--includes/upload/UploadFromUrl.php31
-rw-r--r--includes/upload/UploadStash.php615
-rw-r--r--includes/zhtable/Makefile.py14
-rw-r--r--includes/zhtable/simp2trad_noconvert.manual137
-rw-r--r--includes/zhtable/simpphrases.manual6
-rw-r--r--includes/zhtable/toCN.manual9
-rw-r--r--includes/zhtable/toHK.manual4
-rw-r--r--includes/zhtable/toSimp.manual5
-rw-r--r--includes/zhtable/toTW.manual8
-rw-r--r--includes/zhtable/toTrad.manual6
-rw-r--r--includes/zhtable/trad2simp.manual144
-rw-r--r--includes/zhtable/trad2simp_noconvert.manual1
-rw-r--r--includes/zhtable/tradphrases.manual50
-rw-r--r--includes/zhtable/tradphrases_exclude.manual1
509 files changed, 65551 insertions, 33821 deletions
diff --git a/includes/Action.php b/includes/Action.php
new file mode 100644
index 00000000..d5432b23
--- /dev/null
+++ b/includes/Action.php
@@ -0,0 +1,467 @@
+<?php
+/**
+ * Actions are things which can be done to pages (edit, delete, rollback, etc). They
+ * are distinct from Special Pages because an action must apply to exactly one page.
+ *
+ * To add an action in an extension, create a subclass of Action, and add the key to
+ * $wgActions. There is also the deprecated UnknownAction hook
+ *
+ *
+ * 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
+ *
+ * @file
+ */
+abstract class Action {
+
+ /**
+ * Page on which we're performing the action
+ * @var Article
+ */
+ protected $page;
+
+ /**
+ * IContextSource if specified; otherwise we'll use the Context from the Page
+ * @var IContextSource
+ */
+ protected $context;
+
+ /**
+ * The fields used to create the HTMLForm
+ * @var Array
+ */
+ protected $fields;
+
+ /**
+ * Get the Action subclass which should be used to handle this action, false if
+ * the action is disabled, or null if it's not recognised
+ * @param $action String
+ * @param $overrides Array
+ * @return bool|null|string
+ */
+ private final static function getClass( $action, array $overrides ) {
+ global $wgActions;
+ $action = strtolower( $action );
+
+ if ( !isset( $wgActions[$action] ) ) {
+ return null;
+ }
+
+ if ( $wgActions[$action] === false ) {
+ return false;
+ } elseif ( $wgActions[$action] === true && isset( $overrides[$action] ) ) {
+ return $overrides[$action];
+ } elseif ( $wgActions[$action] === true ) {
+ return ucfirst( $action ) . 'Action';
+ } else {
+ return $wgActions[$action];
+ }
+ }
+
+ /**
+ * Get an appropriate Action subclass for the given action
+ * @param $action String
+ * @param $page Article
+ * @return Action|false|null false if the action is disabled, null
+ * if it is not recognised
+ */
+ public final static function factory( $action, Page $page ) {
+ $class = self::getClass( $action, $page->getActionOverrides() );
+ if ( $class ) {
+ $obj = new $class( $page );
+ return $obj;
+ }
+ return $class;
+ }
+
+ /**
+ * Check if a given action is recognised, even if it's disabled
+ *
+ * @param $name String: name of an action
+ * @return Bool
+ */
+ public final static function exists( $name ) {
+ return self::getClass( $name ) !== null;
+ }
+
+ /**
+ * Get the IContextSource in use here
+ * @return IContextSource
+ */
+ protected final function getContext() {
+ if ( $this->context instanceof IContextSource ) {
+ return $this->context;
+ }
+ return $this->page->getContext();
+ }
+
+ /**
+ * Get the WebRequest being used for this instance
+ *
+ * @return WebRequest
+ */
+ protected final function getRequest() {
+ return $this->getContext()->getRequest();
+ }
+
+ /**
+ * Get the OutputPage being used for this instance
+ *
+ * @return OutputPage
+ */
+ protected final function getOutput() {
+ return $this->getContext()->getOutput();
+ }
+
+ /**
+ * Shortcut to get the User being used for this instance
+ *
+ * @return User
+ */
+ protected final function getUser() {
+ return $this->getContext()->getUser();
+ }
+
+ /**
+ * Shortcut to get the Skin being used for this instance
+ *
+ * @return Skin
+ */
+ protected final function getSkin() {
+ return $this->getContext()->getSkin();
+ }
+
+ /**
+ * Shortcut to get the user Language being used for this instance
+ *
+ * @return Skin
+ */
+ protected final function getLang() {
+ return $this->getContext()->getLang();
+ }
+
+ /**
+ * Shortcut to get the Title object from the page
+ * @return Title
+ */
+ protected final function getTitle() {
+ return $this->page->getTitle();
+ }
+
+ /**
+ * Protected constructor: use Action::factory( $action, $page ) to actually build
+ * these things in the real world
+ * @param Page $page
+ */
+ protected function __construct( Page $page ) {
+ $this->page = $page;
+ }
+
+ /**
+ * Return the name of the action this object responds to
+ * @return String lowercase
+ */
+ public abstract function getName();
+
+ /**
+ * Get the permission required to perform this action. Often, but not always,
+ * the same as the action name
+ */
+ public abstract function getRestriction();
+
+ /**
+ * Checks if the given user (identified by an object) can perform this action. Can be
+ * overridden by sub-classes with more complicated permissions schemes. Failures here
+ * must throw subclasses of ErrorPageError
+ *
+ * @param $user User: the user to check, or null to use the context user
+ * @throws ErrorPageError
+ */
+ protected function checkCanExecute( User $user ) {
+ if ( $this->requiresWrite() && wfReadOnly() ) {
+ throw new ReadOnlyError();
+ }
+
+ if ( $this->getRestriction() !== null && !$user->isAllowed( $this->getRestriction() ) ) {
+ throw new PermissionsError( $this->getRestriction() );
+ }
+
+ if ( $this->requiresUnblock() && $user->isBlocked() ) {
+ $block = $user->mBlock;
+ throw new UserBlockedError( $block );
+ }
+ }
+
+ /**
+ * Whether this action requires the wiki not to be locked
+ * @return Bool
+ */
+ public function requiresWrite() {
+ return true;
+ }
+
+ /**
+ * Whether this action can still be executed by a blocked user
+ * @return Bool
+ */
+ public function requiresUnblock() {
+ return true;
+ }
+
+ /**
+ * Set output headers for noindexing etc. This function will not be called through
+ * the execute() entry point, so only put UI-related stuff in here.
+ */
+ protected function setHeaders() {
+ $out = $this->getOutput();
+ $out->setRobotPolicy( "noindex,nofollow" );
+ $out->setPageTitle( $this->getPageTitle() );
+ $this->getOutput()->setSubtitle( $this->getDescription() );
+ $out->setArticleRelated( true );
+ }
+
+ /**
+ * Returns the name that goes in the \<h1\> page title
+ *
+ * @return String
+ */
+ protected function getPageTitle() {
+ return $this->getTitle()->getPrefixedText();
+ }
+
+ /**
+ * Returns the description that goes below the \<h1\> tag
+ *
+ * @return String
+ */
+ protected function getDescription() {
+ return wfMsg( strtolower( $this->getName() ) );
+ }
+
+ /**
+ * The main action entry point. Do all output for display and send it to the context
+ * output. Do not use globals $wgOut, $wgRequest, etc, in implementations; use
+ * $this->getOutput(), etc.
+ * @throws ErrorPageError
+ */
+ public abstract function show();
+
+ /**
+ * Execute the action in a silent fashion: do not display anything or release any errors.
+ * @param $data Array values that would normally be in the POST request
+ * @param $captureErrors Bool whether to catch exceptions and just return false
+ * @return Bool whether execution was successful
+ */
+ public abstract function execute();
+}
+
+abstract class FormAction extends Action {
+
+ /**
+ * Get an HTMLForm descriptor array
+ * @return Array
+ */
+ protected abstract function getFormFields();
+
+ /**
+ * Add pre- or post-text to the form
+ * @return String HTML which will be sent to $form->addPreText()
+ */
+ protected function preText() { return ''; }
+ protected function postText() { return ''; }
+
+ /**
+ * Play with the HTMLForm if you need to more substantially
+ * @param $form HTMLForm
+ */
+ protected function alterForm( HTMLForm $form ) {}
+
+ /**
+ * Get the HTMLForm to control behaviour
+ * @return HTMLForm|null
+ */
+ protected function getForm() {
+ $this->fields = $this->getFormFields();
+
+ // Give hooks a chance to alter the form, adding extra fields or text etc
+ wfRunHooks( 'ActionModifyFormFields', array( $this->getName(), &$this->fields, $this->page ) );
+
+ $form = new HTMLForm( $this->fields, $this->getContext() );
+ $form->setSubmitCallback( array( $this, 'onSubmit' ) );
+
+ // Retain query parameters (uselang etc)
+ $form->addHiddenField( 'action', $this->getName() ); // Might not be the same as the query string
+ $params = array_diff_key(
+ $this->getRequest()->getQueryValues(),
+ array( 'action' => null, 'title' => null )
+ );
+ $form->addHiddenField( 'redirectparams', wfArrayToCGI( $params ) );
+
+ $form->addPreText( $this->preText() );
+ $form->addPostText( $this->postText() );
+ $this->alterForm( $form );
+
+ // Give hooks a chance to alter the form, adding extra fields or text etc
+ wfRunHooks( 'ActionBeforeFormDisplay', array( $this->getName(), &$form, $this->page ) );
+
+ return $form;
+ }
+
+ /**
+ * Process the form on POST submission. If you return false from getFormFields(),
+ * this will obviously never be reached. If you don't want to do anything with the
+ * form, just return false here
+ * @param $data Array
+ * @return Bool|Array true for success, false for didn't-try, array of errors on failure
+ */
+ public abstract function onSubmit( $data );
+
+ /**
+ * Do something exciting on successful processing of the form. This might be to show
+ * a confirmation message (watch, rollback, etc) or to redirect somewhere else (edit,
+ * protect, etc).
+ */
+ public abstract function onSuccess();
+
+ /**
+ * The basic pattern for actions is to display some sort of HTMLForm UI, maybe with
+ * some stuff underneath (history etc); to do some processing on submission of that
+ * form (delete, protect, etc) and to do something exciting on 'success', be that
+ * display something new or redirect to somewhere. Some actions have more exotic
+ * behaviour, but that's what subclassing is for :D
+ */
+ public function show() {
+ $this->setHeaders();
+
+ // This will throw exceptions if there's a problem
+ $this->checkCanExecute( $this->getUser() );
+
+ $form = $this->getForm();
+ if ( $form->show() ) {
+ $this->onSuccess();
+ }
+ }
+
+ /**
+ * @see Action::execute()
+ * @throws ErrorPageError
+ * @param array|null $data
+ * @param bool $captureErrors
+ * @return bool
+ */
+ 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 will throw exceptions if there's a problem
+ $this->checkCanExecute( $this->getUser() );
+
+ $fields = array();
+ foreach ( $this->fields as $key => $params ) {
+ if ( isset( $data[$key] ) ) {
+ $fields[$key] = $data[$key];
+ } elseif ( isset( $params['default'] ) ) {
+ $fields[$key] = $params['default'];
+ } else {
+ $fields[$key] = null;
+ }
+ }
+ $status = $this->onSubmit( $fields );
+ if ( $status === true ) {
+ // This might do permanent stuff
+ $this->onSuccess();
+ return true;
+ } else {
+ return false;
+ }
+ }
+ catch ( ErrorPageError $e ) {
+ if ( $captureErrors ) {
+ return false;
+ } else {
+ throw $e;
+ }
+ }
+ }
+}
+
+/**
+ * Actions generally fall into two groups: the show-a-form-then-do-something-with-the-input
+ * format (protect, delete, move, etc), and the just-do-something format (watch, rollback,
+ * patrol, etc).
+ */
+abstract class FormlessAction extends Action {
+
+ /**
+ * Show something on GET request.
+ * @return String|null will be added to the HTMLForm if present, or just added to the
+ * output if not. Return null to not add anything
+ */
+ public abstract function onView();
+
+ /**
+ * We don't want an HTMLForm
+ */
+ protected function getFormFields() {
+ return false;
+ }
+
+ public function onSubmit( $data ) {
+ return false;
+ }
+
+ public function onSuccess() {
+ return false;
+ }
+
+ public function show() {
+ $this->setHeaders();
+
+ // This will throw exceptions if there's a problem
+ $this->checkCanExecute( $this->getUser() );
+
+ $this->getOutput()->addHTML( $this->onView() );
+ }
+
+ /**
+ * Execute the action silently, not giving any output. Since these actions don't have
+ * forms, they probably won't have any data, but some (eg rollback) may do
+ * @param $data Array values that would normally be in the GET request
+ * @param $captureErrors Bool whether to catch exceptions and just return false
+ * @return Bool whether execution was successful
+ */
+ public function execute( array $data = null, $captureErrors = true ) {
+ try {
+ // Set a new context so output doesn't leak.
+ $this->context = clone $this->page->getContext();
+ if ( is_array( $data ) ) {
+ $this->context->setRequest( new FauxRequest( $data, false ) );
+ }
+
+ // This will throw exceptions if there's a problem
+ $this->checkCanExecute( $this->getUser() );
+
+ $this->onView();
+ return true;
+ }
+ catch ( ErrorPageError $e ) {
+ if ( $captureErrors ) {
+ return false;
+ } else {
+ throw $e;
+ }
+ }
+ }
+}
diff --git a/includes/AjaxDispatcher.php b/includes/AjaxDispatcher.php
index f7583188..17b154d6 100644
--- a/includes/AjaxDispatcher.php
+++ b/includes/AjaxDispatcher.php
@@ -7,12 +7,6 @@
* Handle ajax requests and send them to the proper handler.
*/
-if ( !( defined( 'MEDIAWIKI' ) && $wgUseAjax ) ) {
- die( 1 );
-}
-
-require_once( 'AjaxFunctions.php' );
-
/**
* Object-Oriented Ajax functions.
* @ingroup Ajax
@@ -74,7 +68,7 @@ class AjaxDispatcher {
* request.
*/
function performAction() {
- global $wgAjaxExportList, $wgOut, $wgUser;
+ global $wgAjaxExportList, $wgOut;
if ( empty( $this->mode ) ) {
return;
@@ -90,13 +84,6 @@ class AjaxDispatcher {
'Bad Request',
"unknown function " . (string) $this->func_name
);
- } elseif ( !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true )
- && !$wgUser->isAllowed( 'read' ) )
- {
- wfHttpError(
- 403,
- 'Forbidden',
- 'You must log in to view pages.' );
} else {
wfDebug( __METHOD__ . ' dispatching ' . $this->func_name . "\n" );
diff --git a/includes/AjaxFunctions.php b/includes/AjaxFunctions.php
deleted file mode 100644
index 8e5de31b..00000000
--- a/includes/AjaxFunctions.php
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php
-/**
- * Handler functions for Ajax requests
- *
- * @file
- * @ingroup Ajax
- */
-
-if ( !defined( 'MEDIAWIKI' ) ) {
- die( 1 );
-}
-
-/**
- * Function converts an Javascript escaped string back into a string with
- * specified charset (default is UTF-8).
- * Modified function from http://pure-essence.net/stuff/code/utf8RawUrlDecode.phps
- *
- * @param $source String escaped with Javascript's escape() function
- * @param $iconv_to String destination character set will be used as second parameter
- * in the iconv function. Default is UTF-8.
- * @return string
- */
-function js_unescape( $source, $iconv_to = 'UTF-8' ) {
- $decodedStr = '';
- $pos = 0;
- $len = strlen ( $source );
-
- while ( $pos < $len ) {
- $charAt = substr ( $source, $pos, 1 );
- if ( $charAt == '%' ) {
- $pos++;
- $charAt = substr ( $source, $pos, 1 );
-
- if ( $charAt == 'u' ) {
- // we got a unicode character
- $pos++;
- $unicodeHexVal = substr ( $source, $pos, 4 );
- $unicode = hexdec ( $unicodeHexVal );
- $decodedStr .= code2utf( $unicode );
- $pos += 4;
- } else {
- // we have an escaped ascii character
- $hexVal = substr ( $source, $pos, 2 );
- $decodedStr .= chr ( hexdec ( $hexVal ) );
- $pos += 2;
- }
- } else {
- $decodedStr .= $charAt;
- $pos++;
- }
- }
-
- if ( $iconv_to != "UTF-8" ) {
- $decodedStr = iconv( "utf-8", $iconv_to, $decodedStr );
- }
-
- return $decodedStr;
-}
-
-/**
- * Function coverts number of utf char into that character.
- * Function taken from: http://www.php.net/manual/en/function.utf8-encode.php#49336
- *
- * @param $num Integer
- * @return utf8char
- */
-function code2utf( $num ) {
- if ( $num < 128 ) {
- return chr( $num );
- }
-
- if ( $num < 2048 ) {
- return chr( ( $num >> 6 ) + 192 ) . chr( ( $num&63 ) + 128 );
- }
-
- if ( $num < 65536 ) {
- return chr( ( $num >> 12 ) + 224 ) . chr( ( ( $num >> 6 )&63 ) + 128 ) . chr( ( $num&63 ) + 128 );
- }
-
- if ( $num < 2097152 ) {
- return chr( ( $num >> 18 ) + 240 ) . chr( ( ( $num >> 12 )&63 ) + 128 ) . chr( ( ( $num >> 6 )&63 ) + 128 ) . chr( ( $num&63 ) + 128 );
- }
-
- return '';
-}
-
-/**
- * Called in some places (currently just extensions)
- * to get the URL for a given file.
- */
-function wfAjaxGetFileUrl( $file ) {
- $file = wfFindFile( $file );
-
- if ( !$file || !$file->exists() ) {
- return null;
- }
-
- $url = $file->getUrl();
-
- return $url;
-}
diff --git a/includes/AjaxResponse.php b/includes/AjaxResponse.php
index 014798f8..b9f80855 100644
--- a/includes/AjaxResponse.php
+++ b/includes/AjaxResponse.php
@@ -6,10 +6,6 @@
* @ingroup Ajax
*/
-if ( !defined( 'MEDIAWIKI' ) ) {
- die( 1 );
-}
-
/**
* Handle responses for Ajax requests (send headers, print
* content, that sort of thing)
diff --git a/includes/Article.php b/includes/Article.php
index 3e8cfd5e..a0cc6a95 100644
--- a/includes/Article.php
+++ b/includes/Article.php
@@ -5,7 +5,11 @@
*/
/**
- * Class representing a MediaWiki article and history.
+ * Class for viewing MediaWiki article and history.
+ *
+ * This maintains WikiPage functions for backwards compatibility.
+ *
+ * @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
@@ -13,198 +17,125 @@
*
* @internal documentation reviewed 15 Mar 2010
*/
-class Article {
+class Article extends Page {
/**@{{
* @private
*/
- var $mComment = ''; // !<
+
+ /**
+ * @var IContextSource
+ */
+ protected $mContext;
+
+ /**
+ * @var WikiPage
+ */
+ protected $mPage;
+
var $mContent; // !<
var $mContentLoaded = false; // !<
- var $mCounter = -1; // !< Not loaded
- var $mCurID = -1; // !< Not loaded
- var $mDataLoaded = false; // !<
- var $mForUpdate = false; // !<
- var $mGoodAdjustment = 0; // !<
- var $mIsRedirect = false; // !<
- var $mLatest = false; // !<
- var $mMinorEdit; // !<
var $mOldId; // !<
- var $mPreparedEdit = false; // !< Title object if set
- var $mRedirectedFrom = null; // !< Title object if set
- var $mRedirectTarget = null; // !< Title object if set
+
+ /**
+ * @var Title
+ */
+ var $mRedirectedFrom = null;
+
+ /**
+ * @var mixed: boolean false or URL string
+ */
var $mRedirectUrl = false; // !<
var $mRevIdFetched = 0; // !<
- var $mRevision; // !< Revision object if set
- var $mTimestamp = ''; // !<
- var $mTitle; // !< Title object
- var $mTotalAdjustment = 0; // !<
- var $mTouched = '19700101000000'; // !<
- var $mUser = -1; // !< Not loaded
- var $mUserText = ''; // !< username from Revision if set
- var $mParserOptions; // !< ParserOptions object
- var $mParserOutput; // !< ParserCache object if set
+
+ /**
+ * @var Revision
+ */
+ var $mRevision = null;
+
+ /**
+ * @var ParserOutput
+ */
+ var $mParserOutput;
+
/**@}}*/
/**
* Constructor and clear the article
- * @param $title Reference to a Title object.
+ * @param $title Title Reference to a Title object.
* @param $oldId Integer revision ID, null to fetch from request, zero for current
*/
public function __construct( Title $title, $oldId = null ) {
- // FIXME: does the reference play any role here?
- $this->mTitle =& $title;
$this->mOldId = $oldId;
+ $this->mPage = $this->newPage( $title );
+ }
+
+ protected function newPage( Title $title ) {
+ return new WikiPage( $title );
}
/**
- * Constructor from an page id
- * @param $id The article ID to load
+ * Constructor from a page id
+ * @param $id Int article ID to load
*/
public static function newFromID( $id ) {
$t = Title::newFromID( $id );
- # FIXME: doesn't inherit right
+ # @todo FIXME: Doesn't inherit right
return $t == null ? null : new self( $t );
# return $t == null ? null : new static( $t ); // PHP 5.3
}
/**
- * Tell the page view functions that this view was redirected
- * from another page on the wiki.
- * @param $from Title object.
- */
- public function setRedirectedFrom( Title $from ) {
- $this->mRedirectedFrom = $from;
- }
-
- /**
- * If this page is a redirect, get its target
+ * Create an Article object of the appropriate class for the given page.
*
- * The target will be fetched from the redirect table if possible.
- * If this page doesn't have an entry there, call insertRedirect()
- * @return mixed Title object, or null if this page is not a redirect
+ * @param $title Title
+ * @param $context IContextSource
+ * @return Article object
*/
- public function getRedirectTarget() {
- if ( !$this->mTitle->isRedirect() ) {
- return null;
+ public static function newFromTitle( $title, IContextSource $context ) {
+ if ( NS_MEDIA == $title->getNamespace() ) {
+ // FIXME: where should this go?
+ $title = Title::makeTitle( NS_FILE, $title->getDBkey() );
}
- if ( $this->mRedirectTarget !== null ) {
- return $this->mRedirectTarget;
- }
-
- # Query the redirect table
- $dbr = wfGetDB( DB_SLAVE );
- $row = $dbr->selectRow( 'redirect',
- array( 'rd_namespace', 'rd_title', 'rd_fragment', 'rd_interwiki' ),
- array( 'rd_from' => $this->getID() ),
- __METHOD__
- );
-
- // rd_fragment and rd_interwiki were added later, populate them if empty
- if ( $row && !is_null( $row->rd_fragment ) && !is_null( $row->rd_interwiki ) ) {
- return $this->mRedirectTarget = Title::makeTitle(
- $row->rd_namespace, $row->rd_title,
- $row->rd_fragment, $row->rd_interwiki );
+ $page = null;
+ wfRunHooks( 'ArticleFromTitle', array( &$title, &$page ) );
+ if ( !$page ) {
+ switch( $title->getNamespace() ) {
+ case NS_FILE:
+ $page = new ImagePage( $title );
+ break;
+ case NS_CATEGORY:
+ $page = new CategoryPage( $title );
+ break;
+ default:
+ $page = new Article( $title );
+ }
}
+ $page->setContext( $context );
- # This page doesn't have an entry in the redirect table
- return $this->mRedirectTarget = $this->insertRedirect();
+ return $page;
}
/**
- * Insert an entry for this page into the redirect table.
+ * Create an Article object of the appropriate class for the given page.
*
- * Don't call this function directly unless you know what you're doing.
- * @return Title object or null if not a redirect
+ * @param $page WikiPage
+ * @param $context IContextSource
+ * @return Article object
*/
- public function insertRedirect() {
- // recurse through to only get the final target
- $retval = Title::newFromRedirectRecurse( $this->getContent() );
- if ( !$retval ) {
- return null;
- }
- $this->insertRedirectEntry( $retval );
- return $retval;
- }
-
- /**
- * Insert or update the redirect table entry for this page to indicate
- * it redirects to $rt .
- * @param $rt Title redirect target
- */
- public function insertRedirectEntry( $rt ) {
- $dbw = wfGetDB( DB_MASTER );
- $dbw->replace( 'redirect', array( 'rd_from' ),
- array(
- 'rd_from' => $this->getID(),
- 'rd_namespace' => $rt->getNamespace(),
- 'rd_title' => $rt->getDBkey(),
- 'rd_fragment' => $rt->getFragment(),
- 'rd_interwiki' => $rt->getInterwiki(),
- ),
- __METHOD__
- );
+ public static function newFromWikiPage( WikiPage $page, IContextSource $context ) {
+ $article = self::newFromTitle( $page->getTitle(), $context );
+ $article->mPage = $page; // override to keep process cached vars
+ return $article;
}
/**
- * Get the Title object or URL this page redirects to
- *
- * @return mixed false, Title of in-wiki target, or string with URL
- */
- public function followRedirect() {
- return $this->getRedirectURL( $this->getRedirectTarget() );
- }
-
- /**
- * Get the Title object this text redirects to
- *
- * @param $text string article content containing redirect info
- * @return mixed false, Title of in-wiki target, or string with URL
- * @deprecated
- */
- public function followRedirectText( $text ) {
- // recurse through to only get the final target
- return $this->getRedirectURL( Title::newFromRedirectRecurse( $text ) );
- }
-
- /**
- * Get the Title object or URL to use for a redirect. We use Title
- * objects for same-wiki, non-special redirects and URLs for everything
- * else.
- * @param $rt Title Redirect target
- * @return mixed false, Title object of local target, or string with URL
+ * Tell the page view functions that this view was redirected
+ * from another page on the wiki.
+ * @param $from Title object.
*/
- public function getRedirectURL( $rt ) {
- if ( $rt ) {
- if ( $rt->getInterwiki() != '' ) {
- if ( $rt->isLocal() ) {
- // Offsite wikis need an HTTP redirect.
- //
- // This can be hard to reverse and may produce loops,
- // so they may be disabled in the site configuration.
- $source = $this->mTitle->getFullURL( 'redirect=no' );
- return $rt->getFullURL( 'rdfrom=' . urlencode( $source ) );
- }
- } else {
- if ( $rt->getNamespace() == NS_SPECIAL ) {
- // Gotta handle redirects to special pages differently:
- // Fill the HTTP response "Location" header and ignore
- // the rest of the page we're on.
- //
- // This can be hard to reverse, so they may be disabled.
- if ( $rt->isSpecial( 'Userlogout' ) ) {
- // rolleyes
- } else {
- return $rt->getFullURL();
- }
- }
-
- return $rt;
- }
- }
-
- // No or invalid redirect
- return false;
+ public function setRedirectedFrom( Title $from ) {
+ $this->mRedirectedFrom = $from;
}
/**
@@ -212,31 +143,22 @@ class Article {
* @return Title object of this page
*/
public function getTitle() {
- return $this->mTitle;
+ return $this->mPage->getTitle();
}
/**
* Clear the object
- * FIXME: shouldn't this be public?
+ * @todo FIXME: Shouldn't this be public?
* @private
*/
public function clear() {
- $this->mDataLoaded = false;
$this->mContentLoaded = false;
- $this->mCurID = $this->mUser = $this->mCounter = -1; # Not loaded
$this->mRedirectedFrom = null; # Title object if set
- $this->mRedirectTarget = null; # Title object if set
- $this->mUserText =
- $this->mTimestamp = $this->mComment = '';
- $this->mGoodAdjustment = $this->mTotalAdjustment = 0;
- $this->mTouched = '19700101000000';
- $this->mForUpdate = false;
- $this->mIsRedirect = false;
$this->mRevIdFetched = 0;
$this->mRedirectUrl = false;
- $this->mLatest = false;
- $this->mPreparedEdit = false;
+
+ $this->mPage->clear();
}
/**
@@ -250,20 +172,18 @@ class Article {
* @return Return the text of this revision
*/
public function getContent() {
- global $wgUser, $wgContLang, $wgMessageCache;
+ global $wgUser;
wfProfileIn( __METHOD__ );
- if ( $this->getID() === 0 ) {
+ if ( $this->mPage->getID() === 0 ) {
# If this is a MediaWiki:x message, then load the messages
# and return the message value for x.
- if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- # If this is a system message, get the default text.
- list( $message, $lang ) = $wgMessageCache->figureMessage( $wgContLang->lcfirst( $this->mTitle->getText() ) );
- $text = wfMsgGetKey( $message, false, $lang, false );
-
- if ( wfEmptyMsg( $message, $text ) )
+ if ( $this->getTitle()->getNamespace() == NS_MEDIAWIKI ) {
+ $text = $this->getTitle()->getDefaultMessageText();
+ if ( $text === false ) {
$text = '';
+ }
} else {
$text = wfMsgExt( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon', 'parsemag' );
}
@@ -279,71 +199,6 @@ class Article {
}
/**
- * Get the text of the current revision. No side-effects...
- *
- * @return Return the text of the current revision
- */
- public function getRawText() {
- // Check process cache for current revision
- if ( $this->mContentLoaded && $this->mOldId == 0 ) {
- return $this->mContent;
- }
-
- $rev = Revision::newFromTitle( $this->mTitle );
- $text = $rev ? $rev->getRawText() : false;
-
- return $text;
- }
-
- /**
- * This function returns the text of a section, specified by a number ($section).
- * A section is text under a heading like == Heading == or \<h1\>Heading\</h1\>, or
- * the first section before any such heading (section 0).
- *
- * If a section contains subsections, these are also returned.
- *
- * @param $text String: text to look in
- * @param $section Integer: section number
- * @return string text of the requested section
- * @deprecated
- */
- public function getSection( $text, $section ) {
- global $wgParser;
- return $wgParser->getSection( $text, $section );
- }
-
- /**
- * Get the text that needs to be saved in order to undo all revisions
- * between $undo and $undoafter. Revisions must belong to the same page,
- * must exist and must not be deleted
- * @param $undo Revision
- * @param $undoafter Revision Must be an earlier revision than $undo
- * @return mixed string on success, false on failure
- */
- public function getUndoText( Revision $undo, Revision $undoafter = null ) {
- $currentRev = Revision::newFromTitle( $this->mTitle );
- if ( !$currentRev ) {
- return false; // no page
- }
- $undo_text = $undo->getText();
- $undoafter_text = $undoafter->getText();
- $cur_text = $currentRev->getText();
-
- if ( $cur_text == $undo_text ) {
- # No use doing a merge if it's just a straight revert.
- return $undoafter_text;
- }
-
- $undone_text = '';
-
- if ( !wfMerge( $undo_text, $undoafter_text, $cur_text, $undone_text ) ) {
- return false;
- }
-
- return $undone_text;
- }
-
- /**
* @return int The oldid of the article that is to be shown, 0 for the
* current revision
*/
@@ -370,14 +225,14 @@ class Article {
if ( isset( $oldid ) ) {
$oldid = intval( $oldid );
if ( $wgRequest->getVal( 'direction' ) == 'next' ) {
- $nextid = $this->mTitle->getNextRevisionID( $oldid );
+ $nextid = $this->getTitle()->getNextRevisionID( $oldid );
if ( $nextid ) {
$oldid = $nextid;
} else {
- $this->mRedirectUrl = $this->mTitle->getFullURL( 'redirect=no' );
+ $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' );
}
} elseif ( $wgRequest->getVal( 'direction' ) == 'prev' ) {
- $previd = $this->mTitle->getPreviousRevisionID( $oldid );
+ $previd = $this->getTitle()->getPreviousRevisionID( $oldid );
if ( $previd ) {
$oldid = $previd;
}
@@ -401,102 +256,12 @@ class Article {
wfProfileIn( __METHOD__ );
- $oldid = $this->getOldID();
- $this->mOldId = $oldid;
- $this->fetchContent( $oldid );
+ $this->fetchContent( $this->getOldID() );
wfProfileOut( __METHOD__ );
}
/**
- * Fetch a page record with the given conditions
- * @param $dbr Database object
- * @param $conditions Array
- * @return mixed Database result resource, or false on failure
- */
- protected function pageData( $dbr, $conditions ) {
- $fields = array(
- 'page_id',
- 'page_namespace',
- 'page_title',
- 'page_restrictions',
- 'page_counter',
- 'page_is_redirect',
- 'page_is_new',
- 'page_random',
- 'page_touched',
- 'page_latest',
- 'page_len',
- );
-
- wfRunHooks( 'ArticlePageDataBefore', array( &$this, &$fields ) );
-
- $row = $dbr->selectRow( 'page', $fields, $conditions, __METHOD__ );
-
- wfRunHooks( 'ArticlePageDataAfter', array( &$this, &$row ) );
-
- return $row;
- }
-
- /**
- * Fetch a page record matching the Title object's namespace and title
- * using a sanitized title string
- *
- * @param $dbr Database object
- * @param $title Title object
- * @return mixed Database result resource, or false on failure
- */
- public function pageDataFromTitle( $dbr, $title ) {
- return $this->pageData( $dbr, array(
- 'page_namespace' => $title->getNamespace(),
- 'page_title' => $title->getDBkey() ) );
- }
-
- /**
- * Fetch a page record matching the requested ID
- *
- * @param $dbr Database
- * @param $id Integer
- */
- protected function pageDataFromId( $dbr, $id ) {
- return $this->pageData( $dbr, array( 'page_id' => $id ) );
- }
-
- /**
- * Set the general counter, title etc data loaded from
- * some source.
- *
- * @param $data Database row object or "fromdb"
- */
- public function loadPageData( $data = 'fromdb' ) {
- if ( $data === 'fromdb' ) {
- $dbr = wfGetDB( DB_MASTER );
- $data = $this->pageDataFromId( $dbr, $this->getId() );
- }
-
- $lc = LinkCache::singleton();
-
- if ( $data ) {
- $lc->addGoodLinkObj( $data->page_id, $this->mTitle, $data->page_len, $data->page_is_redirect, $data->page_latest );
-
- $this->mTitle->mArticleID = intval( $data->page_id );
-
- # Old-fashioned restrictions
- $this->mTitle->loadRestrictions( $data->page_restrictions );
-
- $this->mCounter = intval( $data->page_counter );
- $this->mTouched = wfTimestamp( TS_MW, $data->page_touched );
- $this->mIsRedirect = intval( $data->page_is_redirect );
- $this->mLatest = intval( $data->page_latest );
- } else {
- $lc->addBadLinkObj( $this->mTitle );
- $this->mTitle->mArticleID = 0;
- }
-
- $this->mDataLoaded = true;
- }
-
- /**
* Get text of an article from database
* Does *NOT* follow redirects.
*
@@ -508,57 +273,44 @@ class Article {
return $this->mContent;
}
- $dbr = wfGetDB( DB_MASTER );
-
# Pre-fill content with error message so that if something
# fails we'll have something telling us what we intended.
- $t = $this->mTitle->getPrefixedText();
+ $t = $this->getTitle()->getPrefixedText();
$d = $oldid ? wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid ) : '';
$this->mContent = wfMsgNoTrans( 'missing-article', $t, $d ) ;
if ( $oldid ) {
$revision = Revision::newFromId( $oldid );
- if ( $revision === null ) {
+ if ( !$revision ) {
wfDebug( __METHOD__ . " failed to retrieve specified revision, id $oldid\n" );
return false;
}
-
- $data = $this->pageDataFromId( $dbr, $revision->getPage() );
-
- if ( !$data ) {
- wfDebug( __METHOD__ . " failed to get page data linked to revision id $oldid\n" );
- return false;
- }
-
- $this->mTitle = Title::makeTitle( $data->page_namespace, $data->page_title );
- $this->loadPageData( $data );
- } else {
- if ( !$this->mDataLoaded ) {
- $data = $this->pageDataFromTitle( $dbr, $this->mTitle );
-
- if ( !$data ) {
- wfDebug( __METHOD__ . " failed to find page data for title " . $this->mTitle->getPrefixedText() . "\n" );
+ // Revision title doesn't match the page title given?
+ if ( $this->mPage->getID() != $revision->getPage() ) {
+ $function = array( get_class( $this->mPage ), 'newFromID' );
+ $this->mPage = call_user_func( $function, $revision->getPage() );
+ if ( !$this->mPage->getId() ) {
+ wfDebug( __METHOD__ . " failed to get page data linked to revision id $oldid\n" );
return false;
}
-
- $this->loadPageData( $data );
}
- $revision = Revision::newFromId( $this->mLatest );
- if ( $revision === null ) {
- wfDebug( __METHOD__ . " failed to retrieve current page, rev_id {$this->mLatest}\n" );
+ } else {
+ if ( !$this->mPage->getLatest() ) {
+ wfDebug( __METHOD__ . " failed to find page data for title " . $this->getTitle()->getPrefixedText() . "\n" );
+ return false;
+ }
+
+ $revision = $this->mPage->getRevision();
+ if ( !$revision ) {
+ wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" );
return false;
}
}
- // FIXME: Horrible, horrible! This content-loading interface just plain sucks.
+ // @todo FIXME: Horrible, horrible! This content-loading interface just plain sucks.
// We should instead work with the Revision object when we need it...
$this->mContent = $revision->getText( Revision::FOR_THIS_USER ); // Loads if user is allowed
- $this->mUser = $revision->getUser();
- $this->mUserText = $revision->getUserText();
- $this->mComment = $revision->getComment();
- $this->mTimestamp = wfTimestamp( TS_MW, $revision->getTimestamp() );
-
$this->mRevIdFetched = $revision->getId();
$this->mContentLoaded = true;
$this->mRevision =& $revision;
@@ -569,118 +321,11 @@ class Article {
}
/**
- * Read/write accessor to select FOR UPDATE
- *
- * @param $x Mixed: FIXME
- * @return mixed value of $x, or value stored in Article::mForUpdate
- */
- public function forUpdate( $x = null ) {
- return wfSetVar( $this->mForUpdate, $x );
- }
-
- /**
- * Get options for all SELECT statements
- *
- * @param $options Array: an optional options array which'll be appended to
- * the default
- * @return Array: options
- */
- protected function getSelectOptions( $options = '' ) {
- if ( $this->mForUpdate ) {
- if ( is_array( $options ) ) {
- $options[] = 'FOR UPDATE';
- } else {
- $options = 'FOR UPDATE';
- }
- }
-
- return $options;
- }
-
- /**
- * @return int Page ID
- */
- public function getID() {
- return $this->mTitle->getArticleID();
- }
-
- /**
- * @return bool Whether or not the page exists in the database
+ * No-op
+ * @deprecated since 1.18
*/
- public function exists() {
- return $this->getId() > 0;
- }
-
- /**
- * Check if this page is something we're going to be showing
- * some sort of sensible content for. If we return false, page
- * views (plain action=view) will return an HTTP 404 response,
- * so spiders and robots can know they're following a bad link.
- *
- * @return bool
- */
- public function hasViewableContent() {
- return $this->exists() || $this->mTitle->isAlwaysKnown();
- }
-
- /**
- * @return int The view count for the page
- */
- public function getCount() {
- if ( -1 == $this->mCounter ) {
- $id = $this->getID();
-
- if ( $id == 0 ) {
- $this->mCounter = 0;
- } else {
- $dbr = wfGetDB( DB_SLAVE );
- $this->mCounter = $dbr->selectField( 'page',
- 'page_counter',
- array( 'page_id' => $id ),
- __METHOD__,
- $this->getSelectOptions()
- );
- }
- }
-
- return $this->mCounter;
- }
-
- /**
- * Determine whether a page would be suitable for being counted as an
- * article in the site_stats table based on the title & its content
- *
- * @param $text String: text to analyze
- * @return bool
- */
- public function isCountable( $text ) {
- global $wgUseCommaCount;
-
- $token = $wgUseCommaCount ? ',' : '[[';
-
- return $this->mTitle->isContentPage() && !$this->isRedirect( $text ) && in_string( $token, $text );
- }
-
- /**
- * Tests if the article text represents a redirect
- *
- * @param $text mixed string containing article contents, or boolean
- * @return bool
- */
- public function isRedirect( $text = false ) {
- if ( $text === false ) {
- if ( $this->mDataLoaded ) {
- return $this->mIsRedirect;
- }
-
- // Apparently loadPageData was never called
- $this->loadContent();
- $titleObj = Title::newFromRedirectRecurse( $this->fetchContent() );
- } else {
- $titleObj = Title::newFromRedirect( $text );
- }
-
- return $titleObj !== null;
+ public function forUpdate() {
+ wfDeprecated( __METHOD__ );
}
/**
@@ -694,80 +339,7 @@ class Article {
return true;
}
- return $this->exists() && isset( $this->mRevision ) && $this->mRevision->isCurrent();
- }
-
- /**
- * Loads everything except the text
- * This isn't necessary for all uses, so it's only done if needed.
- */
- protected function loadLastEdit() {
- if ( -1 != $this->mUser ) {
- return;
- }
-
- # New or non-existent articles have no user information
- $id = $this->getID();
- if ( 0 == $id ) {
- return;
- }
-
- $this->mLastRevision = Revision::loadFromPageId( wfGetDB( DB_MASTER ), $id );
- if ( !is_null( $this->mLastRevision ) ) {
- $this->mUser = $this->mLastRevision->getUser();
- $this->mUserText = $this->mLastRevision->getUserText();
- $this->mTimestamp = $this->mLastRevision->getTimestamp();
- $this->mComment = $this->mLastRevision->getComment();
- $this->mMinorEdit = $this->mLastRevision->isMinor();
- $this->mRevIdFetched = $this->mLastRevision->getId();
- }
- }
-
- /**
- * @return string GMT timestamp of last article revision
- **/
-
- public function getTimestamp() {
- // Check if the field has been filled by ParserCache::get()
- if ( !$this->mTimestamp ) {
- $this->loadLastEdit();
- }
-
- return wfTimestamp( TS_MW, $this->mTimestamp );
- }
-
- /**
- * @return int user ID for the user that made the last article revision
- */
- public function getUser() {
- $this->loadLastEdit();
- return $this->mUser;
- }
-
- /**
- * @return string username of the user that made the last article revision
- */
- public function getUserText() {
- $this->loadLastEdit();
- return $this->mUserText;
- }
-
- /**
- * @return string Comment stored for the last article revision
- */
- public function getComment() {
- $this->loadLastEdit();
- return $this->mComment;
- }
-
- /**
- * Returns true if last revision was marked as "minor edit"
- *
- * @return boolean Minor edit indicator for the last article revision.
- */
- public function getMinorEdit() {
- $this->loadLastEdit();
- return $this->mMinorEdit;
+ return $this->mPage->exists() && $this->mRevision && $this->mRevision->isCurrent();
}
/**
@@ -776,52 +348,11 @@ class Article {
* @return int revision ID of last article revision
*/
public function getRevIdFetched() {
- $this->loadLastEdit();
- return $this->mRevIdFetched;
- }
-
- /**
- * FIXME: this does what?
- * @param $limit Integer: default 0.
- * @param $offset Integer: default 0.
- * @return UserArrayFromResult object with User objects of article contributors for requested range
- */
- public function getContributors( $limit = 0, $offset = 0 ) {
- # FIXME: this is expensive; cache this info somewhere.
-
- $dbr = wfGetDB( DB_SLAVE );
- $revTable = $dbr->tableName( 'revision' );
- $userTable = $dbr->tableName( 'user' );
-
- $pageId = $this->getId();
-
- $user = $this->getUser();
-
- if ( $user ) {
- $excludeCond = "AND rev_user != $user";
+ if ( $this->mRevIdFetched ) {
+ return $this->mRevIdFetched;
} else {
- $userText = $dbr->addQuotes( $this->getUserText() );
- $excludeCond = "AND rev_user_text != $userText";
+ return $this->mPage->getLatest();
}
-
- $deletedBit = $dbr->bitAnd( 'rev_deleted', Revision::DELETED_USER ); // username hidden?
-
- $sql = "SELECT {$userTable}.*, rev_user_text as user_name, MAX(rev_timestamp) as timestamp
- FROM $revTable LEFT JOIN $userTable ON rev_user = user_id
- WHERE rev_page = $pageId
- $excludeCond
- AND $deletedBit = 0
- GROUP BY rev_user, rev_user_text
- ORDER BY timestamp DESC";
-
- if ( $limit > 0 ) {
- $sql = $dbr->limitResult( $sql, $limit, $offset );
- }
-
- $sql .= ' ' . $this->getSelectOptions();
- $res = $dbr->query( $sql, __METHOD__ );
-
- return new UserArrayFromResult( $res );
}
/**
@@ -836,67 +367,68 @@ class Article {
# Get variables from query string
$oldid = $this->getOldID();
+
+ # getOldID may want us to redirect somewhere else
+ if ( $this->mRedirectUrl ) {
+ $wgOut->redirect( $this->mRedirectUrl );
+ wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
+ wfProfileOut( __METHOD__ );
+
+ return;
+ }
+
+ $wgOut->setArticleFlag( true );
+ # Set page title (may be overridden by DISPLAYTITLE)
+ $wgOut->setPageTitle( $this->getTitle()->getPrefixedText() );
+
+ # If we got diff in the query, we want to see a diff page instead of the article.
+ if ( $wgRequest->getCheck( 'diff' ) ) {
+ wfDebug( __METHOD__ . ": showing diff page\n" );
+ $this->showDiffPage();
+ wfProfileOut( __METHOD__ );
+
+ return;
+ }
+
+ # Allow frames by default
+ $wgOut->allowClickjacking();
+
$parserCache = ParserCache::singleton();
- $parserOptions = $this->getParserOptions();
+ $parserOptions = $this->mPage->getParserOptions();
# Render printable version, use printable version cache
if ( $wgOut->isPrintable() ) {
$parserOptions->setIsPrintable( true );
$parserOptions->setEditSection( false );
- } else if ( $wgUseETag && !$this->mTitle->quickUserCan( 'edit' ) ) {
+ } elseif ( $wgUseETag && !$this->getTitle()->quickUserCan( 'edit' ) ) {
$parserOptions->setEditSection( false );
}
# Try client and file cache
- if ( $oldid === 0 && $this->checkTouched() ) {
+ if ( $oldid === 0 && $this->mPage->checkTouched() ) {
if ( $wgUseETag ) {
$wgOut->setETag( $parserCache->getETag( $this, $parserOptions ) );
}
# Is it client cached?
- if ( $wgOut->checkLastModified( $this->getTouched() ) ) {
+ if ( $wgOut->checkLastModified( $this->mPage->getTouched() ) ) {
wfDebug( __METHOD__ . ": done 304\n" );
wfProfileOut( __METHOD__ );
return;
# Try file cache
- } else if ( $wgUseFileCache && $this->tryFileCache() ) {
+ } elseif ( $wgUseFileCache && $this->tryFileCache() ) {
wfDebug( __METHOD__ . ": done file cache\n" );
# tell wgOut that output is taken care of
$wgOut->disable();
- $this->viewUpdates();
+ $this->mPage->viewUpdates();
wfProfileOut( __METHOD__ );
return;
}
}
- # getOldID may want us to redirect somewhere else
- if ( $this->mRedirectUrl ) {
- $wgOut->redirect( $this->mRedirectUrl );
- wfDebug( __METHOD__ . ": redirecting due to oldid\n" );
- wfProfileOut( __METHOD__ );
-
- return;
- }
-
- $wgOut->setArticleFlag( true );
- # Set page title (may be overridden by DISPLAYTITLE)
- $wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
-
- # If we got diff in the query, we want to see a diff page instead of the article.
- if ( $wgRequest->getCheck( 'diff' ) ) {
- wfDebug( __METHOD__ . ": showing diff page\n" );
- $this->showDiffPage();
- wfProfileOut( __METHOD__ );
-
- return;
- }
-
- # Allow frames by default
- $wgOut->allowClickjacking();
-
- if ( !$wgUseETag && !$this->mTitle->quickUserCan( 'edit' ) ) {
+ if ( !$wgUseETag && !$this->getTitle()->quickUserCan( 'edit' ) ) {
$parserOptions->setEditSection( false );
}
@@ -931,14 +463,18 @@ class Article {
$wgOut->addParserOutput( $this->mParserOutput );
# Ensure that UI elements requiring revision ID have
# the correct version information.
- $wgOut->setRevisionId( $this->mLatest );
+ $wgOut->setRevisionId( $this->mPage->getLatest() );
$outputDone = true;
+ # Preload timestamp to avoid a DB hit
+ if ( isset( $this->mParserOutput->mTimestamp ) ) {
+ $this->mPage->setTimestamp( $this->mParserOutput->mTimestamp );
+ }
}
}
break;
case 3:
$text = $this->getContent();
- if ( $text === false || $this->getID() == 0 ) {
+ if ( $text === false || $this->mPage->getID() == 0 ) {
wfDebug( __METHOD__ . ": showing missing article\n" );
$this->showMissingArticle();
wfProfileOut( __METHOD__ );
@@ -946,7 +482,7 @@ class Article {
}
# Another whitelist check in case oldid is altering the title
- if ( !$this->mTitle->userCanRead() ) {
+ if ( !$this->getTitle()->userCanRead() ) {
wfDebug( __METHOD__ . ": denied on secondary read check\n" );
$wgOut->loginToUse();
$wgOut->output();
@@ -966,14 +502,14 @@ class Article {
}
# If this "old" version is the current, then try the parser cache...
- if ( $oldid === $this->getLatest() && $this->useParserCache( false ) ) {
+ if ( $oldid === $this->mPage->getLatest() && $this->useParserCache( false ) ) {
$this->mParserOutput = $parserCache->get( $this, $parserOptions );
if ( $this->mParserOutput ) {
- wfDebug( __METHOD__ . ": showing parser cache for current rev permalink\n" );
+ wfDebug( __METHOD__ . ": showing parser cache for current rev permalink\n" );
$wgOut->addParserOutput( $this->mParserOutput );
- $wgOut->setRevisionId( $this->mLatest );
+ $wgOut->setRevisionId( $this->mPage->getLatest() );
$outputDone = true;
- break;
+ break;
}
}
}
@@ -983,7 +519,7 @@ class Article {
$wgOut->setRevisionId( $this->getRevIdFetched() );
# Pages containing custom CSS or JavaScript get special treatment
- if ( $this->mTitle->isCssOrJsPage() || $this->mTitle->isCssJsSubpage() ) {
+ if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) {
wfDebug( __METHOD__ . ": showing CSS/JS source\n" );
$this->showCssOrJsPage();
$outputDone = true;
@@ -995,7 +531,7 @@ class Article {
# Don't append the subtitle if this was an old revision
$wgOut->addHTML( $this->viewRedirect( $rt, !$wasRedirected && $this->isCurrent() ) );
# Parse just to get categories, displaytitle, etc.
- $this->mParserOutput = $wgParser->parse( $text, $this->mTitle, $parserOptions );
+ $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(), $parserOptions );
$wgOut->addParserOutputNoText( $this->mParserOutput );
$outputDone = true;
}
@@ -1007,7 +543,7 @@ class Article {
$key = $parserCache->getKey( $this, $parserOptions );
$poolArticleView = new PoolWorkArticleView( $this, $key, $useParserCache, $parserOptions );
-
+
if ( !$poolArticleView->execute() ) {
# Connection or timeout error
wfProfileOut( __METHOD__ );
@@ -1035,10 +571,11 @@ class Article {
# tents of 'pagetitle-view-mainpage' instead of the default (if
# that's not empty).
# This message always exists because it is in the i18n files
- if ( $this->mTitle->equals( Title::newMainPage() )
- && ( $m = wfMsgForContent( 'pagetitle-view-mainpage' ) ) !== '' )
- {
- $wgOut->setHTMLTitle( $m );
+ if ( $this->getTitle()->equals( Title::newMainPage() ) ) {
+ $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage();
+ if ( !$msg->isDisabled() ) {
+ $wgOut->setHTMLTitle( $msg->title( $this->getTitle() )->text() );
+ }
}
# Now that we've filled $this->mParserOutput, we know whether
@@ -1048,7 +585,7 @@ class Article {
$wgOut->setFollowPolicy( $policy['follow'] );
$this->showViewFooter();
- $this->viewUpdates();
+ $this->mPage->viewUpdates();
wfProfileOut( __METHOD__ );
}
@@ -1066,16 +603,14 @@ class Article {
$unhide = $wgRequest->getInt( 'unhide' ) == 1;
$oldid = $this->getOldID();
- $de = new DifferenceEngine( $this->mTitle, $oldid, $diff, $rcid, $purge, $unhide );
+ $de = new DifferenceEngine( $this->getTitle(), $oldid, $diff, $rcid, $purge, $unhide );
// DifferenceEngine directly fetched the revision:
$this->mRevIdFetched = $de->mNewid;
$de->showDiffPage( $diffOnly );
- // Needed to get the page's current revision
- $this->loadPageData();
- if ( $diff == 0 || $diff == $this->mLatest ) {
+ if ( $diff == 0 || $diff == $this->mPage->getLatest() ) {
# Run view updates for current revision only
- $this->viewUpdates();
+ $this->mPage->viewUpdates();
}
}
@@ -1087,15 +622,19 @@ class Article {
* page views.
*/
protected function showCssOrJsPage() {
- global $wgOut;
+ global $wgOut, $wgLang;
+
+ $dir = $wgLang->getDir();
+ $lang = $wgLang->getCode();
- $wgOut->wrapWikiMsg( "<div id='mw-clearyourcache'>\n$1\n</div>", 'clearyourcache' );
+ $wgOut->wrapWikiMsg( "<div id='mw-clearyourcache' lang='$lang' dir='$dir' class='mw-content-$dir'>\n$1\n</div>",
+ 'clearyourcache' );
// Give hooks a chance to customise the output
- if ( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->mTitle, $wgOut ) ) ) {
+ if ( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->getTitle(), $wgOut ) ) ) {
// Wrap the whole lot in a <pre> and don't parse
$m = array();
- preg_match( '!\.(css|js)$!u', $this->mTitle->getText(), $m );
+ preg_match( '!\.(css|js)$!u', $this->getTitle()->getText(), $m );
$wgOut->addHTML( "<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n" );
$wgOut->addHTML( htmlspecialchars( $this->mContent ) );
$wgOut->addHTML( "\n</pre>\n" );
@@ -1112,13 +651,12 @@ class Article {
global $wgOut, $wgArticleRobotPolicies, $wgNamespaceRobotPolicies;
global $wgDefaultRobotPolicy, $wgRequest;
- $ns = $this->mTitle->getNamespace();
+ $ns = $this->getTitle()->getNamespace();
if ( $ns == NS_USER || $ns == NS_USER_TALK ) {
# Don't index user and user talk pages for blocked users (bug 11443)
- if ( !$this->mTitle->isSubpage() ) {
- $block = new Block();
- if ( $block->load( $this->mTitle->getText() ) ) {
+ if ( !$this->getTitle()->isSubpage() ) {
+ if ( Block::newFromTarget( null, $this->getTitle()->getText() ) instanceof Block ) {
return array(
'index' => 'noindex',
'follow' => 'nofollow'
@@ -1127,7 +665,7 @@ class Article {
}
}
- if ( $this->getID() === 0 || $this->getOldID() ) {
+ if ( $this->mPage->getID() === 0 || $this->getOldID() ) {
# Non-articles (special pages etc), and old revisions
return array(
'index' => 'noindex',
@@ -1157,7 +695,7 @@ class Article {
self::formatRobotPolicy( $wgNamespaceRobotPolicies[$ns] )
);
}
- if ( $this->mTitle->canUseNoindex() && is_object( $this->mParserOutput ) && $this->mParserOutput->getIndexPolicy() ) {
+ if ( $this->getTitle()->canUseNoindex() && is_object( $this->mParserOutput ) && $this->mParserOutput->getIndexPolicy() ) {
# __INDEX__ and __NOINDEX__ magic words, if allowed. Incorporates
# a final sanity check that we have really got the parser output.
$policy = array_merge(
@@ -1166,11 +704,11 @@ class Article {
);
}
- if ( isset( $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()] ) ) {
+ if ( isset( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] ) ) {
# (bug 14900) site config can override user-defined __INDEX__ or __NOINDEX__
$policy = array_merge(
$policy,
- self::formatRobotPolicy( $wgArticleRobotPolicies[$this->mTitle->getPrefixedText()] )
+ self::formatRobotPolicy( $wgArticleRobotPolicies[$this->getTitle()->getPrefixedText()] )
);
}
@@ -1182,7 +720,7 @@ class Article {
* merging of several policies using array_merge().
* @param $policy Mixed, returns empty array on null/false/'', transparent
* to already-converted arrays, converts String.
- * @return associative Array: 'index' => <indexpolicy>, 'follow' => <followpolicy>
+ * @return Array: 'index' => <indexpolicy>, 'follow' => <followpolicy>
*/
public static function formatRobotPolicy( $policy ) {
if ( is_array( $policy ) ) {
@@ -1214,16 +752,15 @@ class Article {
* @return boolean
*/
public function showRedirectedFromHeader() {
- global $wgOut, $wgUser, $wgRequest, $wgRedirectSources;
+ global $wgOut, $wgRequest, $wgRedirectSources;
$rdfrom = $wgRequest->getVal( 'rdfrom' );
- $sk = $wgUser->getSkin();
if ( isset( $this->mRedirectedFrom ) ) {
// This is an internally redirected page view.
// We'll need a backlink to the source page for navigation.
if ( wfRunHooks( 'ArticleViewRedirect', array( &$this ) ) ) {
- $redir = $sk->link(
+ $redir = Linker::link(
$this->mRedirectedFrom,
null,
array(),
@@ -1235,14 +772,14 @@ class Article {
$wgOut->setSubtitle( $s );
// Set the fragment if one was specified in the redirect
- if ( strval( $this->mTitle->getFragment() ) != '' ) {
- $fragment = Xml::escapeJsString( $this->mTitle->getFragmentForURL() );
+ if ( strval( $this->getTitle()->getFragment() ) != '' ) {
+ $fragment = Xml::escapeJsString( $this->getTitle()->getFragmentForURL() );
$wgOut->addInlineScript( "redirectToFragment(\"$fragment\");" );
}
// Add a <link rel="canonical"> tag
$wgOut->addLink( array( 'rel' => 'canonical',
- 'href' => $this->mTitle->getLocalURL() )
+ 'href' => $this->getTitle()->getLocalURL() )
);
return true;
@@ -1251,7 +788,7 @@ class Article {
// This is an externally redirected view, from some other wiki.
// If it was reported from a trusted site, supply a backlink.
if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) {
- $redir = $sk->makeExternalLink( $rdfrom, $rdfrom );
+ $redir = Linker::makeExternalLink( $rdfrom, $rdfrom );
$s = wfMsgExt( 'redirectedfrom', array( 'parseinline', 'replaceafter' ), $redir );
$wgOut->setSubtitle( $s );
@@ -1269,9 +806,8 @@ class Article {
public function showNamespaceHeader() {
global $wgOut;
- if ( $this->mTitle->isTalkPage() ) {
- $msg = wfMsgNoTrans( 'talkpageheader' );
- if ( $msg !== '-' && !wfEmptyMsg( 'talkpageheader', $msg ) ) {
+ if ( $this->getTitle()->isTalkPage() ) {
+ if ( !wfMessage( 'talkpageheader' )->isDisabled() ) {
$wgOut->wrapWikiMsg( "<div class=\"mw-talkpageheader\">\n$1\n</div>", array( 'talkpageheader' ) );
}
}
@@ -1284,7 +820,7 @@ class Article {
global $wgOut, $wgUseTrackbacks;
# check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page
- if ( $this->mTitle->getNamespace() == NS_USER_TALK && IP::isValid( $this->mTitle->getText() ) ) {
+ if ( $this->getTitle()->getNamespace() == NS_USER_TALK && IP::isValid( $this->getTitle()->getText() ) ) {
$wgOut->addWikiMsg( 'anontalkpagetext' );
}
@@ -1296,6 +832,9 @@ class Article {
if ( $wgUseTrackbacks ) {
$this->addTrackbacks();
}
+
+ wfRunHooks( 'ArticleViewFooter', array( $this ) );
+
}
/**
@@ -1308,11 +847,10 @@ class Article {
$rcid = $wgRequest->getVal( 'rcid' );
- if ( !$rcid || !$this->mTitle->quickUserCan( 'patrol' ) ) {
+ if ( !$rcid || !$this->getTitle()->quickUserCan( 'patrol' ) ) {
return;
}
- $sk = $wgUser->getSkin();
$token = $wgUser->editToken( $rcid );
$wgOut->preventClickjacking();
@@ -1320,8 +858,8 @@ class Article {
"<div class='patrollink'>" .
wfMsgHtml(
'markaspatrolledlink',
- $sk->link(
- $this->mTitle,
+ Linker::link(
+ $this->getTitle(),
wfMsgHtml( 'markaspatrolledtext' ),
array(),
array(
@@ -1344,16 +882,16 @@ class Article {
global $wgOut, $wgRequest, $wgUser;
# Show info in user (talk) namespace. Does the user exist? Is he blocked?
- if ( $this->mTitle->getNamespace() == NS_USER || $this->mTitle->getNamespace() == NS_USER_TALK ) {
- $parts = explode( '/', $this->mTitle->getText() );
+ if ( $this->getTitle()->getNamespace() == NS_USER || $this->getTitle()->getNamespace() == NS_USER_TALK ) {
+ $parts = explode( '/', $this->getTitle()->getText() );
$rootPart = $parts[0];
$user = User::newFromName( $rootPart, false /* allow IP users*/ );
$ip = User::isIP( $rootPart );
if ( !$user->isLoggedIn() && !$ip ) { # User does not exist
$wgOut->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n\$1\n</div>",
- array( 'userpage-userdoesnotexist-view', $rootPart ) );
- } else if ( $user->isBlocked() ) { # Show log extract if the user is currently blocked
+ array( 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ) );
+ } elseif ( $user->isBlocked() ) { # Show log extract if the user is currently blocked
LogEventsList::showLogExtract(
$wgOut,
'block',
@@ -1374,7 +912,7 @@ class Article {
wfRunHooks( 'ShowMissingArticle', array( $this ) );
# Show delete and move logs
- LogEventsList::showLogExtract( $wgOut, array( 'delete', 'move' ), $this->mTitle->getPrefixedText(), '',
+ LogEventsList::showLogExtract( $wgOut, array( 'delete', 'move' ), $this->getTitle()->getPrefixedText(), '',
array( 'lim' => 10,
'conds' => array( "log_action != 'revision'" ),
'showIfEmpty' => false,
@@ -1385,14 +923,14 @@ class Article {
$oldid = $this->getOldID();
if ( $oldid ) {
$text = wfMsgNoTrans( 'missing-article',
- $this->mTitle->getPrefixedText(),
+ $this->getTitle()->getPrefixedText(),
wfMsgNoTrans( 'missingarticle-rev', $oldid ) );
- } elseif ( $this->mTitle->getNamespace() === NS_MEDIAWIKI ) {
+ } elseif ( $this->getTitle()->getNamespace() === NS_MEDIAWIKI ) {
// Use the default message text
- $text = $this->getContent();
+ $text = $this->getTitle()->getDefaultMessageText();
} else {
- $createErrors = $this->mTitle->getUserPermissionsErrors( 'create', $wgUser );
- $editErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser );
+ $createErrors = $this->getTitle()->getUserPermissionsErrors( 'create', $wgUser );
+ $editErrors = $this->getTitle()->getUserPermissionsErrors( 'edit', $wgUser );
$errors = array_merge( $createErrors, $editErrors );
if ( !count( $errors ) ) {
@@ -1403,7 +941,7 @@ class Article {
}
$text = "<div class='noarticletext'>\n$text\n</div>";
- if ( !$this->hasViewableContent() ) {
+ if ( !$this->mPage->hasViewableContent() ) {
// If there's no backing content, send a 404 Not Found
// for better machine handling of broken links.
$wgRequest->response()->header( "HTTP/1.1 404 Not Found" );
@@ -1433,10 +971,10 @@ class Article {
return false;
// If the user needs to confirm that they want to see it...
- } else if ( $wgRequest->getInt( 'unhide' ) != 1 ) {
+ } elseif ( $wgRequest->getInt( 'unhide' ) != 1 ) {
# Give explanation and add a link to view the revision...
$oldid = intval( $this->getOldID() );
- $link = $this->mTitle->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';
$wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
@@ -1454,41 +992,25 @@ class Article {
}
/**
- * Should the parser cache be used?
- *
- * @return boolean
- */
- public function useParserCache( $oldid ) {
- global $wgUser, $wgEnableParserCache;
-
- return $wgEnableParserCache
- && $wgUser->getStubThreshold() == 0
- && $this->exists()
- && empty( $oldid )
- && !$this->mTitle->isCssOrJsPage()
- && !$this->mTitle->isCssJsSubpage();
- }
-
- /**
* Execute the uncached parse for action=view
*/
public function doViewParse() {
global $wgOut;
$oldid = $this->getOldID();
- $parserOptions = $this->getParserOptions();
+ $parserOptions = $this->mPage->getParserOptions();
# Render printable version, use printable version cache
$parserOptions->setIsPrintable( $wgOut->isPrintable() );
# Don't show section-edit links on old revisions... this way lies madness.
- if ( !$this->isCurrent() || $wgOut->isPrintable() || !$this->mTitle->quickUserCan( 'edit' ) ) {
+ if ( !$this->isCurrent() || $wgOut->isPrintable() || !$this->getTitle()->quickUserCan( 'edit' ) ) {
$parserOptions->setEditSection( false );
}
-
+
$useParserCache = $this->useParserCache( $oldid );
$this->outputWikiText( $this->getContent(), $useParserCache, $parserOptions );
-
+
return true;
}
@@ -1503,13 +1025,13 @@ class Article {
public function tryDirtyCache() {
global $wgOut;
$parserCache = ParserCache::singleton();
- $options = $this->getParserOptions();
-
+ $options = $this->mPage->getParserOptions();
+
if ( $wgOut->isPrintable() ) {
$options->setIsPrintable( true );
$options->setEditSection( false );
}
-
+
$output = $parserCache->getDirty( $this, $options );
if ( $output ) {
@@ -1532,48 +1054,46 @@ class Article {
/**
* View redirect
*
- * @param $target Title object or Array of destination(s) to redirect
+ * @param $target Title|Array of destination(s) to redirect
* @param $appendSubtitle Boolean [optional]
* @param $forceKnown Boolean: should the image be shown as a bluelink regardless of existence?
* @return string containing HMTL with redirect link
*/
public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) {
- global $wgOut, $wgContLang, $wgStylePath, $wgUser;
+ global $wgOut, $wgLang, $wgStylePath;
if ( !is_array( $target ) ) {
$target = array( $target );
}
- $imageDir = $wgContLang->getDir();
+ $imageDir = $wgLang->getDir();
if ( $appendSubtitle ) {
$wgOut->appendSubtitle( wfMsgHtml( 'redirectpagesub' ) );
}
- $sk = $wgUser->getSkin();
// the loop prepends the arrow image before the link, so the first case needs to be outside
$title = array_shift( $target );
if ( $forceKnown ) {
- $link = $sk->linkKnown( $title, htmlspecialchars( $title->getFullText() ) );
+ $link = Linker::linkKnown( $title, htmlspecialchars( $title->getFullText() ) );
} else {
- $link = $sk->link( $title, htmlspecialchars( $title->getFullText() ) );
+ $link = Linker::link( $title, htmlspecialchars( $title->getFullText() ) );
}
$nextRedirect = $wgStylePath . '/common/images/nextredirect' . $imageDir . '.png';
- $alt = $wgContLang->isRTL() ? '←' : '→';
+ $alt = $wgLang->isRTL() ? '←' : '→';
// Automatically append redirect=no to each link, since most of them are redirect pages themselves.
- // FIXME: where this happens?
foreach ( $target as $rt ) {
$link .= Html::element( 'img', array( 'src' => $nextRedirect, 'alt' => $alt ) );
if ( $forceKnown ) {
- $link .= $sk->linkKnown( $rt, htmlspecialchars( $rt->getFullText() ) );
+ $link .= Linker::linkKnown( $rt, htmlspecialchars( $rt->getFullText(), array(), array( 'redirect' => 'no' ) ) );
} else {
- $link .= $sk->link( $rt, htmlspecialchars( $rt->getFullText() ) );
+ $link .= Linker::link( $rt, htmlspecialchars( $rt->getFullText() ), array(), array( 'redirect' => 'no' ) );
}
}
- $imageUrl = $wgStylePath . '/common/images/redirect' . $imageDir . '.png';
+ $imageUrl = $wgStylePath . '/common/images/redirect' . $imageDir . '.png';
return '<div class="redirectMsg">' .
Html::element( 'img', array( 'src' => $imageUrl, 'alt' => '#REDIRECT' ) ) .
'<span class="redirectText">' . $link . '</span></div>';
@@ -1583,12 +1103,12 @@ class Article {
* Builds trackback links for article display if $wgUseTrackbacks is set to true
*/
public function addTrackbacks() {
- global $wgOut, $wgUser;
+ global $wgOut;
$dbr = wfGetDB( DB_SLAVE );
$tbs = $dbr->select( 'trackbacks',
array( 'tb_id', 'tb_title', 'tb_url', 'tb_ex', 'tb_name' ),
- array( 'tb_page' => $this->getID() )
+ array( 'tb_page' => $this->mPage->getID() )
);
if ( !$dbr->numRows( $tbs ) ) {
@@ -1601,9 +1121,9 @@ class Article {
foreach ( $tbs as $o ) {
$rmvtxt = "";
- if ( $wgUser->isAllowed( 'trackback' ) ) {
- $delurl = $this->mTitle->getFullURL( "action=deletetrackback&tbid=" .
- $o->tb_id . "&token=" . urlencode( $wgUser->editToken() ) );
+ if ( $this->getContext()->getUser()->isAllowed( 'trackback' ) ) {
+ $delurl = $this->getTitle()->getFullURL( "action=deletetrackback&tbid=" .
+ $o->tb_id . "&token=" . urlencode( $this->getContext()->getUser()->editToken() ) );
$rmvtxt = wfMsg( 'trackbackremove', htmlspecialchars( $delurl ) );
}
@@ -1621,29 +1141,10 @@ class Article {
/**
* Removes trackback record for current article from trackbacks table
+ * @deprecated since 1.18
*/
public function deletetrackback() {
- global $wgUser, $wgRequest, $wgOut;
-
- if ( !$wgUser->matchEditToken( $wgRequest->getVal( 'token' ) ) ) {
- $wgOut->addWikiMsg( 'sessionfailure' );
-
- return;
- }
-
- $permission_errors = $this->mTitle->getUserPermissionsErrors( 'delete', $wgUser );
-
- if ( count( $permission_errors ) ) {
- $wgOut->showPermissionsErrorPage( $permission_errors );
-
- return;
- }
-
- $db = wfGetDB( DB_MASTER );
- $db->delete( 'trackbacks', array( 'tb_id' => $wgRequest->getInt( 'tbid' ) ) );
-
- $wgOut->addWikiMsg( 'trackbackdeleteok' );
- $this->mTitle->invalidateCache();
+ return Action::factory( 'deletetrackback', $this )->show();
}
/**
@@ -1661,766 +1162,24 @@ class Article {
* Handle action=purge
*/
public function purge() {
- global $wgUser, $wgRequest, $wgOut;
-
- if ( $wgUser->isAllowed( 'purge' ) || $wgRequest->wasPosted() ) {
- //FIXME: shouldn't this be in doPurge()?
- if ( wfRunHooks( 'ArticlePurge', array( &$this ) ) ) {
- $this->doPurge();
- $this->view();
- }
- } else {
- $formParams = array(
- 'method' => 'post',
- 'action' => $wgRequest->getRequestURL(),
- );
-
- $wgOut->addWikiMsg( 'confirm-purge-top' );
-
- $form = Html::openElement( 'form', $formParams );
- $form .= Xml::submitButton( wfMsg( 'confirm_purge_button' ) );
- $form .= Html::closeElement( 'form' );
-
- $wgOut->addHTML( $form );
- $wgOut->addWikiMsg( 'confirm-purge-bottom' );
-
- $wgOut->setPageTitle( $this->mTitle->getPrefixedText() );
- $wgOut->setRobotPolicy( 'noindex,nofollow' );
- }
- }
-
- /**
- * Perform the actions of a page purging
- */
- public function doPurge() {
- global $wgUseSquid;
-
- // Invalidate the cache
- $this->mTitle->invalidateCache();
-
- if ( $wgUseSquid ) {
- // Commit the transaction before the purge is sent
- $dbw = wfGetDB( DB_MASTER );
- $dbw->commit();
-
- // Send purge
- $update = SquidUpdate::newSimplePurge( $this->mTitle );
- $update->doUpdate();
- }
-
- if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- global $wgMessageCache;
-
- if ( $this->getID() == 0 ) {
- $text = false;
- } else {
- $text = $this->getRawText();
- }
-
- $wgMessageCache->replace( $this->mTitle->getDBkey(), $text );
- }
- }
-
- /**
- * Insert a new empty page record for this article.
- * This *must* be followed up by creating a revision
- * and running $this->updateRevisionOn( ... );
- * or else the record will be left in a funky state.
- * Best if all done inside a transaction.
- *
- * @param $dbw Database
- * @return int The newly created page_id key, or false if the title already existed
- * @private
- */
- public function insertOn( $dbw ) {
- wfProfileIn( __METHOD__ );
-
- $page_id = $dbw->nextSequenceValue( 'page_page_id_seq' );
- $dbw->insert( 'page', array(
- 'page_id' => $page_id,
- 'page_namespace' => $this->mTitle->getNamespace(),
- 'page_title' => $this->mTitle->getDBkey(),
- 'page_counter' => 0,
- 'page_restrictions' => '',
- 'page_is_redirect' => 0, # Will set this shortly...
- 'page_is_new' => 1,
- 'page_random' => wfRandom(),
- 'page_touched' => $dbw->timestamp(),
- 'page_latest' => 0, # Fill this in shortly...
- 'page_len' => 0, # Fill this in shortly...
- ), __METHOD__, 'IGNORE' );
-
- $affected = $dbw->affectedRows();
-
- if ( $affected ) {
- $newid = $dbw->insertId();
- $this->mTitle->resetArticleId( $newid );
- }
- wfProfileOut( __METHOD__ );
-
- return $affected ? $newid : false;
- }
-
- /**
- * Update the page record to point to a newly saved revision.
- *
- * @param $dbw DatabaseBase: object
- * @param $revision Revision: For ID number, and text used to set
- length and redirect status fields
- * @param $lastRevision Integer: if given, will not overwrite the page field
- * when different from the currently set value.
- * Giving 0 indicates the new page flag should be set
- * on.
- * @param $lastRevIsRedirect Boolean: if given, will optimize adding and
- * removing rows in redirect table.
- * @return bool true on success, false on failure
- * @private
- */
- public function updateRevisionOn( &$dbw, $revision, $lastRevision = null, $lastRevIsRedirect = null ) {
- wfProfileIn( __METHOD__ );
-
- $text = $revision->getText();
- $rt = Title::newFromRedirectRecurse( $text );
-
- $conditions = array( 'page_id' => $this->getId() );
-
- if ( !is_null( $lastRevision ) ) {
- # An extra check against threads stepping on each other
- $conditions['page_latest'] = $lastRevision;
- }
-
- $dbw->update( 'page',
- array( /* SET */
- 'page_latest' => $revision->getId(),
- 'page_touched' => $dbw->timestamp(),
- 'page_is_new' => ( $lastRevision === 0 ) ? 1 : 0,
- 'page_is_redirect' => $rt !== null ? 1 : 0,
- 'page_len' => strlen( $text ),
- ),
- $conditions,
- __METHOD__ );
-
- $result = $dbw->affectedRows() != 0;
- if ( $result ) {
- $this->updateRedirectOn( $dbw, $rt, $lastRevIsRedirect );
- }
-
- wfProfileOut( __METHOD__ );
- return $result;
- }
-
- /**
- * Add row to the redirect table if this is a redirect, remove otherwise.
- *
- * @param $dbw Database
- * @param $redirectTitle a title object pointing to the redirect target,
- * or NULL if this is not a redirect
- * @param $lastRevIsRedirect If given, will optimize adding and
- * removing rows in redirect table.
- * @return bool true on success, false on failure
- * @private
- */
- public function updateRedirectOn( &$dbw, $redirectTitle, $lastRevIsRedirect = null ) {
- // Always update redirects (target link might have changed)
- // Update/Insert if we don't know if the last revision was a redirect or not
- // Delete if changing from redirect to non-redirect
- $isRedirect = !is_null( $redirectTitle );
-
- if ( $isRedirect || is_null( $lastRevIsRedirect ) || $lastRevIsRedirect !== $isRedirect ) {
- wfProfileIn( __METHOD__ );
- if ( $isRedirect ) {
- $this->insertRedirectEntry( $redirectTitle );
- } else {
- // This is not a redirect, remove row from redirect table
- $where = array( 'rd_from' => $this->getId() );
- $dbw->delete( 'redirect', $where, __METHOD__ );
- }
-
- if ( $this->getTitle()->getNamespace() == NS_FILE ) {
- RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect( $this->getTitle() );
- }
- wfProfileOut( __METHOD__ );
-
- return ( $dbw->affectedRows() != 0 );
- }
-
- return true;
- }
-
- /**
- * If the given revision is newer than the currently set page_latest,
- * update the page record. Otherwise, do nothing.
- *
- * @param $dbw Database object
- * @param $revision Revision object
- * @return mixed
- */
- public function updateIfNewerOn( &$dbw, $revision ) {
- wfProfileIn( __METHOD__ );
-
- $row = $dbw->selectRow(
- array( 'revision', 'page' ),
- array( 'rev_id', 'rev_timestamp', 'page_is_redirect' ),
- array(
- 'page_id' => $this->getId(),
- 'page_latest=rev_id' ),
- __METHOD__ );
-
- if ( $row ) {
- if ( wfTimestamp( TS_MW, $row->rev_timestamp ) >= $revision->getTimestamp() ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
- $prev = $row->rev_id;
- $lastRevIsRedirect = (bool)$row->page_is_redirect;
- } else {
- # No or missing previous revision; mark the page as new
- $prev = 0;
- $lastRevIsRedirect = null;
- }
-
- $ret = $this->updateRevisionOn( $dbw, $revision, $prev, $lastRevIsRedirect );
-
- wfProfileOut( __METHOD__ );
- return $ret;
- }
-
- /**
- * @param $section empty/null/false or a section number (0, 1, 2, T1, T2...)
- * @param $text String: new text of the section
- * @param $summary String: new section's subject, only if $section is 'new'
- * @param $edittime String: revision timestamp or null to use the current revision
- * @return string Complete article text, or null if error
- */
- public function replaceSection( $section, $text, $summary = '', $edittime = null ) {
- wfProfileIn( __METHOD__ );
-
- if ( strval( $section ) == '' ) {
- // Whole-page edit; let the whole text through
- } else {
- if ( is_null( $edittime ) ) {
- $rev = Revision::newFromTitle( $this->mTitle );
- } else {
- $dbw = wfGetDB( DB_MASTER );
- $rev = Revision::loadFromTimestamp( $dbw, $this->mTitle, $edittime );
- }
-
- if ( !$rev ) {
- wfDebug( "Article::replaceSection asked for bogus section (page: " .
- $this->getId() . "; section: $section; edittime: $edittime)\n" );
- wfProfileOut( __METHOD__ );
- return null;
- }
-
- $oldtext = $rev->getText();
-
- if ( $section == 'new' ) {
- # Inserting a new section
- $subject = $summary ? wfMsgForContent( 'newsectionheaderdefaultlevel', $summary ) . "\n\n" : '';
- $text = strlen( trim( $oldtext ) ) > 0
- ? "{$oldtext}\n\n{$subject}{$text}"
- : "{$subject}{$text}";
- } else {
- # Replacing an existing section; roll out the big guns
- global $wgParser;
-
- $text = $wgParser->replaceSection( $oldtext, $section, $text );
- }
- }
-
- wfProfileOut( __METHOD__ );
- return $text;
- }
-
- /**
- * This function is not deprecated until somebody fixes the core not to use
- * it. Nevertheless, use Article::doEdit() instead.
- */
- function insertNewArticle( $text, $summary, $isminor, $watchthis, $suppressRC = false, $comment = false, $bot = false ) {
- $flags = EDIT_NEW | EDIT_DEFER_UPDATES | EDIT_AUTOSUMMARY |
- ( $isminor ? EDIT_MINOR : 0 ) |
- ( $suppressRC ? EDIT_SUPPRESS_RC : 0 ) |
- ( $bot ? EDIT_FORCE_BOT : 0 );
-
- # If this is a comment, add the summary as headline
- if ( $comment && $summary != "" ) {
- $text = wfMsgForContent( 'newsectionheaderdefaultlevel', $summary ) . "\n\n" . $text;
- }
- $this->doEdit( $text, $summary, $flags );
-
- $dbw = wfGetDB( DB_MASTER );
- if ( $watchthis ) {
- if ( !$this->mTitle->userIsWatching() ) {
- $dbw->begin();
- $this->doWatch();
- $dbw->commit();
- }
- } else {
- if ( $this->mTitle->userIsWatching() ) {
- $dbw->begin();
- $this->doUnwatch();
- $dbw->commit();
- }
- }
- $this->doRedirect( $this->isRedirect( $text ) );
- }
-
- /**
- * @deprecated use Article::doEdit()
- */
- function updateArticle( $text, $summary, $minor, $watchthis, $forceBot = false, $sectionanchor = '' ) {
- $flags = EDIT_UPDATE | EDIT_DEFER_UPDATES | EDIT_AUTOSUMMARY |
- ( $minor ? EDIT_MINOR : 0 ) |
- ( $forceBot ? EDIT_FORCE_BOT : 0 );
-
- $status = $this->doEdit( $text, $summary, $flags );
-
- if ( !$status->isOK() ) {
- return false;
- }
-
- $dbw = wfGetDB( DB_MASTER );
- if ( $watchthis ) {
- if ( !$this->mTitle->userIsWatching() ) {
- $dbw->begin();
- $this->doWatch();
- $dbw->commit();
- }
- } else {
- if ( $this->mTitle->userIsWatching() ) {
- $dbw->begin();
- $this->doUnwatch();
- $dbw->commit();
- }
- }
-
- $extraQuery = ''; // Give extensions a chance to modify URL query on update
- wfRunHooks( 'ArticleUpdateBeforeRedirect', array( $this, &$sectionanchor, &$extraQuery ) );
-
- $this->doRedirect( $this->isRedirect( $text ), $sectionanchor, $extraQuery );
- return true;
- }
-
- /**
- * Check flags and add EDIT_NEW or EDIT_UPDATE to them as needed.
- * @param $flags Int
- * @return Int updated $flags
- */
- function checkFlags( $flags ) {
- if ( !( $flags & EDIT_NEW ) && !( $flags & EDIT_UPDATE ) ) {
- if ( $this->mTitle->getArticleID() ) {
- $flags |= EDIT_UPDATE;
- } else {
- $flags |= EDIT_NEW;
- }
- }
-
- return $flags;
- }
-
- /**
- * Article::doEdit()
- *
- * Change an existing article or create a new article. Updates RC and all necessary caches,
- * optionally via the deferred update array.
- *
- * $wgUser must be set before calling this function.
- *
- * @param $text String: new text
- * @param $summary String: edit summary
- * @param $flags Integer bitfield:
- * EDIT_NEW
- * Article is known or assumed to be non-existent, create a new one
- * EDIT_UPDATE
- * Article is known or assumed to be pre-existing, update it
- * EDIT_MINOR
- * Mark this edit minor, if the user is allowed to do so
- * EDIT_SUPPRESS_RC
- * Do not log the change in recentchanges
- * EDIT_FORCE_BOT
- * Mark the edit a "bot" edit regardless of user rights
- * EDIT_DEFER_UPDATES
- * Defer some of the updates until the end of index.php
- * EDIT_AUTOSUMMARY
- * Fill in blank summaries with generated text where possible
- *
- * If neither EDIT_NEW nor EDIT_UPDATE is specified, the status of the article will be detected.
- * If EDIT_UPDATE is specified and the article doesn't exist, the function will an
- * edit-gone-missing error. If EDIT_NEW is specified and the article does exist, an
- * edit-already-exists error will be returned. These two conditions are also possible with
- * auto-detection due to MediaWiki's performance-optimised locking strategy.
- *
- * @param $baseRevId the revision ID this edit was based off, if any
- * @param $user Optional user object, $wgUser will be used if not passed
- *
- * @return Status object. Possible errors:
- * edit-hook-aborted: The ArticleSave hook aborted the edit but didn't set the fatal flag of $status
- * edit-gone-missing: In update mode, but the article didn't exist
- * edit-conflict: In update mode, the article changed unexpectedly
- * edit-no-change: Warning that the text was the same as before
- * edit-already-exists: In creation mode, but the article already exists
- *
- * Extensions may define additional errors.
- *
- * $return->value will contain an associative array with members as follows:
- * new: Boolean indicating if the function attempted to create a new article
- * revision: The revision object for the inserted revision, or null
- *
- * Compatibility note: this function previously returned a boolean value indicating success/failure
- */
- public function doEdit( $text, $summary, $flags = 0, $baseRevId = false, $user = null ) {
- global $wgUser, $wgDBtransactions, $wgUseAutomaticEditSummaries;
-
- # Low-level sanity check
- if ( $this->mTitle->getText() === '' ) {
- throw new MWException( 'Something is trying to edit an article with an empty title' );
- }
-
- wfProfileIn( __METHOD__ );
-
- $user = is_null( $user ) ? $wgUser : $user;
- $status = Status::newGood( array() );
-
- # Load $this->mTitle->getArticleID() and $this->mLatest if it's not already
- $this->loadPageData();
-
- $flags = $this->checkFlags( $flags );
-
- if ( !wfRunHooks( 'ArticleSave', array( &$this, &$user, &$text, &$summary,
- $flags & EDIT_MINOR, null, null, &$flags, &$status ) ) )
- {
- wfDebug( __METHOD__ . ": ArticleSave hook aborted save!\n" );
-
- if ( $status->isOK() ) {
- $status->fatal( 'edit-hook-aborted' );
- }
-
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- # Silently ignore EDIT_MINOR if not allowed
- $isminor = ( $flags & EDIT_MINOR ) && $user->isAllowed( 'minoredit' );
- $bot = $flags & EDIT_FORCE_BOT;
-
- $oldtext = $this->getRawText(); // current revision
- $oldsize = strlen( $oldtext );
-
- # Provide autosummaries if one is not provided and autosummaries are enabled.
- if ( $wgUseAutomaticEditSummaries && $flags & EDIT_AUTOSUMMARY && $summary == '' ) {
- $summary = $this->getAutosummary( $oldtext, $text, $flags );
- }
-
- $editInfo = $this->prepareTextForEdit( $text );
- $text = $editInfo->pst;
- $newsize = strlen( $text );
-
- $dbw = wfGetDB( DB_MASTER );
- $now = wfTimestampNow();
- $this->mTimestamp = $now;
-
- if ( $flags & EDIT_UPDATE ) {
- # Update article, but only if changed.
- $status->value['new'] = false;
-
- # Make sure the revision is either completely inserted or not inserted at all
- if ( !$wgDBtransactions ) {
- $userAbort = ignore_user_abort( true );
- }
-
- $changed = ( strcmp( $text, $oldtext ) != 0 );
-
- if ( $changed ) {
- $this->mGoodAdjustment = (int)$this->isCountable( $text )
- - (int)$this->isCountable( $oldtext );
- $this->mTotalAdjustment = 0;
-
- if ( !$this->mLatest ) {
- # Article gone missing
- wfDebug( __METHOD__ . ": EDIT_UPDATE specified but article doesn't exist\n" );
- $status->fatal( 'edit-gone-missing' );
-
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- $revision = new Revision( array(
- 'page' => $this->getId(),
- 'comment' => $summary,
- 'minor_edit' => $isminor,
- 'text' => $text,
- 'parent_id' => $this->mLatest,
- 'user' => $user->getId(),
- 'user_text' => $user->getName(),
- ) );
-
- $dbw->begin();
- $revisionId = $revision->insertOn( $dbw );
-
- # Update page
- #
- # Note that we use $this->mLatest instead of fetching a value from the master DB
- # during the course of this function. This makes sure that EditPage can detect
- # edit conflicts reliably, either by $ok here, or by $article->getTimestamp()
- # before this function is called. A previous function used a separate query, this
- # creates a window where concurrent edits can cause an ignored edit conflict.
- $ok = $this->updateRevisionOn( $dbw, $revision, $this->mLatest );
-
- if ( !$ok ) {
- /* Belated edit conflict! Run away!! */
- $status->fatal( 'edit-conflict' );
-
- # Delete the invalid revision if the DB is not transactional
- if ( !$wgDBtransactions ) {
- $dbw->delete( 'revision', array( 'rev_id' => $revisionId ), __METHOD__ );
- }
-
- $revisionId = 0;
- $dbw->rollback();
- } else {
- global $wgUseRCPatrol;
- wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, $baseRevId, $user ) );
- # Update recentchanges
- if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
- # Mark as patrolled if the user can do so
- $patrolled = $wgUseRCPatrol && $this->mTitle->userCan( 'autopatrol' );
- # Add RC row to the DB
- $rc = RecentChange::notifyEdit( $now, $this->mTitle, $isminor, $user, $summary,
- $this->mLatest, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
- $revisionId, $patrolled
- );
-
- # Log auto-patrolled edits
- if ( $patrolled ) {
- PatrolLog::record( $rc, true );
- }
- }
- $user->incEditCount();
- $dbw->commit();
- }
- } else {
- $status->warning( 'edit-no-change' );
- $revision = null;
- // Keep the same revision ID, but do some updates on it
- $revisionId = $this->getRevIdFetched();
- // Update page_touched, this is usually implicit in the page update
- // Other cache updates are done in onArticleEdit()
- $this->mTitle->invalidateCache();
- }
-
- if ( !$wgDBtransactions ) {
- ignore_user_abort( $userAbort );
- }
-
- // Now that ignore_user_abort is restored, we can respond to fatal errors
- if ( !$status->isOK() ) {
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- # Invalidate cache of this article and all pages using this article
- # as a template. Partly deferred.
- Article::onArticleEdit( $this->mTitle );
- # Update links tables, site stats, etc.
- $this->editUpdates( $text, $summary, $isminor, $now, $revisionId, $changed );
- } else {
- # Create new article
- $status->value['new'] = true;
-
- # Set statistics members
- # We work out if it's countable after PST to avoid counter drift
- # when articles are created with {{subst:}}
- $this->mGoodAdjustment = (int)$this->isCountable( $text );
- $this->mTotalAdjustment = 1;
-
- $dbw->begin();
-
- # Add the page record; stake our claim on this title!
- # This will return false if the article already exists
- $newid = $this->insertOn( $dbw );
-
- if ( $newid === false ) {
- $dbw->rollback();
- $status->fatal( 'edit-already-exists' );
-
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- # Save the revision text...
- $revision = new Revision( array(
- 'page' => $newid,
- 'comment' => $summary,
- 'minor_edit' => $isminor,
- 'text' => $text,
- 'user' => $user->getId(),
- 'user_text' => $user->getName(),
- ) );
- $revisionId = $revision->insertOn( $dbw );
-
- $this->mTitle->resetArticleID( $newid );
-
- # Update the page record with revision data
- $this->updateRevisionOn( $dbw, $revision, 0 );
-
- wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, false, $user ) );
-
- # Update recentchanges
- if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
- global $wgUseRCPatrol, $wgUseNPPatrol;
-
- # Mark as patrolled if the user can do so
- $patrolled = ( $wgUseRCPatrol || $wgUseNPPatrol ) && $this->mTitle->userCan( 'autopatrol' );
- # Add RC row to the DB
- $rc = RecentChange::notifyNew( $now, $this->mTitle, $isminor, $user, $summary, $bot,
- '', strlen( $text ), $revisionId, $patrolled );
-
- # Log auto-patrolled edits
- if ( $patrolled ) {
- PatrolLog::record( $rc, true );
- }
- }
- $user->incEditCount();
- $dbw->commit();
-
- # Update links, etc.
- $this->editUpdates( $text, $summary, $isminor, $now, $revisionId, true );
-
- # Clear caches
- Article::onArticleCreate( $this->mTitle );
-
- wfRunHooks( 'ArticleInsertComplete', array( &$this, &$user, $text, $summary,
- $flags & EDIT_MINOR, null, null, &$flags, $revision ) );
- }
-
- # Do updates right now unless deferral was requested
- if ( !( $flags & EDIT_DEFER_UPDATES ) ) {
- wfDoUpdates();
- }
-
- // Return the new revision (or null) to the caller
- $status->value['revision'] = $revision;
-
- wfRunHooks( 'ArticleSaveComplete', array( &$this, &$user, $text, $summary,
- $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId ) );
-
- wfProfileOut( __METHOD__ );
- return $status;
- }
-
- /**
- * @deprecated wrapper for doRedirect
- */
- public function showArticle( $text, $subtitle , $sectionanchor = '', $me2, $now, $summary, $oldid ) {
- wfDeprecated( __METHOD__ );
- $this->doRedirect( $this->isRedirect( $text ), $sectionanchor );
- }
-
- /**
- * Output a redirect back to the article.
- * This is typically used after an edit.
- *
- * @param $noRedir Boolean: add redirect=no
- * @param $sectionAnchor String: section to redirect to, including "#"
- * @param $extraQuery String: extra query params
- */
- public function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) {
- global $wgOut;
-
- if ( $noRedir ) {
- $query = 'redirect=no';
- if ( $extraQuery )
- $query .= "&$extraQuery";
- } else {
- $query = $extraQuery;
- }
-
- $wgOut->redirect( $this->mTitle->getFullURL( $query ) . $sectionAnchor );
+ return Action::factory( 'purge', $this )->show();
}
/**
* Mark this particular edit/page as patrolled
+ * @deprecated since 1.18
*/
public function markpatrolled() {
- global $wgOut, $wgUser, $wgRequest;
-
- $wgOut->setRobotPolicy( 'noindex,nofollow' );
-
- # If we haven't been given an rc_id value, we can't do anything
- $rcid = (int) $wgRequest->getVal( 'rcid' );
-
- if ( !$wgUser->matchEditToken( $wgRequest->getVal( 'token' ), $rcid ) ) {
- $wgOut->showErrorPage( 'sessionfailure-title', 'sessionfailure' );
- return;
- }
-
- $rc = RecentChange::newFromId( $rcid );
-
- if ( is_null( $rc ) ) {
- $wgOut->showErrorPage( 'markedaspatrollederror', 'markedaspatrollederrortext' );
- return;
- }
-
- # It would be nice to see where the user had actually come from, but for now just guess
- $returnto = $rc->getAttribute( 'rc_type' ) == RC_NEW ? 'Newpages' : 'Recentchanges';
- $return = SpecialPage::getTitleFor( $returnto );
-
- $errors = $rc->doMarkPatrolled();
-
- if ( in_array( array( 'rcpatroldisabled' ), $errors ) ) {
- $wgOut->showErrorPage( 'rcpatroldisabled', 'rcpatroldisabledtext' );
-
- return;
- }
-
- if ( in_array( array( 'hookaborted' ), $errors ) ) {
- // The hook itself has handled any output
- return;
- }
-
- if ( in_array( array( 'markedaspatrollederror-noautopatrol' ), $errors ) ) {
- $wgOut->setPageTitle( wfMsg( 'markedaspatrollederror' ) );
- $wgOut->addWikiMsg( 'markedaspatrollederror-noautopatrol' );
- $wgOut->returnToMain( false, $return );
-
- return;
- }
-
- if ( !empty( $errors ) ) {
- $wgOut->showPermissionsErrorPage( $errors );
-
- return;
- }
-
- # Inform the user
- $wgOut->setPageTitle( wfMsg( 'markedaspatrolled' ) );
- $wgOut->addWikiMsg( 'markedaspatrolledtext', $rc->getTitle()->getPrefixedText() );
- $wgOut->returnToMain( false, $return );
+ Action::factory( 'markpatrolled', $this )->show();
}
/**
- * User-interface handler for the "watch" action
+ * User-interface handler for the "watch" action.
+ * Requires Request to pass a token as of 1.18.
+ * @deprecated since 1.18
*/
public function watch() {
- global $wgUser, $wgOut;
-
- if ( $wgUser->isAnon() ) {
- $wgOut->showErrorPage( 'watchnologin', 'watchnologintext' );
- return;
- }
-
- if ( wfReadOnly() ) {
- $wgOut->readOnlyPage();
- return;
- }
-
- if ( $this->doWatch() ) {
- $wgOut->setPagetitle( wfMsg( 'addedwatch' ) );
- $wgOut->setRobotPolicy( 'noindex,nofollow' );
- $wgOut->addWikiMsg( 'addedwatchtext', $this->mTitle->getPrefixedText() );
- }
-
- $wgOut->returnToMain( true, $this->mTitle->getPrefixedText() );
+ Action::factory( 'watch', $this )->show();
}
/**
@@ -2429,64 +1188,30 @@ class Article {
* This is safe to be called multiple times
*
* @return bool true on successful watch operation
+ * @deprecated since 1.18
*/
public function doWatch() {
global $wgUser;
-
- if ( $wgUser->isAnon() ) {
- return false;
- }
-
- if ( wfRunHooks( 'WatchArticle', array( &$wgUser, &$this ) ) ) {
- $wgUser->addWatch( $this->mTitle );
- return wfRunHooks( 'WatchArticleComplete', array( &$wgUser, &$this ) );
- }
-
- return false;
+ return WatchAction::doWatch( $this->getTitle(), $wgUser );
}
/**
* User interface handler for the "unwatch" action.
+ * Requires Request to pass a token as of 1.18.
+ * @deprecated since 1.18
*/
public function unwatch() {
- global $wgUser, $wgOut;
-
- if ( $wgUser->isAnon() ) {
- $wgOut->showErrorPage( 'watchnologin', 'watchnologintext' );
- return;
- }
-
- if ( wfReadOnly() ) {
- $wgOut->readOnlyPage();
- return;
- }
-
- if ( $this->doUnwatch() ) {
- $wgOut->setPagetitle( wfMsg( 'removedwatch' ) );
- $wgOut->setRobotPolicy( 'noindex,nofollow' );
- $wgOut->addWikiMsg( 'removedwatchtext', $this->mTitle->getPrefixedText() );
- }
-
- $wgOut->returnToMain( true, $this->mTitle->getPrefixedText() );
+ Action::factory( 'unwatch', $this )->show();
}
/**
* Stop watching a page
* @return bool true on successful unwatch
+ * @deprecated since 1.18
*/
public function doUnwatch() {
global $wgUser;
-
- if ( $wgUser->isAnon() ) {
- return false;
- }
-
- if ( wfRunHooks( 'UnwatchArticle', array( &$wgUser, &$this ) ) ) {
- $wgUser->removeWatch( $this->mTitle );
- return wfRunHooks( 'UnwatchArticleComplete', array( &$wgUser, &$this ) );
- }
-
- return false;
+ return WatchAction::doUnwatch( $this->getTitle(), $wgUser );
}
/**
@@ -2505,316 +1230,60 @@ class Article {
}
/**
- * Update the article's restriction field, and leave a log entry.
- *
- * @param $limit Array: set of restriction keys
- * @param $reason String
- * @param &$cascade Integer. Set to false if cascading protection isn't allowed.
- * @param $expiry Array: per restriction type expiration
- * @return bool true on success
+ * Info about this page
+ * Called for ?action=info when $wgAllowPageInfo is on.
*/
- public function updateRestrictions( $limit = array(), $reason = '', &$cascade = 0, $expiry = array() ) {
- global $wgUser, $wgContLang;
-
- $restrictionTypes = $this->mTitle->getRestrictionTypes();
-
- $id = $this->mTitle->getArticleID();
-
- if ( $id <= 0 ) {
- wfDebug( "updateRestrictions failed: article id $id <= 0\n" );
- return false;
- }
-
- if ( wfReadOnly() ) {
- wfDebug( "updateRestrictions failed: read-only\n" );
- return false;
- }
-
- if ( !$this->mTitle->userCan( 'protect' ) ) {
- wfDebug( "updateRestrictions failed: insufficient permissions\n" );
- return false;
- }
-
- if ( !$cascade ) {
- $cascade = false;
- }
-
- // Take this opportunity to purge out expired restrictions
- Title::purgeExpiredRestrictions();
-
- # FIXME: Same limitations as described in ProtectionForm.php (line 37);
- # we expect a single selection, but the schema allows otherwise.
- $current = array();
- $updated = Article::flattenRestrictions( $limit );
- $changed = false;
-
- foreach ( $restrictionTypes as $action ) {
- if ( isset( $expiry[$action] ) ) {
- # Get current restrictions on $action
- $aLimits = $this->mTitle->getRestrictions( $action );
- $current[$action] = implode( '', $aLimits );
- # Are any actual restrictions being dealt with here?
- $aRChanged = count( $aLimits ) || !empty( $limit[$action] );
-
- # If something changed, we need to log it. Checking $aRChanged
- # assures that "unprotecting" a page that is not protected does
- # not log just because the expiry was "changed".
- if ( $aRChanged && $this->mTitle->mRestrictionsExpiry[$action] != $expiry[$action] ) {
- $changed = true;
- }
- }
- }
-
- $current = Article::flattenRestrictions( $current );
-
- $changed = ( $changed || $current != $updated );
- $changed = $changed || ( $updated && $this->mTitle->areRestrictionsCascading() != $cascade );
- $protect = ( $updated != '' );
-
- # If nothing's changed, do nothing
- if ( $changed ) {
- if ( wfRunHooks( 'ArticleProtect', array( &$this, &$wgUser, $limit, $reason ) ) ) {
- $dbw = wfGetDB( DB_MASTER );
-
- # Prepare a null revision to be added to the history
- $modified = $current != '' && $protect;
-
- if ( $protect ) {
- $comment_type = $modified ? 'modifiedarticleprotection' : 'protectedarticle';
- } else {
- $comment_type = 'unprotectedarticle';
- }
-
- $comment = $wgContLang->ucfirst( wfMsgForContent( $comment_type, $this->mTitle->getPrefixedText() ) );
-
- # Only restrictions with the 'protect' right can cascade...
- # Otherwise, people who cannot normally protect can "protect" pages via transclusion
- $editrestriction = isset( $limit['edit'] ) ? array( $limit['edit'] ) : $this->mTitle->getRestrictions( 'edit' );
-
- # The schema allows multiple restrictions
- if ( !in_array( 'protect', $editrestriction ) && !in_array( 'sysop', $editrestriction ) ) {
- $cascade = false;
- }
-
- $cascade_description = '';
-
- if ( $cascade ) {
- $cascade_description = ' [' . wfMsgForContent( 'protect-summary-cascade' ) . ']';
- }
-
- if ( $reason ) {
- $comment .= ": $reason";
- }
-
- $editComment = $comment;
- $encodedExpiry = array();
- $protect_description = '';
- foreach ( $limit as $action => $restrictions ) {
- if ( !isset( $expiry[$action] ) )
- $expiry[$action] = Block::infinity();
-
- $encodedExpiry[$action] = Block::encodeExpiry( $expiry[$action], $dbw );
- if ( $restrictions != '' ) {
- $protect_description .= "[$action=$restrictions] (";
- if ( $encodedExpiry[$action] != 'infinity' ) {
- $protect_description .= wfMsgForContent( 'protect-expiring',
- $wgContLang->timeanddate( $expiry[$action], false, false ) ,
- $wgContLang->date( $expiry[$action], false, false ) ,
- $wgContLang->time( $expiry[$action], false, false ) );
- } else {
- $protect_description .= wfMsgForContent( 'protect-expiry-indefinite' );
- }
-
- $protect_description .= ') ';
- }
- }
- $protect_description = trim( $protect_description );
-
- if ( $protect_description && $protect ) {
- $editComment .= " ($protect_description)";
- }
-
- if ( $cascade ) {
- $editComment .= "$cascade_description";
- }
-
- # Update restrictions table
- foreach ( $limit as $action => $restrictions ) {
- if ( $restrictions != '' ) {
- $dbw->replace( 'page_restrictions', array( array( 'pr_page', 'pr_type' ) ),
- array( 'pr_page' => $id,
- 'pr_type' => $action,
- 'pr_level' => $restrictions,
- 'pr_cascade' => ( $cascade && $action == 'edit' ) ? 1 : 0,
- 'pr_expiry' => $encodedExpiry[$action]
- ),
- __METHOD__
- );
- } else {
- $dbw->delete( 'page_restrictions', array( 'pr_page' => $id,
- 'pr_type' => $action ), __METHOD__ );
- }
- }
-
- # Insert a null revision
- $nullRevision = Revision::newNullRevision( $dbw, $id, $editComment, true );
- $nullRevId = $nullRevision->insertOn( $dbw );
-
- $latest = $this->getLatest();
- # Update page record
- $dbw->update( 'page',
- array( /* SET */
- 'page_touched' => $dbw->timestamp(),
- 'page_restrictions' => '',
- 'page_latest' => $nullRevId
- ), array( /* WHERE */
- 'page_id' => $id
- ), 'Article::protect'
- );
-
- wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $nullRevision, $latest, $wgUser ) );
- wfRunHooks( 'ArticleProtectComplete', array( &$this, &$wgUser, $limit, $reason ) );
-
- # Update the protection log
- $log = new LogPage( 'protect' );
- if ( $protect ) {
- $params = array( $protect_description, $cascade ? 'cascade' : '' );
- $log->addEntry( $modified ? 'modify' : 'protect', $this->mTitle, trim( $reason ), $params );
- } else {
- $log->addEntry( 'unprotect', $this->mTitle, $reason );
- }
- } # End hook
- } # End "changed" check
-
- return true;
+ public function info() {
+ Action::factory( 'info', $this )->show();
}
/**
- * Take an array of page restrictions and flatten it to a string
- * suitable for insertion into the page_restrictions field.
- * @param $limit Array
- * @return String
+ * Overriden by ImagePage class, only present here to avoid a fatal error
+ * Called for ?action=revert
*/
- protected static function flattenRestrictions( $limit ) {
- if ( !is_array( $limit ) ) {
- throw new MWException( 'Article::flattenRestrictions given non-array restriction set' );
- }
-
- $bits = array();
- ksort( $limit );
-
- foreach ( $limit as $action => $restrictions ) {
- if ( $restrictions != '' ) {
- $bits[] = "$action=$restrictions";
- }
- }
+ public function revert() {
+ Action::factory( 'revert', $this )->show();
+ }
- return implode( ':', $bits );
+ /**
+ * User interface for rollback operations
+ */
+ public function rollback() {
+ Action::factory( 'rollback', $this )->show();
}
/**
- * Auto-generates a deletion reason
+ * Output a redirect back to the article.
+ * This is typically used after an edit.
*
- * @param &$hasHistory Boolean: whether the page has a history
- * @return mixed String containing deletion reason or empty string, or boolean false
- * if no revision occurred
+ * @deprecated in 1.18; call $wgOut->redirect() directly
+ * @param $noRedir Boolean: add redirect=no
+ * @param $sectionAnchor String: section to redirect to, including "#"
+ * @param $extraQuery String: extra query params
*/
- public function generateReason( &$hasHistory ) {
- global $wgContLang;
-
- $dbw = wfGetDB( DB_MASTER );
- // Get the last revision
- $rev = Revision::newFromTitle( $this->mTitle );
-
- if ( is_null( $rev ) ) {
- return false;
- }
-
- // Get the article's contents
- $contents = $rev->getText();
- $blank = false;
-
- // If the page is blank, use the text from the previous revision,
- // which can only be blank if there's a move/import/protect dummy revision involved
- if ( $contents == '' ) {
- $prev = $rev->getPrevious();
-
- if ( $prev ) {
- $contents = $prev->getText();
- $blank = true;
- }
- }
-
- // Find out if there was only one contributor
- // Only scan the last 20 revisions
- $res = $dbw->select( 'revision', 'rev_user_text',
- array( 'rev_page' => $this->getID(), $dbw->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0' ),
- __METHOD__,
- array( 'LIMIT' => 20 )
- );
-
- if ( $res === false ) {
- // This page has no revisions, which is very weird
- return false;
- }
-
- $hasHistory = ( $res->numRows() > 1 );
- $row = $dbw->fetchObject( $res );
-
- if ( $row ) { // $row is false if the only contributor is hidden
- $onlyAuthor = $row->rev_user_text;
- // Try to find a second contributor
- foreach ( $res as $row ) {
- if ( $row->rev_user_text != $onlyAuthor ) { // Bug 22999
- $onlyAuthor = false;
- break;
- }
- }
- } else {
- $onlyAuthor = false;
- }
+ public function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) {
+ wfDeprecated( __METHOD__ );
+ global $wgOut;
- // Generate the summary with a '$1' placeholder
- if ( $blank ) {
- // The current revision is blank and the one before is also
- // blank. It's just not our lucky day
- $reason = wfMsgForContent( 'exbeforeblank', '$1' );
+ if ( $noRedir ) {
+ $query = 'redirect=no';
+ if ( $extraQuery )
+ $query .= "&$extraQuery";
} else {
- if ( $onlyAuthor ) {
- $reason = wfMsgForContent( 'excontentauthor', '$1', $onlyAuthor );
- } else {
- $reason = wfMsgForContent( 'excontent', '$1' );
- }
- }
-
- if ( $reason == '-' ) {
- // Allow these UI messages to be blanked out cleanly
- return '';
+ $query = $extraQuery;
}
- // Replace newlines with spaces to prevent uglyness
- $contents = preg_replace( "/[\n\r]/", ' ', $contents );
- // Calculate the maximum amount of chars to get
- // Max content length = max comment length - length of the comment (excl. $1) - '...'
- $maxLength = 255 - ( strlen( $reason ) - 2 ) - 3;
- $contents = $wgContLang->truncate( $contents, $maxLength );
- // Remove possible unfinished links
- $contents = preg_replace( '/\[\[([^\]]*)\]?$/', '$1', $contents );
- // Now replace the '$1' placeholder
- $reason = str_replace( '$1', $contents, $reason );
-
- return $reason;
+ $wgOut->redirect( $this->getTitle()->getFullURL( $query ) . $sectionAnchor );
}
-
- /*
+ /**
* UI entry point for page deletion
*/
public function delete() {
- global $wgUser, $wgOut, $wgRequest;
+ global $wgOut, $wgRequest;
$confirm = $wgRequest->wasPosted() &&
- $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
+ $this->getContext()->getUser()->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) );
$this->DeleteReasonList = $wgRequest->getText( 'wpDeleteReasonList', 'other' );
$this->DeleteReason = $wgRequest->getText( 'wpReason' );
@@ -2829,7 +1298,7 @@ class Article {
}
# Flag to hide all contents of the archived revisions
- $suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed( 'suppressrevision' );
+ $suppress = $wgRequest->getVal( 'wpSuppress' ) && $this->getContext()->getUser()->isAllowed( 'suppressrevision' );
# This code desperately needs to be totally rewritten
@@ -2841,7 +1310,7 @@ class Article {
}
# Check permissions
- $permission_errors = $this->mTitle->getUserPermissionsErrors( 'delete', $wgUser );
+ $permission_errors = $this->getTitle()->getUserPermissionsErrors( 'delete', $this->getContext()->getUser() );
if ( count( $permission_errors ) > 0 ) {
$wgOut->showPermissionsErrorPage( $permission_errors );
@@ -2849,33 +1318,34 @@ class Article {
return;
}
- $wgOut->setPagetitle( wfMsg( 'delete-confirm', $this->mTitle->getPrefixedText() ) );
+ $wgOut->setPagetitle( wfMsg( 'delete-confirm', $this->getTitle()->getPrefixedText() ) );
# Better double-check that it hasn't been deleted yet!
$dbw = wfGetDB( DB_MASTER );
- $conds = $this->mTitle->pageCond();
+ $conds = $this->getTitle()->pageCond();
$latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ );
if ( $latest === false ) {
$wgOut->showFatalError(
Html::rawElement(
'div',
array( 'class' => 'error mw-error-cannotdelete' ),
- wfMsgExt( 'cannotdelete', array( 'parse' ), $this->mTitle->getPrefixedText() )
+ wfMsgExt( 'cannotdelete', array( 'parse' ),
+ wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) )
)
);
$wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
LogEventsList::showLogExtract(
$wgOut,
'delete',
- $this->mTitle->getPrefixedText()
+ $this->getTitle()->getPrefixedText()
);
return;
}
# Hack for big sites
- $bigHistory = $this->isBigDeletion();
- if ( $bigHistory && !$this->mTitle->userCan( 'bigdelete' ) ) {
+ $bigHistory = $this->mPage->isBigDeletion();
+ if ( $bigHistory && !$this->getTitle()->userCan( 'bigdelete' ) ) {
global $wgLang, $wgDeleteRevisionsLimit;
$wgOut->wrapWikiMsg( "<div class='error'>\n$1\n</div>\n",
@@ -2887,9 +1357,9 @@ class Article {
if ( $confirm ) {
$this->doDelete( $reason, $suppress );
- if ( $wgRequest->getCheck( 'wpWatch' ) && $wgUser->isLoggedIn() ) {
+ if ( $wgRequest->getCheck( 'wpWatch' ) && $this->getContext()->getUser()->isLoggedIn() ) {
$this->doWatch();
- } elseif ( $this->mTitle->userIsWatching() ) {
+ } elseif ( $this->getTitle()->userIsWatching() ) {
$this->doUnwatch();
}
@@ -2906,12 +1376,14 @@ class Article {
if ( $hasHistory && !$confirm ) {
global $wgLang;
- $skin = $wgUser->getSkin();
- $revisions = $this->estimateRevisionCount();
- //FIXME: lego
+ $revisions = $this->mPage->estimateRevisionCount();
+ // @todo FIXME: i18n issue/patchwork message
$wgOut->addHTML( '<strong class="mw-delete-warning-revisions">' .
wfMsgExt( 'historywarning', array( 'parseinline' ), $wgLang->formatNum( $revisions ) ) .
- wfMsgHtml( 'word-separator' ) . $skin->historyLink() .
+ wfMsgHtml( 'word-separator' ) . Linker::link( $this->getTitle(),
+ wfMsgHtml( 'history' ),
+ array( 'rel' => 'archives' ),
+ array( 'action' => 'history' ) ) .
'</strong>'
);
@@ -2926,103 +1398,24 @@ class Article {
}
/**
- * @return bool whether or not the page surpasses $wgDeleteRevisionsLimit revisions
- */
- public function isBigDeletion() {
- global $wgDeleteRevisionsLimit;
-
- if ( $wgDeleteRevisionsLimit ) {
- $revCount = $this->estimateRevisionCount();
-
- return $revCount > $wgDeleteRevisionsLimit;
- }
-
- return false;
- }
-
- /**
- * @return int approximate revision count
- */
- public function estimateRevisionCount() {
- $dbr = wfGetDB( DB_SLAVE );
-
- // For an exact count...
- // return $dbr->selectField( 'revision', 'COUNT(*)',
- // array( 'rev_page' => $this->getId() ), __METHOD__ );
- return $dbr->estimateRowCount( 'revision', '*',
- array( 'rev_page' => $this->getId() ), __METHOD__ );
- }
-
- /**
- * Get the last N authors
- * @param $num Integer: number of revisions to get
- * @param $revLatest String: the latest rev_id, selected from the master (optional)
- * @return array Array of authors, duplicates not removed
- */
- public function getLastNAuthors( $num, $revLatest = 0 ) {
- wfProfileIn( __METHOD__ );
- // First try the slave
- // If that doesn't have the latest revision, try the master
- $continue = 2;
- $db = wfGetDB( DB_SLAVE );
-
- do {
- $res = $db->select( array( 'page', 'revision' ),
- array( 'rev_id', 'rev_user_text' ),
- array(
- 'page_namespace' => $this->mTitle->getNamespace(),
- 'page_title' => $this->mTitle->getDBkey(),
- 'rev_page = page_id'
- ), __METHOD__, $this->getSelectOptions( array(
- 'ORDER BY' => 'rev_timestamp DESC',
- 'LIMIT' => $num
- ) )
- );
-
- if ( !$res ) {
- wfProfileOut( __METHOD__ );
- return array();
- }
-
- $row = $db->fetchObject( $res );
-
- if ( $continue == 2 && $revLatest && $row->rev_id != $revLatest ) {
- $db = wfGetDB( DB_MASTER );
- $continue--;
- } else {
- $continue = 0;
- }
- } while ( $continue );
-
- $authors = array( $row->rev_user_text );
-
- foreach ( $res as $row ) {
- $authors[] = $row->rev_user_text;
- }
-
- wfProfileOut( __METHOD__ );
- return $authors;
- }
-
- /**
* Output deletion confirmation dialog
- * FIXME: Move to another file?
+ * @todo FIXME: Move to another file?
* @param $reason String: prefilled reason
*/
public function confirmDelete( $reason ) {
- global $wgOut, $wgUser;
+ global $wgOut;
wfDebug( "Article::confirmDelete\n" );
- $deleteBackLink = $wgUser->getSkin()->linkKnown( $this->mTitle );
+ $deleteBackLink = Linker::linkKnown( $this->getTitle() );
$wgOut->setSubtitle( wfMsgHtml( 'delete-backlink', $deleteBackLink ) );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
$wgOut->addWikiMsg( 'confirmdeletetext' );
wfRunHooks( 'ArticleConfirmDelete', array( $this, $wgOut, &$reason ) );
- if ( $wgUser->isAllowed( 'suppressrevision' ) ) {
- $suppress = "<tr id=\"wpDeleteSuppressRow\" name=\"wpDeleteSuppressRow\">
+ if ( $this->getContext()->getUser()->isAllowed( 'suppressrevision' ) ) {
+ $suppress = "<tr id=\"wpDeleteSuppressRow\">
<td></td>
<td class='mw-input'><strong>" .
Xml::checkLabel( wfMsg( 'revdelete-suppress' ),
@@ -3032,10 +1425,10 @@ class Article {
} else {
$suppress = '';
}
- $checkWatch = $wgUser->getBoolOption( 'watchdeletion' ) || $this->mTitle->userIsWatching();
+ $checkWatch = $this->getContext()->getUser()->getBoolOption( 'watchdeletion' ) || $this->getTitle()->userIsWatching();
$form = Xml::openElement( 'form', array( 'method' => 'post',
- 'action' => $this->mTitle->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ) ) .
+ 'action' => $this->getTitle()->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ) ) .
Xml::openElement( 'fieldset', array( 'id' => 'mw-delete-table' ) ) .
Xml::tags( 'legend', null, wfMsgExt( 'delete-legend', array( 'parsemag', 'escapenoentities' ) ) ) .
Xml::openElement( 'table', array( 'id' => 'mw-deleteconfirm-table' ) ) .
@@ -3065,7 +1458,7 @@ class Article {
</tr>";
# Disallow watching if user is not logged in
- if ( $wgUser->isLoggedIn() ) {
+ if ( $this->getContext()->getUser()->isLoggedIn() ) {
$form .= "
<tr>
<td></td>
@@ -3087,13 +1480,12 @@ class Article {
</tr>" .
Xml::closeElement( 'table' ) .
Xml::closeElement( 'fieldset' ) .
- Html::hidden( 'wpEditToken', $wgUser->editToken() ) .
+ Html::hidden( 'wpEditToken', $this->getContext()->getUser()->editToken() ) .
Xml::closeElement( 'form' );
- if ( $wgUser->isAllowed( 'editinterface' ) ) {
- $skin = $wgUser->getSkin();
+ if ( $this->getContext()->getUser()->isAllowed( 'editinterface' ) ) {
$title = Title::makeTitle( NS_MEDIAWIKI, 'Deletereason-dropdown' );
- $link = $skin->link(
+ $link = Linker::link(
$title,
wfMsgHtml( 'delete-edit-reasonlist' ),
array(),
@@ -3105,7 +1497,7 @@ class Article {
$wgOut->addHTML( $form );
$wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) );
LogEventsList::showLogExtract( $wgOut, 'delete',
- $this->mTitle->getPrefixedText()
+ $this->getTitle()->getPrefixedText()
);
}
@@ -3113,31 +1505,29 @@ class Article {
* Perform a deletion and output success or failure messages
*/
public function doDelete( $reason, $suppress = false ) {
- global $wgOut, $wgUser;
+ global $wgOut;
- $id = $this->mTitle->getArticleID( Title::GAID_FOR_UPDATE );
+ $id = $this->getTitle()->getArticleID( Title::GAID_FOR_UPDATE );
$error = '';
- if ( wfRunHooks( 'ArticleDelete', array( &$this, &$wgUser, &$reason, &$error ) ) ) {
- if ( $this->doDeleteArticle( $reason, $suppress, $id ) ) {
- $deleted = $this->mTitle->getPrefixedText();
+ if ( $this->mPage->doDeleteArticle( $reason, $suppress, $id, $error ) ) {
+ $deleted = $this->getTitle()->getPrefixedText();
- $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
- $wgOut->setRobotPolicy( 'noindex,nofollow' );
+ $wgOut->setPagetitle( wfMsg( 'actioncomplete' ) );
+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
- $loglink = '[[Special:Log/delete|' . wfMsgNoTrans( 'deletionlog' ) . ']]';
+ $loglink = '[[Special:Log/delete|' . wfMsgNoTrans( 'deletionlog' ) . ']]';
- $wgOut->addWikiMsg( 'deletedtext', $deleted, $loglink );
- $wgOut->returnToMain( false );
- wfRunHooks( 'ArticleDeleteComplete', array( &$this, &$wgUser, $reason, $id ) );
- }
+ $wgOut->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink );
+ $wgOut->returnToMain( false );
} else {
if ( $error == '' ) {
$wgOut->showFatalError(
Html::rawElement(
'div',
array( 'class' => 'error mw-error-cannotdelete' ),
- wfMsgExt( 'cannotdelete', array( 'parse' ), $this->mTitle->getPrefixedText() )
+ wfMsgExt( 'cannotdelete', array( 'parse' ),
+ wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) )
)
);
@@ -3146,7 +1536,7 @@ class Article {
LogEventsList::showLogExtract(
$wgOut,
'delete',
- $this->mTitle->getPrefixedText()
+ $this->getTitle()->getPrefixedText()
);
} else {
$wgOut->showFatalError( $error );
@@ -3155,574 +1545,6 @@ class Article {
}
/**
- * Back-end article deletion
- * Deletes the article with database consistency, writes logs, purges caches
- *
- * @param $reason string delete reason for deletion log
- * @param suppress bitfield
- * Revision::DELETED_TEXT
- * Revision::DELETED_COMMENT
- * Revision::DELETED_USER
- * Revision::DELETED_RESTRICTED
- * @param $id int article ID
- * @param $commit boolean defaults to true, triggers transaction end
- * @return boolean true if successful
- */
- public function doDeleteArticle( $reason, $suppress = false, $id = 0, $commit = true ) {
- global $wgDeferredUpdateList, $wgUseTrackbacks;
-
- wfDebug( __METHOD__ . "\n" );
-
- $dbw = wfGetDB( DB_MASTER );
- $t = $this->mTitle->getDBkey();
- $id = $id ? $id : $this->mTitle->getArticleID( Title::GAID_FOR_UPDATE );
-
- if ( $t === '' || $id == 0 ) {
- return false;
- }
-
- $u = new SiteStatsUpdate( 0, 1, - (int)$this->isCountable( $this->getRawText() ), -1 );
- array_push( $wgDeferredUpdateList, $u );
-
- // Bitfields to further suppress the content
- if ( $suppress ) {
- $bitfield = 0;
- // This should be 15...
- $bitfield |= Revision::DELETED_TEXT;
- $bitfield |= Revision::DELETED_COMMENT;
- $bitfield |= Revision::DELETED_USER;
- $bitfield |= Revision::DELETED_RESTRICTED;
- } else {
- $bitfield = 'rev_deleted';
- }
-
- $dbw->begin();
- // For now, shunt the revision data into the archive table.
- // Text is *not* removed from the text table; bulk storage
- // is left intact to avoid breaking block-compression or
- // immutable storage schemes.
- //
- // For backwards compatibility, note that some older archive
- // table entries will have ar_text and ar_flags fields still.
- //
- // In the future, we may keep revisions and mark them with
- // the rev_deleted field, which is reserved for this purpose.
- $dbw->insertSelect( 'archive', array( 'page', 'revision' ),
- array(
- 'ar_namespace' => 'page_namespace',
- 'ar_title' => 'page_title',
- 'ar_comment' => 'rev_comment',
- 'ar_user' => 'rev_user',
- 'ar_user_text' => 'rev_user_text',
- 'ar_timestamp' => 'rev_timestamp',
- 'ar_minor_edit' => 'rev_minor_edit',
- 'ar_rev_id' => 'rev_id',
- 'ar_text_id' => 'rev_text_id',
- 'ar_text' => '\'\'', // Be explicit to appease
- 'ar_flags' => '\'\'', // MySQL's "strict mode"...
- 'ar_len' => 'rev_len',
- 'ar_page_id' => 'page_id',
- 'ar_deleted' => $bitfield
- ), array(
- 'page_id' => $id,
- 'page_id = rev_page'
- ), __METHOD__
- );
-
- # Delete restrictions for it
- $dbw->delete( 'page_restrictions', array ( 'pr_page' => $id ), __METHOD__ );
-
- # Now that it's safely backed up, delete it
- $dbw->delete( 'page', array( 'page_id' => $id ), __METHOD__ );
- $ok = ( $dbw->affectedRows() > 0 ); // getArticleId() uses slave, could be laggy
-
- if ( !$ok ) {
- $dbw->rollback();
- return false;
- }
-
- # Fix category table counts
- $cats = array();
- $res = $dbw->select( 'categorylinks', 'cl_to', array( 'cl_from' => $id ), __METHOD__ );
-
- foreach ( $res as $row ) {
- $cats [] = $row->cl_to;
- }
-
- $this->updateCategoryCounts( array(), $cats );
-
- # If using cascading deletes, we can skip some explicit deletes
- if ( !$dbw->cascadingDeletes() ) {
- $dbw->delete( 'revision', array( 'rev_page' => $id ), __METHOD__ );
-
- if ( $wgUseTrackbacks )
- $dbw->delete( 'trackbacks', array( 'tb_page' => $id ), __METHOD__ );
-
- # Delete outgoing links
- $dbw->delete( 'pagelinks', array( 'pl_from' => $id ) );
- $dbw->delete( 'imagelinks', array( 'il_from' => $id ) );
- $dbw->delete( 'categorylinks', array( 'cl_from' => $id ) );
- $dbw->delete( 'templatelinks', array( 'tl_from' => $id ) );
- $dbw->delete( 'externallinks', array( 'el_from' => $id ) );
- $dbw->delete( 'langlinks', array( 'll_from' => $id ) );
- $dbw->delete( 'iwlinks', array( 'iwl_from' => $id ) );
- $dbw->delete( 'redirect', array( 'rd_from' => $id ) );
- }
-
- # If using cleanup triggers, we can skip some manual deletes
- if ( !$dbw->cleanupTriggers() ) {
- # Clean up recentchanges entries...
- $dbw->delete( 'recentchanges',
- array( 'rc_type != ' . RC_LOG,
- 'rc_namespace' => $this->mTitle->getNamespace(),
- 'rc_title' => $this->mTitle->getDBkey() ),
- __METHOD__ );
- $dbw->delete( 'recentchanges',
- array( 'rc_type != ' . RC_LOG, 'rc_cur_id' => $id ),
- __METHOD__ );
- }
-
- # Clear caches
- Article::onArticleDelete( $this->mTitle );
-
- # Clear the cached article id so the interface doesn't act like we exist
- $this->mTitle->resetArticleID( 0 );
-
- # Log the deletion, if the page was suppressed, log it at Oversight instead
- $logtype = $suppress ? 'suppress' : 'delete';
- $log = new LogPage( $logtype );
-
- # Make sure logging got through
- $log->addEntry( 'delete', $this->mTitle, $reason, array() );
-
- if ( $commit ) {
- $dbw->commit();
- }
-
- return true;
- }
-
- /**
- * Roll back the most recent consecutive set of edits to a page
- * from the same user; fails if there are no eligible edits to
- * roll back to, e.g. user is the sole contributor. This function
- * performs permissions checks on $wgUser, then calls commitRollback()
- * to do the dirty work
- *
- * @param $fromP String: Name of the user whose edits to rollback.
- * @param $summary String: Custom summary. Set to default summary if empty.
- * @param $token String: Rollback token.
- * @param $bot Boolean: If true, mark all reverted edits as bot.
- *
- * @param $resultDetails Array: contains result-specific array of additional values
- * 'alreadyrolled' : 'current' (rev)
- * success : 'summary' (str), 'current' (rev), 'target' (rev)
- *
- * @return array of errors, each error formatted as
- * array(messagekey, param1, param2, ...).
- * On success, the array is empty. This array can also be passed to
- * OutputPage::showPermissionsErrorPage().
- */
- public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails ) {
- global $wgUser;
-
- $resultDetails = null;
-
- # Check permissions
- $editErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser );
- $rollbackErrors = $this->mTitle->getUserPermissionsErrors( 'rollback', $wgUser );
- $errors = array_merge( $editErrors, wfArrayDiff2( $rollbackErrors, $editErrors ) );
-
- if ( !$wgUser->matchEditToken( $token, array( $this->mTitle->getPrefixedText(), $fromP ) ) ) {
- $errors[] = array( 'sessionfailure' );
- }
-
- if ( $wgUser->pingLimiter( 'rollback' ) || $wgUser->pingLimiter() ) {
- $errors[] = array( 'actionthrottledtext' );
- }
-
- # If there were errors, bail out now
- if ( !empty( $errors ) ) {
- return $errors;
- }
-
- return $this->commitRollback( $fromP, $summary, $bot, $resultDetails );
- }
-
- /**
- * Backend implementation of doRollback(), please refer there for parameter
- * and return value documentation
- *
- * NOTE: This function does NOT check ANY permissions, it just commits the
- * rollback to the DB Therefore, you should only call this function direct-
- * ly if you want to use custom permissions checks. If you don't, use
- * doRollback() instead.
- */
- public function commitRollback( $fromP, $summary, $bot, &$resultDetails ) {
- global $wgUseRCPatrol, $wgUser, $wgLang;
-
- $dbw = wfGetDB( DB_MASTER );
-
- if ( wfReadOnly() ) {
- return array( array( 'readonlytext' ) );
- }
-
- # Get the last editor
- $current = Revision::newFromTitle( $this->mTitle );
- if ( is_null( $current ) ) {
- # Something wrong... no page?
- return array( array( 'notanarticle' ) );
- }
-
- $from = str_replace( '_', ' ', $fromP );
- # User name given should match up with the top revision.
- # If the user was deleted then $from should be empty.
- if ( $from != $current->getUserText() ) {
- $resultDetails = array( 'current' => $current );
- return array( array( 'alreadyrolled',
- htmlspecialchars( $this->mTitle->getPrefixedText() ),
- htmlspecialchars( $fromP ),
- htmlspecialchars( $current->getUserText() )
- ) );
- }
-
- # Get the last edit not by this guy...
- # Note: these may not be public values
- $user = intval( $current->getRawUser() );
- $user_text = $dbw->addQuotes( $current->getRawUserText() );
- $s = $dbw->selectRow( 'revision',
- array( 'rev_id', 'rev_timestamp', 'rev_deleted' ),
- array( 'rev_page' => $current->getPage(),
- "rev_user != {$user} OR rev_user_text != {$user_text}"
- ), __METHOD__,
- array( 'USE INDEX' => 'page_timestamp',
- 'ORDER BY' => 'rev_timestamp DESC' )
- );
- if ( $s === false ) {
- # No one else ever edited this page
- return array( array( 'cantrollback' ) );
- } else if ( $s->rev_deleted & Revision::DELETED_TEXT || $s->rev_deleted & Revision::DELETED_USER ) {
- # Only admins can see this text
- return array( array( 'notvisiblerev' ) );
- }
-
- $set = array();
- if ( $bot && $wgUser->isAllowed( 'markbotedits' ) ) {
- # Mark all reverted edits as bot
- $set['rc_bot'] = 1;
- }
-
- if ( $wgUseRCPatrol ) {
- # Mark all reverted edits as patrolled
- $set['rc_patrolled'] = 1;
- }
-
- if ( count( $set ) ) {
- $dbw->update( 'recentchanges', $set,
- array( /* WHERE */
- 'rc_cur_id' => $current->getPage(),
- 'rc_user_text' => $current->getUserText(),
- "rc_timestamp > '{$s->rev_timestamp}'",
- ), __METHOD__
- );
- }
-
- # Generate the edit summary if necessary
- $target = Revision::newFromId( $s->rev_id );
- if ( empty( $summary ) ) {
- if ( $from == '' ) { // no public user name
- $summary = wfMsgForContent( 'revertpage-nouser' );
- } else {
- $summary = wfMsgForContent( 'revertpage' );
- }
- }
-
- # Allow the custom summary to use the same args as the default message
- $args = array(
- $target->getUserText(), $from, $s->rev_id,
- $wgLang->timeanddate( wfTimestamp( TS_MW, $s->rev_timestamp ), true ),
- $current->getId(), $wgLang->timeanddate( $current->getTimestamp() )
- );
- $summary = wfMsgReplaceArgs( $summary, $args );
-
- # Save
- $flags = EDIT_UPDATE;
-
- if ( $wgUser->isAllowed( 'minoredit' ) ) {
- $flags |= EDIT_MINOR;
- }
-
- if ( $bot && ( $wgUser->isAllowed( 'markbotedits' ) || $wgUser->isAllowed( 'bot' ) ) ) {
- $flags |= EDIT_FORCE_BOT;
- }
-
- # Actually store the edit
- $status = $this->doEdit( $target->getText(), $summary, $flags, $target->getId() );
- if ( !empty( $status->value['revision'] ) ) {
- $revId = $status->value['revision']->getId();
- } else {
- $revId = false;
- }
-
- wfRunHooks( 'ArticleRollbackComplete', array( $this, $wgUser, $target, $current ) );
-
- $resultDetails = array(
- 'summary' => $summary,
- 'current' => $current,
- 'target' => $target,
- 'newid' => $revId
- );
-
- return array();
- }
-
- /**
- * User interface for rollback operations
- */
- public function rollback() {
- global $wgUser, $wgOut, $wgRequest;
-
- $details = null;
-
- $result = $this->doRollback(
- $wgRequest->getVal( 'from' ),
- $wgRequest->getText( 'summary' ),
- $wgRequest->getVal( 'token' ),
- $wgRequest->getBool( 'bot' ),
- $details
- );
-
- if ( in_array( array( 'actionthrottledtext' ), $result ) ) {
- $wgOut->rateLimited();
- return;
- }
-
- if ( isset( $result[0][0] ) && ( $result[0][0] == 'alreadyrolled' || $result[0][0] == 'cantrollback' ) ) {
- $wgOut->setPageTitle( wfMsg( 'rollbackfailed' ) );
- $errArray = $result[0];
- $errMsg = array_shift( $errArray );
- $wgOut->addWikiMsgArray( $errMsg, $errArray );
-
- if ( isset( $details['current'] ) ) {
- $current = $details['current'];
-
- if ( $current->getComment() != '' ) {
- $wgOut->addWikiMsgArray( 'editcomment', array(
- $wgUser->getSkin()->formatComment( $current->getComment() ) ), array( 'replaceafter' ) );
- }
- }
-
- return;
- }
-
- # Display permissions errors before read-only message -- there's no
- # point in misleading the user into thinking the inability to rollback
- # is only temporary.
- if ( !empty( $result ) && $result !== array( array( 'readonlytext' ) ) ) {
- # array_diff is completely broken for arrays of arrays, sigh.
- # Remove any 'readonlytext' error manually.
- $out = array();
- foreach ( $result as $error ) {
- if ( $error != array( 'readonlytext' ) ) {
- $out [] = $error;
- }
- }
- $wgOut->showPermissionsErrorPage( $out );
-
- return;
- }
-
- if ( $result == array( array( 'readonlytext' ) ) ) {
- $wgOut->readOnlyPage();
-
- return;
- }
-
- $current = $details['current'];
- $target = $details['target'];
- $newId = $details['newid'];
- $wgOut->setPageTitle( wfMsg( 'actioncomplete' ) );
- $wgOut->setRobotPolicy( 'noindex,nofollow' );
-
- if ( $current->getUserText() === '' ) {
- $old = wfMsg( 'rev-deleted-user' );
- } else {
- $old = $wgUser->getSkin()->userLink( $current->getUser(), $current->getUserText() )
- . $wgUser->getSkin()->userToolLinks( $current->getUser(), $current->getUserText() );
- }
-
- $new = $wgUser->getSkin()->userLink( $target->getUser(), $target->getUserText() )
- . $wgUser->getSkin()->userToolLinks( $target->getUser(), $target->getUserText() );
- $wgOut->addHTML( wfMsgExt( 'rollback-success', array( 'parse', 'replaceafter' ), $old, $new ) );
- $wgOut->returnToMain( false, $this->mTitle );
-
- if ( !$wgRequest->getBool( 'hidediff', false ) && !$wgUser->getBoolOption( 'norollbackdiff', false ) ) {
- $de = new DifferenceEngine( $this->mTitle, $current->getId(), $newId, false, true );
- $de->showDiff( '', '' );
- }
- }
-
- /**
- * Do standard deferred updates after page view
- */
- public function viewUpdates() {
- global $wgDeferredUpdateList, $wgDisableCounters, $wgUser;
- if ( wfReadOnly() ) {
- return;
- }
-
- # Don't update page view counters on views from bot users (bug 14044)
- if ( !$wgDisableCounters && !$wgUser->isAllowed( 'bot' ) && $this->getID() ) {
- Article::incViewCount( $this->getID() );
- $u = new SiteStatsUpdate( 1, 0, 0 );
- array_push( $wgDeferredUpdateList, $u );
- }
-
- # Update newtalk / watchlist notification status
- $wgUser->clearNotification( $this->mTitle );
- }
-
- /**
- * Prepare text which is about to be saved.
- * Returns a stdclass with source, pst and output members
- */
- public function prepareTextForEdit( $text, $revid = null ) {
- if ( $this->mPreparedEdit && $this->mPreparedEdit->newText == $text && $this->mPreparedEdit->revid == $revid ) {
- // Already prepared
- return $this->mPreparedEdit;
- }
-
- global $wgParser;
-
- $edit = (object)array();
- $edit->revid = $revid;
- $edit->newText = $text;
- $edit->pst = $this->preSaveTransform( $text );
- $edit->popts = $this->getParserOptions();
- $edit->output = $wgParser->parse( $edit->pst, $this->mTitle, $edit->popts, true, true, $revid );
- $edit->oldText = $this->getContent();
-
- $this->mPreparedEdit = $edit;
-
- return $edit;
- }
-
- /**
- * Do standard deferred updates after page edit.
- * Update links tables, site stats, search index and message cache.
- * Purges pages that include this page if the text was changed here.
- * Every 100th edit, prune the recent changes table.
- *
- * @private
- * @param $text String: New text of the article
- * @param $summary String: Edit summary
- * @param $minoredit Boolean: Minor edit
- * @param $timestamp_of_pagechange Timestamp associated with the page change
- * @param $newid Integer: rev_id value of the new revision
- * @param $changed Boolean: Whether or not the content actually changed
- */
- public function editUpdates( $text, $summary, $minoredit, $timestamp_of_pagechange, $newid, $changed = true ) {
- global $wgDeferredUpdateList, $wgMessageCache, $wgUser, $wgEnableParserCache;
-
- wfProfileIn( __METHOD__ );
-
- # Parse the text
- # Be careful not to double-PST: $text is usually already PST-ed once
- if ( !$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag( 'vary-revision' ) ) {
- wfDebug( __METHOD__ . ": No prepared edit or vary-revision is set...\n" );
- $editInfo = $this->prepareTextForEdit( $text, $newid );
- } else {
- wfDebug( __METHOD__ . ": No vary-revision, using prepared edit...\n" );
- $editInfo = $this->mPreparedEdit;
- }
-
- # Save it to the parser cache
- if ( $wgEnableParserCache ) {
- $parserCache = ParserCache::singleton();
- $parserCache->save( $editInfo->output, $this, $editInfo->popts );
- }
-
- # Update the links tables
- $u = new LinksUpdate( $this->mTitle, $editInfo->output );
- $u->doUpdate();
-
- wfRunHooks( 'ArticleEditUpdates', array( &$this, &$editInfo, $changed ) );
-
- if ( wfRunHooks( 'ArticleEditUpdatesDeleteFromRecentchanges', array( &$this ) ) ) {
- if ( 0 == mt_rand( 0, 99 ) ) {
- // Flush old entries from the `recentchanges` table; we do this on
- // random requests so as to avoid an increase in writes for no good reason
- global $wgRCMaxAge;
-
- $dbw = wfGetDB( DB_MASTER );
- $cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
- $recentchanges = $dbw->tableName( 'recentchanges' );
- $sql = "DELETE FROM $recentchanges WHERE rc_timestamp < '{$cutoff}'";
-
- $dbw->query( $sql );
- }
- }
-
- $id = $this->getID();
- $title = $this->mTitle->getPrefixedDBkey();
- $shortTitle = $this->mTitle->getDBkey();
-
- if ( 0 == $id ) {
- wfProfileOut( __METHOD__ );
- return;
- }
-
- $u = new SiteStatsUpdate( 0, 1, $this->mGoodAdjustment, $this->mTotalAdjustment );
- array_push( $wgDeferredUpdateList, $u );
- $u = new SearchUpdate( $id, $title, $text );
- array_push( $wgDeferredUpdateList, $u );
-
- # If this is another user's talk page, update newtalk
- # Don't do this if $changed = false otherwise some idiot can null-edit a
- # load of user talk pages and piss people off, nor if it's a minor edit
- # by a properly-flagged bot.
- if ( $this->mTitle->getNamespace() == NS_USER_TALK && $shortTitle != $wgUser->getTitleKey() && $changed
- && !( $minoredit && $wgUser->isAllowed( 'nominornewtalk' ) )
- ) {
- if ( wfRunHooks( 'ArticleEditUpdateNewTalk', array( &$this ) ) ) {
- $other = User::newFromName( $shortTitle, false );
- if ( !$other ) {
- wfDebug( __METHOD__ . ": invalid username\n" );
- } elseif ( User::isIP( $shortTitle ) ) {
- // An anonymous user
- $other->setNewtalk( true );
- } elseif ( $other->isLoggedIn() ) {
- $other->setNewtalk( true );
- } else {
- wfDebug( __METHOD__ . ": don't need to notify a nonexistent user\n" );
- }
- }
- }
-
- if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- $wgMessageCache->replace( $shortTitle, $text );
- }
-
- wfProfileOut( __METHOD__ );
- }
-
- /**
- * Perform article updates on a special page creation.
- *
- * @param $rev Revision object
- *
- * @todo This is a shitty interface function. Kill it and replace the
- * other shitty functions like editUpdates and such so it's not needed
- * anymore.
- */
- public function createUpdates( $rev ) {
- $this->mGoodAdjustment = $this->isCountable( $rev->getText() );
- $this->mTotalAdjustment = 1;
- $this->editUpdates( $rev->getText(), $rev->getComment(),
- $rev->isMinor(), wfTimestamp(), $rev->getId(), true );
- }
-
- /**
* Generate the navigation links when browsing through an article revisions
* It shows the information as:
* Revision as of \<date\>; view current revision
@@ -3746,16 +1568,17 @@ class Article {
}
$revision = Revision::newFromId( $oldid );
+ $timestamp = $revision->getTimestamp();
+
+ $current = ( $oldid == $this->mPage->getLatest() );
+ $td = $wgLang->timeanddate( $timestamp, true );
+ $tddate = $wgLang->date( $timestamp, true );
+ $tdtime = $wgLang->time( $timestamp, true );
- $current = ( $oldid == $this->mLatest );
- $td = $wgLang->timeanddate( $this->mTimestamp, true );
- $tddate = $wgLang->date( $this->mTimestamp, true );
- $tdtime = $wgLang->time( $this->mTimestamp, true );
- $sk = $wgUser->getSkin();
$lnk = $current
? wfMsgHtml( 'currentrevisionlink' )
- : $sk->link(
- $this->mTitle,
+ : Linker::link(
+ $this->getTitle(),
wfMsgHtml( 'currentrevisionlink' ),
array(),
$extraParams,
@@ -3763,8 +1586,8 @@ class Article {
);
$curdiff = $current
? wfMsgHtml( 'diff' )
- : $sk->link(
- $this->mTitle,
+ : Linker::link(
+ $this->getTitle(),
wfMsgHtml( 'diff' ),
array(),
array(
@@ -3773,10 +1596,10 @@ class Article {
) + $extraParams,
array( 'known', 'noclasses' )
);
- $prev = $this->mTitle->getPreviousRevisionID( $oldid ) ;
+ $prev = $this->getTitle()->getPreviousRevisionID( $oldid ) ;
$prevlink = $prev
- ? $sk->link(
- $this->mTitle,
+ ? Linker::link(
+ $this->getTitle(),
wfMsgHtml( 'previousrevision' ),
array(),
array(
@@ -3787,8 +1610,8 @@ class Article {
)
: wfMsgHtml( 'previousrevision' );
$prevdiff = $prev
- ? $sk->link(
- $this->mTitle,
+ ? Linker::link(
+ $this->getTitle(),
wfMsgHtml( 'diff' ),
array(),
array(
@@ -3800,8 +1623,8 @@ class Article {
: wfMsgHtml( 'diff' );
$nextlink = $current
? wfMsgHtml( 'nextrevision' )
- : $sk->link(
- $this->mTitle,
+ : Linker::link(
+ $this->getTitle(),
wfMsgHtml( 'nextrevision' ),
array(),
array(
@@ -3812,8 +1635,8 @@ class Article {
);
$nextdiff = $current
? wfMsgHtml( 'diff' )
- : $sk->link(
- $this->mTitle,
+ : Linker::link(
+ $this->getTitle(),
wfMsgHtml( 'diff' ),
array(),
array(
@@ -3829,23 +1652,22 @@ class Article {
$canHide = $wgUser->isAllowed( 'deleterevision' );
if ( $canHide || ( $revision->getVisibility() && $wgUser->isAllowed( 'deletedhistory' ) ) ) {
if ( !$revision->userCan( Revision::DELETED_RESTRICTED ) ) {
- $cdel = $sk->revDeleteLinkDisabled( $canHide ); // rev was hidden from Sysops
+ $cdel = Linker::revDeleteLinkDisabled( $canHide ); // rev was hidden from Sysops
} else {
$query = array(
'type' => 'revision',
- 'target' => $this->mTitle->getPrefixedDbkey(),
+ 'target' => $this->getTitle()->getPrefixedDbkey(),
'ids' => $oldid
);
- $cdel = $sk->revDeleteLink( $query, $revision->isDeleted( File::DELETED_RESTRICTED ), $canHide );
+ $cdel = Linker::revDeleteLink( $query, $revision->isDeleted( File::DELETED_RESTRICTED ), $canHide );
}
$cdel .= ' ';
}
# Show user links if allowed to see them. If hidden, then show them only if requested...
- $userlinks = $sk->revUserTools( $revision, !$unhide );
+ $userlinks = Linker::revUserTools( $revision, !$unhide );
- $m = wfMsg( 'revision-info-current' );
- $infomsg = $current && !wfEmptyMsg( 'revision-info-current', $m ) && $m != '-'
+ $infomsg = $current && !wfMessage( 'revision-info-current' )->isDisabled()
? 'revision-info-current'
: 'revision-info';
@@ -3867,20 +1689,6 @@ class Article {
$wgOut->setSubtitle( $r );
}
- /**
- * This function is called right before saving the wikitext,
- * so we can do things like signatures and links-in-context.
- *
- * @param $text String article contents
- * @return string article contents with altered wikitext markup (signatures
- * converted, {{subst:}}, templates, etc.)
- */
- public function preSaveTransform( $text ) {
- global $wgParser, $wgUser;
-
- return $wgParser->preSaveTransform( $text, $this->mTitle, $wgUser, ParserOptions::newFromUser( $wgUser ) );
- }
-
/* Caching functions */
/**
@@ -3900,8 +1708,8 @@ class Article {
$called = true;
if ( $this->isFileCacheable() ) {
- $cache = new HTMLFileCache( $this->mTitle );
- if ( $cache->isFileCacheGood( $this->mTouched ) ) {
+ $cache = new HTMLFileCache( $this->getTitle() );
+ if ( $cache->isFileCacheGood( $this->mPage->getTouched() ) ) {
wfDebug( "Article::tryFileCache(): about to load file\n" );
$cache->loadFromFileCache();
return true;
@@ -3924,7 +1732,7 @@ class Article {
$cacheable = false;
if ( HTMLFileCache::useFileCache() ) {
- $cacheable = $this->getID() && !$this->mRedirectedFrom && !$this->mTitle->isRedirect();
+ $cacheable = $this->mPage->getID() && !$this->mRedirectedFrom && !$this->getTitle()->isRedirect();
// Extension may have reason to disable file caching on some pages.
if ( $cacheable ) {
$cacheable = wfRunHooks( 'IsFileCacheable', array( &$this ) );
@@ -3934,462 +1742,77 @@ class Article {
return $cacheable;
}
- /**
- * Loads page_touched and returns a value indicating if it should be used
- * @return boolean true if not a redirect
- */
- public function checkTouched() {
- if ( !$this->mDataLoaded ) {
- $this->loadPageData();
- }
-
- return !$this->mIsRedirect;
- }
-
- /**
- * Get the page_touched field
- * @return string containing GMT timestamp
- */
- public function getTouched() {
- if ( !$this->mDataLoaded ) {
- $this->loadPageData();
- }
-
- return $this->mTouched;
- }
-
- /**
- * Get the page_latest field
- * @return integer rev_id of current revision
- */
- public function getLatest() {
- if ( !$this->mDataLoaded ) {
- $this->loadPageData();
- }
-
- return (int)$this->mLatest;
- }
-
- /**
- * Edit an article without doing all that other stuff
- * The article must already exist; link tables etc
- * are not updated, caches are not flushed.
- *
- * @param $text String: text submitted
- * @param $comment String: comment submitted
- * @param $minor Boolean: whereas it's a minor modification
- */
- public function quickEdit( $text, $comment = '', $minor = 0 ) {
- wfProfileIn( __METHOD__ );
-
- $dbw = wfGetDB( DB_MASTER );
- $revision = new Revision( array(
- 'page' => $this->getId(),
- 'text' => $text,
- 'comment' => $comment,
- 'minor_edit' => $minor ? 1 : 0,
- ) );
- $revision->insertOn( $dbw );
- $this->updateRevisionOn( $dbw, $revision );
-
- global $wgUser;
- wfRunHooks( 'NewRevisionFromEditComplete', array( $this, $revision, false, $wgUser ) );
-
- wfProfileOut( __METHOD__ );
- }
-
- /**
- * Used to increment the view counter
- *
- * @param $id Integer: article id
- */
- public static function incViewCount( $id ) {
- $id = intval( $id );
-
- global $wgHitcounterUpdateFreq;
-
- $dbw = wfGetDB( DB_MASTER );
- $pageTable = $dbw->tableName( 'page' );
- $hitcounterTable = $dbw->tableName( 'hitcounter' );
- $acchitsTable = $dbw->tableName( 'acchits' );
- $dbType = $dbw->getType();
-
- if ( $wgHitcounterUpdateFreq <= 1 || $dbType == 'sqlite' ) {
- $dbw->query( "UPDATE $pageTable SET page_counter = page_counter + 1 WHERE page_id = $id" );
-
- return;
- }
-
- # Not important enough to warrant an error page in case of failure
- $oldignore = $dbw->ignoreErrors( true );
-
- $dbw->query( "INSERT INTO $hitcounterTable (hc_id) VALUES ({$id})" );
-
- $checkfreq = intval( $wgHitcounterUpdateFreq / 25 + 1 );
- if ( ( rand() % $checkfreq != 0 ) or ( $dbw->lastErrno() != 0 ) ) {
- # Most of the time (or on SQL errors), skip row count check
- $dbw->ignoreErrors( $oldignore );
-
- return;
- }
-
- $res = $dbw->query( "SELECT COUNT(*) as n FROM $hitcounterTable" );
- $row = $dbw->fetchObject( $res );
- $rown = intval( $row->n );
-
- if ( $rown >= $wgHitcounterUpdateFreq ) {
- wfProfileIn( 'Article::incViewCount-collect' );
- $old_user_abort = ignore_user_abort( true );
-
- $dbw->lockTables( array(), array( 'hitcounter' ), __METHOD__, false );
- $tabletype = $dbType == 'mysql' ? "ENGINE=HEAP " : '';
- $dbw->query( "CREATE TEMPORARY TABLE $acchitsTable $tabletype AS " .
- "SELECT hc_id,COUNT(*) AS hc_n FROM $hitcounterTable " .
- 'GROUP BY hc_id', __METHOD__ );
- $dbw->delete( 'hitcounter', '*', __METHOD__ );
- $dbw->unlockTables( __METHOD__ );
-
- if ( $dbType == 'mysql' ) {
- $dbw->query( "UPDATE $pageTable,$acchitsTable SET page_counter=page_counter + hc_n " .
- 'WHERE page_id = hc_id', __METHOD__ );
- } else {
- $dbw->query( "UPDATE $pageTable SET page_counter=page_counter + hc_n " .
- "FROM $acchitsTable WHERE page_id = hc_id", __METHOD__ );
- }
- $dbw->query( "DROP TABLE $acchitsTable", __METHOD__ );
-
- ignore_user_abort( $old_user_abort );
- wfProfileOut( 'Article::incViewCount-collect' );
- }
-
- $dbw->ignoreErrors( $oldignore );
- }
-
- /**#@+
- * The onArticle*() functions are supposed to be a kind of hooks
- * which should be called whenever any of the specified actions
- * are done.
- *
- * This is a good place to put code to clear caches, for instance.
- *
- * This is called on page move and undelete, as well as edit
- *
- * @param $title a title object
- */
- public static function onArticleCreate( $title ) {
- # Update existence markers on article/talk tabs...
- if ( $title->isTalkPage() ) {
- $other = $title->getSubjectPage();
- } else {
- $other = $title->getTalkPage();
- }
-
- $other->invalidateCache();
- $other->purgeSquid();
-
- $title->touchLinks();
- $title->purgeSquid();
- $title->deleteTitleProtection();
- }
-
- /**
- * Clears caches when article is deleted
- */
- public static function onArticleDelete( $title ) {
- global $wgMessageCache;
-
- # Update existence markers on article/talk tabs...
- if ( $title->isTalkPage() ) {
- $other = $title->getSubjectPage();
- } else {
- $other = $title->getTalkPage();
- }
-
- $other->invalidateCache();
- $other->purgeSquid();
-
- $title->touchLinks();
- $title->purgeSquid();
-
- # File cache
- HTMLFileCache::clearFileCache( $title );
-
- # Messages
- if ( $title->getNamespace() == NS_MEDIAWIKI ) {
- $wgMessageCache->replace( $title->getDBkey(), false );
- }
-
- # Images
- if ( $title->getNamespace() == NS_FILE ) {
- $update = new HTMLCacheUpdate( $title, 'imagelinks' );
- $update->doUpdate();
- }
-
- # User talk pages
- if ( $title->getNamespace() == NS_USER_TALK ) {
- $user = User::newFromName( $title->getText(), false );
- $user->setNewtalk( false );
- }
-
- # Image redirects
- RepoGroup::singleton()->getLocalRepo()->invalidateImageRedirect( $title );
- }
-
- /**
- * Purge caches on page update etc
- *
- * @param $title Title object
- * @todo: verify that $title is always a Title object (and never false or null), add Title hint to parameter $title
- */
- public static function onArticleEdit( $title ) {
- global $wgDeferredUpdateList;
-
- // Invalidate caches of articles which include this page
- $wgDeferredUpdateList[] = new HTMLCacheUpdate( $title, 'templatelinks' );
-
- // Invalidate the caches of all pages which redirect here
- $wgDeferredUpdateList[] = new HTMLCacheUpdate( $title, 'redirect' );
-
- # Purge squid for this page only
- $title->purgeSquid();
-
- # Clear file cache for this page only
- HTMLFileCache::clearFileCache( $title );
- }
-
/**#@-*/
/**
- * Overriden by ImagePage class, only present here to avoid a fatal error
- * Called for ?action=revert
+ * Add the primary page-view wikitext to the output buffer
+ * Saves the text into the parser cache if possible.
+ * Updates templatelinks if it is out of date.
+ *
+ * @param $text String
+ * @param $cache Boolean
+ * @param $parserOptions mixed ParserOptions object, or boolean false
*/
- public function revert() {
+ public function outputWikiText( $text, $cache = true, $parserOptions = false ) {
global $wgOut;
- $wgOut->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
- }
-
- /**
- * Info about this page
- * Called for ?action=info when $wgAllowPageInfo is on.
- */
- public function info() {
- global $wgLang, $wgOut, $wgAllowPageInfo, $wgUser;
-
- if ( !$wgAllowPageInfo ) {
- $wgOut->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
- return;
- }
-
- $page = $this->mTitle->getSubjectPage();
-
- $wgOut->setPagetitle( $page->getPrefixedText() );
- $wgOut->setPageTitleActionText( wfMsg( 'info_short' ) );
- $wgOut->setSubtitle( wfMsgHtml( 'infosubtitle' ) );
-
- if ( !$this->mTitle->exists() ) {
- $wgOut->addHTML( '<div class="noarticletext">' );
- if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
- // This doesn't quite make sense; the user is asking for
- // information about the _page_, not the message... -- RC
- $wgOut->addHTML( htmlspecialchars( wfMsgWeirdKey( $this->mTitle->getText() ) ) );
- } else {
- $msg = $wgUser->isLoggedIn()
- ? 'noarticletext'
- : 'noarticletextanon';
- $wgOut->addHTML( wfMsgExt( $msg, 'parse' ) );
- }
- $wgOut->addHTML( '</div>' );
- } else {
- $dbr = wfGetDB( DB_SLAVE );
- $wl_clause = array(
- 'wl_title' => $page->getDBkey(),
- 'wl_namespace' => $page->getNamespace() );
- $numwatchers = $dbr->selectField(
- 'watchlist',
- 'COUNT(*)',
- $wl_clause,
- __METHOD__,
- $this->getSelectOptions() );
-
- $pageInfo = $this->pageCountInfo( $page );
- $talkInfo = $this->pageCountInfo( $page->getTalkPage() );
-
-
- //FIXME: unescaped messages
- $wgOut->addHTML( "<ul><li>" . wfMsg( "numwatchers", $wgLang->formatNum( $numwatchers ) ) . '</li>' );
- $wgOut->addHTML( "<li>" . wfMsg( 'numedits', $wgLang->formatNum( $pageInfo['edits'] ) ) . '</li>' );
-
- if ( $talkInfo ) {
- $wgOut->addHTML( '<li>' . wfMsg( "numtalkedits", $wgLang->formatNum( $talkInfo['edits'] ) ) . '</li>' );
- }
-
- $wgOut->addHTML( '<li>' . wfMsg( "numauthors", $wgLang->formatNum( $pageInfo['authors'] ) ) . '</li>' );
+ $this->mParserOutput = $this->getOutputFromWikitext( $text, $cache, $parserOptions );
- if ( $talkInfo ) {
- $wgOut->addHTML( '<li>' . wfMsg( 'numtalkauthors', $wgLang->formatNum( $talkInfo['authors'] ) ) . '</li>' );
- }
+ $this->doCascadeProtectionUpdates( $this->mParserOutput );
- $wgOut->addHTML( '</ul>' );
- }
+ $wgOut->addParserOutput( $this->mParserOutput );
}
/**
- * Return the total number of edits and number of unique editors
- * on a given page. If page does not exist, returns false.
+ * Lightweight method to get the parser output for a page, checking the parser cache
+ * and so on. Doesn't consider most of the stuff that WikiPage::view is forced to
+ * consider, so it's not appropriate to use there.
*
- * @param $title Title object
- * @return mixed array or boolean false
- */
- public function pageCountInfo( $title ) {
- $id = $title->getArticleId();
-
- if ( $id == 0 ) {
- return false;
- }
-
- $dbr = wfGetDB( DB_SLAVE );
- $rev_clause = array( 'rev_page' => $id );
- $edits = $dbr->selectField(
- 'revision',
- 'COUNT(rev_page)',
- $rev_clause,
- __METHOD__,
- $this->getSelectOptions()
- );
- $authors = $dbr->selectField(
- 'revision',
- 'COUNT(DISTINCT rev_user_text)',
- $rev_clause,
- __METHOD__,
- $this->getSelectOptions()
- );
-
- return array( 'edits' => $edits, 'authors' => $authors );
- }
-
- /**
- * Return a list of templates used by this article.
- * Uses the templatelinks table
+ * @since 1.16 (r52326) for LiquidThreads
*
- * @return Array of Title objects
+ * @param $oldid mixed integer Revision ID or null
+ * @param $user User The relevant user
+ * @return ParserOutput or false if the given revsion ID is not found
*/
- public function getUsedTemplates() {
- $result = array();
- $id = $this->mTitle->getArticleID();
-
- if ( $id == 0 ) {
- return array();
- }
-
- $dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select( array( 'templatelinks' ),
- array( 'tl_namespace', 'tl_title' ),
- array( 'tl_from' => $id ),
- __METHOD__ );
-
- if ( $res !== false ) {
- foreach ( $res as $row ) {
- $result[] = Title::makeTitle( $row->tl_namespace, $row->tl_title );
- }
- }
+ public function getParserOutput( $oldid = null, User $user = null ) {
+ global $wgEnableParserCache, $wgUser;
+ $user = is_null( $user ) ? $wgUser : $user;
- return $result;
- }
+ wfProfileIn( __METHOD__ );
+ // Should the parser cache be used?
+ $useParserCache = $wgEnableParserCache &&
+ $user->getStubThreshold() == 0 &&
+ $this->mPage->exists() &&
+ $oldid === null;
- /**
- * Returns a list of hidden categories this page is a member of.
- * Uses the page_props and categorylinks tables.
- *
- * @return Array of Title objects
- */
- public function getHiddenCategories() {
- $result = array();
- $id = $this->mTitle->getArticleID();
+ wfDebug( __METHOD__ . ': using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
- if ( $id == 0 ) {
- return array();
+ if ( $user->getStubThreshold() ) {
+ wfIncrStats( 'pcache_miss_stub' );
}
- $dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select( array( 'categorylinks', 'page_props', 'page' ),
- array( 'cl_to' ),
- array( 'cl_from' => $id, 'pp_page=page_id', 'pp_propname' => 'hiddencat',
- 'page_namespace' => NS_CATEGORY, 'page_title=cl_to' ),
- __METHOD__ );
-
- if ( $res !== false ) {
- foreach ( $res as $row ) {
- $result[] = Title::makeTitle( NS_CATEGORY, $row->cl_to );
+ if ( $useParserCache ) {
+ $parserOutput = ParserCache::singleton()->get( $this, $this->mPage->getParserOptions() );
+ if ( $parserOutput !== false ) {
+ wfProfileOut( __METHOD__ );
+ return $parserOutput;
}
}
- return $result;
- }
-
- /**
- * Return an applicable autosummary if one exists for the given edit.
- * @param $oldtext String: the previous text of the page.
- * @param $newtext String: The submitted text of the page.
- * @param $flags Bitmask: a bitmask of flags submitted for the edit.
- * @return string An appropriate autosummary, or an empty string.
- */
- public static function getAutosummary( $oldtext, $newtext, $flags ) {
- global $wgContLang;
-
- # Decide what kind of autosummary is needed.
-
- # Redirect autosummaries
- $ot = Title::newFromRedirect( $oldtext );
- $rt = Title::newFromRedirect( $newtext );
-
- if ( is_object( $rt ) && ( !is_object( $ot ) || !$rt->equals( $ot ) || $ot->getFragment() != $rt->getFragment() ) ) {
- return wfMsgForContent( 'autoredircomment', $rt->getFullText() );
- }
-
- # New page autosummaries
- if ( $flags & EDIT_NEW && strlen( $newtext ) ) {
- # If they're making a new article, give its text, truncated, in the summary.
-
- $truncatedtext = $wgContLang->truncate(
- str_replace( "\n", ' ', $newtext ),
- max( 0, 200 - strlen( wfMsgForContent( 'autosumm-new' ) ) ) );
-
- return wfMsgForContent( 'autosumm-new', $truncatedtext );
- }
-
- # Blanking autosummaries
- if ( $oldtext != '' && $newtext == '' ) {
- return wfMsgForContent( 'autosumm-blank' );
- } elseif ( strlen( $oldtext ) > 10 * strlen( $newtext ) && strlen( $newtext ) < 500 ) {
- # Removing more than 90% of the article
-
- $truncatedtext = $wgContLang->truncate(
- $newtext,
- max( 0, 200 - strlen( wfMsgForContent( 'autosumm-replace' ) ) ) );
-
- return wfMsgForContent( 'autosumm-replace', $truncatedtext );
+ // Cache miss; parse and output it.
+ if ( $oldid === null ) {
+ $text = $this->mPage->getRawText();
+ } else {
+ $rev = Revision::newFromTitle( $this->getTitle(), $oldid );
+ if ( $rev === null ) {
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+ $text = $rev->getText();
}
- # If we reach this point, there's no applicable autosummary for our case, so our
- # autosummary is empty.
- return '';
- }
-
- /**
- * Add the primary page-view wikitext to the output buffer
- * Saves the text into the parser cache if possible.
- * Updates templatelinks if it is out of date.
- *
- * @param $text String
- * @param $cache Boolean
- * @param $parserOptions mixed ParserOptions object, or boolean false
- */
- public function outputWikiText( $text, $cache = true, $parserOptions = false ) {
- global $wgOut;
-
- $this->mParserOutput = $this->getOutputFromWikitext( $text, $cache, $parserOptions );
- $wgOut->addParserOutput( $this->mParserOutput );
+ wfProfileOut( __METHOD__ );
+ return $this->getOutputFromWikitext( $text, $useParserCache );
}
/**
@@ -4400,24 +1823,24 @@ class Article {
* @param $text string
* @param $cache boolean
* @param $parserOptions parsing options, defaults to false
- * @return string containing parsed output
+ * @return ParserOutput
*/
public function getOutputFromWikitext( $text, $cache = true, $parserOptions = false ) {
global $wgParser, $wgEnableParserCache, $wgUseFileCache;
if ( !$parserOptions ) {
- $parserOptions = $this->getParserOptions();
+ $parserOptions = $this->mPage->getParserOptions();
}
$time = - wfTime();
- $this->mParserOutput = $wgParser->parse( $text, $this->mTitle,
+ $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(),
$parserOptions, true, true, $this->getRevIdFetched() );
$time += wfTime();
# Timing hack
if ( $time > 3 ) {
wfDebugLog( 'slow-parse', sprintf( "%-5.2f %s", $time,
- $this->mTitle->getPrefixedDBkey() ) );
+ $this->getTitle()->getPrefixedDBkey() ) );
}
if ( $wgEnableParserCache && $cache && $this->mParserOutput->isCacheable() ) {
@@ -4432,213 +1855,156 @@ class Article {
$wgUseFileCache = false;
}
- $this->doCascadeProtectionUpdates( $this->mParserOutput );
+ if ( $this->isCurrent() ) {
+ $this->mPage->doCascadeProtectionUpdates( $this->mParserOutput );
+ }
return $this->mParserOutput;
}
/**
- * Get parser options suitable for rendering the primary article wikitext
- * @return mixed ParserOptions object or boolean false
+ * Sets the context this Article is executed in
+ *
+ * @param $context IContextSource
+ * @since 1.18
*/
- public function getParserOptions() {
- global $wgUser;
-
- if ( !$this->mParserOptions ) {
- $this->mParserOptions = new ParserOptions( $wgUser );
- $this->mParserOptions->setTidy( true );
- $this->mParserOptions->enableLimitReport();
- }
-
- // Clone to allow modifications of the return value without affecting
- // the cache
- return clone $this->mParserOptions;
+ public function setContext( $context ) {
+ $this->mContext = $context;
}
/**
- * Updates cascading protections
+ * Gets the context this Article is executed in
*
- * @param $parserOutput mixed ParserOptions object, or boolean false
- **/
- protected function doCascadeProtectionUpdates( $parserOutput ) {
- if ( !$this->isCurrent() || wfReadOnly() || !$this->mTitle->areRestrictionsCascading() ) {
- return;
- }
-
- // templatelinks table may have become out of sync,
- // especially if using variable-based transclusions.
- // For paranoia, check if things have changed and if
- // so apply updates to the database. This will ensure
- // that cascaded protections apply as soon as the changes
- // are visible.
-
- # Get templates from templatelinks
- $id = $this->mTitle->getArticleID();
-
- $tlTemplates = array();
-
- $dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->select( array( 'templatelinks' ),
- array( 'tl_namespace', 'tl_title' ),
- array( 'tl_from' => $id ),
- __METHOD__
- );
-
- foreach ( $res as $row ) {
- $tlTemplates["{$row->tl_namespace}:{$row->tl_title}"] = true;
- }
-
- # Get templates from parser output.
- $poTemplates = array();
- foreach ( $parserOutput->getTemplates() as $ns => $templates ) {
- foreach ( $templates as $dbk => $id ) {
- $poTemplates["$ns:$dbk"] = true;
- }
- }
-
- # Get the diff
- $templates_diff = array_diff_key( $poTemplates, $tlTemplates );
-
- if ( count( $templates_diff ) > 0 ) {
- # Whee, link updates time.
- $u = new LinksUpdate( $this->mTitle, $parserOutput, false );
- $u->doUpdate();
+ * @return IContextSource
+ * @since 1.18
+ */
+ public function getContext() {
+ if ( $this->mContext instanceof IContextSource ) {
+ return $this->mContext;
+ } else {
+ wfDebug( __METHOD__ . " called and \$mContext is null. Return RequestContext::getMain(); for sanity\n" );
+ return RequestContext::getMain();
}
}
/**
- * Update all the appropriate counts in the category table, given that
- * we've added the categories $added and deleted the categories $deleted.
+ * Use PHP's magic __get handler to handle accessing of
+ * raw WikiPage fields for backwards compatibility.
*
- * @param $added array The names of categories that were added
- * @param $deleted array The names of categories that were deleted
+ * @param $fname String Field name
*/
- public function updateCategoryCounts( $added, $deleted ) {
- $ns = $this->mTitle->getNamespace();
- $dbw = wfGetDB( DB_MASTER );
-
- # First make sure the rows exist. If one of the "deleted" ones didn't
- # exist, we might legitimately not create it, but it's simpler to just
- # create it and then give it a negative value, since the value is bogus
- # anyway.
- #
- # Sometimes I wish we had INSERT ... ON DUPLICATE KEY UPDATE.
- $insertCats = array_merge( $added, $deleted );
- if ( !$insertCats ) {
- # Okay, nothing to do
- return;
- }
-
- $insertRows = array();
-
- foreach ( $insertCats as $cat ) {
- $insertRows[] = array(
- 'cat_id' => $dbw->nextSequenceValue( 'category_cat_id_seq' ),
- 'cat_title' => $cat
- );
- }
- $dbw->insert( 'category', $insertRows, __METHOD__, 'IGNORE' );
-
- $addFields = array( 'cat_pages = cat_pages + 1' );
- $removeFields = array( 'cat_pages = cat_pages - 1' );
-
- if ( $ns == NS_CATEGORY ) {
- $addFields[] = 'cat_subcats = cat_subcats + 1';
- $removeFields[] = 'cat_subcats = cat_subcats - 1';
- } elseif ( $ns == NS_FILE ) {
- $addFields[] = 'cat_files = cat_files + 1';
- $removeFields[] = 'cat_files = cat_files - 1';
- }
-
- if ( $added ) {
- $dbw->update(
- 'category',
- $addFields,
- array( 'cat_title' => $added ),
- __METHOD__
- );
+ public function __get( $fname ) {
+ if ( property_exists( $this->mPage, $fname ) ) {
+ #wfWarn( "Access to raw $fname field " . __CLASS__ );
+ return $this->mPage->$fname;
}
+ trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
+ }
- if ( $deleted ) {
- $dbw->update(
- 'category',
- $removeFields,
- array( 'cat_title' => $deleted ),
- __METHOD__
- );
+ /**
+ * Use PHP's magic __set handler to handle setting of
+ * raw WikiPage fields for backwards compatibility.
+ *
+ * @param $fname String Field name
+ * @param $fvalue mixed New value
+ * @param $args Array Arguments to the method
+ */
+ public function __set( $fname, $fvalue ) {
+ if ( property_exists( $this->mPage, $fname ) ) {
+ #wfWarn( "Access to raw $fname field of " . __CLASS__ );
+ $this->mPage->$fname = $fvalue;
+ // Note: extensions may want to toss on new fields
+ } elseif ( !in_array( $fname, array( 'mContext', 'mPage' ) ) ) {
+ $this->mPage->$fname = $fvalue;
+ } else {
+ trigger_error( 'Inaccessible property via __get(): ' . $fname, E_USER_NOTICE );
}
}
/**
- * Lightweight method to get the parser output for a page, checking the parser cache
- * and so on. Doesn't consider most of the stuff that Article::view is forced to
- * consider, so it's not appropriate to use there.
+ * Use PHP's magic __call handler to transform instance calls to
+ * WikiPage functions for backwards compatibility.
*
- * @since 1.16 (r52326) for LiquidThreads
- *
- * @param $oldid mixed integer Revision ID or null
+ * @param $fname String Name of called method
+ * @param $args Array Arguments to the method
*/
- public function getParserOutput( $oldid = null ) {
- global $wgEnableParserCache, $wgUser;
+ public function __call( $fname, $args ) {
+ if ( is_callable( array( $this->mPage, $fname ) ) ) {
+ #wfWarn( "Call to " . __CLASS__ . "::$fname; please use WikiPage instead" );
+ return call_user_func_array( array( $this->mPage, $fname ), $args );
+ }
+ trigger_error( 'Inaccessible function via __call(): ' . $fname, E_USER_ERROR );
+ }
- // Should the parser cache be used?
- $useParserCache = $wgEnableParserCache &&
- $wgUser->getStubThreshold() == 0 &&
- $this->exists() &&
- $oldid === null;
+ // ****** B/C functions to work-around PHP silliness with __call and references ****** //
+ public function updateRestrictions( $limit = array(), $reason = '', &$cascade = 0, $expiry = array() ) {
+ return $this->mPage->updateRestrictions( $limit, $reason, $cascade, $expiry );
+ }
- wfDebug( __METHOD__ . ': using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" );
+ public function doDeleteArticle( $reason, $suppress = false, $id = 0, $commit = true, &$error = '' ) {
+ return $this->mPage->doDeleteArticle( $reason, $suppress, $id, $commit, $error );
+ }
- if ( $wgUser->getStubThreshold() ) {
- wfIncrStats( 'pcache_miss_stub' );
- }
+ public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) {
+ global $wgUser;
+ $user = is_null( $user ) ? $wgUser : $user;
+ return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user );
+ }
- $parserOutput = false;
- if ( $useParserCache ) {
- $parserOutput = ParserCache::singleton()->get( $this, $this->getParserOptions() );
- }
+ public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) {
+ global $wgUser;
+ $guser = is_null( $guser ) ? $wgUser : $guser;
+ return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser );
+ }
- if ( $parserOutput === false ) {
- // Cache miss; parse and output it.
- $rev = Revision::newFromTitle( $this->getTitle(), $oldid );
+ public function generateReason( &$hasHistory ) {
+ return $this->mPage->getAutoDeleteReason( $hasHistory );
+ }
- return $this->getOutputFromWikitext( $rev->getText(), $useParserCache );
- } else {
- return $parserOutput;
- }
+ // ****** B/C functions for static methods ( __callStatic is PHP>=5.3 ) ****** //
+ public static function selectFields() {
+ return WikiPage::selectFields();
}
- // Deprecated methods
- /**
- * Get the database which should be used for reads
- *
- * @return Database
- * @deprecated - just call wfGetDB( DB_MASTER ) instead
- */
- function getDB() {
- wfDeprecated( __METHOD__ );
- return wfGetDB( DB_MASTER );
+ public static function onArticleCreate( $title ) {
+ return WikiPage::onArticleCreate( $title );
+ }
+
+ public static function onArticleDelete( $title ) {
+ return WikiPage::onArticleDelete( $title );
+ }
+
+ public static function onArticleEdit( $title ) {
+ return WikiPage::onArticleEdit( $title );
}
+ public static function getAutosummary( $oldtext, $newtext, $flags ) {
+ return WikiPage::getAutosummary( $oldtext, $newtext, $flags );
+ }
+ // ******
}
class PoolWorkArticleView extends PoolCounterWork {
+
+ /**
+ * @var Article
+ */
private $mArticle;
-
+
function __construct( $article, $key, $useParserCache, $parserOptions ) {
parent::__construct( 'ArticleView', $key );
$this->mArticle = $article;
$this->cacheable = $useParserCache;
$this->parserOptions = $parserOptions;
}
-
+
function doWork() {
return $this->mArticle->doViewParse();
}
-
+
function getCachedWork() {
global $wgOut;
-
+
$parserCache = ParserCache::singleton();
$this->mArticle->mParserOutput = $parserCache->get( $this->mArticle, $this->parserOptions );
@@ -4652,21 +2018,24 @@ class PoolWorkArticleView extends PoolCounterWork {
}
return false;
}
-
+
function fallback() {
return $this->mArticle->tryDirtyCache();
}
-
+
+ /**
+ * @param $status Status
+ */
function error( $status ) {
global $wgOut;
$wgOut->clearHTML(); // for release() errors
$wgOut->enableClientCache( false );
$wgOut->setRobotPolicy( 'noindex,nofollow' );
-
+
$errortext = $status->getWikiText( false, 'view-pool-error' );
$wgOut->addWikiText( '<div class="errorbox">' . $errortext . '</div>' );
-
+
return false;
}
}
diff --git a/includes/AuthPlugin.php b/includes/AuthPlugin.php
index 7dc99259..eebb52d6 100644
--- a/includes/AuthPlugin.php
+++ b/includes/AuthPlugin.php
@@ -131,6 +131,8 @@ class AuthPlugin {
* and use the same keys. 'Realname' 'Emailaddress' and 'Nickname'
* all reference this.
*
+ * @param $prop string
+ *
* @return Boolean
*/
public function allowPropChange( $prop = '' ) {
@@ -254,10 +256,21 @@ class AuthPlugin {
* Get an instance of a User object
*
* @param $user User
+ *
+ * @return AuthPluginUser
*/
public function getUserInstance( User &$user ) {
return new AuthPluginUser( $user );
}
+
+ /**
+ * Get a list of domains (in HTMLForm options format) used.
+ *
+ * @return array
+ */
+ public function domainList() {
+ return array();
+ }
}
class AuthPluginUser {
diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php
index 347ed694..134e53ea 100644
--- a/includes/AutoLoader.php
+++ b/includes/AutoLoader.php
@@ -14,20 +14,18 @@ global $wgAutoloadLocalClasses;
$wgAutoloadLocalClasses = array(
# Includes
+ 'Action' => 'includes/Action.php',
'AjaxDispatcher' => 'includes/AjaxDispatcher.php',
'AjaxResponse' => 'includes/AjaxResponse.php',
'AlphabeticPager' => 'includes/Pager.php',
- 'APCBagOStuff' => 'includes/BagOStuff.php',
'Article' => 'includes/Article.php',
'AtomFeed' => 'includes/Feed.php',
'AuthPlugin' => 'includes/AuthPlugin.php',
'AuthPluginUser' => 'includes/AuthPlugin.php',
'Autopromote' => 'includes/Autopromote.php',
'BacklinkCache' => 'includes/BacklinkCache.php',
- 'BagOStuff' => 'includes/BagOStuff.php',
+ 'BaseTemplate' => 'includes/SkinTemplate.php',
'Block' => 'includes/Block.php',
- 'CacheDependency' => 'includes/CacheDependency.php',
- 'CacheTime' => 'includes/parser/ParserOutput.php',
'Category' => 'includes/Category.php',
'Categoryfinder' => 'includes/Categoryfinder.php',
'CategoryPage' => 'includes/CategoryPage.php',
@@ -39,28 +37,22 @@ $wgAutoloadLocalClasses = array(
'CdbWriter' => 'includes/Cdb.php',
'CdbWriter_DBA' => 'includes/Cdb.php',
'CdbWriter_PHP' => 'includes/Cdb_PHP.php',
- 'ChangesList' => 'includes/ChangesList.php',
'ChangesFeed' => 'includes/ChangesFeed.php',
+ 'ChangesList' => 'includes/ChangesList.php',
'ChangeTags' => 'includes/ChangeTags.php',
'ChannelFeed' => 'includes/Feed.php',
'Collation' => 'includes/Collation.php',
- 'Cookie' => 'includes/HttpFunctions.php',
- 'CookieJar' => 'includes/HttpFunctions.php',
'ConcatenatedGzipHistoryBlob' => 'includes/HistoryBlob.php',
'ConfEditor' => 'includes/ConfEditor.php',
'ConfEditorParseError' => 'includes/ConfEditor.php',
'ConfEditorToken' => 'includes/ConfEditor.php',
- 'ConstantDependency' => 'includes/CacheDependency.php',
- 'CreativeCommonsRdf' => 'includes/Metadata.php',
- 'Credits' => 'includes/Credits.php',
- 'CSSJanus' => 'includes/libs/CSSJanus.php',
- 'CSSMin' => 'includes/libs/CSSMin.php',
- 'DBABagOStuff' => 'includes/BagOStuff.php',
- 'DependencyWrapper' => 'includes/CacheDependency.php',
+ 'ContextSource' => 'includes/RequestContext.php',
+ 'Cookie' => 'includes/Cookie.php',
+ 'CookieJar' => 'includes/Cookie.php',
'DiffHistoryBlob' => 'includes/HistoryBlob.php',
'DjVuImage' => 'includes/DjVuImage.php',
'DoubleReplacer' => 'includes/StringUtils.php',
- 'DublinCoreRdf' => 'includes/Metadata.php',
+ 'DummyLinker' => 'includes/Linker.php',
'Dump7ZipOutput' => 'includes/Export.php',
'DumpBZip2Output' => 'includes/Export.php',
'DumpFileOutput' => 'includes/Export.php',
@@ -72,145 +64,133 @@ $wgAutoloadLocalClasses = array(
'DumpNotalkFilter' => 'includes/Export.php',
'DumpOutput' => 'includes/Export.php',
'DumpPipeOutput' => 'includes/Export.php',
- 'eAccelBagOStuff' => 'includes/BagOStuff.php',
'EditPage' => 'includes/EditPage.php',
'EmailNotification' => 'includes/UserMailer.php',
'EnhancedChangesList' => 'includes/ChangesList.php',
'ErrorPageError' => 'includes/Exception.php',
- 'Exif' => 'includes/Exif.php',
'ExplodeIterator' => 'includes/StringUtils.php',
'ExternalEdit' => 'includes/ExternalEdit.php',
+ 'ExternalStore' => 'includes/ExternalStore.php',
'ExternalStoreDB' => 'includes/ExternalStoreDB.php',
'ExternalStoreHttp' => 'includes/ExternalStoreHttp.php',
- 'ExternalStore' => 'includes/ExternalStore.php',
'ExternalUser' => 'includes/ExternalUser.php',
- 'FatalError' => 'includes/Exception.php',
'FakeTitle' => 'includes/FakeTitle.php',
- 'FakeMemCachedClient' => 'includes/ObjectCache.php',
+ 'Fallback' => 'includes/Fallback.php',
+ 'FatalError' => 'includes/Exception.php',
'FauxRequest' => 'includes/WebRequest.php',
'FauxResponse' => 'includes/WebResponse.php',
'FeedItem' => 'includes/Feed.php',
'FeedUtils' => 'includes/FeedUtils.php',
'FileDeleteForm' => 'includes/FileDeleteForm.php',
- 'FileDependency' => 'includes/CacheDependency.php',
- 'FileRevertForm' => 'includes/FileRevertForm.php',
'ForkController' => 'includes/ForkController.php',
- 'FormatExif' => 'includes/Exif.php',
+ 'FormlessAction' => 'includes/Action.php',
+ 'FormAction' => 'includes/Action.php',
'FormOptions' => 'includes/FormOptions.php',
- 'GlobalDependency' => 'includes/CacheDependency.php',
- 'HashBagOStuff' => 'includes/BagOStuff.php',
+ 'FormSpecialPage' => 'includes/SpecialPage.php',
+ 'GenderCache' => 'includes/GenderCache.php',
'HashtableReplacer' => 'includes/StringUtils.php',
- 'HistoryBlobCurStub' => 'includes/HistoryBlob.php',
'HistoryBlob' => 'includes/HistoryBlob.php',
+ 'HistoryBlobCurStub' => 'includes/HistoryBlob.php',
'HistoryBlobStub' => 'includes/HistoryBlob.php',
'HistoryPage' => 'includes/HistoryPage.php',
'HistoryPager' => 'includes/HistoryPage.php',
+ 'Hooks' => 'includes/Hooks.php',
'Html' => 'includes/Html.php',
- 'HTMLCacheUpdate' => 'includes/HTMLCacheUpdate.php',
- 'HTMLCacheUpdateJob' => 'includes/HTMLCacheUpdate.php',
- 'HTMLFileCache' => 'includes/HTMLFileCache.php',
+ 'HTMLCheckField' => 'includes/HTMLForm.php',
+ 'HTMLEditTools' => 'includes/HTMLForm.php',
+ 'HTMLFloatField' => 'includes/HTMLForm.php',
'HTMLForm' => 'includes/HTMLForm.php',
'HTMLFormField' => 'includes/HTMLForm.php',
- 'HTMLTextField' => 'includes/HTMLForm.php',
- 'HTMLIntField' => 'includes/HTMLForm.php',
- 'HTMLTextAreaField' => 'includes/HTMLForm.php',
- 'HTMLFloatField' => 'includes/HTMLForm.php',
'HTMLHiddenField' => 'includes/HTMLForm.php',
- 'HTMLSubmitField' => 'includes/HTMLForm.php',
- 'HTMLEditTools' => 'includes/HTMLForm.php',
- 'HTMLCheckField' => 'includes/HTMLForm.php',
- 'HTMLSelectField' => 'includes/HTMLForm.php',
- 'HTMLSelectOrOtherField' => 'includes/HTMLForm.php',
+ 'HTMLInfoField' => 'includes/HTMLForm.php',
+ 'HTMLIntField' => 'includes/HTMLForm.php',
'HTMLMultiSelectField' => 'includes/HTMLForm.php',
'HTMLRadioField' => 'includes/HTMLForm.php',
- 'HTMLInfoField' => 'includes/HTMLForm.php',
+ 'HTMLSelectAndOtherField' => 'includes/HTMLForm.php',
+ 'HTMLSelectField' => 'includes/HTMLForm.php',
+ 'HTMLSelectOrOtherField' => 'includes/HTMLForm.php',
+ 'HTMLSubmitField' => 'includes/HTMLForm.php',
+ 'HTMLTextAreaField' => 'includes/HTMLForm.php',
+ 'HTMLTextField' => 'includes/HTMLForm.php',
'Http' => 'includes/HttpFunctions.php',
'HttpRequest' => 'includes/HttpFunctions.old.php',
+ 'IContextSource' => 'includes/RequestContext.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',
'ImageQueryPage' => 'includes/ImageQueryPage.php',
+ 'ImportStreamSource' => 'includes/Import.php',
+ 'ImportStringSource' => 'includes/Import.php',
'IncludableSpecialPage' => 'includes/SpecialPage.php',
'IndexPager' => 'includes/Pager.php',
- 'Interwiki' => 'includes/Interwiki.php',
+ 'Interwiki' => 'includes/interwiki/Interwiki.php',
'IP' => 'includes/IP.php',
- 'JavaScriptMinifier' => 'includes/libs/JavaScriptMinifier.php',
- 'LCStore_DB' => 'includes/LocalisationCache.php',
'LCStore_CDB' => 'includes/LocalisationCache.php',
+ 'LCStore_DB' => 'includes/LocalisationCache.php',
'LCStore_Null' => 'includes/LocalisationCache.php',
+ 'LegacyTemplate' => 'includes/SkinLegacy.php',
'License' => 'includes/Licenses.php',
'Licenses' => 'includes/Licenses.php',
- 'LinkBatch' => 'includes/LinkBatch.php',
- 'LinkCache' => 'includes/LinkCache.php',
'Linker' => 'includes/Linker.php',
'LinkFilter' => 'includes/LinkFilter.php',
'LinksUpdate' => 'includes/LinksUpdate.php',
'LocalisationCache' => 'includes/LocalisationCache.php',
'LocalisationCache_BulkLoad' => 'includes/LocalisationCache.php',
+ 'LogEventsList' => 'includes/LogEventsList.php',
'LogPage' => 'includes/LogPage.php',
'LogPager' => 'includes/LogEventsList.php',
- 'LogEventsList' => 'includes/LogEventsList.php',
- 'LogReader' => 'includes/LogEventsList.php',
- 'LogViewer' => 'includes/LogEventsList.php',
- 'MacBinary' => 'includes/MacBinary.php',
- 'MagicWordArray' => 'includes/MagicWord.php',
'MagicWord' => 'includes/MagicWord.php',
+ 'MagicWordArray' => 'includes/MagicWord.php',
'MailAddress' => 'includes/UserMailer.php',
- 'MathRenderer' => 'includes/Math.php',
- 'MediaWikiBagOStuff' => 'includes/BagOStuff.php',
- 'MediaWiki_I18N' => 'includes/SkinTemplate.php',
'MediaWiki' => 'includes/Wiki.php',
- 'MemCachedClientforWiki' => 'includes/memcached-client.php',
+ 'MediaWiki_I18N' => 'includes/SkinTemplate.php',
'Message' => 'includes/Message.php',
'MessageBlobStore' => 'includes/MessageBlobStore.php',
- 'MessageCache' => 'includes/MessageCache.php',
'MimeMagic' => 'includes/MimeMagic.php',
'MWException' => 'includes/Exception.php',
+ 'MWExceptionHandler' => 'includes/Exception.php',
+ 'MWFunction' => 'includes/MWFunction.php',
'MWHttpRequest' => 'includes/HttpFunctions.php',
- 'MWMemcached' => 'includes/memcached-client.php',
+ 'MWInit' => 'includes/Init.php',
'MWNamespace' => 'includes/Namespace.php',
'OldChangesList' => 'includes/ChangesList.php',
'OutputPage' => 'includes/OutputPage.php',
- 'PageQueryPage' => 'includes/PageQueryPage.php',
+ 'Page' => 'includes/WikiPage.php',
'PageHistory' => 'includes/HistoryPage.php',
'PageHistoryPager' => 'includes/HistoryPage.php',
+ 'PageQueryPage' => 'includes/PageQueryPage.php',
'Pager' => 'includes/Pager.php',
'PasswordError' => 'includes/User.php',
'PatrolLog' => 'includes/PatrolLog.php',
+ 'PermissionsError' => 'includes/Exception.php',
'PhpHttpRequest' => 'includes/HttpFunctions.php',
'PoolCounter' => 'includes/PoolCounter.php',
'PoolCounter_Stub' => 'includes/PoolCounter.php',
'PoolCounterWork' => 'includes/PoolCounter.php',
'Preferences' => 'includes/Preferences.php',
+ 'PreferencesForm' => 'includes/Preferences.php',
'PrefixSearch' => 'includes/PrefixSearch.php',
- 'Profiler' => 'includes/Profiler.php',
- 'ProfilerSimple' => 'includes/ProfilerSimple.php',
- 'ProfilerSimpleText' => 'includes/ProfilerSimpleText.php',
- 'ProfilerSimpleUDP' => 'includes/ProfilerSimpleUDP.php',
'ProtectionForm' => 'includes/ProtectionForm.php',
'QueryPage' => 'includes/QueryPage.php',
'QuickTemplate' => 'includes/SkinTemplate.php',
'RawPage' => 'includes/RawPage.php',
'RCCacheEntry' => 'includes/ChangesList.php',
'RdfMetaData' => 'includes/Metadata.php',
+ 'ReadOnlyError' => 'includes/Exception.php',
'RecentChange' => 'includes/RecentChange.php',
+ 'RedirectSpecialPage' => 'includes/SpecialPage.php',
'RegexlikeReplacer' => 'includes/StringUtils.php',
'ReplacementArray' => 'includes/StringUtils.php',
'Replacer' => 'includes/StringUtils.php',
- 'ResourceLoader' => 'includes/resourceloader/ResourceLoader.php',
- 'ResourceLoaderContext' => 'includes/resourceloader/ResourceLoaderContext.php',
- 'ResourceLoaderModule' => 'includes/resourceloader/ResourceLoaderModule.php',
- 'ResourceLoaderWikiModule' => 'includes/resourceloader/ResourceLoaderWikiModule.php',
- 'ResourceLoaderFileModule' => 'includes/resourceloader/ResourceLoaderFileModule.php',
- 'ResourceLoaderSiteModule' => 'includes/resourceloader/ResourceLoaderSiteModule.php',
- 'ResourceLoaderUserModule' => 'includes/resourceloader/ResourceLoaderUserModule.php',
- 'ResourceLoaderUserOptionsModule' => 'includes/resourceloader/ResourceLoaderUserOptionsModule.php',
- 'ResourceLoaderStartUpModule' => 'includes/resourceloader/ResourceLoaderStartUpModule.php',
+ 'RequestContext' => 'includes/RequestContext.php',
'ReverseChronologicalPager' => 'includes/Pager.php',
+ 'Rev_Item' => 'includes/RevisionList.php',
+ 'Rev_List' => 'includes/RevisionList.php',
'Revision' => 'includes/Revision.php',
- 'RevisionDelete' => 'includes/revisiondelete/RevisionDelete.php',
+ 'RevisionList' => 'includes/RevisionList.php',
'RSSFeed' => 'includes/Feed.php',
'Sanitizer' => 'includes/Sanitizer.php',
'SiteConfiguration' => 'includes/SiteConfiguration.php',
@@ -218,64 +198,88 @@ $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',
+ 'SpecialListBots' => 'includes/SpecialPage.php',
'SpecialMycontributions' => 'includes/SpecialPage.php',
'SpecialMypage' => 'includes/SpecialPage.php',
'SpecialMytalk' => 'includes/SpecialPage.php',
+ 'SpecialMyuploads' => 'includes/SpecialPage.php',
'SpecialPage' => 'includes/SpecialPage.php',
+ 'SpecialPageFactory' => 'includes/SpecialPageFactory.php',
'SpecialRedirectToSpecial' => 'includes/SpecialPage.php',
- 'SqlBagOStuff' => 'includes/BagOStuff.php',
- 'SquidUpdate' => 'includes/SquidUpdate.php',
'SquidPurgeClient' => 'includes/SquidPurgeClient.php',
'SquidPurgeClientPool' => 'includes/SquidPurgeClient.php',
'Status' => 'includes/Status.php',
+ 'StringUtils' => 'includes/StringUtils.php',
'StubContLang' => 'includes/StubObject.php',
- 'StubUserLang' => 'includes/StubObject.php',
'StubObject' => 'includes/StubObject.php',
- 'StringUtils' => 'includes/StringUtils.php',
+ 'StubUserLang' => 'includes/StubObject.php',
'TablePager' => 'includes/Pager.php',
- 'TitleDependency' => 'includes/CacheDependency.php',
'Title' => 'includes/Title.php',
'TitleArray' => 'includes/TitleArray.php',
'TitleArrayFromResult' => 'includes/TitleArray.php',
- 'TitleListDependency' => 'includes/CacheDependency.php',
+ 'ThrottledError' => 'includes/Exception.php',
'UnlistedSpecialPage' => 'includes/SpecialPage.php',
'UppercaseCollation' => 'includes/Collation.php',
'User' => 'includes/User.php',
'UserArray' => 'includes/UserArray.php',
'UserArrayFromResult' => 'includes/UserArray.php',
+ 'UserBlockedError' => 'includes/Exception.php',
'UserMailer' => 'includes/UserMailer.php',
'UserRightsProxy' => 'includes/UserRightsProxy.php',
+ 'ViewCountUpdate' => 'includes/ViewCountUpdate.php',
'WantedQueryPage' => 'includes/QueryPage.php',
'WatchedItem' => 'includes/WatchedItem.php',
- 'WatchlistEditor' => 'includes/WatchlistEditor.php',
'WebRequest' => 'includes/WebRequest.php',
'WebRequestUpload' => 'includes/WebRequest.php',
'WebResponse' => 'includes/WebResponse.php',
+ 'WikiCategoryPage' => 'includes/WikiCategoryPage.php',
'WikiError' => 'includes/WikiError.php',
'WikiErrorMsg' => 'includes/WikiError.php',
'WikiExporter' => 'includes/Export.php',
+ 'WikiFilePage' => 'includes/WikiFilePage.php',
+ 'WikiImporter' => 'includes/Import.php',
+ 'WikiPage' => 'includes/WikiPage.php',
+ 'WikiRevision' => 'includes/Import.php',
'WikiMap' => 'includes/WikiMap.php',
'WikiReference' => 'includes/WikiMap.php',
'WikiXmlError' => 'includes/WikiError.php',
- 'WinCacheBagOStuff' => 'includes/BagOStuff.php',
- 'XCacheBagOStuff' => 'includes/BagOStuff.php',
- 'XmlDumpWriter' => 'includes/Export.php',
'Xml' => 'includes/Xml.php',
+ 'XmlDumpWriter' => 'includes/Export.php',
'XmlJsCode' => 'includes/Xml.php',
'XmlSelect' => 'includes/Xml.php',
'XmlTypeCheck' => 'includes/XmlTypeCheck.php',
'ZhClient' => 'includes/ZhClient.php',
+ 'ZipDirectoryReader' => 'includes/ZipDirectoryReader.php',
+
+ # includes/actions
+ 'CreditsAction' => 'includes/actions/CreditsAction.php',
+ 'DeletetrackbackAction' => 'includes/actions/DeletetrackbackAction.php',
+ 'InfoAction' => 'includes/actions/InfoAction.php',
+ 'MarkpatrolledAction' => 'includes/actions/MarkpatrolledAction.php',
+ 'PurgeAction' => 'includes/actions/PurgeAction.php',
+ 'RevertAction' => 'includes/actions/RevertAction.php',
+ 'RevertFileAction' => 'includes/actions/RevertAction.php',
+ 'RevisiondeleteAction' => 'includes/actions/RevisiondeleteAction.php',
+ 'RollbackAction' => 'includes/actions/RollbackAction.php',
+ 'UnwatchAction' => 'includes/actions/WatchAction.php',
+ 'WatchAction' => 'includes/actions/WatchAction.php',
# includes/api
'ApiBase' => 'includes/api/ApiBase.php',
'ApiBlock' => 'includes/api/ApiBlock.php',
+ 'ApiComparePages' => 'includes/api/ApiComparePages.php',
'ApiDelete' => 'includes/api/ApiDelete.php',
'ApiDisabled' => 'includes/api/ApiDisabled.php',
'ApiEditPage' => 'includes/api/ApiEditPage.php',
'ApiEmailUser' => 'includes/api/ApiEmailUser.php',
'ApiExpandTemplates' => 'includes/api/ApiExpandTemplates.php',
+ 'ApiFeedContributions' => 'includes/api/ApiFeedContributions.php',
'ApiFeedWatchlist' => 'includes/api/ApiFeedWatchlist.php',
+ 'ApiFileRevert' => 'includes/api/ApiFileRevert.php',
'ApiFormatBase' => 'includes/api/ApiFormatBase.php',
'ApiFormatDbg' => 'includes/api/ApiFormatDbg.php',
'ApiFormatDump' => 'includes/api/ApiFormatDump.php',
@@ -286,6 +290,7 @@ $wgAutoloadLocalClasses = array(
'ApiFormatTxt' => 'includes/api/ApiFormatTxt.php',
'ApiFormatWddx' => 'includes/api/ApiFormatWddx.php',
'ApiFormatXml' => 'includes/api/ApiFormatXml.php',
+ 'ApiFormatXmlRsd' => 'includes/api/ApiRsd.php',
'ApiFormatYaml' => 'includes/api/ApiFormatYaml.php',
'ApiHelp' => 'includes/api/ApiHelp.php',
'ApiImport' => 'includes/api/ApiImport.php',
@@ -301,14 +306,13 @@ $wgAutoloadLocalClasses = array(
'ApiPatrol' => 'includes/api/ApiPatrol.php',
'ApiProtect' => 'includes/api/ApiProtect.php',
'ApiPurge' => 'includes/api/ApiPurge.php',
- 'ApiRsd' => 'includes/api/ApiRsd.php',
'ApiQuery' => 'includes/api/ApiQuery.php',
'ApiQueryAllCategories' => 'includes/api/ApiQueryAllCategories.php',
'ApiQueryAllimages' => 'includes/api/ApiQueryAllimages.php',
'ApiQueryAllLinks' => 'includes/api/ApiQueryAllLinks.php',
- 'ApiQueryAllUsers' => 'includes/api/ApiQueryAllUsers.php',
'ApiQueryAllmessages' => 'includes/api/ApiQueryAllmessages.php',
'ApiQueryAllpages' => 'includes/api/ApiQueryAllpages.php',
+ 'ApiQueryAllUsers' => 'includes/api/ApiQueryAllUsers.php',
'ApiQueryBacklinks' => 'includes/api/ApiQueryBacklinks.php',
'ApiQueryBase' => 'includes/api/ApiQueryBase.php',
'ApiQueryBlocks' => 'includes/api/ApiQueryBlocks.php',
@@ -319,20 +323,22 @@ $wgAutoloadLocalClasses = array(
'ApiQueryDeletedrevs' => 'includes/api/ApiQueryDeletedrevs.php',
'ApiQueryDisabled' => 'includes/api/ApiQueryDisabled.php',
'ApiQueryDuplicateFiles' => 'includes/api/ApiQueryDuplicateFiles.php',
+ 'ApiQueryExternalLinks' => 'includes/api/ApiQueryExternalLinks.php',
'ApiQueryExtLinksUsage' => 'includes/api/ApiQueryExtLinksUsage.php',
'ApiQueryFilearchive' => 'includes/api/ApiQueryFilearchive.php',
- 'ApiQueryExternalLinks' => 'includes/api/ApiQueryExternalLinks.php',
'ApiQueryGeneratorBase' => 'includes/api/ApiQueryBase.php',
'ApiQueryImageInfo' => 'includes/api/ApiQueryImageInfo.php',
'ApiQueryImages' => 'includes/api/ApiQueryImages.php',
'ApiQueryInfo' => 'includes/api/ApiQueryInfo.php',
- 'ApiQueryIWLinks' => 'includes/api/ApiQueryIWLinks.php',
'ApiQueryIWBacklinks' => 'includes/api/ApiQueryIWBacklinks.php',
+ 'ApiQueryIWLinks' => 'includes/api/ApiQueryIWLinks.php',
+ 'ApiQueryLangBacklinks' => 'includes/api/ApiQueryLangBacklinks.php',
'ApiQueryLangLinks' => 'includes/api/ApiQueryLangLinks.php',
'ApiQueryLinks' => 'includes/api/ApiQueryLinks.php',
'ApiQueryLogEvents' => 'includes/api/ApiQueryLogEvents.php',
'ApiQueryPageProps' => 'includes/api/ApiQueryPageProps.php',
'ApiQueryProtectedTitles' => 'includes/api/ApiQueryProtectedTitles.php',
+ 'ApiQueryQueryPage' => 'includes/api/ApiQueryQueryPage.php',
'ApiQueryRandom' => 'includes/api/ApiQueryRandom.php',
'ApiQueryRecentChanges' => 'includes/api/ApiQueryRecentChanges.php',
'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php',
@@ -346,29 +352,37 @@ $wgAutoloadLocalClasses = array(
'ApiQueryWatchlistRaw' => 'includes/api/ApiQueryWatchlistRaw.php',
'ApiResult' => 'includes/api/ApiResult.php',
'ApiRollback' => 'includes/api/ApiRollback.php',
+ 'ApiRsd' => 'includes/api/ApiRsd.php',
'ApiUnblock' => 'includes/api/ApiUnblock.php',
'ApiUndelete' => 'includes/api/ApiUndelete.php',
- 'ApiUserrights' => 'includes/api/ApiUserrights.php',
'ApiUpload' => 'includes/api/ApiUpload.php',
+ 'ApiUserrights' => 'includes/api/ApiUserrights.php',
'ApiWatch' => 'includes/api/ApiWatch.php',
-
'UsageException' => 'includes/api/ApiMain.php',
- # includes/extauth
- 'ExternalUser_Hardcoded' => 'includes/extauth/Hardcoded.php',
- 'ExternalUser_MediaWiki' => 'includes/extauth/MediaWiki.php',
- 'ExternalUser_vB' => 'includes/extauth/vB.php',
-
- # includes/json
- 'Services_JSON' => 'includes/json/Services_JSON.php',
- 'Services_JSON_Error' => 'includes/json/Services_JSON.php',
- 'FormatJson' => 'includes/json/FormatJson.php',
+ # includes/cache
+ 'CacheDependency' => 'includes/cache/CacheDependency.php',
+ 'ConstantDependency' => 'includes/cache/CacheDependency.php',
+ 'DependencyWrapper' => 'includes/cache/CacheDependency.php',
+ 'FileDependency' => 'includes/cache/CacheDependency.php',
+ 'GlobalDependency' => 'includes/cache/CacheDependency.php',
+ 'HTMLCacheUpdate' => 'includes/cache/HTMLCacheUpdate.php',
+ 'HTMLCacheUpdateJob' => 'includes/cache/HTMLCacheUpdate.php',
+ 'HTMLFileCache' => 'includes/cache/HTMLFileCache.php',
+ 'LinkBatch' => 'includes/cache/LinkBatch.php',
+ 'LinkCache' => 'includes/cache/LinkCache.php',
+ 'MessageCache' => 'includes/cache/MessageCache.php',
+ 'SquidUpdate' => 'includes/cache/SquidUpdate.php',
+ 'TitleDependency' => 'includes/cache/CacheDependency.php',
+ 'TitleListDependency' => 'includes/cache/CacheDependency.php',
# includes/db
- 'Blob' => 'includes/db/Database.php',
+ 'Blob' => 'includes/db/DatabaseUtility.php',
'ChronologyProtector' => 'includes/db/LBFactory.php',
+ 'CloneDatabase' => 'includes/db/CloneDatabase.php',
'Database' => 'includes/db/DatabaseMysql.php',
'DatabaseBase' => 'includes/db/Database.php',
+ 'DatabaseIbm_db2' => 'includes/db/DatabaseIbm_db2.php',
'DatabaseMssql' => 'includes/db/DatabaseMssql.php',
'DatabaseMysql' => 'includes/db/DatabaseMysql.php',
'DatabaseOracle' => 'includes/db/DatabaseOracle.php',
@@ -376,51 +390,57 @@ $wgAutoloadLocalClasses = array(
'DatabaseSqlite' => 'includes/db/DatabaseSqlite.php',
'DatabaseSqliteStandalone' => 'includes/db/DatabaseSqlite.php',
'DatabaseType' => 'includes/db/Database.php',
- 'DBConnectionError' => 'includes/db/Database.php',
- 'DBError' => 'includes/db/Database.php',
- 'DBObject' => 'includes/db/Database.php',
- 'DBQueryError' => 'includes/db/Database.php',
- 'DBUnexpectedError' => 'includes/db/Database.php',
- 'FakeResultWrapper' => 'includes/db/Database.php',
- 'Field' => 'includes/db/Database.php',
+ 'DBConnectionError' => 'includes/db/DatabaseError.php',
+ 'DBError' => 'includes/db/DatabaseError.php',
+ 'DBObject' => 'includes/db/DatabaseUtility.php',
+ 'DBMasterPos' => 'includes/db/DatabaseUtility.php',
+ 'DBQueryError' => 'includes/db/DatabaseError.php',
+ 'DBUnexpectedError' => 'includes/db/DatabaseError.php',
+ 'FakeResultWrapper' => 'includes/db/DatabaseUtility.php',
+ 'Field' => 'includes/db/DatabaseUtility.php',
'IBM_DB2Blob' => 'includes/db/DatabaseIbm_db2.php',
+ 'IBM_DB2Field' => 'includes/db/DatabaseIbm_db2.php',
'LBFactory' => 'includes/db/LBFactory.php',
'LBFactory_Multi' => 'includes/db/LBFactory_Multi.php',
'LBFactory_Simple' => 'includes/db/LBFactory.php',
'LBFactory_Single' => 'includes/db/LBFactory_Single.php',
- 'LikeMatch' => 'includes/db/Database.php',
+ 'LikeMatch' => 'includes/db/DatabaseUtility.php',
'LoadBalancer' => 'includes/db/LoadBalancer.php',
'LoadBalancer_Single' => 'includes/db/LBFactory_Single.php',
'LoadMonitor' => 'includes/db/LoadMonitor.php',
'LoadMonitor_MySQL' => 'includes/db/LoadMonitor.php',
+ 'LoadMonitor_Null' => 'includes/db/LoadMonitor.php',
'MySQLField' => 'includes/db/DatabaseMysql.php',
'MySQLMasterPos' => 'includes/db/DatabaseMysql.php',
'ORAField' => 'includes/db/DatabaseOracle.php',
'ORAResult' => 'includes/db/DatabaseOracle.php',
'PostgresField' => 'includes/db/DatabasePostgres.php',
- 'ResultWrapper' => 'includes/db/Database.php',
+ 'ResultWrapper' => 'includes/db/DatabaseUtility.php',
'SQLiteField' => 'includes/db/DatabaseSqlite.php',
- 'DatabaseIbm_db2' => 'includes/db/DatabaseIbm_db2.php',
- 'IBM_DB2Field' => 'includes/db/DatabaseIbm_db2.php',
# includes/diff
- 'ArrayDiffFormatter' => 'includes/diff/WikiDiff.php',
- '_DiffEngine' => 'includes/diff/WikiDiff.php',
+ '_DiffEngine' => 'includes/diff/DairikiDiff.php',
+ '_DiffOp' => 'includes/diff/DairikiDiff.php',
+ '_DiffOp_Add' => 'includes/diff/DairikiDiff.php',
+ '_DiffOp_Change' => 'includes/diff/DairikiDiff.php',
+ '_DiffOp_Copy' => 'includes/diff/DairikiDiff.php',
+ '_DiffOp_Delete' => 'includes/diff/DairikiDiff.php',
+ '_HWLDF_WordAccumulator' => 'includes/diff/DairikiDiff.php',
+ 'ArrayDiffFormatter' => 'includes/diff/DairikiDiff.php',
+ 'Diff' => 'includes/diff/DairikiDiff.php',
'DifferenceEngine' => 'includes/diff/DifferenceEngine.php',
- 'DiffFormatter' => 'includes/diff/WikiDiff.php',
- 'Diff' => 'includes/diff/WikiDiff.php',
- '_DiffOp_Add' => 'includes/diff/WikiDiff.php',
- '_DiffOp_Change' => 'includes/diff/WikiDiff.php',
- '_DiffOp_Copy' => 'includes/diff/WikiDiff.php',
- '_DiffOp_Delete' => 'includes/diff/WikiDiff.php',
- '_DiffOp' => 'includes/diff/WikiDiff.php',
- '_HWLDF_WordAccumulator' => 'includes/diff/WikiDiff.php',
- 'MappedDiff' => 'includes/diff/WikiDiff.php',
+ 'DiffFormatter' => 'includes/diff/DairikiDiff.php',
+ 'MappedDiff' => 'includes/diff/DairikiDiff.php',
'RangeDifference' => 'includes/diff/WikiDiff3.php',
- 'TableDiffFormatter' => 'includes/diff/WikiDiff.php',
- 'UnifiedDiffFormatter' => 'includes/diff/WikiDiff.php',
+ 'TableDiffFormatter' => 'includes/diff/DairikiDiff.php',
+ 'UnifiedDiffFormatter' => 'includes/diff/DairikiDiff.php',
'WikiDiff3' => 'includes/diff/WikiDiff3.php',
- 'WordLevelDiff' => 'includes/diff/WikiDiff.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/filerepo
'ArchivedFile' => 'includes/filerepo/ArchivedFile.php',
@@ -433,7 +453,6 @@ $wgAutoloadLocalClasses = array(
'ForeignDBRepo' => 'includes/filerepo/ForeignDBRepo.php',
'ForeignDBViaLBRepo' => 'includes/filerepo/ForeignDBViaLBRepo.php',
'FSRepo' => 'includes/filerepo/FSRepo.php',
- 'Image' => 'includes/filerepo/Image.php',
'LocalFile' => 'includes/filerepo/LocalFile.php',
'LocalFileDeleteBatch' => 'includes/filerepo/LocalFile.php',
'LocalFileMoveBatch' => 'includes/filerepo/LocalFile.php',
@@ -445,24 +464,43 @@ $wgAutoloadLocalClasses = array(
# includes/installer
'CliInstaller' => 'includes/installer/CliInstaller.php',
- 'Installer' => 'includes/installer/Installer.php',
'DatabaseInstaller' => 'includes/installer/DatabaseInstaller.php',
'DatabaseUpdater' => 'includes/installer/DatabaseUpdater.php',
+ 'Ibm_db2Installer' => 'includes/installer/Ibm_db2Installer.php',
+ 'Ibm_db2Updater' => 'includes/installer/Ibm_db2Updater.php',
+ 'InstallDocFormatter' => 'includes/installer/InstallDocFormatter.php',
+ 'Installer' => 'includes/installer/Installer.php',
'LBFactory_InstallerFake' => 'includes/installer/Installer.php',
'LocalSettingsGenerator' => 'includes/installer/LocalSettingsGenerator.php',
- 'WebInstaller' => 'includes/installer/WebInstaller.php',
- 'WebInstallerPage' => 'includes/installer/WebInstallerPage.php',
- 'WebInstallerOutput' => 'includes/installer/WebInstallerOutput.php',
'MysqlInstaller' => 'includes/installer/MysqlInstaller.php',
'MysqlUpdater' => 'includes/installer/MysqlUpdater.php',
- 'PhpXmlBugTester' => 'includes/installer/PhpBugTests.php',
+ 'OracleInstaller' => 'includes/installer/OracleInstaller.php',
+ 'OracleUpdater' => 'includes/installer/OracleUpdater.php',
'PhpRefCallBugTester' => 'includes/installer/PhpBugTests.php',
+ 'PhpXmlBugTester' => 'includes/installer/PhpBugTests.php',
'PostgresInstaller' => 'includes/installer/PostgresInstaller.php',
'PostgresUpdater' => 'includes/installer/PostgresUpdater.php',
'SqliteInstaller' => 'includes/installer/SqliteInstaller.php',
'SqliteUpdater' => 'includes/installer/SqliteUpdater.php',
- 'OracleInstaller' => 'includes/installer/OracleInstaller.php',
- 'OracleUpdater' => 'includes/installer/OracleUpdater.php',
+ 'WebInstaller' => 'includes/installer/WebInstaller.php',
+ 'WebInstaller_Complete' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_Copying' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_DBConnect' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_DBSettings' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_Document' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_ExistingWiki' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_Install' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_Language' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_Name' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_Options' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_Readme' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_ReleaseNotes' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_Restart' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_Upgrade' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_UpgradeDoc' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstaller_Welcome' => 'includes/installer/WebInstallerPage.php',
+ 'WebInstallerOutput' => 'includes/installer/WebInstallerOutput.php',
+ 'WebInstallerPage' => 'includes/installer/WebInstallerPage.php',
# includes/job
'DoubleRedirectJob' => 'includes/job/DoubleRedirectJob.php',
@@ -473,19 +511,37 @@ $wgAutoloadLocalClasses = array(
'RefreshLinksJob2' => 'includes/job/RefreshLinksJob.php',
'UploadFromUrlJob' => 'includes/job/UploadFromUrlJob.php',
+ # 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',
+ 'CSSMin' => 'includes/libs/CSSMin.php',
+ 'HttpStatus' => 'includes/libs/HttpStatus.php',
'IEContentAnalyzer' => 'includes/libs/IEContentAnalyzer.php',
'IEUrlExtension' => 'includes/libs/IEUrlExtension.php',
- 'Spyc' => 'includes/libs/spyc.php',
+ 'JavaScriptMinifier' => 'includes/libs/JavaScriptMinifier.php',
+ 'JSMinPlus' => 'includes/libs/jsminplus.php',
+ 'JSParser' => 'includes/libs/jsminplus.php',
# includes/media
'BitmapHandler' => 'includes/media/Bitmap.php',
'BitmapHandler_ClientOnly' => 'includes/media/Bitmap_ClientOnly.php',
+ 'BitmapMetadataHandler' => 'includes/media/BitmapMetadataHandler.php',
'BmpHandler' => 'includes/media/BMP.php',
'DjVuHandler' => 'includes/media/DjVu.php',
+ 'Exif' => 'includes/media/Exif.php',
+ 'FormatExif' => 'includes/media/FormatMetadata.php',
+ 'FormatMetadata' => 'includes/media/FormatMetadata.php',
'GIFHandler' => 'includes/media/GIF.php',
'GIFMetadataExtractor' => 'includes/media/GIFMetadataExtractor.php',
'ImageHandler' => 'includes/media/Generic.php',
+ 'IPTC' => 'includes/media/IPTC.php',
+ 'JpegHandler' => 'includes/media/Jpeg.php',
+ 'JpegMetadataExtractor' => 'includes/media/JpegMetadataExtractor.php',
+ 'ExifBitmapHandler' => 'includes/media/ExifBitmap.php',
'MediaHandler' => 'includes/media/Generic.php',
'MediaTransformError' => 'includes/media/MediaTransformOutput.php',
'MediaTransformOutput' => 'includes/media/MediaTransformOutput.php',
@@ -496,38 +552,72 @@ $wgAutoloadLocalClasses = array(
'ThumbnailImage' => 'includes/media/MediaTransformOutput.php',
'TiffHandler' => 'includes/media/Tiff.php',
'TransformParameterError' => 'includes/media/MediaTransformOutput.php',
+ 'XMPInfo' => 'includes/media/XMPInfo.php',
+ 'XMPReader' => 'includes/media/XMP.php',
+ 'XMPValidate' => 'includes/media/XMPValidate.php',
# includes/normal
'UtfNormal' => 'includes/normal/UtfNormal.php',
+ # includes/objectcache
+ 'APCBagOStuff' => 'includes/objectcache/APCBagOStuff.php',
+ 'BagOStuff' => 'includes/objectcache/BagOStuff.php',
+ 'DBABagOStuff' => 'includes/objectcache/DBABagOStuff.php',
+ 'eAccelBagOStuff' => 'includes/objectcache/eAccelBagOStuff.php',
+ 'EhcacheBagOStuff' => 'includes/objectcache/EhcacheBagOStuff.php',
+ 'EmptyBagOStuff' => 'includes/objectcache/EmptyBagOStuff.php',
+ 'FakeMemCachedClient' => 'includes/objectcache/EmptyBagOStuff.php',
+ 'HashBagOStuff' => 'includes/objectcache/HashBagOStuff.php',
+ 'MediaWikiBagOStuff' => 'includes/objectcache/SqlBagOStuff.php',
+ 'MemCachedClientforWiki' => 'includes/objectcache/MemcachedClient.php',
+ 'MemcachedPhpBagOStuff' => 'includes/objectcache/MemcachedPhpBagOStuff.php',
+ 'MultiWriteBagOStuff' => 'includes/objectcache/MultiWriteBagOStuff.php',
+ 'MWMemcached' => 'includes/objectcache/MemcachedClient.php',
+ 'ObjectCache' => 'includes/objectcache/ObjectCache.php',
+ 'SqlBagOStuff' => 'includes/objectcache/SqlBagOStuff.php',
+ 'WinCacheBagOStuff' => 'includes/objectcache/WinCacheBagOStuff.php',
+ 'XCacheBagOStuff' => 'includes/objectcache/XCacheBagOStuff.php',
+
# includes/parser
+ 'CacheTime' => 'includes/parser/ParserOutput.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',
- 'OnlyIncludeReplacer' => 'includes/parser/Parser.php',
- 'PPCustomFrame_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'MWTidy' => 'includes/parser/Tidy.php',
'PPCustomFrame_DOM' => 'includes/parser/Preprocessor_DOM.php',
+ 'PPCustomFrame_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPCustomFrame_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'PPDAccum_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPDAccum_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'PPDPart' => 'includes/parser/Preprocessor_DOM.php',
'PPDPart_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPDPart_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'PPDStack' => 'includes/parser/Preprocessor_DOM.php',
'PPDStackElement' => 'includes/parser/Preprocessor_DOM.php',
'PPDStackElement_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPDStackElement_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'PPDStack_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPDStack_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'PPFrame' => 'includes/parser/Preprocessor.php',
'PPFrame_DOM' => 'includes/parser/Preprocessor_DOM.php',
'PPFrame_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPFrame_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'PPNode' => 'includes/parser/Preprocessor.php',
'PPNode_DOM' => 'includes/parser/Preprocessor_DOM.php',
'PPNode_Hash_Array' => 'includes/parser/Preprocessor_Hash.php',
'PPNode_Hash_Attr' => 'includes/parser/Preprocessor_Hash.php',
'PPNode_Hash_Text' => 'includes/parser/Preprocessor_Hash.php',
'PPNode_Hash_Tree' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPNode_HipHop_Array' => 'includes/parser/Preprocessor_HipHop.hphp',
+ 'PPNode_HipHop_Attr' => 'includes/parser/Preprocessor_HipHop.hphp',
+ 'PPNode_HipHop_Text' => 'includes/parser/Preprocessor_HipHop.hphp',
+ 'PPNode_HipHop_Tree' => 'includes/parser/Preprocessor_HipHop.hphp',
'PPTemplateFrame_DOM' => 'includes/parser/Preprocessor_DOM.php',
'PPTemplateFrame_Hash' => 'includes/parser/Preprocessor_Hash.php',
+ 'PPTemplateFrame_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
'Parser' => 'includes/parser/Parser.php',
'ParserCache' => 'includes/parser/ParserCache.php',
'ParserOptions' => 'includes/parser/ParserOptions.php',
@@ -537,15 +627,55 @@ $wgAutoloadLocalClasses = array(
'Preprocessor' => 'includes/parser/Preprocessor.php',
'Preprocessor_DOM' => 'includes/parser/Preprocessor_DOM.php',
'Preprocessor_Hash' => 'includes/parser/Preprocessor_Hash.php',
- 'StripState' => 'includes/parser/Parser.php',
- 'MWTidy' => 'includes/parser/Tidy.php',
+ 'Preprocessor_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp',
+ 'StripState' => 'includes/parser/StripState.php',
+
+ # includes/profiler
+ 'Profiler' => 'includes/profiler/Profiler.php',
+ 'ProfilerSimple' => 'includes/profiler/ProfilerSimple.php',
+ 'ProfilerSimpleText' => 'includes/profiler/ProfilerSimpleText.php',
+ 'ProfilerSimpleTrace' => 'includes/profiler/ProfilerSimpleTrace.php',
+ 'ProfilerSimpleUDP' => 'includes/profiler/ProfilerSimpleUDP.php',
+ 'ProfilerStub' => 'includes/profiler/ProfilerStub.php',
+
+ # includes/resourceloader
+ 'ResourceLoader' => 'includes/resourceloader/ResourceLoader.php',
+ 'ResourceLoaderContext' => 'includes/resourceloader/ResourceLoaderContext.php',
+ 'ResourceLoaderFileModule' => 'includes/resourceloader/ResourceLoaderFileModule.php',
+ 'ResourceLoaderFilePageModule' => 'includes/resourceloader/ResourceLoaderFilePageModule.php',
+ 'ResourceLoaderModule' => 'includes/resourceloader/ResourceLoaderModule.php',
+ 'ResourceLoaderNoscriptModule' => 'includes/resourceloader/ResourceLoaderNoscriptModule.php',
+ 'ResourceLoaderSiteModule' => 'includes/resourceloader/ResourceLoaderSiteModule.php',
+ 'ResourceLoaderStartUpModule' => 'includes/resourceloader/ResourceLoaderStartUpModule.php',
+ 'ResourceLoaderUserGroupsModule' => 'includes/resourceloader/ResourceLoaderUserGroupsModule.php',
+ 'ResourceLoaderUserModule' => 'includes/resourceloader/ResourceLoaderUserModule.php',
+ 'ResourceLoaderUserOptionsModule' => 'includes/resourceloader/ResourceLoaderUserOptionsModule.php',
+ 'ResourceLoaderUserTokensModule' => 'includes/resourceloader/ResourceLoaderUserTokensModule.php',
+ 'ResourceLoaderWikiModule' => 'includes/resourceloader/ResourceLoaderWikiModule.php',
+
+ # includes/revisiondelete
+ 'RevDel_ArchivedFileItem' => 'includes/revisiondelete/RevisionDelete.php',
+ 'RevDel_ArchivedFileList' => 'includes/revisiondelete/RevisionDelete.php',
+ 'RevDel_ArchiveItem' => 'includes/revisiondelete/RevisionDelete.php',
+ 'RevDel_ArchiveList' => 'includes/revisiondelete/RevisionDelete.php',
+ 'RevDel_FileItem' => 'includes/revisiondelete/RevisionDelete.php',
+ 'RevDel_FileList' => 'includes/revisiondelete/RevisionDelete.php',
+ 'RevDel_Item' => 'includes/revisiondelete/RevisionDeleteAbstracts.php',
+ 'RevDel_List' => 'includes/revisiondelete/RevisionDeleteAbstracts.php',
+ 'RevDel_LogItem' => 'includes/revisiondelete/RevisionDelete.php',
+ 'RevDel_LogList' => 'includes/revisiondelete/RevisionDelete.php',
+ 'RevDel_RevisionItem' => 'includes/revisiondelete/RevisionDelete.php',
+ 'RevDel_RevisionList' => 'includes/revisiondelete/RevisionDelete.php',
+ 'RevisionDelete' => 'includes/revisiondelete/RevisionDelete.php',
+ 'RevisionDeleter' => 'includes/revisiondelete/RevisionDeleter.php',
+ 'RevisionDeleteUser' => 'includes/revisiondelete/RevisionDeleteUser.php',
# includes/search
'MySQLSearchResultSet' => 'includes/search/SearchMySQL.php',
'PostgresSearchResult' => 'includes/search/SearchPostgres.php',
'PostgresSearchResultSet' => 'includes/search/SearchPostgres.php',
- 'SearchEngineDummy' => 'includes/search/SearchEngine.php',
'SearchEngine' => 'includes/search/SearchEngine.php',
+ 'SearchEngineDummy' => 'includes/search/SearchEngine.php',
'SearchHighlighter' => 'includes/search/SearchEngine.php',
'SearchIBM_DB2' => 'includes/search/SearchIBM_DB2.php',
'SearchMssql' => 'includes/search/SearchMssql.php',
@@ -562,30 +692,26 @@ $wgAutoloadLocalClasses = array(
'SqlSearchResultSet' => 'includes/search/SearchEngine.php',
# includes/specials
- 'SpecialAllmessages' => 'includes/specials/SpecialAllmessages.php',
'ActiveUsersPager' => 'includes/specials/SpecialActiveusers.php',
'AllmessagesTablePager' => 'includes/specials/SpecialAllmessages.php',
'AncientPagesPage' => 'includes/specials/SpecialAncientpages.php',
+ 'BlockListPager' => 'includes/specials/SpecialBlockList.php',
'BrokenRedirectsPage' => 'includes/specials/SpecialBrokenRedirects.php',
'ContribsPager' => 'includes/specials/SpecialContributions.php',
'DBLockForm' => 'includes/specials/SpecialLockdb.php',
'DBUnlockForm' => 'includes/specials/SpecialUnlockdb.php',
'DeadendPagesPage' => 'includes/specials/SpecialDeadendpages.php',
- 'DeletedContributionsPage' => 'includes/specials/SpecialDeletedContributions.php',
'DeletedContribsPager' => 'includes/specials/SpecialDeletedContributions.php',
+ 'DeletedContributionsPage' => 'includes/specials/SpecialDeletedContributions.php',
'DisambiguationsPage' => 'includes/specials/SpecialDisambiguations.php',
'DoubleRedirectsPage' => 'includes/specials/SpecialDoubleRedirects.php',
'EmailConfirmation' => 'includes/specials/SpecialConfirmemail.php',
'EmailInvalidation' => 'includes/specials/SpecialConfirmemail.php',
- 'SpecialEmailUser' => 'includes/specials/SpecialEmailuser.php',
'FewestrevisionsPage' => 'includes/specials/SpecialFewestrevisions.php',
'FileDuplicateSearchPage' => 'includes/specials/SpecialFileDuplicateSearch.php',
- 'IPBlockForm' => 'includes/specials/SpecialBlockip.php',
- 'IPBlocklistPager' => 'includes/specials/SpecialIpblocklist.php',
- 'IPUnblockForm' => 'includes/specials/SpecialIpblocklist.php',
+ 'HTMLBlockedUsersItemSelect' => 'includes/specials/SpecialBlockList.php',
'ImportReporter' => 'includes/specials/SpecialImport.php',
- 'ImportStreamSource' => 'includes/Import.php',
- 'ImportStringSource' => 'includes/Import.php',
+ 'IPBlockForm' => 'includes/specials/SpecialBlock.php',
'LinkSearchPage' => 'includes/specials/SpecialLinkSearch.php',
'ListredirectsPage' => 'includes/specials/SpecialListredirects.php',
'LoginForm' => 'includes/specials/SpecialUserlogin.php',
@@ -596,46 +722,42 @@ $wgAutoloadLocalClasses = array(
'MostimagesPage' => 'includes/specials/SpecialMostimages.php',
'MostlinkedCategoriesPage' => 'includes/specials/SpecialMostlinkedcategories.php',
'MostlinkedPage' => 'includes/specials/SpecialMostlinked.php',
+ 'MostlinkedTemplatesPage' => 'includes/specials/SpecialMostlinkedtemplates.php',
'MostrevisionsPage' => 'includes/specials/SpecialMostrevisions.php',
'MovePageForm' => 'includes/specials/SpecialMovepage.php',
- 'SpecialNewpages' => 'includes/specials/SpecialNewpages.php',
- 'SpecialContributions' => 'includes/specials/SpecialContributions.php',
'NewPagesPager' => 'includes/specials/SpecialNewpages.php',
'PageArchive' => 'includes/specials/SpecialUndelete.php',
- 'SpecialResetpass' => 'includes/specials/SpecialResetpass.php',
'PopularPagesPage' => 'includes/specials/SpecialPopularpages.php',
- 'PreferencesForm' => 'includes/Preferences.php',
'RandomPage' => 'includes/specials/SpecialRandompage.php',
- 'SpecialRevisionDelete' => 'includes/specials/SpecialRevisiondelete.php',
- 'RevisionDeleter' => 'includes/revisiondelete/RevisionDeleter.php',
- 'RevDel_List' => 'includes/revisiondelete/RevisionDeleteAbstracts.php',
- 'RevDel_Item' => 'includes/revisiondelete/RevisionDeleteAbstracts.php',
- 'RevDel_RevisionList' => 'includes/revisiondelete/RevisionDelete.php',
- 'RevDel_RevisionItem' => 'includes/revisiondelete/RevisionDelete.php',
- 'RevDel_ArchiveList' => 'includes/revisiondelete/RevisionDelete.php',
- 'RevDel_ArchiveItem' => 'includes/revisiondelete/RevisionDelete.php',
- 'RevDel_FileList' => 'includes/revisiondelete/RevisionDelete.php',
- 'RevDel_FileItem' => 'includes/revisiondelete/RevisionDelete.php',
- 'RevDel_ArchivedFileList' => 'includes/revisiondelete/RevisionDelete.php',
- 'RevDel_ArchivedFileItem' => 'includes/revisiondelete/RevisionDelete.php',
- 'RevDel_LogList' => 'includes/revisiondelete/RevisionDelete.php',
- 'RevDel_LogItem' => 'includes/revisiondelete/RevisionDelete.php',
'ShortPagesPage' => 'includes/specials/SpecialShortpages.php',
'SpecialActiveUsers' => 'includes/specials/SpecialActiveusers.php',
+ 'SpecialAllmessages' => 'includes/specials/SpecialAllmessages.php',
'SpecialAllpages' => 'includes/specials/SpecialAllpages.php',
'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',
'SpecialCategories' => 'includes/specials/SpecialCategories.php',
+ 'SpecialChangePassword' => 'includes/specials/SpecialChangePassword.php',
'SpecialComparePages' => 'includes/specials/SpecialComparePages.php',
+ 'SpecialContributions' => 'includes/specials/SpecialContributions.php',
+ 'SpecialEditWatchlist' => 'includes/specials/SpecialEditWatchlist.php',
+ 'SpecialEmailUser' => 'includes/specials/SpecialEmailuser.php',
'SpecialExport' => 'includes/specials/SpecialExport.php',
'SpecialFilepath' => 'includes/specials/SpecialFilepath.php',
'SpecialImport' => 'includes/specials/SpecialImport.php',
+ 'SpecialListFiles' => 'includes/specials/SpecialListfiles.php',
'SpecialListGroupRights' => 'includes/specials/SpecialListgrouprights.php',
+ 'SpecialListUsers' => 'includes/specials/SpecialListusers.php',
'SpecialLockdb' => 'includes/specials/SpecialLockdb.php',
'SpecialLog' => 'includes/specials/SpecialLog.php',
'SpecialMergeHistory' => 'includes/specials/SpecialMergeHistory.php',
'SpecialMostlinkedtemplates' => 'includes/specials/SpecialMostlinkedtemplates.php',
+ 'SpecialNewFiles' => 'includes/specials/SpecialNewimages.php',
+ 'SpecialNewpages' => 'includes/specials/SpecialNewpages.php',
+ 'SpecialPasswordReset' => 'includes/specials/SpecialPasswordReset.php',
+ 'SpecialPermanentLink' => 'includes/SpecialPage.php',
'SpecialPreferences' => 'includes/specials/SpecialPreferences.php',
'SpecialPrefixindex' => 'includes/specials/SpecialPrefixindex.php',
'SpecialProtectedpages' => 'includes/specials/SpecialProtectedpages.php',
@@ -643,21 +765,24 @@ $wgAutoloadLocalClasses = array(
'SpecialRandomredirect' => 'includes/specials/SpecialRandomredirect.php',
'SpecialRecentChanges' => 'includes/specials/SpecialRecentchanges.php',
'SpecialRecentchangeslinked' => 'includes/specials/SpecialRecentchangeslinked.php',
+ 'SpecialRevisionDelete' => 'includes/specials/SpecialRevisiondelete.php',
'SpecialSearch' => 'includes/specials/SpecialSearch.php',
- 'SpecialUploadStash' => 'includes/specials/SpecialUploadStash.php',
'SpecialSpecialpages' => 'includes/specials/SpecialSpecialpages.php',
'SpecialStatistics' => 'includes/specials/SpecialStatistics.php',
'SpecialTags' => 'includes/specials/SpecialTags.php',
+ 'SpecialUnblock' => 'includes/specials/SpecialUnblock.php',
+ 'SpecialUndelete' => 'includes/specials/SpecialUndelete.php',
'SpecialUnlockdb' => 'includes/specials/SpecialUnlockdb.php',
'SpecialUpload' => 'includes/specials/SpecialUpload.php',
+ 'SpecialUploadStash' => 'includes/specials/SpecialUploadStash.php',
'SpecialUserlogout' => 'includes/specials/SpecialUserlogout.php',
'SpecialVersion' => 'includes/specials/SpecialVersion.php',
+ 'SpecialWatchlist' => 'includes/specials/SpecialWatchlist.php',
'SpecialWhatlinkshere' => 'includes/specials/SpecialWhatlinkshere.php',
- 'SpecialWhatLinksHere' => 'includes/specials/SpecialWhatlinkshere.php',
'UncategorizedCategoriesPage' => 'includes/specials/SpecialUncategorizedcategories.php',
+ 'UncategorizedImagesPage' => 'includes/specials/SpecialUncategorizedimages.php',
'UncategorizedPagesPage' => 'includes/specials/SpecialUncategorizedpages.php',
'UncategorizedTemplatesPage' => 'includes/specials/SpecialUncategorizedtemplates.php',
- 'UndeleteForm' => 'includes/specials/SpecialUndelete.php',
'UnusedCategoriesPage' => 'includes/specials/SpecialUnusedcategories.php',
'UnusedimagesPage' => 'includes/specials/SpecialUnusedimages.php',
'UnusedtemplatesPage' => 'includes/specials/SpecialUnusedtemplates.php',
@@ -670,9 +795,7 @@ $wgAutoloadLocalClasses = array(
'WantedFilesPage' => 'includes/specials/SpecialWantedfiles.php',
'WantedPagesPage' => 'includes/specials/SpecialWantedpages.php',
'WantedTemplatesPage' => 'includes/specials/SpecialWantedtemplates.php',
- 'WhatLinksHerePage' => 'includes/specials/SpecialWhatlinkshere.php',
- 'WikiImporter' => 'includes/Import.php',
- 'WikiRevision' => 'includes/Import.php',
+ 'WatchlistEditor' => 'includes/specials/SpecialEditWatchlist.php',
'WithoutInterwikiPage' => 'includes/specials/SpecialWithoutinterwiki.php',
# includes/templates
@@ -681,66 +804,71 @@ $wgAutoloadLocalClasses = array(
# includes/upload
'UploadBase' => 'includes/upload/UploadBase.php',
- 'UploadFromStash' => 'includes/upload/UploadFromStash.php',
'UploadFromFile' => 'includes/upload/UploadFromFile.php',
+ 'UploadFromStash' => 'includes/upload/UploadFromStash.php',
'UploadFromUrl' => 'includes/upload/UploadFromUrl.php',
'UploadStash' => 'includes/upload/UploadStash.php',
- 'UploadStashFile' => 'includes/upload/UploadStash.php',
- 'UploadStashNotAvailableException' => 'includes/upload/UploadStash.php',
- 'UploadStashFileNotFoundException' => 'includes/upload/UploadStash.php',
'UploadStashBadPathException' => 'includes/upload/UploadStash.php',
'UploadStashBadVersionException' => 'includes/upload/UploadStash.php',
+ 'UploadStashFile' => 'includes/upload/UploadStash.php',
'UploadStashFileException' => 'includes/upload/UploadStash.php',
+ 'UploadStashFileNotFoundException' => 'includes/upload/UploadStash.php',
+ 'UploadStashNotAvailableException' => 'includes/upload/UploadStash.php',
'UploadStashZeroLengthFileException' => 'includes/upload/UploadStash.php',
# languages
- 'Language' => 'languages/Language.php',
'FakeConverter' => 'languages/Language.php',
+ 'Language' => 'languages/Language.php',
'LanguageConverter' => 'languages/LanguageConverter.php',
# maintenance
- 'AnsiTermColorer' => 'maintenance/tests/testHelpers.inc',
'ConvertLinks' => 'maintenance/convertLinks.php',
- 'DbTestPreviewer' => 'maintenance/tests/testHelpers.inc',
- 'DbTestRecorder' => 'maintenance/tests/testHelpers.inc',
'DeleteArchivedFilesImplementation' => 'maintenance/deleteArchivedFiles.inc',
'DeleteArchivedRevisionsImplementation' => 'maintenance/deleteArchivedRevisions.inc',
'DeleteDefaultMessages' => 'maintenance/deleteDefaultMessages.php',
- 'DummyTermColorer' => 'maintenance/tests/testHelpers.inc',
'FakeMaintenance' => 'maintenance/Maintenance.php',
+ 'LoggedUpdateMaintenance' => 'maintenance/Maintenance.php',
'Maintenance' => 'maintenance/Maintenance.php',
- 'ParserTest' => 'maintenance/tests/parser/parserTest.inc',
- 'ParserTestParserHook' => 'maintenance/tests/parser/parserTestsParserHook.php',
- 'ParserTestStaticParserHook' => 'maintenance/tests/parser/parserTestsStaticParserHook.php',
+ 'FixExtLinksProtocolRelative' => 'maintenance/fixExtLinksProtocolRelative.php',
'PopulateCategory' => 'maintenance/populateCategory.php',
'PopulateLogSearch' => 'maintenance/populateLogSearch.php',
'PopulateLogUsertext' => 'maintenance/populateLogUsertext.php',
'PopulateParentId' => 'maintenance/populateParentId.php',
'PopulateRevisionLength' => 'maintenance/populateRevisionLength.php',
- 'RemoteTestRecorder' => 'maintenance/tests/testHelpers.inc',
'SevenZipStream' => 'maintenance/7zip.inc',
'Sqlite' => 'maintenance/sqlite.inc',
- 'TestFileIterator' => 'maintenance/tests/testHelpers.inc',
- 'TestRecorder' => 'maintenance/tests/testHelpers.inc',
'UpdateCollation' => 'maintenance/updateCollation.php',
'UpdateRestrictions' => 'maintenance/updateRestrictions.php',
'UserDupes' => 'maintenance/userDupes.inc',
- # maintenance/tests/selenium
- 'Selenium' => 'maintenance/tests/selenium/Selenium.php',
- 'SeleniumLoader' => 'maintenance/tests/selenium/SeleniumLoader.php',
- 'SeleniumTestCase' => 'maintenance/tests/selenium/SeleniumTestCase.php',
- 'SeleniumTestConsoleLogger' => 'maintenance/tests/selenium/SeleniumTestConsoleLogger.php',
- 'SeleniumTestHTMLLogger' => 'maintenance/tests/selenium/SeleniumTestHTMLLogger.php',
- 'SeleniumTestListener' => 'maintenance/tests/selenium/SeleniumTestListener.php',
- 'SeleniumTestSuite' => 'maintenance/tests/selenium/SeleniumTestSuite.php',
- 'SeleniumConfig' => 'maintenance/tests/selenium/SeleniumConfig.php',
-
# maintenance/language
'csvStatsOutput' => 'maintenance/language/StatOutputs.php',
'statsOutput' => 'maintenance/language/StatOutputs.php',
'textStatsOutput' => 'maintenance/language/StatOutputs.php',
'wikiStatsOutput' => 'maintenance/language/StatOutputs.php',
+
+ # tests
+ 'AnsiTermColorer' => 'tests/testHelpers.inc',
+ 'DbTestPreviewer' => 'tests/testHelpers.inc',
+ 'DbTestRecorder' => 'tests/testHelpers.inc',
+ 'DummyTermColorer' => 'tests/testHelpers.inc',
+ 'TestFileIterator' => 'tests/testHelpers.inc',
+ 'TestRecorder' => 'tests/testHelpers.inc',
+
+ # tests/parser
+ 'ParserTest' => 'tests/parser/parserTest.inc',
+ 'ParserTestParserHook' => 'tests/parser/parserTestsParserHook.php',
+ 'ParserTestStaticParserHook' => 'tests/parser/parserTestsStaticParserHook.php',
+
+ # tests/selenium
+ 'Selenium' => 'tests/selenium/Selenium.php',
+ 'SeleniumLoader' => 'tests/selenium/SeleniumLoader.php',
+ 'SeleniumTestCase' => 'tests/selenium/SeleniumTestCase.php',
+ 'SeleniumTestConsoleLogger' => 'tests/selenium/SeleniumTestConsoleLogger.php',
+ 'SeleniumTestHTMLLogger' => 'tests/selenium/SeleniumTestHTMLLogger.php',
+ 'SeleniumTestListener' => 'tests/selenium/SeleniumTestListener.php',
+ 'SeleniumTestSuite' => 'tests/selenium/SeleniumTestSuite.php',
+ 'SeleniumConfig' => 'tests/selenium/SeleniumConfig.php',
);
class AutoLoader {
@@ -792,16 +920,6 @@ class AutoLoader {
return true;
}
- static function loadAllExtensions() {
- global $wgAutoloadClasses;
-
- foreach ( $wgAutoloadClasses as $class => $file ) {
- if ( !( class_exists( $class, false ) || interface_exists( $class, false ) ) ) {
- require( $file );
- }
- }
- }
-
/**
* Force a class to be run through the autoloader, helpful for things like
* Sanitizer that have define()s outside of their class definition. Of course
diff --git a/includes/Autopromote.php b/includes/Autopromote.php
index b4d89b24..83f3c20b 100644
--- a/includes/Autopromote.php
+++ b/includes/Autopromote.php
@@ -8,7 +8,7 @@ class Autopromote {
/**
* Get the groups for the given user based on $wgAutopromote.
*
- * @param $user The user to get the groups for
+ * @param $user User The user to get the groups for
* @return array Array of groups to promote to.
*/
public static function getAutopromoteGroups( User $user ) {
@@ -28,8 +28,47 @@ class Autopromote {
}
/**
+ * Get the groups for the given user based on the given criteria.
+ *
+ * Does not return groups the user already belongs to or has once belonged.
+ *
+ * @param $user The user to get the groups for
+ * @param $event String key in $wgAutopromoteOnce (each one has groups/criteria)
+ *
+ * @return array Groups the user should be promoted to.
+ *
+ * @see $wgAutopromoteOnce
+ */
+ public static function getAutopromoteOnceGroups( User $user, $event ) {
+ global $wgAutopromoteOnce;
+
+ $promote = array();
+
+ if ( isset( $wgAutopromoteOnce[$event] ) && count( $wgAutopromoteOnce[$event] ) ) {
+ $currentGroups = $user->getGroups();
+ $formerGroups = $user->getFormerGroups();
+ foreach ( $wgAutopromoteOnce[$event] as $group => $cond ) {
+ // Do not check if the user's already a member
+ if ( in_array( $group, $currentGroups ) ) {
+ continue;
+ }
+ // Do not autopromote if the user has belonged to the group
+ if ( in_array( $group, $formerGroups ) ) {
+ continue;
+ }
+ // Finally - check the conditions
+ if ( self::recCheckCondition( $cond, $user ) ) {
+ $promote[] = $group;
+ }
+ }
+ }
+
+ return $promote;
+ }
+
+ /**
* Recursively check a condition. Conditions are in the form
- * array( '&' or '|' or '^', cond1, cond2, ... )
+ * array( '&' or '|' or '^' or '!', cond1, cond2, ... )
* where cond1, cond2, ... are themselves conditions; *OR*
* APCOND_EMAILCONFIRMED, *OR*
* array( APCOND_EMAILCONFIRMED ), *OR*
@@ -40,7 +79,7 @@ class Autopromote {
* self::checkCondition for evaluation of the latter type.
*
* @param $cond Mixed: a condition, possibly containing other conditions
- * @param $user The user to check the conditions against
+ * @param $user User The user to check the conditions against
* @return bool Whether the condition is true
*/
private static function recCheckCondition( $cond, User $user ) {
@@ -48,7 +87,7 @@ class Autopromote {
if ( is_array( $cond ) && count( $cond ) >= 2 && in_array( $cond[0], $validOps ) ) {
# Recursive condition
- if ( $cond[0] == '&' ) {
+ if ( $cond[0] == '&' ) { // AND (all conds pass)
foreach ( array_slice( $cond, 1 ) as $subcond ) {
if ( !self::recCheckCondition( $subcond, $user ) ) {
return false;
@@ -56,7 +95,7 @@ class Autopromote {
}
return true;
- } elseif ( $cond[0] == '|' ) {
+ } elseif ( $cond[0] == '|' ) { // OR (at least one cond passes)
foreach ( array_slice( $cond, 1 ) as $subcond ) {
if ( self::recCheckCondition( $subcond, $user ) ) {
return true;
@@ -64,18 +103,13 @@ class Autopromote {
}
return false;
- } elseif ( $cond[0] == '^' ) {
- $res = null;
- foreach ( array_slice( $cond, 1 ) as $subcond ) {
- if ( is_null( $res ) ) {
- $res = self::recCheckCondition( $subcond, $user );
- } else {
- $res = ( $res xor self::recCheckCondition( $subcond, $user ) );
- }
+ } 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.' );
}
-
- return $res;
- } elseif ( $cond[0] == '!' ) {
+ return self::recCheckCondition( $cond[1], $user )
+ xor self::recCheckCondition( $cond[2], $user );
+ } elseif ( $cond[0] == '!' ) { // NOT (no conds pass)
foreach ( array_slice( $cond, 1 ) as $subcond ) {
if ( self::recCheckCondition( $subcond, $user ) ) {
return false;
@@ -101,7 +135,7 @@ class Autopromote {
* ates them.
*
* @param $cond Array: A condition, which must not contain other conditions
- * @param $user The user to check the condition against
+ * @param $user User The user to check the condition against
* @return bool Whether the condition is true for the user
*/
private static function checkCondition( $cond, User $user ) {
@@ -112,7 +146,7 @@ class Autopromote {
switch( $cond[0] ) {
case APCOND_EMAILCONFIRMED:
- if ( User::isValidEmailAddr( $user->getEmail() ) ) {
+ if ( Sanitizer::validateEmail( $user->getEmail() ) ) {
if ( $wgEmailAuthentication ) {
return (bool)$user->getEmailAuthenticationTimestamp();
} else {
@@ -137,6 +171,8 @@ class Autopromote {
return IP::isInRange( wfGetIP(), $cond[1] );
case APCOND_BLOCKED:
return $user->isBlocked();
+ case APCOND_ISBOT:
+ return in_array( 'bot', User::getGroupPermissions( $user->getGroups() ) );
default:
$result = null;
wfRunHooks( 'AutopromoteCondition', array( $cond[0], array_slice( $cond, 1 ), $user, &$result ) );
diff --git a/includes/BacklinkCache.php b/includes/BacklinkCache.php
index 02b0f170..8d1571ec 100644
--- a/includes/BacklinkCache.php
+++ b/includes/BacklinkCache.php
@@ -1,21 +1,71 @@
<?php
/**
- * Class for fetching backlink lists, approximate backlink counts and partitions.
- * Instances of this class should typically be fetched with $title->getBacklinkCache().
+ * File for BacklinkCache class
+ * @file
+ */
+
+/**
+ * Class for fetching backlink lists, approximate backlink counts and
+ * partitions. This is a shared cache.
+ *
+ * Instances of this class should typically be fetched with the method
+ * $title->getBacklinkCache().
+ *
+ * Ideally you should only get your backlinks from here when you think
+ * there is some advantage in caching them. Otherwise it's just a waste
+ * of memory.
*
- * Ideally you should only get your backlinks from here when you think there is some
- * advantage in caching them. Otherwise it's just a waste of memory.
+ * Introduced by r47317
+ *
+ * @internal documentation reviewed on 18 Mar 2011 by hashar
+ *
+ * @author Tim Starling
+ * @copyright © 2009, Tim Starling, Domas Mituzas
+ * @copyright © 2010, Max Sem
+ * @copyright © 2011, Ashar Voultoiz
*/
class BacklinkCache {
- var $partitionCache = array();
- var $fullResultCache = array();
- var $title;
- var $db;
+
+ /**
+ * Multi dimensions array representing batches. Keys are:
+ * > (string) links table name
+ * > 'numRows' : Number of rows for this link table
+ * > 'batches' : array( $start, $end )
+ *
+ * @see BacklinkCache::partitionResult()
+ *
+ * Cleared with BacklinkCache::clear()
+ */
+ protected $partitionCache = array();
+
+ /**
+ * Contains the whole links from a database result.
+ * This is raw data that will be partitioned in $partitionCache
+ *
+ * Initialized with BacklinkCache::getLinks()
+ * Cleared with BacklinkCache::clear()
+ */
+ protected $fullResultCache = array();
+
+ /**
+ * Local copy of a database object.
+ *
+ * Accessor: BacklinkCache::getDB()
+ * Mutator : BacklinkCache::setDB()
+ * Cleared with BacklinkCache::clear()
+ */
+ protected $db;
+
+ /**
+ * Local copy of a Title object
+ */
+ protected $title;
const CACHE_EXPIRY = 3600;
/**
* Create a new BacklinkCache
+ * @param Title $title : Title object to create a backlink cache for.
*/
function __construct( $title ) {
$this->title = $title;
@@ -23,16 +73,17 @@ class BacklinkCache {
/**
* Serialization handler, diasallows to serialize the database to prevent
- * failures after this class is deserialized from cache with dead DB connection.
+ * failures after this class is deserialized from cache with dead DB
+ * connection.
*/
function __sleep() {
return array( 'partitionCache', 'fullResultCache', 'title' );
}
/**
- * Clear locally stored data
+ * Clear locally stored data and database object.
*/
- function clear() {
+ public function clear() {
$this->partitionCache = array();
$this->fullResultCache = array();
unset( $this->db );
@@ -40,11 +91,18 @@ class BacklinkCache {
/**
* Set the Database object to use
+ *
+ * @param $db DatabaseBase
*/
public function setDB( $db ) {
$this->db = $db;
}
+ /**
+ * Get the slave connection to the database
+ * When non existing, will initialize the connection.
+ * @return Database object
+ */
protected function getDB() {
if ( !isset( $this->db ) ) {
$this->db = wfGetDB( DB_SLAVE );
@@ -58,7 +116,7 @@ class BacklinkCache {
* @param $table String
* @param $startId Integer or false
* @param $endId Integer or false
- * @return TitleArray
+ * @return TitleArrayFromResult
*/
public function getLinks( $table, $startId = false, $endId = false ) {
wfProfileIn( __METHOD__ );
@@ -95,6 +153,7 @@ class BacklinkCache {
return $ta;
}
+ // @todo FIXME: Make this a function?
if ( !isset( $this->fullResultCache[$table] ) ) {
wfDebug( __METHOD__ . ": from DB\n" );
$res = $this->getDB()->select(
@@ -117,14 +176,15 @@ class BacklinkCache {
/**
* Get the field name prefix for a given table
+ * @param $table String
*/
protected function getPrefix( $table ) {
static $prefixes = array(
- 'pagelinks' => 'pl',
- 'imagelinks' => 'il',
+ 'pagelinks' => 'pl',
+ 'imagelinks' => 'il',
'categorylinks' => 'cl',
'templatelinks' => 'tl',
- 'redirect' => 'rd',
+ 'redirect' => 'rd',
);
if ( isset( $prefixes[$table] ) ) {
@@ -135,18 +195,32 @@ class BacklinkCache {
}
/**
- * Get the SQL condition array for selecting backlinks, with a join on the page table
+ * Get the SQL condition array for selecting backlinks, with a join
+ * on the page table.
+ * @param $table String
*/
protected function getConditions( $table ) {
$prefix = $this->getPrefix( $table );
+ // @todo FIXME: imagelinks and categorylinks do not rely on getNamespace,
+ // they could be moved up for nicer case statements
switch ( $table ) {
case 'pagelinks':
case 'templatelinks':
+ $conds = array(
+ "{$prefix}_namespace" => $this->title->getNamespace(),
+ "{$prefix}_title" => $this->title->getDBkey(),
+ "page_id={$prefix}_from"
+ );
+ break;
case 'redirect':
$conds = array(
"{$prefix}_namespace" => $this->title->getNamespace(),
- "{$prefix}_title" => $this->title->getDBkey(),
+ "{$prefix}_title" => $this->title->getDBkey(),
+ $this->getDb()->makeList( array(
+ "{$prefix}_interwiki = ''",
+ "{$prefix}_interwiki is null",
+ ), LIST_OR ),
"page_id={$prefix}_from"
);
break;
@@ -171,6 +245,8 @@ class BacklinkCache {
/**
* Get the approximate number of backlinks
+ * @param $table String
+ * @return integer
*/
public function getNumLinks( $table ) {
if ( isset( $this->fullResultCache[$table] ) ) {
@@ -189,15 +265,17 @@ class BacklinkCache {
/**
* Partition the backlinks into batches.
- * Returns an array giving the start and end of each range. The first batch has
- * a start of false, and the last batch has an end of false.
+ * Returns an array giving the start and end of each range. The first
+ * batch has a start of false, and the last batch has an end of false.
*
* @param $table String: the links table name
* @param $batchSize Integer
* @return Array
*/
public function partition( $table, $batchSize ) {
- // Try cache
+
+ // 1) try partition cache ...
+
if ( isset( $this->partitionCache[$table][$batchSize] ) ) {
wfDebug( __METHOD__ . ": got from partition cache\n" );
return $this->partitionCache[$table][$batchSize]['batches'];
@@ -206,7 +284,8 @@ class BacklinkCache {
$this->partitionCache[$table][$batchSize] = false;
$cacheEntry =& $this->partitionCache[$table][$batchSize];
- // Try full result cache
+ // 2) ... then try full result cache ...
+
if ( isset( $this->fullResultCache[$table] ) ) {
$cacheEntry = $this->partitionResult( $this->fullResultCache[$table], $batchSize );
wfDebug( __METHOD__ . ": got from full result cache\n" );
@@ -214,7 +293,8 @@ class BacklinkCache {
return $cacheEntry['batches'];
}
- // Try memcached
+ // 3) ... fallback to memcached ...
+
global $wgMemc;
$memcKey = wfMemcKey(
@@ -233,7 +313,9 @@ class BacklinkCache {
return $cacheEntry['batches'];
}
- // Fetch from database
+
+ // 4) ... finally fetch from the slow database :(
+
$this->getLinks( $table );
$cacheEntry = $this->partitionResult( $this->fullResultCache[$table], $batchSize );
// Save to memcached
@@ -245,6 +327,9 @@ class BacklinkCache {
/**
* Partition a DB result with backlinks in it into batches
+ * @param $res ResultWrapper database result
+ * @param $batchSize integer
+ * @return array @see
*/
protected function partitionResult( $res, $batchSize ) {
$batches = array();
diff --git a/includes/BagOStuff.php b/includes/BagOStuff.php
deleted file mode 100644
index 63c96de7..00000000
--- a/includes/BagOStuff.php
+++ /dev/null
@@ -1,906 +0,0 @@
-<?php
-/**
- * Classes to cache objects in PHP accelerators, SQL database or DBA files
- *
- * Copyright © 2003-2004 Brion Vibber <brion@pobox.com>
- * http://www.mediawiki.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Cache
- */
-
-/**
- * @defgroup Cache Cache
- */
-
-/**
- * interface is intended to be more or less compatible with
- * the PHP memcached client.
- *
- * backends for local hash array and SQL table included:
- * <code>
- * $bag = new HashBagOStuff();
- * $bag = new SqlBagOStuff(); # connect to db first
- * </code>
- *
- * @ingroup Cache
- */
-abstract class BagOStuff {
- var $debugMode = false;
-
- public function set_debug( $bool ) {
- $this->debugMode = $bool;
- }
-
- /* *** THE GUTS OF THE OPERATION *** */
- /* Override these with functional things in subclasses */
-
- /**
- * Get an item with the given key. Returns false if it does not exist.
- * @param $key string
- */
- abstract public function get( $key );
-
- /**
- * Set an item.
- * @param $key string
- * @param $value mixed
- * @param $exptime int Either an interval in seconds or a unix timestamp for expiry
- */
- abstract public function set( $key, $value, $exptime = 0 );
-
- /*
- * Delete an item.
- * @param $key string
- * @param $time int Amount of time to delay the operation (mostly memcached-specific)
- */
- abstract public function delete( $key, $time = 0 );
-
- public function lock( $key, $timeout = 0 ) {
- /* stub */
- return true;
- }
-
- public function unlock( $key ) {
- /* stub */
- return true;
- }
-
- public function keys() {
- /* stub */
- return array();
- }
-
- /* *** Emulated functions *** */
- /* Better performance can likely be got with custom written versions */
- public function get_multi( $keys ) {
- $out = array();
-
- foreach ( $keys as $key ) {
- $out[$key] = $this->get( $key );
- }
-
- return $out;
- }
-
- public function set_multi( $hash, $exptime = 0 ) {
- foreach ( $hash as $key => $value ) {
- $this->set( $key, $value, $exptime );
- }
- }
-
- public function add( $key, $value, $exptime = 0 ) {
- if ( !$this->get( $key ) ) {
- $this->set( $key, $value, $exptime );
-
- return true;
- }
- }
-
- public function add_multi( $hash, $exptime = 0 ) {
- foreach ( $hash as $key => $value ) {
- $this->add( $key, $value, $exptime );
- }
- }
-
- public function delete_multi( $keys, $time = 0 ) {
- foreach ( $keys as $key ) {
- $this->delete( $key, $time );
- }
- }
-
- public function replace( $key, $value, $exptime = 0 ) {
- if ( $this->get( $key ) !== false ) {
- $this->set( $key, $value, $exptime );
- }
- }
-
- /**
- * @param $key String: Key to increase
- * @param $value Integer: Value to add to $key (Default 1)
- * @return null if lock is not possible else $key value increased by $value
- */
- public function incr( $key, $value = 1 ) {
- if ( !$this->lock( $key ) ) {
- return null;
- }
-
- $value = intval( $value );
-
- if ( ( $n = $this->get( $key ) ) !== false ) {
- $n += $value;
- $this->set( $key, $n ); // exptime?
- }
- $this->unlock( $key );
-
- return $n;
- }
-
- public function decr( $key, $value = 1 ) {
- return $this->incr( $key, - $value );
- }
-
- public function debug( $text ) {
- if ( $this->debugMode ) {
- wfDebug( "BagOStuff debug: $text\n" );
- }
- }
-
- /**
- * Convert an optionally relative time to an absolute time
- */
- protected function convertExpiry( $exptime ) {
- if ( ( $exptime != 0 ) && ( $exptime < 86400 * 3650 /* 10 years */ ) ) {
- return time() + $exptime;
- } else {
- return $exptime;
- }
- }
-}
-
-/**
- * Functional versions!
- * This is a test of the interface, mainly. It stores things in an associative
- * array, which is not going to persist between program runs.
- *
- * @ingroup Cache
- */
-class HashBagOStuff extends BagOStuff {
- var $bag;
-
- function __construct() {
- $this->bag = array();
- }
-
- protected function expire( $key ) {
- $et = $this->bag[$key][1];
-
- if ( ( $et == 0 ) || ( $et > time() ) ) {
- return false;
- }
-
- $this->delete( $key );
-
- return true;
- }
-
- function get( $key ) {
- if ( !isset( $this->bag[$key] ) ) {
- return false;
- }
-
- if ( $this->expire( $key ) ) {
- return false;
- }
-
- return $this->bag[$key][0];
- }
-
- function set( $key, $value, $exptime = 0 ) {
- $this->bag[$key] = array( $value, $this->convertExpiry( $exptime ) );
- }
-
- function delete( $key, $time = 0 ) {
- if ( !isset( $this->bag[$key] ) ) {
- return false;
- }
-
- unset( $this->bag[$key] );
-
- return true;
- }
-
- function keys() {
- return array_keys( $this->bag );
- }
-}
-
-/**
- * Class to store objects in the database
- *
- * @ingroup Cache
- */
-class SqlBagOStuff extends BagOStuff {
- var $lb, $db;
- var $lastExpireAll = 0;
-
- protected function getDB() {
- global $wgDBtype;
-
- if ( !isset( $this->db ) ) {
- /* We must keep a separate connection to MySQL in order to avoid deadlocks
- * However, SQLite has an opposite behaviour.
- * @todo Investigate behaviour for other databases
- */
- if ( $wgDBtype == 'sqlite' ) {
- $this->db = wfGetDB( DB_MASTER );
- } else {
- $this->lb = wfGetLBFactory()->newMainLB();
- $this->db = $this->lb->getConnection( DB_MASTER );
- $this->db->clearFlag( DBO_TRX );
- }
- }
-
- return $this->db;
- }
-
- public function get( $key ) {
- # expire old entries if any
- $this->garbageCollect();
- $db = $this->getDB();
- $row = $db->selectRow( 'objectcache', array( 'value', 'exptime' ),
- array( 'keyname' => $key ), __METHOD__ );
-
- if ( !$row ) {
- $this->debug( 'get: no matching rows' );
- return false;
- }
-
- $this->debug( "get: retrieved data; expiry time is " . $row->exptime );
-
- if ( $this->isExpired( $row->exptime ) ) {
- $this->debug( "get: key has expired, deleting" );
- try {
- $db->begin();
- # Put the expiry time in the WHERE condition to avoid deleting a
- # newly-inserted value
- $db->delete( 'objectcache',
- array(
- 'keyname' => $key,
- 'exptime' => $row->exptime
- ), __METHOD__ );
- $db->commit();
- } catch ( DBQueryError $e ) {
- $this->handleWriteError( $e );
- }
-
- return false;
- }
-
- return $this->unserialize( $db->decodeBlob( $row->value ) );
- }
-
- public function set( $key, $value, $exptime = 0 ) {
- $db = $this->getDB();
- $exptime = intval( $exptime );
-
- if ( $exptime < 0 ) {
- $exptime = 0;
- }
-
- if ( $exptime == 0 ) {
- $encExpiry = $this->getMaxDateTime();
- } else {
- if ( $exptime < 3.16e8 ) { # ~10 years
- $exptime += time();
- }
-
- $encExpiry = $db->timestamp( $exptime );
- }
- try {
- $db->begin();
- // (bug 24425) use a replace if the db supports it instead of
- // delete/insert to avoid clashes with conflicting keynames
- $db->replace( 'objectcache', array( 'keyname' ),
- array(
- 'keyname' => $key,
- 'value' => $db->encodeBlob( $this->serialize( $value ) ),
- 'exptime' => $encExpiry
- ), __METHOD__ );
- $db->commit();
- } catch ( DBQueryError $e ) {
- $this->handleWriteError( $e );
-
- return false;
- }
-
- return true;
- }
-
- public function delete( $key, $time = 0 ) {
- $db = $this->getDB();
-
- try {
- $db->begin();
- $db->delete( 'objectcache', array( 'keyname' => $key ), __METHOD__ );
- $db->commit();
- } catch ( DBQueryError $e ) {
- $this->handleWriteError( $e );
-
- return false;
- }
-
- return true;
- }
-
- public function incr( $key, $step = 1 ) {
- $db = $this->getDB();
- $step = intval( $step );
-
- try {
- $db->begin();
- $row = $db->selectRow( 'objectcache', array( 'value', 'exptime' ),
- array( 'keyname' => $key ), __METHOD__, array( 'FOR UPDATE' ) );
- if ( $row === false ) {
- // Missing
- $db->commit();
-
- return null;
- }
- $db->delete( 'objectcache', array( 'keyname' => $key ), __METHOD__ );
- if ( $this->isExpired( $row->exptime ) ) {
- // Expired, do not reinsert
- $db->commit();
-
- return null;
- }
-
- $oldValue = intval( $this->unserialize( $db->decodeBlob( $row->value ) ) );
- $newValue = $oldValue + $step;
- $db->insert( 'objectcache',
- array(
- 'keyname' => $key,
- 'value' => $db->encodeBlob( $this->serialize( $newValue ) ),
- 'exptime' => $row->exptime
- ), __METHOD__, 'IGNORE' );
-
- if ( $db->affectedRows() == 0 ) {
- // Race condition. See bug 28611
- $newValue = null;
- }
- $db->commit();
- } catch ( DBQueryError $e ) {
- $this->handleWriteError( $e );
-
- return null;
- }
-
- return $newValue;
- }
-
- public function keys() {
- $db = $this->getDB();
- $res = $db->select( 'objectcache', array( 'keyname' ), false, __METHOD__ );
- $result = array();
-
- foreach ( $res as $row ) {
- $result[] = $row->keyname;
- }
-
- return $result;
- }
-
- protected function isExpired( $exptime ) {
- return $exptime != $this->getMaxDateTime() && wfTimestamp( TS_UNIX, $exptime ) < time();
- }
-
- protected function getMaxDateTime() {
- if ( time() > 0x7fffffff ) {
- return $this->getDB()->timestamp( 1 << 62 );
- } else {
- return $this->getDB()->timestamp( 0x7fffffff );
- }
- }
-
- protected function garbageCollect() {
- /* Ignore 99% of requests */
- if ( !mt_rand( 0, 100 ) ) {
- $now = time();
- /* Avoid repeating the delete within a few seconds */
- if ( $now > ( $this->lastExpireAll + 1 ) ) {
- $this->lastExpireAll = $now;
- $this->expireAll();
- }
- }
- }
-
- public function expireAll() {
- $db = $this->getDB();
- $now = $db->timestamp();
-
- try {
- $db->begin();
- $db->delete( 'objectcache', array( 'exptime < ' . $db->addQuotes( $now ) ), __METHOD__ );
- $db->commit();
- } catch ( DBQueryError $e ) {
- $this->handleWriteError( $e );
- }
- }
-
- public function deleteAll() {
- $db = $this->getDB();
-
- try {
- $db->begin();
- $db->delete( 'objectcache', '*', __METHOD__ );
- $db->commit();
- } catch ( DBQueryError $e ) {
- $this->handleWriteError( $e );
- }
- }
-
- /**
- * Serialize an object and, if possible, compress the representation.
- * On typical message and page data, this can provide a 3X decrease
- * in storage requirements.
- *
- * @param $data mixed
- * @return string
- */
- protected function serialize( &$data ) {
- $serial = serialize( $data );
-
- if ( function_exists( 'gzdeflate' ) ) {
- return gzdeflate( $serial );
- } else {
- return $serial;
- }
- }
-
- /**
- * Unserialize and, if necessary, decompress an object.
- * @param $serial string
- * @return mixed
- */
- protected function unserialize( $serial ) {
- if ( function_exists( 'gzinflate' ) ) {
- $decomp = @gzinflate( $serial );
-
- if ( false !== $decomp ) {
- $serial = $decomp;
- }
- }
-
- $ret = unserialize( $serial );
-
- return $ret;
- }
-
- /**
- * Handle a DBQueryError which occurred during a write operation.
- * Ignore errors which are due to a read-only database, rethrow others.
- */
- protected function handleWriteError( $exception ) {
- $db = $this->getDB();
-
- if ( !$db->wasReadOnlyError() ) {
- throw $exception;
- }
-
- try {
- $db->rollback();
- } catch ( DBQueryError $e ) {
- }
-
- wfDebug( __METHOD__ . ": ignoring query error\n" );
- $db->ignoreErrors( false );
- }
-}
-
-/**
- * Backwards compatibility alias
- */
-class MediaWikiBagOStuff extends SqlBagOStuff { }
-
-/**
- * This is a wrapper for APC's shared memory functions
- *
- * @ingroup Cache
- */
-class APCBagOStuff extends BagOStuff {
- public function get( $key ) {
- $val = apc_fetch( $key );
-
- if ( is_string( $val ) ) {
- $val = unserialize( $val );
- }
-
- return $val;
- }
-
- public function set( $key, $value, $exptime = 0 ) {
- apc_store( $key, serialize( $value ), $exptime );
-
- return true;
- }
-
- public function delete( $key, $time = 0 ) {
- apc_delete( $key );
-
- return true;
- }
-
- public function keys() {
- $info = apc_cache_info( 'user' );
- $list = $info['cache_list'];
- $keys = array();
-
- foreach ( $list as $entry ) {
- $keys[] = $entry['info'];
- }
-
- return $keys;
- }
-}
-
-/**
- * This is a wrapper for eAccelerator's shared memory functions.
- *
- * This is basically identical to the deceased Turck MMCache version,
- * mostly because eAccelerator is based on Turck MMCache.
- *
- * @ingroup Cache
- */
-class eAccelBagOStuff extends BagOStuff {
- public function get( $key ) {
- $val = eaccelerator_get( $key );
-
- if ( is_string( $val ) ) {
- $val = unserialize( $val );
- }
-
- return $val;
- }
-
- public function set( $key, $value, $exptime = 0 ) {
- eaccelerator_put( $key, serialize( $value ), $exptime );
-
- return true;
- }
-
- public function delete( $key, $time = 0 ) {
- eaccelerator_rm( $key );
-
- return true;
- }
-
- public function lock( $key, $waitTimeout = 0 ) {
- eaccelerator_lock( $key );
-
- return true;
- }
-
- public function unlock( $key ) {
- eaccelerator_unlock( $key );
-
- return true;
- }
-}
-
-/**
- * Wrapper for XCache object caching functions; identical interface
- * to the APC wrapper
- *
- * @ingroup Cache
- */
-class XCacheBagOStuff extends BagOStuff {
- /**
- * Get a value from the XCache object cache
- *
- * @param $key String: cache key
- * @return mixed
- */
- public function get( $key ) {
- $val = xcache_get( $key );
-
- if ( is_string( $val ) ) {
- $val = unserialize( $val );
- }
-
- return $val;
- }
-
- /**
- * Store a value in the XCache object cache
- *
- * @param $key String: cache key
- * @param $value Mixed: object to store
- * @param $expire Int: expiration time
- * @return bool
- */
- public function set( $key, $value, $expire = 0 ) {
- xcache_set( $key, serialize( $value ), $expire );
-
- return true;
- }
-
- /**
- * Remove a value from the XCache object cache
- *
- * @param $key String: cache key
- * @param $time Int: not used in this implementation
- * @return bool
- */
- public function delete( $key, $time = 0 ) {
- xcache_unset( $key );
-
- return true;
- }
-}
-
-/**
- * Cache that uses DBA as a backend.
- * Slow due to the need to constantly open and close the file to avoid holding
- * writer locks. Intended for development use only, as a memcached workalike
- * for systems that don't have it.
- *
- * @ingroup Cache
- */
-class DBABagOStuff extends BagOStuff {
- var $mHandler, $mFile, $mReader, $mWriter, $mDisabled;
-
- public function __construct( $dir = false ) {
- global $wgDBAhandler;
-
- if ( $dir === false ) {
- global $wgTmpDirectory;
- $dir = $wgTmpDirectory;
- }
-
- $this->mFile = "$dir/mw-cache-" . wfWikiID();
- $this->mFile .= '.db';
- wfDebug( __CLASS__ . ": using cache file {$this->mFile}\n" );
- $this->mHandler = $wgDBAhandler;
- }
-
- /**
- * Encode value and expiry for storage
- */
- function encode( $value, $expiry ) {
- # Convert to absolute time
- $expiry = $this->convertExpiry( $expiry );
-
- return sprintf( '%010u', intval( $expiry ) ) . ' ' . serialize( $value );
- }
-
- /**
- * @return list containing value first and expiry second
- */
- function decode( $blob ) {
- if ( !is_string( $blob ) ) {
- return array( null, 0 );
- } else {
- return array(
- unserialize( substr( $blob, 11 ) ),
- intval( substr( $blob, 0, 10 ) )
- );
- }
- }
-
- function getReader() {
- if ( file_exists( $this->mFile ) ) {
- $handle = dba_open( $this->mFile, 'rl', $this->mHandler );
- } else {
- $handle = $this->getWriter();
- }
-
- if ( !$handle ) {
- wfDebug( "Unable to open DBA cache file {$this->mFile}\n" );
- }
-
- return $handle;
- }
-
- function getWriter() {
- $handle = dba_open( $this->mFile, 'cl', $this->mHandler );
-
- if ( !$handle ) {
- wfDebug( "Unable to open DBA cache file {$this->mFile}\n" );
- }
-
- return $handle;
- }
-
- function get( $key ) {
- wfProfileIn( __METHOD__ );
- wfDebug( __METHOD__ . "($key)\n" );
-
- $handle = $this->getReader();
- if ( !$handle ) {
- wfProfileOut( __METHOD__ );
- return null;
- }
-
- $val = dba_fetch( $key, $handle );
- list( $val, $expiry ) = $this->decode( $val );
-
- # Must close ASAP because locks are held
- dba_close( $handle );
-
- if ( !is_null( $val ) && $expiry && $expiry < time() ) {
- # Key is expired, delete it
- $handle = $this->getWriter();
- dba_delete( $key, $handle );
- dba_close( $handle );
- wfDebug( __METHOD__ . ": $key expired\n" );
- $val = null;
- }
-
- wfProfileOut( __METHOD__ );
- return $val;
- }
-
- function set( $key, $value, $exptime = 0 ) {
- wfProfileIn( __METHOD__ );
- wfDebug( __METHOD__ . "($key)\n" );
-
- $blob = $this->encode( $value, $exptime );
-
- $handle = $this->getWriter();
- if ( !$handle ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- $ret = dba_replace( $key, $blob, $handle );
- dba_close( $handle );
-
- wfProfileOut( __METHOD__ );
- return $ret;
- }
-
- function delete( $key, $time = 0 ) {
- wfProfileIn( __METHOD__ );
- wfDebug( __METHOD__ . "($key)\n" );
-
- $handle = $this->getWriter();
- if ( !$handle ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- $ret = dba_delete( $key, $handle );
- dba_close( $handle );
-
- wfProfileOut( __METHOD__ );
- return $ret;
- }
-
- function add( $key, $value, $exptime = 0 ) {
- wfProfileIn( __METHOD__ );
-
- $blob = $this->encode( $value, $exptime );
-
- $handle = $this->getWriter();
-
- if ( !$handle ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- $ret = dba_insert( $key, $blob, $handle );
-
- # Insert failed, check to see if it failed due to an expired key
- if ( !$ret ) {
- list( $value, $expiry ) = $this->decode( dba_fetch( $key, $handle ) );
-
- if ( $expiry < time() ) {
- # Yes expired, delete and try again
- dba_delete( $key, $handle );
- $ret = dba_insert( $key, $blob, $handle );
- # This time if it failed then it will be handled by the caller like any other race
- }
- }
-
- dba_close( $handle );
-
- wfProfileOut( __METHOD__ );
- return $ret;
- }
-
- function keys() {
- $reader = $this->getReader();
- $k1 = dba_firstkey( $reader );
-
- if ( !$k1 ) {
- return array();
- }
-
- $result[] = $k1;
-
- while ( $key = dba_nextkey( $reader ) ) {
- $result[] = $key;
- }
-
- return $result;
- }
-}
-
-/**
- * Wrapper for WinCache object caching functions; identical interface
- * to the APC wrapper
- *
- * @ingroup Cache
- */
-class WinCacheBagOStuff extends BagOStuff {
-
- /**
- * Get a value from the WinCache object cache
- *
- * @param $key String: cache key
- * @return mixed
- */
- public function get( $key ) {
- $val = wincache_ucache_get( $key );
-
- if ( is_string( $val ) ) {
- $val = unserialize( $val );
- }
-
- return $val;
- }
-
- /**
- * Store a value in the WinCache object cache
- *
- * @param $key String: cache key
- * @param $value Mixed: object to store
- * @param $expire Int: expiration time
- * @return bool
- */
- public function set( $key, $value, $expire = 0 ) {
- wincache_ucache_set( $key, serialize( $value ), $expire );
-
- return true;
- }
-
- /**
- * Remove a value from the WinCache object cache
- *
- * @param $key String: cache key
- * @param $time Int: not used in this implementation
- * @return bool
- */
- public function delete( $key, $time = 0 ) {
- wincache_ucache_delete( $key );
-
- return true;
- }
-
- public function keys() {
- $info = wincache_ucache_info();
- $list = $info['ucache_entries'];
- $keys = array();
-
- foreach ( $list as $entry ) {
- $keys[] = $entry['key_name'];
- }
-
- return $keys;
- }
-}
diff --git a/includes/Block.php b/includes/Block.php
index 7c5f0ddd..27181d86 100644
--- a/includes/Block.php
+++ b/includes/Block.php
@@ -1,53 +1,93 @@
<?php
/**
- * @file
* Blocks and bans object
- */
-
-/**
- * The block class
- * All the functions in this class assume the object is either explicitly
- * loaded or filled. It is not load-on-demand. There are no accessors.
*
- * Globals used: $wgAutoblockExpiry, $wgAntiLockFlags
+ * 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.
*
- * @todo This could be used everywhere, but it isn't.
+ * 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 Block {
- /* public*/ var $mAddress, $mUser, $mBy, $mReason, $mTimestamp, $mAuto, $mId, $mExpiry,
- $mRangeStart, $mRangeEnd, $mAnonOnly, $mEnableAutoblock, $mHideName,
- $mBlockEmail, $mByName, $mAngryAutoblock, $mAllowUsertalk;
- /* private */ var $mNetworkBits, $mIntegerAddr, $mForUpdate, $mFromMaster;
+ /* public*/ var $mReason, $mTimestamp, $mAuto, $mExpiry, $mHideName;
+
+ protected
+ $mId,
+ $mFromMaster,
- const EB_KEEP_EXPIRED = 1;
- const EB_FOR_UPDATE = 2;
- const EB_RANGE_ONLY = 4;
+ $mBlockEmail,
+ $mDisableUsertalk,
+ $mCreateAccount;
+ /// @var User|String
+ protected $target;
+
+ /// @var Block::TYPE_ constant. Can only be USER, IP or RANGE internally
+ protected $type;
+
+ /// @var User
+ protected $blocker;
+
+ /// @var Bool
+ protected $isHardblock = true;
+
+ /// @var Bool
+ protected $isAutoblocking = true;
+
+ # TYPE constants
+ const TYPE_USER = 1;
+ const TYPE_IP = 2;
+ const TYPE_RANGE = 3;
+ const TYPE_AUTO = 4;
+ const TYPE_ID = 5;
+
+ /**
+ * Constructor
+ * @todo FIXME: Don't know what the best format to have for this constructor is, but fourteen
+ * optional parameters certainly isn't it.
+ */
function __construct( $address = '', $user = 0, $by = 0, $reason = '',
$timestamp = 0, $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0,
- $hideName = 0, $blockEmail = 0, $allowUsertalk = 0, $byName = false )
+ $hideName = 0, $blockEmail = 0, $allowUsertalk = 0 )
{
- $this->mId = 0;
- # Expand valid IPv6 addresses
- $address = IP::sanitizeIP( $address );
- $this->mAddress = $address;
- $this->mUser = $user;
- $this->mBy = $by;
+ if( $timestamp === 0 ){
+ $timestamp = wfTimestampNow();
+ }
+
+ if( count( func_get_args() ) > 0 ){
+ # Soon... :D
+ # wfDeprecated( __METHOD__ . " with arguments" );
+ }
+
+ $this->setTarget( $address );
+ $this->setBlocker( User::newFromID( $by ) );
$this->mReason = $reason;
$this->mTimestamp = wfTimestamp( TS_MW, $timestamp );
$this->mAuto = $auto;
- $this->mAnonOnly = $anonOnly;
- $this->mCreateAccount = $createAccount;
- $this->mExpiry = self::decodeExpiry( $expiry );
- $this->mEnableAutoblock = $enableAutoblock;
+ $this->isHardblock( !$anonOnly );
+ $this->prevents( 'createaccount', $createAccount );
+ if ( $expiry == 'infinity' || $expiry == Block::infinity() ) {
+ $this->mExpiry = 'infinity';
+ } else {
+ $this->mExpiry = wfTimestamp( TS_MW, $expiry );
+ }
+ $this->isAutoblocking( $enableAutoblock );
$this->mHideName = $hideName;
- $this->mBlockEmail = $blockEmail;
- $this->mAllowUsertalk = $allowUsertalk;
- $this->mForUpdate = false;
+ $this->prevents( 'sendemail', $blockEmail );
+ $this->prevents( 'editownusertalk', !$allowUsertalk );
+
$this->mFromMaster = false;
- $this->mByName = $byName;
- $this->mAngryAutoblock = false;
- $this->initialiseRange();
}
/**
@@ -57,56 +97,54 @@ class Block {
*
* @param $address String: IP address of user/anon
* @param $user Integer: user id of user
- * @param $killExpired Boolean: delete expired blocks on load
* @return Block Object
+ * @deprecated since 1.18
*/
- public static function newFromDB( $address, $user = 0, $killExpired = true ) {
- $block = new Block;
- $block->load( $address, $user, $killExpired );
-
- if ( $block->isValid() ) {
- return $block;
- } else {
- return null;
- }
+ public static function newFromDB( $address, $user = 0 ) {
+ return self::newFromTarget( User::whoIs( $user ), $address );
}
/**
* Load a blocked user from their block id.
*
* @param $id Integer: Block id to search for
- * @return Block object
+ * @return Block object or null
*/
public static function newFromID( $id ) {
$dbr = wfGetDB( DB_SLAVE );
- $res = $dbr->resultObject( $dbr->select( 'ipblocks', '*',
- array( 'ipb_id' => $id ), __METHOD__ ) );
- $block = new Block;
-
- if ( $block->loadFromResult( $res ) ) {
- return $block;
+ $res = $dbr->selectRow(
+ 'ipblocks',
+ '*',
+ array( 'ipb_id' => $id ),
+ __METHOD__
+ );
+ if ( $res ) {
+ return Block::newFromRow( $res );
} else {
return null;
}
}
/**
- * Check if two blocks are effectively equal
+ * Check if two blocks are effectively equal. Doesn't check irrelevant things like
+ * the blocking user or the block timestamp, only things which affect the blocked user *
*
- * @return Boolean
+ * @param $block Block
+ *
+ * @return bool
*/
public function equals( Block $block ) {
return (
- $this->mAddress == $block->mAddress
- && $this->mUser == $block->mUser
+ (string)$this->target == (string)$block->target
+ && $this->type == $block->type
&& $this->mAuto == $block->mAuto
- && $this->mAnonOnly == $block->mAnonOnly
- && $this->mCreateAccount == $block->mCreateAccount
+ && $this->isHardblock() == $block->isHardblock()
+ && $this->prevents( 'createaccount' ) == $block->prevents( 'createaccount' )
&& $this->mExpiry == $block->mExpiry
- && $this->mEnableAutoblock == $block->mEnableAutoblock
+ && $this->isAutoblocking() == $block->isAutoblocking()
&& $this->mHideName == $block->mHideName
- && $this->mBlockEmail == $block->mBlockEmail
- && $this->mAllowUsertalk == $block->mAllowUsertalk
+ && $this->prevents( 'sendemail' ) == $block->prevents( 'sendemail' )
+ && $this->prevents( 'editownusertalk' ) == $block->prevents( 'editownusertalk' )
&& $this->mReason == $block->mReason
);
}
@@ -114,251 +152,232 @@ class Block {
/**
* Clear all member variables in the current object. Does not clear
* the block from the DB.
+ * @deprecated since 1.18
*/
public function clear() {
- $this->mAddress = $this->mReason = $this->mTimestamp = '';
- $this->mId = $this->mAnonOnly = $this->mCreateAccount =
- $this->mEnableAutoblock = $this->mAuto = $this->mUser =
- $this->mBy = $this->mHideName = $this->mBlockEmail = $this->mAllowUsertalk = 0;
- $this->mByName = false;
+ # Noop
}
/**
- * Get the DB object and set the reference parameter to the select options.
- * The options array will contain FOR UPDATE if appropriate.
+ * Get a block from the DB, with either the given address or the given username
*
- * @param $options Array
- * @return Database
+ * @param $address string The IP address of the user, or blank to skip IP blocks
+ * @param $user int The user ID, or zero for anonymous users
+ * @return Boolean: the user is blocked from editing
+ * @deprecated since 1.18
*/
- protected function &getDBOptions( &$options ) {
- global $wgAntiLockFlags;
+ public function load( $address = '', $user = 0 ) {
+ wfDeprecated( __METHOD__ );
+ if( $user ){
+ $username = User::whoIs( $user );
+ $block = self::newFromTarget( $username, $address );
+ } else {
+ $block = self::newFromTarget( null, $address );
+ }
- if ( $this->mForUpdate || $this->mFromMaster ) {
- $db = wfGetDB( DB_MASTER );
- if ( !$this->mForUpdate || ( $wgAntiLockFlags & ALF_NO_BLOCK_LOCK ) ) {
- $options = array();
- } else {
- $options = array( 'FOR UPDATE' );
+ if( $block instanceof Block ){
+ # This is mildly evil, but hey, it's B/C :D
+ foreach( $block as $variable => $value ){
+ $this->$variable = $value;
}
+ return true;
} else {
- $db = wfGetDB( DB_SLAVE );
- $options = array();
+ return false;
}
-
- return $db;
}
/**
- * Get a block from the DB, with either the given address or the given username
- *
- * @param $address string The IP address of the user, or blank to skip IP blocks
- * @param $user int The user ID, or zero for anonymous users
- * @param $killExpired bool Whether to delete expired rows while loading
- * @return Boolean: the user is blocked from editing
- *
+ * Load a block from the database which affects the already-set $this->target:
+ * 1) A block directly on the given user or IP
+ * 2) A rangeblock encompasing the given IP (smallest first)
+ * 3) An autoblock on the given IP
+ * @param $vagueTarget User|String also search for blocks affecting this target. Doesn't
+ * make any sense to use TYPE_AUTO / TYPE_ID here. Leave blank to skip IP lookups.
+ * @return Bool whether a relevant block was found
*/
- public function load( $address = '', $user = 0, $killExpired = true ) {
- wfDebug( "Block::load: '$address', '$user', $killExpired\n" );
+ protected function newLoad( $vagueTarget = null ) {
+ $db = wfGetDB( $this->mFromMaster ? DB_MASTER : DB_SLAVE );
- $options = array();
- $db = $this->getDBOptions( $options );
-
- if ( 0 == $user && $address === '' ) {
- # Invalid user specification, not blocked
- $this->clear();
-
- return false;
+ if( $this->type !== null ){
+ $conds = array(
+ 'ipb_address' => array( (string)$this->target ),
+ );
+ } else {
+ $conds = array( 'ipb_address' => array() );
}
- # Try user block
- if ( $user ) {
- $res = $db->resultObject( $db->select( 'ipblocks', '*', array( 'ipb_user' => $user ),
- __METHOD__, $options ) );
-
- if ( $this->loadFromResult( $res, $killExpired ) ) {
- return true;
+ # Be aware that the != '' check is explicit, since empty values will be
+ # passed by some callers (bug 29116)
+ if( $vagueTarget != ''){
+ list( $target, $type ) = self::parseTarget( $vagueTarget );
+ switch( $type ) {
+ case self::TYPE_USER:
+ # Slightly wierd, but who are we to argue?
+ $conds['ipb_address'][] = (string)$target;
+ break;
+
+ case self::TYPE_IP:
+ $conds['ipb_address'][] = (string)$target;
+ $conds[] = self::getRangeCond( IP::toHex( $target ) );
+ $conds = $db->makeList( $conds, LIST_OR );
+ break;
+
+ case self::TYPE_RANGE:
+ list( $start, $end ) = IP::parseRange( $target );
+ $conds['ipb_address'][] = (string)$target;
+ $conds[] = self::getRangeCond( $start, $end );
+ $conds = $db->makeList( $conds, LIST_OR );
+ break;
+
+ default:
+ throw new MWException( "Tried to load block with invalid type" );
}
}
- # Try IP block
- # TODO: improve performance by merging this query with the autoblock one
- # Slightly tricky while handling killExpired as well
- if ( $address !== '' ) {
- $conds = array( 'ipb_address' => $address, 'ipb_auto' => 0 );
- $res = $db->resultObject( $db->select( 'ipblocks', '*', $conds, __METHOD__, $options ) );
-
- if ( $this->loadFromResult( $res, $killExpired ) ) {
- if ( $user && $this->mAnonOnly ) {
- # Block is marked anon-only
- # Whitelist this IP address against autoblocks and range blocks
- # (but not account creation blocks -- bug 13611)
- if ( !$this->mCreateAccount ) {
- $this->clear();
- }
-
- return false;
- } else {
- return true;
- }
- }
- }
+ $res = $db->select( 'ipblocks', '*', $conds, __METHOD__ );
- # Try range block
- if ( $this->loadRange( $address, $killExpired, $user ) ) {
- if ( $user && $this->mAnonOnly ) {
- # Respect account creation blocks on logged-in users -- bug 13611
- if ( !$this->mCreateAccount ) {
- $this->clear();
- }
+ # This result could contain a block on the user, a block on the IP, and a russian-doll
+ # set of rangeblocks. We want to choose the most specific one, so keep a leader board.
+ $bestRow = null;
- return false;
- } else {
- return true;
- }
- }
+ # Lower will be better
+ $bestBlockScore = 100;
- # Try autoblock
- if ( $address ) {
- $conds = array( 'ipb_address' => $address, 'ipb_auto' => 1 );
+ # This is begging for $this = $bestBlock, but that's not allowed in PHP :(
+ $bestBlockPreventsEdit = null;
- if ( $user ) {
- $conds['ipb_anon_only'] = 0;
- }
+ foreach( $res as $row ){
+ $block = Block::newFromRow( $row );
- $res = $db->resultObject( $db->select( 'ipblocks', '*', $conds, __METHOD__, $options ) );
+ # Don't use expired blocks
+ if( $block->deleteIfExpired() ){
+ continue;
+ }
- if ( $this->loadFromResult( $res, $killExpired ) ) {
- return true;
+ # Don't use anon only blocks on users
+ if( $this->type == self::TYPE_USER && !$block->isHardblock() ){
+ continue;
}
- }
- # Give up
- $this->clear();
- return false;
- }
+ 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 );
+ $start = wfBaseconvert( $block->getRangeStart(), 16, 10 );
+ $size = log( $end - $start + 1, 2 );
+
+ # This has the nice property that a /32 block is ranked equally with a
+ # single-IP block, which is exactly what it is...
+ $score = self::TYPE_RANGE - 1 + ( $size / 128 );
- /**
- * Fill in member variables from a result wrapper
- *
- * @param $res ResultWrapper: row from the ipblocks table
- * @param $killExpired Boolean: whether to delete expired rows while loading
- * @return Boolean
- */
- protected function loadFromResult( ResultWrapper $res, $killExpired = true ) {
- $ret = false;
-
- if ( 0 != $res->numRows() ) {
- # Get first block
- $row = $res->fetchObject();
- $this->initFromRow( $row );
-
- if ( $killExpired ) {
- # If requested, delete expired rows
- do {
- $killed = $this->deleteIfExpired();
- if ( $killed ) {
- $row = $res->fetchObject();
- if ( $row ) {
- $this->initFromRow( $row );
- }
- }
- } while ( $killed && $row );
-
- # If there were any left after the killing finished, return true
- if ( $row ) {
- $ret = true;
- }
} else {
- $ret = true;
+ $score = $block->getType();
+ }
+
+ if( $score < $bestBlockScore ){
+ $bestBlockScore = $score;
+ $bestRow = $row;
+ $bestBlockPreventsEdit = $block->prevents( 'edit' );
}
}
- $res->free();
- return $ret;
+ if( $bestRow !== null ){
+ $this->initFromRow( $bestRow );
+ $this->prevents( 'edit', $bestBlockPreventsEdit );
+ return true;
+ } else {
+ return false;
+ }
}
/**
- * Search the database for any range blocks matching the given address, and
- * load the row if one is found.
- *
- * @param $address String: IP address range
- * @param $killExpired Boolean: whether to delete expired rows while loading
- * @param $user Integer: if not 0, then sets ipb_anon_only
- * @return Boolean
+ * Get a set of SQL conditions which will select rangeblocks encompasing a given range
+ * @param $start String Hexadecimal IP representation
+ * @param $end String Hexadecimal IP represenation, or null to use $start = $end
+ * @return String
*/
- public function loadRange( $address, $killExpired = true, $user = 0 ) {
- $iaddr = IP::toHex( $address );
-
- if ( $iaddr === false ) {
- # Invalid address
- return false;
+ public static function getRangeCond( $start, $end = null ) {
+ if ( $end === null ) {
+ $end = $start;
}
+ # Per bug 14634, we want to include relevant active rangeblocks; for
+ # rangeblocks, we want to include larger ranges which enclose the given
+ # range. We know that all blocks must be smaller than $wgBlockCIDRLimit,
+ # so we can improve performance by filtering on a LIKE clause
+ $chunk = self::getIpFragment( $start );
+ $dbr = wfGetDB( DB_SLAVE );
+ $like = $dbr->buildLike( $chunk, $dbr->anyString() );
- # Only scan ranges which start in this /16, this improves search speed
- # Blocks should not cross a /16 boundary.
- $range = substr( $iaddr, 0, 4 );
+ # Fairly hard to make a malicious SQL statement out of hex characters,
+ # but stranger things have happened...
+ $safeStart = $dbr->addQuotes( $start );
+ $safeEnd = $dbr->addQuotes( $end );
- $options = array();
- $db = $this->getDBOptions( $options );
- $conds = array(
- 'ipb_range_start' . $db->buildLike( $range, $db->anyString() ),
- "ipb_range_start <= '$iaddr'",
- "ipb_range_end >= '$iaddr'"
+ return $dbr->makeList(
+ array(
+ "ipb_range_start $like",
+ "ipb_range_start <= $safeStart",
+ "ipb_range_end >= $safeEnd",
+ ),
+ LIST_AND
);
+ }
- if ( $user ) {
- $conds['ipb_anon_only'] = 0;
+ /**
+ * Get the component of an IP address which is certain to be the same between an IP
+ * address and a rangeblock containing that IP address.
+ * @param $hex String Hexadecimal IP representation
+ * @return String
+ */
+ protected static function getIpFragment( $hex ) {
+ global $wgBlockCIDRLimit;
+ if ( substr( $hex, 0, 3 ) == 'v6-' ) {
+ return 'v6-' . substr( substr( $hex, 3 ), 0, floor( $wgBlockCIDRLimit['IPv6'] / 4 ) );
+ } else {
+ return substr( $hex, 0, floor( $wgBlockCIDRLimit['IPv4'] / 4 ) );
}
-
- $res = $db->resultObject( $db->select( 'ipblocks', '*', $conds, __METHOD__, $options ) );
- $success = $this->loadFromResult( $res, $killExpired );
-
- return $success;
}
/**
* Given a database row from the ipblocks table, initialize
* member variables
- *
* @param $row ResultWrapper: a row from the ipblocks table
*/
- public function initFromRow( $row ) {
- $this->mAddress = $row->ipb_address;
+ protected function initFromRow( $row ) {
+ $this->setTarget( $row->ipb_address );
+ $this->setBlocker( User::newFromId( $row->ipb_by ) );
+
$this->mReason = $row->ipb_reason;
$this->mTimestamp = wfTimestamp( TS_MW, $row->ipb_timestamp );
- $this->mUser = $row->ipb_user;
- $this->mBy = $row->ipb_by;
$this->mAuto = $row->ipb_auto;
- $this->mAnonOnly = $row->ipb_anon_only;
- $this->mCreateAccount = $row->ipb_create_account;
- $this->mEnableAutoblock = $row->ipb_enable_autoblock;
- $this->mBlockEmail = $row->ipb_block_email;
- $this->mAllowUsertalk = $row->ipb_allow_usertalk;
$this->mHideName = $row->ipb_deleted;
$this->mId = $row->ipb_id;
- $this->mExpiry = self::decodeExpiry( $row->ipb_expiry );
- if ( isset( $row->user_name ) ) {
- $this->mByName = $row->user_name;
+ // I wish I didn't have to do this
+ $db = wfGetDB( DB_SLAVE );
+ if ( $row->ipb_expiry == $db->getInfinity() ) {
+ $this->mExpiry = 'infinity';
} else {
- $this->mByName = $row->ipb_by_text;
+ $this->mExpiry = wfTimestamp( TS_MW, $row->ipb_expiry );
}
- $this->mRangeStart = $row->ipb_range_start;
- $this->mRangeEnd = $row->ipb_range_end;
+ $this->isHardblock( !$row->ipb_anon_only );
+ $this->isAutoblocking( $row->ipb_enable_autoblock );
+
+ $this->prevents( 'createaccount', $row->ipb_create_account );
+ $this->prevents( 'sendemail', $row->ipb_block_email );
+ $this->prevents( 'editownusertalk', !$row->ipb_allow_usertalk );
}
/**
- * Once $mAddress has been set, get the range they came from.
- * Wrapper for IP::parseRange
+ * Create a new Block object from a database row
+ * @param $row ResultWrapper row from the ipblocks table
+ * @return Block
*/
- protected function initialiseRange() {
- $this->mRangeStart = '';
- $this->mRangeEnd = '';
-
- if ( $this->mUser == 0 ) {
- list( $this->mRangeStart, $this->mRangeEnd ) = IP::parseRange( $this->mAddress );
- }
+ public static function newFromRow( $row ){
+ $block = new Block;
+ $block->initFromRow( $row );
+ return $block;
}
/**
@@ -371,12 +390,12 @@ class Block {
return false;
}
- if ( !$this->mId ) {
- throw new MWException( "Block::delete() now requires that the mId member be filled\n" );
+ if ( !$this->getId() ) {
+ throw new MWException( "Block::delete() requires that the mId member be filled\n" );
}
$dbw = wfGetDB( DB_MASTER );
- $dbw->delete( 'ipblocks', array( 'ipb_id' => $this->mId ), __METHOD__ );
+ $dbw->delete( 'ipblocks', array( 'ipb_id' => $this->getId() ), __METHOD__ );
return $dbw->affectedRows() > 0;
}
@@ -385,16 +404,16 @@ class Block {
* Insert a block into the block table. Will fail if there is a conflicting
* block (same name and options) already in the database.
*
- * @return Boolean: whether or not the insertion was successful.
+ * @param $dbw DatabaseBase if you have one available
+ * @return mixed: false on failure, assoc array on success:
+ * ('id' => block ID, 'autoIds' => array of autoblock IDs)
*/
public function insert( $dbw = null ) {
wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" );
- if ( $dbw === null )
+ if ( $dbw === null ) {
$dbw = wfGetDB( DB_MASTER );
-
- $this->validateBlockParams();
- $this->initialiseRange();
+ }
# Don't collide with expired blocks
Block::purgeExpired();
@@ -402,139 +421,125 @@ class Block {
$ipb_id = $dbw->nextSequenceValue( 'ipblocks_ipb_id_seq' );
$dbw->insert(
'ipblocks',
- array(
- 'ipb_id' => $ipb_id,
- 'ipb_address' => $this->mAddress,
- 'ipb_user' => $this->mUser,
- 'ipb_by' => $this->mBy,
- 'ipb_by_text' => $this->mByName,
- 'ipb_reason' => $this->mReason,
- 'ipb_timestamp' => $dbw->timestamp( $this->mTimestamp ),
- 'ipb_auto' => $this->mAuto,
- 'ipb_anon_only' => $this->mAnonOnly,
- 'ipb_create_account' => $this->mCreateAccount,
- 'ipb_enable_autoblock' => $this->mEnableAutoblock,
- 'ipb_expiry' => self::encodeExpiry( $this->mExpiry, $dbw ),
- 'ipb_range_start' => $this->mRangeStart,
- 'ipb_range_end' => $this->mRangeEnd,
- 'ipb_deleted' => intval( $this->mHideName ), // typecast required for SQLite
- 'ipb_block_email' => $this->mBlockEmail,
- 'ipb_allow_usertalk' => $this->mAllowUsertalk
- ),
- 'Block::insert',
+ $this->getDatabaseArray(),
+ __METHOD__,
array( 'IGNORE' )
);
$affected = $dbw->affectedRows();
+ $this->mId = $dbw->insertId();
- if ( $affected )
- $this->doRetroactiveAutoblock();
+ if ( $affected ) {
+ $auto_ipd_ids = $this->doRetroactiveAutoblock();
+ return array( 'id' => $this->mId, 'autoIds' => $auto_ipd_ids );
+ }
- return (bool)$affected;
+ return false;
}
/**
* Update a block in the DB with new parameters.
* The ID field needs to be loaded first.
+ *
+ * @return Int number of affected rows, which should probably be 1 or something's
+ * gone slightly awry
*/
public function update() {
wfDebug( "Block::update; timestamp {$this->mTimestamp}\n" );
$dbw = wfGetDB( DB_MASTER );
- $this->validateBlockParams();
-
$dbw->update(
'ipblocks',
- array(
- 'ipb_user' => $this->mUser,
- 'ipb_by' => $this->mBy,
- 'ipb_by_text' => $this->mByName,
- 'ipb_reason' => $this->mReason,
- 'ipb_timestamp' => $dbw->timestamp( $this->mTimestamp ),
- 'ipb_auto' => $this->mAuto,
- 'ipb_anon_only' => $this->mAnonOnly,
- 'ipb_create_account' => $this->mCreateAccount,
- 'ipb_enable_autoblock' => $this->mEnableAutoblock,
- 'ipb_expiry' => self::encodeExpiry( $this->mExpiry, $dbw ),
- 'ipb_range_start' => $this->mRangeStart,
- 'ipb_range_end' => $this->mRangeEnd,
- 'ipb_deleted' => $this->mHideName,
- 'ipb_block_email' => $this->mBlockEmail,
- 'ipb_allow_usertalk' => $this->mAllowUsertalk
- ),
- array( 'ipb_id' => $this->mId ),
- 'Block::update'
+ $this->getDatabaseArray( $dbw ),
+ array( 'ipb_id' => $this->getId() ),
+ __METHOD__
);
return $dbw->affectedRows();
}
/**
- * Make sure all the proper members are set to sane values
- * before adding/updating a block
+ * Get an array suitable for passing to $dbw->insert() or $dbw->update()
+ * @param $db DatabaseBase
+ * @return Array
*/
- protected function validateBlockParams() {
- # Unset ipb_anon_only for user blocks, makes no sense
- if ( $this->mUser ) {
- $this->mAnonOnly = 0;
+ protected function getDatabaseArray( $db = null ){
+ if( !$db ){
+ $db = wfGetDB( DB_SLAVE );
}
+ $expiry = $db->encodeExpiry( $this->mExpiry );
+
+ $a = array(
+ 'ipb_address' => (string)$this->target,
+ 'ipb_user' => $this->target instanceof User ? $this->target->getID() : 0,
+ 'ipb_by' => $this->getBlocker()->getId(),
+ 'ipb_by_text' => $this->getBlocker()->getName(),
+ 'ipb_reason' => $this->mReason,
+ 'ipb_timestamp' => $db->timestamp( $this->mTimestamp ),
+ 'ipb_auto' => $this->mAuto,
+ 'ipb_anon_only' => !$this->isHardblock(),
+ 'ipb_create_account' => $this->prevents( 'createaccount' ),
+ 'ipb_enable_autoblock' => $this->isAutoblocking(),
+ 'ipb_expiry' => $expiry,
+ 'ipb_range_start' => $this->getRangeStart(),
+ 'ipb_range_end' => $this->getRangeEnd(),
+ 'ipb_deleted' => intval( $this->mHideName ), // typecast required for SQLite
+ 'ipb_block_email' => $this->prevents( 'sendemail' ),
+ 'ipb_allow_usertalk' => !$this->prevents( 'editownusertalk' )
+ );
- # Unset ipb_enable_autoblock for IP blocks, makes no sense
- if ( !$this->mUser ) {
- $this->mEnableAutoblock = 0;
- }
+ return $a;
+ }
- # bug 18860: non-anon-only IP blocks should be allowed to block email
- if ( !$this->mUser && $this->mAnonOnly ) {
- $this->mBlockEmail = 0;
- }
+ /**
+ * Retroactively autoblocks the last IP used by the user (if it is a user)
+ * blocked by this Block.
+ *
+ * @return Array: block IDs of retroactive autoblocks made
+ */
+ protected function doRetroactiveAutoblock() {
+ $blockIds = array();
+ # If autoblock is enabled, autoblock the LAST IP(s) used
+ if ( $this->isAutoblocking() && $this->getType() == self::TYPE_USER ) {
+ wfDebug( "Doing retroactive autoblocks for " . $this->getTarget() . "\n" );
- if ( !$this->mByName ) {
- if ( $this->mBy ) {
- $this->mByName = User::whoIs( $this->mBy );
- } else {
- global $wgUser;
- $this->mByName = $wgUser->getName();
+ $continue = wfRunHooks(
+ 'PerformRetroactiveAutoblock', array( $this, &$blockIds ) );
+
+ if ( $continue ) {
+ self::defaultRetroactiveAutoblock( $this, $blockIds );
}
}
+ return $blockIds;
}
/**
* Retroactively autoblocks the last IP used by the user (if it is a user)
- * blocked by this Block.
+ * blocked by this Block. This will use the recentchanges table.
*
- * @return Boolean: whether or not a retroactive autoblock was made.
+ * @param Block $block
+ * @param Array &$blockIds
+ * @return Array: block IDs of retroactive autoblocks made
*/
- public function doRetroactiveAutoblock() {
+ protected static function defaultRetroactiveAutoblock( Block $block, array &$blockIds ) {
$dbr = wfGetDB( DB_SLAVE );
- # If autoblock is enabled, autoblock the LAST IP used
- # - stolen shamelessly from CheckUser_body.php
- if ( $this->mEnableAutoblock && $this->mUser ) {
- wfDebug( "Doing retroactive autoblocks for " . $this->mAddress . "\n" );
+ $options = array( 'ORDER BY' => 'rc_timestamp DESC' );
+ $conds = array( 'rc_user_text' => (string)$block->getTarget() );
- $options = array( 'ORDER BY' => 'rc_timestamp DESC' );
- $conds = array( 'rc_user_text' => $this->mAddress );
-
- if ( $this->mAngryAutoblock ) {
- // Block any IP used in the last 7 days. Up to five IPs.
- $conds[] = 'rc_timestamp < ' . $dbr->addQuotes( $dbr->timestamp( time() - ( 7 * 86400 ) ) );
- $options['LIMIT'] = 5;
- } else {
- // Just the last IP used.
- $options['LIMIT'] = 1;
- }
+ // Just the last IP used.
+ $options['LIMIT'] = 1;
- $res = $dbr->select( 'recentchanges', array( 'rc_ip' ), $conds,
- __METHOD__ , $options );
+ $res = $dbr->select( 'recentchanges', array( 'rc_ip' ), $conds,
+ __METHOD__ , $options );
- if ( !$dbr->numRows( $res ) ) {
- # No results, don't autoblock anything
- wfDebug( "No IP found to retroactively autoblock\n" );
- } else {
- foreach ( $res as $row ) {
- if ( $row->rc_ip ) {
- $this->doAutoblock( $row->rc_ip );
- }
+ if ( !$dbr->numRows( $res ) ) {
+ # No results, don't autoblock anything
+ wfDebug( "No IP found to retroactively autoblock\n" );
+ } else {
+ foreach ( $res as $row ) {
+ if ( $row->rc_ip ) {
+ $id = $block->doAutoblock( $row->rc_ip );
+ if ( $id ) $blockIds[] = $id;
}
}
}
@@ -542,6 +547,7 @@ class Block {
/**
* Checks whether a given IP is on the autoblock whitelist.
+ * TODO: this probably belongs somewhere else, but not sure where...
*
* @param $ip String: The IP to check
* @return Boolean
@@ -587,73 +593,70 @@ class Block {
* Autoblocks the given IP, referring to this Block.
*
* @param $autoblockIP String: the IP to autoblock.
- * @param $justInserted Boolean: the main block was just inserted
- * @return Boolean: whether or not an autoblock was inserted.
+ * @return mixed: block ID if an autoblock was inserted, false if not.
*/
- public function doAutoblock( $autoblockIP, $justInserted = false ) {
+ public function doAutoblock( $autoblockIP ) {
# If autoblocks are disabled, go away.
- if ( !$this->mEnableAutoblock ) {
- return;
+ if ( !$this->isAutoblocking() ) {
+ return false;
}
- # Check for presence on the autoblock whitelist
- if ( Block::isWhitelistedFromAutoblocks( $autoblockIP ) ) {
- return;
+ # Check for presence on the autoblock whitelist.
+ if ( self::isWhitelistedFromAutoblocks( $autoblockIP ) ) {
+ return false;
}
- # # Allow hooks to cancel the autoblock.
+ # Allow hooks to cancel the autoblock.
if ( !wfRunHooks( 'AbortAutoblock', array( $autoblockIP, &$this ) ) ) {
wfDebug( "Autoblock aborted by hook.\n" );
return false;
}
- # It's okay to autoblock. Go ahead and create/insert the block.
+ # It's okay to autoblock. Go ahead and insert/update the block...
- $ipblock = Block::newFromDB( $autoblockIP );
+ # Do not add a *new* block if the IP is already blocked.
+ $ipblock = Block::newFromTarget( $autoblockIP );
if ( $ipblock ) {
- # If the user is already blocked. Then check if the autoblock would
- # exceed the user block. If it would exceed, then do nothing, else
- # prolong block time
- if ( $this->mExpiry &&
- ( $this->mExpiry < Block::getAutoblockExpiry( $ipblock->mTimestamp ) )
+ # Check if the block is an autoblock and would exceed the user block
+ # if renewed. If so, do nothing, otherwise prolong the block time...
+ if ( $ipblock->mAuto && // @TODO: why not compare $ipblock->mExpiry?
+ $this->mExpiry > Block::getAutoblockExpiry( $ipblock->mTimestamp )
) {
- return;
- }
-
- # Just update the timestamp
- if ( !$justInserted ) {
+ # Reset block timestamp to now and its expiry to
+ # $wgAutoblockExpiry in the future
$ipblock->updateTimestamp();
}
+ return false;
+ }
- return;
- } else {
- $ipblock = new Block;
- }
-
- # Make a new block object with the desired properties
- wfDebug( "Autoblocking {$this->mAddress}@" . $autoblockIP . "\n" );
- $ipblock->mAddress = $autoblockIP;
- $ipblock->mUser = 0;
- $ipblock->mBy = $this->mBy;
- $ipblock->mByName = $this->mByName;
- $ipblock->mReason = wfMsgForContent( 'autoblocker', $this->mAddress, $this->mReason );
- $ipblock->mTimestamp = wfTimestampNow();
- $ipblock->mAuto = 1;
- $ipblock->mCreateAccount = $this->mCreateAccount;
+ # Make a new block object with the desired properties.
+ $autoblock = new Block;
+ wfDebug( "Autoblocking {$this->getTarget()}@" . $autoblockIP . "\n" );
+ $autoblock->setTarget( $autoblockIP );
+ $autoblock->setBlocker( $this->getBlocker() );
+ $autoblock->mReason = wfMsgForContent( 'autoblocker', $this->getTarget(), $this->mReason );
+ $timestamp = wfTimestampNow();
+ $autoblock->mTimestamp = $timestamp;
+ $autoblock->mAuto = 1;
+ $autoblock->prevents( 'createaccount', $this->prevents( 'createaccount' ) );
# Continue suppressing the name if needed
- $ipblock->mHideName = $this->mHideName;
- $ipblock->mAllowUsertalk = $this->mAllowUsertalk;
+ $autoblock->mHideName = $this->mHideName;
+ $autoblock->prevents( 'editownusertalk', $this->prevents( 'editownusertalk' ) );
- # If the user is already blocked with an expiry date, we don't
- # want to pile on top of that!
- if ( $this->mExpiry ) {
- $ipblock->mExpiry = min( $this->mExpiry, Block::getAutoblockExpiry( $this->mTimestamp ) );
+ if ( $this->mExpiry == 'infinity' ) {
+ # Original block was indefinite, start an autoblock now
+ $autoblock->mExpiry = Block::getAutoblockExpiry( $timestamp );
} else {
- $ipblock->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp );
+ # If the user is already blocked with an expiry date, we don't
+ # want to pile on top of that.
+ $autoblock->mExpiry = min( $this->mExpiry, Block::getAutoblockExpiry( $timestamp ) );
}
- # Insert it
- return $ipblock->insert();
+ # Insert the block...
+ $status = $autoblock->insert();
+ return $status
+ ? $status['id']
+ : false;
}
/**
@@ -681,12 +684,13 @@ class Block {
* @return Boolean
*/
public function isExpired() {
- wfDebug( "Block::isExpired() checking current " . wfTimestampNow() . " vs $this->mExpiry\n" );
+ $timestamp = wfTimestampNow();
+ wfDebug( "Block::isExpired() checking current " . $timestamp . " vs $this->mExpiry\n" );
if ( !$this->mExpiry ) {
return false;
} else {
- return wfTimestampNow() > $this->mExpiry;
+ return $timestamp > $this->mExpiry;
}
}
@@ -695,7 +699,7 @@ class Block {
* @return Boolean
*/
public function isValid() {
- return $this->mAddress != '';
+ return $this->getTarget() != null;
}
/**
@@ -711,20 +715,58 @@ class Block {
array( /* SET */
'ipb_timestamp' => $dbw->timestamp( $this->mTimestamp ),
'ipb_expiry' => $dbw->timestamp( $this->mExpiry ),
- ), array( /* WHERE */
- 'ipb_address' => $this->mAddress
- ), 'Block::updateTimestamp'
+ ),
+ array( /* WHERE */
+ 'ipb_address' => (string)$this->getTarget()
+ ),
+ __METHOD__
);
}
}
/**
+ * Get the IP address at the start of the range in Hex form
+ * @return String IP in Hex form
+ */
+ public function getRangeStart() {
+ switch( $this->type ) {
+ case self::TYPE_USER:
+ return '';
+ case self::TYPE_IP:
+ return IP::toHex( $this->target );
+ case self::TYPE_RANGE:
+ list( $start, /*...*/ ) = IP::parseRange( $this->target );
+ return $start;
+ default: throw new MWException( "Block with invalid type" );
+ }
+ }
+
+ /**
+ * Get the IP address at the start of the range in Hex form
+ * @return String IP in Hex form
+ */
+ public function getRangeEnd() {
+ switch( $this->type ) {
+ case self::TYPE_USER:
+ return '';
+ case self::TYPE_IP:
+ return IP::toHex( $this->target );
+ case self::TYPE_RANGE:
+ list( /*...*/, $end ) = IP::parseRange( $this->target );
+ return $end;
+ default: throw new MWException( "Block with invalid type" );
+ }
+ }
+
+ /**
* Get the user id of the blocking sysop
*
* @return Integer
*/
public function getBy() {
- return $this->mBy;
+ return $this->getBlocker() instanceof User
+ ? $this->getBlocker()->getId()
+ : 0;
}
/**
@@ -733,32 +775,102 @@ class Block {
* @return String
*/
public function getByName() {
- return $this->mByName;
+ return $this->getBlocker() instanceof User
+ ? $this->getBlocker()->getName()
+ : null;
+ }
+
+ /**
+ * Get the block ID
+ * @return int
+ */
+ public function getId() {
+ return $this->mId;
}
/**
* Get/set the SELECT ... FOR UPDATE flag
+ * @deprecated since 1.18
+ *
+ * @param $x Bool
*/
public function forUpdate( $x = null ) {
- return wfSetVar( $this->mForUpdate, $x );
+ # noop
}
/**
* Get/set a flag determining whether the master is used for reads
+ *
+ * @param $x Bool
+ * @return Bool
*/
public function fromMaster( $x = null ) {
return wfSetVar( $this->mFromMaster, $x );
}
/**
+ * Get/set whether the Block is a hardblock (affects logged-in users on a given IP/range
+ * @param $x Bool
+ * @return Bool
+ */
+ public function isHardblock( $x = null ) {
+ wfSetVar( $this->isHardblock, $x );
+
+ # You can't *not* hardblock a user
+ return $this->getType() == self::TYPE_USER
+ ? true
+ : $this->isHardblock;
+ }
+
+ public function isAutoblocking( $x = null ) {
+ wfSetVar( $this->isAutoblocking, $x );
+
+ # You can't put an autoblock on an IP or range as we don't have any history to
+ # look over to get more IPs from
+ return $this->getType() == self::TYPE_USER
+ ? $this->isAutoblocking
+ : false;
+ }
+
+ /**
+ * Get/set whether the Block prevents a given action
+ * @param $action String
+ * @param $x Bool
+ * @return Bool
+ */
+ public function prevents( $action, $x = null ) {
+ switch( $action ) {
+ case 'edit':
+ # For now... <evil laugh>
+ return true;
+
+ case 'createaccount':
+ return wfSetVar( $this->mCreateAccount, $x );
+
+ case 'sendemail':
+ return wfSetVar( $this->mBlockEmail, $x );
+
+ case 'editownusertalk':
+ return wfSetVar( $this->mDisableUsertalk, $x );
+
+ default:
+ return null;
+ }
+ }
+
+ /**
* Get the block name, but with autoblocked IPs hidden as per standard privacy policy
- * @return String
+ * @return String, text is escaped
*/
public function getRedactedName() {
if ( $this->mAuto ) {
- return '#' . $this->mId;
+ return Html::rawElement(
+ 'span',
+ array( 'class' => 'mw-autoblockid' ),
+ wfMessage( 'autoblockid', $this->mId )
+ );
} else {
- return $this->mAddress;
+ return htmlspecialchars( $this->getTarget() );
}
}
@@ -768,33 +880,29 @@ class Block {
* @param $expiry String: timestamp for expiry, or
* @param $db Database object
* @return String
+ * @deprecated since 1.18; use $dbw->encodeExpiry() instead
*/
public static function encodeExpiry( $expiry, $db ) {
- if ( $expiry == '' || $expiry == Block::infinity() ) {
- return Block::infinity();
- } else {
- return $db->timestamp( $expiry );
- }
+ return $db->encodeExpiry( $expiry );
}
/**
* Decode expiry which has come from the DB
*
* @param $expiry String: Database expiry format
- * @param $timestampType Requested timestamp format
+ * @param $timestampType Int Requested timestamp format
* @return String
+ * @deprecated since 1.18; use $wgLang->decodeExpiry() instead
*/
public static function decodeExpiry( $expiry, $timestampType = TS_MW ) {
- if ( $expiry == '' || $expiry == Block::infinity() ) {
- return Block::infinity();
- } else {
- return wfTimestamp( $timestampType, $expiry );
- }
+ global $wgContLang;
+ return $wgContLang->formatExpiry( $expiry, $timestampType );
}
/**
* Get a timestamp of the expiry for autoblocks
*
+ * @param $timestamp String|Int
* @return String
*/
public static function getAutoblockExpiry( $timestamp ) {
@@ -808,35 +916,10 @@ class Block {
* For example, 127.111.113.151/24 -> 127.111.113.0/24
* @param $range String: IP address to normalize
* @return string
+ * @deprecated since 1.18, call IP::sanitizeRange() directly
*/
public static function normaliseRange( $range ) {
- $parts = explode( '/', $range );
- if ( count( $parts ) == 2 ) {
- // IPv6
- if ( IP::isIPv6( $range ) && $parts[1] >= 64 && $parts[1] <= 128 ) {
- $bits = $parts[1];
- $ipint = IP::toUnsigned( $parts[0] );
- # Native 32 bit functions WON'T work here!!!
- # Convert to a padded binary number
- $network = wfBaseConvert( $ipint, 10, 2, 128 );
- # Truncate the last (128-$bits) bits and replace them with zeros
- $network = str_pad( substr( $network, 0, $bits ), 128, 0, STR_PAD_RIGHT );
- # Convert back to an integer
- $network = wfBaseConvert( $network, 2, 10 );
- # Reform octet address
- $newip = IP::toOctet( $network );
- $range = "$newip/{$parts[1]}";
- } // IPv4
- elseif ( IP::isIPv4( $range ) && $parts[1] >= 16 && $parts[1] <= 32 ) {
- $shift = 32 - $parts[1];
- $ipint = IP::toUnsigned( $parts[0] );
- $ipint = $ipint >> $shift << $shift;
- $newip = long2ip( $ipint );
- $range = "$newip/{$parts[1]}";
- }
- }
-
- return $range;
+ return IP::sanitizeRange( $range );
}
/**
@@ -849,25 +932,12 @@ class Block {
/**
* Get a value to insert into expiry field of the database when infinite expiry
- * is desired. In principle this could be DBMS-dependant, but currently all
- * supported DBMS's support the string "infinity", so we just use that.
- *
+ * is desired
+ * @deprecated since 1.18, call $dbr->getInfinity() directly
* @return String
*/
public static function infinity() {
- # This is a special keyword for timestamps in PostgreSQL, and
- # works with CHAR(14) as well because "i" sorts after all numbers.
-
- # BEGIN DatabaseMssql hack
- # Since MSSQL doesn't recognize the infinity keyword, set date manually.
- # TO-DO: Refactor for better DB portability and remove magic date
- $dbr = wfGetDB( DB_SLAVE );
- if ( $dbr->getType() == 'mssql' ) {
- return '3000-01-31 00:00:00.000';
- }
- # End DatabaseMssql hack
-
- return 'infinity';
+ return wfGetDB( DB_SLAVE )->getInfinity();
}
/**
@@ -875,8 +945,10 @@ class Block {
*
* @param $encoded_expiry String: Database encoded expiry time
* @return Html-escaped String
+ * @deprecated since 1.18; use $wgLang->formatExpiry() instead
*/
public static function formatExpiry( $encoded_expiry ) {
+ global $wgContLang;
static $msg = null;
if ( is_null( $msg ) ) {
@@ -888,8 +960,8 @@ class Block {
}
}
- $expiry = Block::decodeExpiry( $encoded_expiry );
- if ( $expiry == 'infinity' ) {
+ $expiry = $wgContLang->formatExpiry( $encoded_expiry, TS_MW );
+ if ( $expiry == wfGetDB( DB_SLAVE )->getInfinity() ) {
$expirystr = $msg['infiniteblock'];
} else {
global $wgLang;
@@ -902,21 +974,179 @@ class Block {
}
/**
- * Convert a typed-in expiry time into something we can put into the database.
- * @param $expiry_input String: whatever was typed into the form
- * @return String: more database friendly
+ * Convert a submitted expiry time, which may be relative ("2 weeks", etc) or absolute
+ * ("24 May 2034"), into an absolute timestamp we can put into the database.
+ * @param $expiry String: whatever was typed into the form
+ * @return String: timestamp or "infinity" string for th DB implementation
+ * @deprecated since 1.18 moved to SpecialBlock::parseExpiryInput()
*/
- public static function parseExpiryInput( $expiry_input ) {
- if ( $expiry_input == 'infinite' || $expiry_input == 'indefinite' ) {
- $expiry = 'infinity';
+ public static function parseExpiryInput( $expiry ) {
+ wfDeprecated( __METHOD__ );
+ return SpecialBlock::parseExpiryInput( $expiry );
+ }
+
+ /**
+ * Given a target and the target's type, get an existing Block object if possible.
+ * @param $specificTarget String|User|Int a block target, which may be one of several types:
+ * * A user to block, in which case $target will be a User
+ * * An IP to block, in which case $target will be a User generated by using
+ * User::newFromName( $ip, false ) to turn off name validation
+ * * An IP range, in which case $target will be a String "123.123.123.123/18" etc
+ * * The ID of an existing block, in the format "#12345" (since pure numbers are valid
+ * usernames
+ * Calling this with a user, IP address or range will not select autoblocks, and will
+ * only select a block where the targets match exactly (so looking for blocks on
+ * 1.2.3.4 will not select 1.2.0.0/16 or even 1.2.3.4/32)
+ * @param $vagueTarget String|User|Int as above, but we will search for *any* block which
+ * affects that target (so for an IP address, get ranges containing that IP; and also
+ * get any relevant autoblocks). Leave empty or blank to skip IP-based lookups.
+ * @param $fromMaster Bool whether to use the DB_MASTER database
+ * @return Block|null (null if no relevant block could be found). The target and type
+ * of the returned Block will refer to the actual block which was found, which might
+ * not be the same as the target you gave if you used $vagueTarget!
+ */
+ public static function newFromTarget( $specificTarget, $vagueTarget = null, $fromMaster = false ) {
+
+ list( $target, $type ) = self::parseTarget( $specificTarget );
+ if( $type == Block::TYPE_ID || $type == Block::TYPE_AUTO ){
+ return Block::newFromID( $target );
+
+ } 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 ) ) ) {
+ $block = new Block();
+ $block->fromMaster( $fromMaster );
+
+ if( $type !== null ){
+ $block->setTarget( $target );
+ }
+
+ if( $block->newLoad( $vagueTarget ) ){
+ return $block;
+ } else {
+ return null;
+ }
} else {
- $expiry = strtotime( $expiry_input );
+ return null;
+ }
+ }
- if ( $expiry < 0 || $expiry === false ) {
- return false;
+ /**
+ * From an existing Block, get the target and the type of target. Note that it is
+ * always safe to treat the target as a string; for User objects this will return
+ * User::__toString() which in turn gives User::getName().
+ *
+ * @param $target String|Int|User
+ * @return array( User|String, Block::TYPE_ constant )
+ */
+ public static function parseTarget( $target ) {
+ $target = trim( $target );
+
+ # We may have been through this before
+ 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 ){
+ return array( null, null );
+ }
+
+ if ( IP::isValid( $target ) ) {
+ # We can still create a User if it's an IP address, but we need to turn
+ # off validation checking (which would exclude IP addresses)
+ return array(
+ User::newFromName( IP::sanitizeIP( $target ), false ),
+ Block::TYPE_IP
+ );
+
+ } elseif ( IP::isValidBlock( $target ) ) {
+ # Can't create a User from an IP range
+ return array( IP::sanitizeRange( $target ), Block::TYPE_RANGE );
}
- return $expiry;
+ # Consider the possibility that this is not a username at all
+ # but actually an old subpage (bug #29797)
+ if( strpos( $target, '/' ) !== false ){
+ # An old subpage, drill down to the user behind it
+ $parts = explode( '/', $target );
+ $target = $parts[0];
+ }
+
+ $userObj = User::newFromName( $target );
+ if ( $userObj instanceof User ) {
+ # Note that since numbers are valid usernames, a $target of "12345" will be
+ # considered a User. If you want to pass a block ID, prepend a hash "#12345",
+ # since hash characters are not valid in usernames or titles generally.
+ return array( $userObj, Block::TYPE_USER );
+
+ } elseif ( preg_match( '/^#\d+$/', $target ) ) {
+ # Autoblock reference in the form "#12345"
+ return array( substr( $target, 1 ), Block::TYPE_AUTO );
+
+ } else {
+ # WTF?
+ return array( null, null );
+ }
+ }
+
+ /**
+ * Get the type of target for this particular block
+ * @return Block::TYPE_ constant, will never be TYPE_ID
+ */
+ public function getType() {
+ return $this->mAuto
+ ? self::TYPE_AUTO
+ : $this->type;
+ }
+
+ /**
+ * Get the target and target type for this particular Block. Note that for autoblocks,
+ * this returns the unredacted name; frontend functions need to call $block->getRedactedName()
+ * in this situation.
+ * @return array( User|String, Block::TYPE_ constant )
+ * @todo FIXME: This should be an integral part of the Block member variables
+ */
+ public function getTargetAndType() {
+ return array( $this->getTarget(), $this->getType() );
+ }
+
+ /**
+ * Get the target for this particular Block. Note that for autoblocks,
+ * this returns the unredacted name; frontend functions need to call $block->getRedactedName()
+ * in this situation.
+ * @return User|String
+ */
+ public function getTarget() {
+ return $this->target;
+ }
+
+ /**
+ * Set the target for this block, and update $this->type accordingly
+ * @param $target Mixed
+ */
+ public function setTarget( $target ){
+ list( $this->target, $this->type ) = self::parseTarget( $target );
+ }
+
+ /**
+ * Get the user who implemented this block
+ * @return User
+ */
+ public function getBlocker(){
+ return $this->blocker;
+ }
+
+ /**
+ * Set the user who implemented (or will implement) this block
+ * @param $user User
+ */
+ public function setBlocker( User $user ){
+ $this->blocker = $user;
}
}
diff --git a/includes/Category.php b/includes/Category.php
index 614933ff..9d9b5a67 100644
--- a/includes/Category.php
+++ b/includes/Category.php
@@ -13,7 +13,10 @@ class Category {
/** Name of the category, normalized to DB-key form */
private $mName = null;
private $mID = null;
- /** Category page title */
+ /**
+ * Category page title
+ * @var Title
+ */
private $mTitle = null;
/** Counts of membership (cat_pages, cat_subcats, cat_files) */
private $mPages = null, $mSubcats = null, $mFiles = null;
@@ -100,7 +103,7 @@ class Category {
* Factory function.
*
* @param $title Title for the category page
- * @return Mixed: category, or false on a totally invalid name
+ * @return category|false on a totally invalid name
*/
public static function newFromTitle( $title ) {
$cat = new self();
@@ -129,7 +132,7 @@ class Category {
* @param $row result set row, must contain the cat_xxx fields. If the fields are null,
* the resulting Category object will represent an empty category if a title object
* was given. If the fields are null and no title was given, this method fails and returns false.
- * @param $title optional title object for the category represented by the given row.
+ * @param Title $title optional title object for the category represented by the given row.
* May be provided if it is already known, to avoid having to re-create a title object later.
* @return Category
*/
@@ -182,7 +185,7 @@ class Category {
public function getFileCount() { return $this->getX( 'mFiles' ); }
/**
- * @return mixed The Title for this category, or false on failure.
+ * @return Title|false Title for this category, or false on failure.
*/
public function getTitle() {
if ( $this->mTitle ) return $this->mTitle;
diff --git a/includes/CategoryPage.php b/includes/CategoryPage.php
index f990b79b..6a0f6132 100644
--- a/includes/CategoryPage.php
+++ b/includes/CategoryPage.php
@@ -1,6 +1,6 @@
<?php
/**
- * Special handling for category description pages.
+ * Class for viewing MediaWiki category description pages.
* Modelled after ImagePage.php.
*
* @file
@@ -17,6 +17,22 @@ class CategoryPage extends Article {
# Subclasses can change this to override the viewer class.
protected $mCategoryViewerClass = 'CategoryViewer';
+ protected function newPage( Title $title ) {
+ // Overload mPage with a category-specific page
+ return new WikiCategoryPage( $title );
+ }
+
+ /**
+ * Constructor from a page id
+ * @param $id Int article ID to load
+ */
+ public static function newFromID( $id ) {
+ $t = Title::newFromID( $id );
+ # @todo FIXME: Doesn't inherit right
+ return $t == null ? null : new self( $t );
+ # return $t == null ? null : new static( $t ); // PHP 5.3
+ }
+
function view() {
global $wgRequest, $wgUser;
@@ -42,27 +58,6 @@ class CategoryPage extends Article {
}
}
- /**
- * Don't return a 404 for categories in use.
- * In use defined as: either the actual page exists
- * or the category currently has members.
- */
- function hasViewableContent() {
- if ( parent::hasViewableContent() ) {
- return true;
- } else {
- $cat = Category::newFromTitle( $this->mTitle );
- // If any of these are not 0, then has members
- if ( $cat->getPageCount()
- || $cat->getSubcatCount()
- || $cat->getFileCount()
- ) {
- return true;
- }
- }
- return false;
- }
-
function openShowCategory() {
# For overloading
}
@@ -70,27 +65,77 @@ class CategoryPage extends Article {
function closeShowCategory() {
global $wgOut, $wgRequest;
+ // Use these as defaults for back compat --catrope
+ $oldFrom = $wgRequest->getVal( 'from' );
+ $oldUntil = $wgRequest->getVal( 'until' );
+
+ $reqArray = $wgRequest->getValues();
+
$from = $until = array();
foreach ( array( 'page', 'subcat', 'file' ) as $type ) {
- $from[$type] = $wgRequest->getVal( "{$type}from" );
- $until[$type] = $wgRequest->getVal( "{$type}until" );
+ $from[$type] = $wgRequest->getVal( "{$type}from", $oldFrom );
+ $until[$type] = $wgRequest->getVal( "{$type}until", $oldUntil );
+
+ // Do not want old-style from/until propagating in nav links.
+ if ( !isset( $reqArray["{$type}from"] ) && isset( $reqArray["from"] ) ) {
+ $reqArray["{$type}from"] = $reqArray["from"];
+ }
+ if ( !isset( $reqArray["{$type}to"] ) && isset( $reqArray["to"] ) ) {
+ $reqArray["{$type}to"] = $reqArray["to"];
+ }
}
- $viewer = new $this->mCategoryViewerClass( $this->mTitle, $from, $until, $wgRequest->getValues() );
+ unset( $reqArray["from"] );
+ unset( $reqArray["to"] );
+
+ $viewer = new $this->mCategoryViewerClass( $this->mTitle, $from, $until, $reqArray );
$wgOut->addHTML( $viewer->getHTML() );
}
}
class CategoryViewer {
- var $title, $limit, $from, $until,
+ var $limit, $from, $until,
$articles, $articles_start_char,
$children, $children_start_char,
- $showGallery, $gallery,
- $imgsNoGalley, $imgsNoGallery_start_char,
- $skin, $collation;
- # Category object for this page
+ $showGallery, $imgsNoGalley,
+ $imgsNoGallery_start_char,
+ $imgsNoGallery;
+
+ /**
+ * @var
+ */
+ var $nextPage;
+
+ /**
+ * @var Array
+ */
+ var $flip;
+
+ /**
+ * @var Title
+ */
+ var $title;
+
+ /**
+ * @var Collation
+ */
+ var $collation;
+
+ /**
+ * @var ImageGallery
+ */
+ var $gallery;
+
+ /**
+ * Category object for this page
+ * @var Category
+ */
private $cat;
- # The original query array, to be used in generating paging links.
+
+ /**
+ * The original query array, to be used in generating paging links.
+ * @var array
+ */
private $query;
function __construct( $title, $from = '', $until = '', $query = array() ) {
@@ -111,7 +156,7 @@ class CategoryViewer {
* @return string HTML output
*/
public function getHTML() {
- global $wgOut, $wgCategoryMagicGallery, $wgContLang;
+ global $wgOut, $wgCategoryMagicGallery, $wgLang, $wgContLang;
wfProfileIn( __METHOD__ );
$this->showGallery = $wgCategoryMagicGallery && !$wgOut->mNoGallery;
@@ -127,7 +172,7 @@ class CategoryViewer {
if ( $r == '' ) {
// If there is no category content to display, only
// show the top part of the navigation links.
- // FIXME: cannot be completely suppressed because it
+ // @todo FIXME: Cannot be completely suppressed because it
// is unknown if 'until' or 'from' makes this
// give 0 results.
$r = $r . $this->getCategoryTop();
@@ -142,6 +187,12 @@ class CategoryViewer {
$r = wfMsgExt( 'category-empty', array( 'parse' ) );
}
+ $pageLang = $this->title->getPageLanguage();
+ $langAttribs = array( 'lang' => $wgLang->getCode(), 'dir' => $wgLang->getDir() );
+ # close the previous div, show the headings in user language,
+ # then open a new div with the page content language again
+ $r = Html::openElement( 'div', $langAttribs ) . $r . '</div>';
+
wfProfileOut( __METHOD__ );
return $wgContLang->convert( $r );
}
@@ -160,14 +211,6 @@ class CategoryViewer {
}
}
- function getSkin() {
- if ( !$this->skin ) {
- global $wgUser;
- $this->skin = $wgUser->getSkin();
- }
- return $this->skin;
- }
-
/**
* Add a subcategory to the internal lists, using a Category object
*/
@@ -175,7 +218,7 @@ class CategoryViewer {
// Subcategory; strip the 'Category' namespace from the link text.
$title = $cat->getTitle();
- $link = $this->getSkin()->link( $title, $title->getText() );
+ $link = Linker::link( $title, htmlspecialchars( $title->getText() ) );
if ( $title->isRedirect() ) {
// This didn't used to add redirect-in-category, but might
// as well be consistent with the rest of the sections
@@ -190,7 +233,7 @@ class CategoryViewer {
/**
* Add a subcategory to the internal lists, using a title object
- * @deprecated kept for compatibility, please use addSubcategoryObject instead
+ * @deprecated since 1.17 kept for compatibility, please use addSubcategoryObject instead
*/
function addSubcategory( Title $title, $sortkey, $pageLength ) {
$this->addSubcategoryObject( Category::newFromTitle( $title ), $sortkey, $pageLength );
@@ -233,7 +276,7 @@ class CategoryViewer {
$this->gallery->add( $title );
}
} else {
- $link = $this->getSkin()->link( $title );
+ $link = Linker::link( $title );
if ( $isRedirect ) {
// This seems kind of pointless given 'mw-redirect' class,
// but keeping for back-compatibility with user css.
@@ -252,7 +295,7 @@ class CategoryViewer {
function addPage( $title, $sortkey, $pageLength, $isRedirect = false ) {
global $wgContLang;
- $link = $this->getSkin()->link( $title );
+ $link = Linker::link( $title );
if ( $isRedirect ) {
// This seems kind of pointless given 'mw-redirect' class,
// but keeping for back-compatiability with user css.
@@ -309,7 +352,7 @@ class CategoryViewer {
'page_is_redirect', 'cl_sortkey', 'cat_id', 'cat_title',
'cat_subcats', 'cat_pages', 'cat_files',
'cl_sortkey_prefix', 'cl_collation' ),
- array( 'cl_to' => $this->title->getDBkey() ) + $extraConds,
+ array_merge( array( 'cl_to' => $this->title->getDBkey() ), $extraConds ),
__METHOD__,
array(
'USE INDEX' => array( 'categorylinks' => 'cl_sortkey' ),
@@ -385,7 +428,7 @@ class CategoryViewer {
# Don't show articles section if there are none.
$r = '';
- # FIXME, here and in the other two sections: we don't need to bother
+ # @todo FIXME: Here and in the other two sections: we don't need to bother
# with this rigamarole if the entire category contents fit on one page
# and have already been retrieved. We can just use $rescnt in that
# case and save a query and some logic.
@@ -460,13 +503,20 @@ class CategoryViewer {
* @private
*/
function formatList( $articles, $articles_start_char, $cutoff = 6 ) {
+ $list = '';
if ( count ( $articles ) > $cutoff ) {
- return self::columnList( $articles, $articles_start_char );
+ $list = self::columnList( $articles, $articles_start_char );
} elseif ( count( $articles ) > 0 ) {
// for short lists of articles in categories.
- return self::shortList( $articles, $articles_start_char );
+ $list = self::shortList( $articles, $articles_start_char );
}
- return '';
+
+ $pageLang = $this->title->getPageLanguage();
+ $attribs = array( 'lang' => $pageLang->getCode(), 'dir' => $pageLang->getDir(),
+ 'class' => 'mw-content-'.$pageLang->getDir() );
+ $list = Html::rawElement( 'div', $attribs, $list );
+
+ return $list;
}
/**
@@ -539,10 +589,8 @@ class CategoryViewer {
static function shortList( $articles, $articles_start_char ) {
$r = '<h3>' . htmlspecialchars( $articles_start_char[0] ) . "</h3>\n";
$r .= '<ul><li>' . $articles[0] . '</li>';
- for ( $index = 1; $index < count( $articles ); $index++ )
- {
- if ( $articles_start_char[$index] != $articles_start_char[$index - 1] )
- {
+ for ( $index = 1; $index < count( $articles ); $index++ ) {
+ if ( $articles_start_char[$index] != $articles_start_char[$index - 1] ) {
$r .= "</ul><h3>" . htmlspecialchars( $articles_start_char[$index] ) . "</h3>\n<ul>";
}
@@ -563,7 +611,7 @@ class CategoryViewer {
*/
private function pagingLinks( $first, $last, $type = '' ) {
global $wgLang;
- $sk = $this->getSkin();
+
$limitText = $wgLang->formatNum( $this->limit );
$prevLink = wfMsgExt( 'prevn', array( 'escape', 'parsemag' ), $limitText );
@@ -572,8 +620,8 @@ class CategoryViewer {
$prevQuery = $this->query;
$prevQuery["{$type}until"] = $first;
unset( $prevQuery["{$type}from"] );
- $prevLink = $sk->linkKnown(
- $this->title,
+ $prevLink = Linker::linkKnown(
+ $this->addFragmentToTitle( $this->title, $type ),
$prevLink,
array(),
$prevQuery
@@ -586,8 +634,8 @@ class CategoryViewer {
$lastQuery = $this->query;
$lastQuery["{$type}from"] = $last;
unset( $lastQuery["{$type}until"] );
- $nextLink = $sk->linkKnown(
- $this->title,
+ $nextLink = Linker::linkKnown(
+ $this->addFragmentToTitle( $this->title, $type ),
$nextLink,
array(),
$lastQuery
@@ -598,8 +646,34 @@ class CategoryViewer {
}
/**
+ * Takes a title, and adds the fragment identifier that
+ * corresponds to the correct segment of the category.
+ *
+ * @param Title $title: The title (usually $this->title)
+ * @param String $section: Which section
+ */
+ private function addFragmentToTitle( $title, $section ) {
+ switch ( $section ) {
+ case 'page':
+ $fragment = 'mw-pages';
+ break;
+ case 'subcat':
+ $fragment = 'mw-subcategories';
+ break;
+ case 'file':
+ $fragment = 'mw-category-media';
+ break;
+ default:
+ throw new MWException( __METHOD__ .
+ " Invalid section $section." );
+ }
+
+ return Title::makeTitle( $title->getNamespace(),
+ $title->getDBkey(), $fragment );
+ }
+ /**
* What to do if the category table conflicts with the number of results
- * returned? This function says what. Each type is considered independantly
+ * returned? This function says what. Each type is considered independently
* of the other types.
*
* Note for grepping: uses the messages category-article-count,
@@ -640,8 +714,7 @@ class CategoryViewer {
}
if ( $dbcnt == $rescnt || ( ( $rescnt == $this->limit || $fromOrUntil )
- && $dbcnt > $rescnt ) )
- {
+ && $dbcnt > $rescnt ) ) {
# Case 1: seems sane.
$totalcnt = $dbcnt;
} elseif ( $rescnt < $this->limit && !$fromOrUntil ) {
diff --git a/includes/Categoryfinder.php b/includes/Categoryfinder.php
index 1f08b7f8..2567de0d 100644
--- a/includes/Categoryfinder.php
+++ b/includes/Categoryfinder.php
@@ -42,6 +42,7 @@ class Categoryfinder {
* @param $article_ids Array of article IDs
* @param $categories FIXME
* @param $mode String: FIXME, default 'AND'.
+ * @todo FIXME: $categories/$mode
*/
function seed( $article_ids, $categories, $mode = 'AND' ) {
$this->articles = $article_ids;
@@ -85,9 +86,9 @@ class Categoryfinder {
/**
* This functions recurses through the parent representation, trying to match the conditions
- * @param $id The article/category to check
- * @param $conds The array of categories to match
- * @param $path used to check for recursion loops
+ * @param $id int The article/category to check
+ * @param $conds array The array of categories to match
+ * @param $path array used to check for recursion loops
* @return bool Does this match the conditions?
*/
function check( $id, &$conds, $path = array() ) {
diff --git a/includes/Cdb.php b/includes/Cdb.php
index 60477485..d7a2bca5 100644
--- a/includes/Cdb.php
+++ b/includes/Cdb.php
@@ -13,6 +13,10 @@
abstract class CdbReader {
/**
* Open a file and return a subclass instance
+ *
+ * @param $fileName string
+ *
+ * @return CdbReader
*/
public static function open( $fileName ) {
if ( self::haveExtension() ) {
@@ -25,6 +29,8 @@ abstract class CdbReader {
/**
* Returns true if the native extension is available
+ *
+ * @return bool
*/
public static function haveExtension() {
if ( !function_exists( 'dba_handlers' ) ) {
@@ -49,6 +55,8 @@ abstract class CdbReader {
/**
* Get a value with a given key. Only string values are supported.
+ *
+ * @param $key string
*/
abstract public function get( $key );
}
@@ -61,6 +69,10 @@ abstract class CdbWriter {
/**
* Open a writer and return a subclass instance.
* The user must have write access to the directory, for temporary file creation.
+ *
+ * @param $fileName string
+ *
+ * @return bool
*/
public static function open( $fileName ) {
if ( CdbReader::haveExtension() ) {
diff --git a/includes/Cdb_PHP.php b/includes/Cdb_PHP.php
index 1485cc66..f4029ba5 100644
--- a/includes/Cdb_PHP.php
+++ b/includes/Cdb_PHP.php
@@ -16,6 +16,11 @@ class CdbFunctions {
/**
* Take a modulo of a signed integer as if it were an unsigned integer.
* $b must be less than 0x40000000 and greater than 0
+ *
+ * @param $a
+ * @param $b
+ *
+ * @return int
*/
public static function unsignedMod( $a, $b ) {
if ( $a & 0x80000000 ) {
@@ -25,9 +30,12 @@ class CdbFunctions {
return $a % $b;
}
}
-
+
/**
* Shift a signed integer right as if it were unsigned
+ * @param $a
+ * @param $b
+ * @return int
*/
public static function unsignedShiftRight( $a, $b ) {
if ( $b == 0 ) {
@@ -42,6 +50,10 @@ class CdbFunctions {
/**
* The CDB hash function.
+ *
+ * @param $s
+ *
+ * @return
*/
public static function hash( $s ) {
$h = 5381;
@@ -103,11 +115,16 @@ class CdbReader_PHP extends CdbReader {
}
function close() {
- if( isset($this->handle) )
+ if( isset( $this->handle ) ) {
fclose( $this->handle );
+ }
unset( $this->handle );
}
+ /**
+ * @param $key
+ * @return bool|string
+ */
public function get( $key ) {
// strval is required
if ( $this->find( strval( $key ) ) ) {
@@ -117,6 +134,11 @@ class CdbReader_PHP extends CdbReader {
}
}
+ /**
+ * @param $key
+ * @param $pos
+ * @return bool
+ */
protected function match( $key, $pos ) {
$buf = $this->read( strlen( $key ), $pos );
return $buf === $key;
@@ -126,6 +148,12 @@ class CdbReader_PHP extends CdbReader {
$this->loop = 0;
}
+ /**
+ * @throws MWException
+ * @param $length
+ * @param $pos
+ * @return string
+ */
protected function read( $length, $pos ) {
if ( fseek( $this->handle, $pos ) == -1 ) {
// This can easily happen if the internal pointers are incorrect
@@ -145,6 +173,8 @@ class CdbReader_PHP extends CdbReader {
/**
* Unpack an unsigned integer and throw an exception if it needs more than 31 bits
+ * @param $s
+ * @return
*/
protected function unpack31( $s ) {
$data = unpack( 'V', $s );
@@ -156,12 +186,18 @@ class CdbReader_PHP extends CdbReader {
/**
* Unpack a 32-bit signed integer
+ * @param $s
+ * @return int
*/
protected function unpackSigned( $s ) {
$data = unpack( 'va/vb', $s );
return $data['a'] | ( $data['b'] << 16 );
}
+ /**
+ * @param $key
+ * @return bool
+ */
protected function findNext( $key ) {
if ( !$this->loop ) {
$u = CdbFunctions::hash( $key );
@@ -204,6 +240,10 @@ class CdbReader_PHP extends CdbReader {
return false;
}
+ /**
+ * @param $key
+ * @return bool
+ */
protected function find( $key ) {
$this->findStart();
return $this->findNext( $key );
@@ -240,6 +280,11 @@ class CdbWriter_PHP extends CdbWriter {
}
}
+ /**
+ * @param $key
+ * @param $value
+ * @return
+ */
public function set( $key, $value ) {
if ( strval( $key ) === '' ) {
// DBA cross-check hack
@@ -251,10 +296,14 @@ class CdbWriter_PHP extends CdbWriter {
$this->addend( strlen( $key ), strlen( $value ), CdbFunctions::hash( $key ) );
}
+ /**
+ * @throws MWException
+ */
public function close() {
$this->finish();
- if( isset($this->handle) )
+ if( isset($this->handle) ) {
fclose( $this->handle );
+ }
if ( wfIsWindows() && file_exists($this->realFileName) ) {
unlink( $this->realFileName );
}
@@ -264,6 +313,10 @@ class CdbWriter_PHP extends CdbWriter {
unset( $this->handle );
}
+ /**
+ * @throws MWException
+ * @param $buf
+ */
protected function write( $buf ) {
$len = fwrite( $this->handle, $buf );
if ( $len !== strlen( $buf ) ) {
@@ -271,6 +324,10 @@ class CdbWriter_PHP extends CdbWriter {
}
}
+ /**
+ * @throws MWException
+ * @param $len
+ */
protected function posplus( $len ) {
$newpos = $this->pos + $len;
if ( $newpos > 0x7fffffff ) {
@@ -279,6 +336,11 @@ class CdbWriter_PHP extends CdbWriter {
$this->pos = $newpos;
}
+ /**
+ * @param $keylen
+ * @param $datalen
+ * @param $h
+ */
protected function addend( $keylen, $datalen, $h ) {
$this->hplist[] = array(
'h' => $h,
@@ -291,6 +353,11 @@ class CdbWriter_PHP extends CdbWriter {
$this->posplus( $datalen );
}
+ /**
+ * @throws MWException
+ * @param $keylen
+ * @param $datalen
+ */
protected function addbegin( $keylen, $datalen ) {
if ( $keylen > 0x7fffffff ) {
throw new MWException( __METHOD__.': key length too long' );
@@ -302,6 +369,9 @@ class CdbWriter_PHP extends CdbWriter {
$this->write( $buf );
}
+ /**
+ * @throws MWException
+ */
protected function finish() {
// Hack for DBA cross-check
$this->hplist = array_reverse( $this->hplist );
diff --git a/includes/ChangeTags.php b/includes/ChangeTags.php
index 7f0fee21..c8e522df 100644
--- a/includes/ChangeTags.php
+++ b/includes/ChangeTags.php
@@ -1,8 +1,5 @@
<?php
-if( !defined( 'MEDIAWIKI' ) )
- die;
-
class ChangeTags {
static function formatSummaryRow( $tags, $page ) {
if( !$tags )
@@ -28,11 +25,8 @@ class ChangeTags {
}
static function tagDescription( $tag ) {
- $msg = wfMsgExt( "tag-$tag", 'parseinline' );
- if ( wfEmptyMsg( "tag-$tag", $msg ) ) {
- return htmlspecialchars( $tag );
- }
- return $msg;
+ $msg = wfMessage( "tag-$tag" );
+ return $msg->exists() ? $msg->parse() : htmlspecialchars( $tag );
}
## Basic utility method to add tags to a particular change, given its rc_id, rev_id and/or log_id.
@@ -150,18 +144,26 @@ class ChangeTags {
}
/**
- * If $fullForm is set to false, then it returns an array of (label, form).
- * If $fullForm is true, it returns an entire form.
+ * Build a text box to select a change tag
+ *
+ * @param $selected String: tag to select by default
+ * @param $fullForm Boolean:
+ * - if false, then it returns an array of (label, form).
+ * - if true, it returns an entire form around the selector.
+ * @param $title Title object to send the form to.
+ * Used when, and only when $fullForm is true.
+ * @return String or array:
+ * - if $fullForm is false: Array with
+ * - if $fullForm is true: String, html fragment
*/
- static function buildTagFilterSelector( $selected='', $fullForm = false /* used to put a full form around the selector */ ) {
+ public static function buildTagFilterSelector( $selected='', $fullForm = false, Title $title = null ) {
global $wgUseTagFilter;
if ( !$wgUseTagFilter || !count( self::listDefinedTags() ) )
return $fullForm ? '' : array();
- global $wgTitle;
-
- $data = array( wfMsgExt( 'tag-filter', 'parseinline' ), Xml::input( 'tagfilter', 20, $selected ) );
+ $data = array( Html::rawElement( 'label', array( 'for' => 'tagfilter' ), wfMsgExt( 'tag-filter', 'parseinline' ) ),
+ Xml::input( 'tagfilter', 20, $selected ) );
if ( !$fullForm ) {
return $data;
@@ -175,7 +177,11 @@ class ChangeTags {
return $html;
}
- /** Basically lists defined tags which count even if they aren't applied to anything */
+ /**
+ *Basically lists defined tags which count even if they aren't applied to anything
+ *
+ * @return array
+ */
static function listDefinedTags() {
// Caching...
global $wgMemc;
diff --git a/includes/ChangesFeed.php b/includes/ChangesFeed.php
index f07b6505..c4c4a8a1 100644
--- a/includes/ChangesFeed.php
+++ b/includes/ChangesFeed.php
@@ -24,15 +24,19 @@ class ChangesFeed {
*
* @param $title String: feed's title
* @param $description String: feed's description
+ * @param $url String: url of origin page
* @return ChannelFeed subclass or false on failure
*/
- public function getFeedObject( $title, $description ) {
- global $wgSitename, $wgLanguageCode, $wgFeedClasses, $wgTitle;
- $feedTitle = "$wgSitename - {$title} [$wgLanguageCode]";
- if( !isset($wgFeedClasses[$this->format] ) )
+ public function getFeedObject( $title, $description, $url ) {
+ global $wgSitename, $wgLanguageCode, $wgFeedClasses;
+
+ if ( !isset( $wgFeedClasses[$this->format] ) ) {
return false;
+ }
+
+ $feedTitle = "$wgSitename - {$title} [$wgLanguageCode]";
return new $wgFeedClasses[$this->format](
- $feedTitle, htmlspecialchars( $description ), $wgTitle->getFullUrl() );
+ $feedTitle, htmlspecialchars( $description ), $url );
}
/**
@@ -57,11 +61,11 @@ class ChangesFeed {
FeedUtils::checkPurge( $timekey, $key );
- /*
- * Bumping around loading up diffs can be pretty slow, so where
- * possible we want to cache the feed output so the next visitor
- * gets it quick too.
- */
+ /**
+ * Bumping around loading up diffs can be pretty slow, so where
+ * possible we want to cache the feed output so the next visitor
+ * gets it quick too.
+ */
$cachedFeed = $this->loadFromCache( $lastmod, $timekey, $key );
if( is_string( $cachedFeed ) ) {
wfDebug( "RC: Outputting cached feed\n" );
@@ -106,12 +110,12 @@ class ChangesFeed {
$feedLastmod = $messageMemc->get( $timekey );
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
- * since it was rendered. This keeps a swarm of requests
- * from being too bad on a super-frequently edited wiki.
- */
+ /**
+ * If the cached feed was rendered very recently, we may
+ * go ahead and use it even if there have been edits made
+ * since it was rendered. This keeps a swarm of requests
+ * from being too bad on a super-frequently edited wiki.
+ */
$feedAge = time() - wfTimestamp( TS_UNIX, $feedLastmod );
$feedLastmodUnix = wfTimestamp( TS_UNIX, $feedLastmod );
@@ -145,6 +149,7 @@ class ChangesFeed {
$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 ) {
@@ -157,16 +162,27 @@ class ChangesFeed {
foreach( $sorted as $obj ) {
$title = Title::makeTitle( $obj->rc_namespace, $obj->rc_title );
- $talkpage = $title->getTalkPage();
+ $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_this_oldid ) {
+ $url = $title->getFullURL(
+ 'diff=' . $obj->rc_this_oldid .
+ '&oldid=' . $obj->rc_last_oldid
+ );
+ } else {
+ // log entry or something like that.
+ $url = $title->getFullURL();
+ }
+
$item = new FeedItem(
$title->getPrefixedText(),
FeedUtils::formatDiff( $obj ),
- $obj->rc_this_oldid ? $title->getFullURL( 'diff=' . $obj->rc_this_oldid . '&oldid=prev' ) : $title->getFullURL(),
+ $url,
$obj->rc_timestamp,
($obj->rc_deleted & Revision::DELETED_USER) ? wfMsgHtml('rev-deleted-user') : $obj->rc_user_text,
- $talkpage->getFullURL()
+ $talkpage
);
$feed->outItem( $item );
}
diff --git a/includes/ChangesList.php b/includes/ChangesList.php
index b8bc4f55..1858dc3a 100644
--- a/includes/ChangesList.php
+++ b/includes/ChangesList.php
@@ -16,6 +16,10 @@ class RCCacheEntry extends RecentChange {
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;
@@ -27,39 +31,64 @@ class RCCacheEntry extends RecentChange {
/**
* Base class for all changes lists
*/
-class ChangesList {
+class ChangesList extends ContextSource {
+
+ /**
+ * @var Skin
+ */
public $skin;
+
protected $watchlist = false;
+ protected $message;
+
/**
- * Changeslist contructor
- * @param $skin Skin
- */
- public function __construct( $skin ) {
- $this->skin = $skin;
+ * Changeslist contructor
+ *
+ * @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 specified user
- * Some users might want to use an enhanced list format, for instance
+ * Fetch an appropriate changes list class for the main context
+ * This first argument used to be an User object.
*
- * @param $user User to fetch the list class for
- * @return ChangesList derivative
+ * @deprecated in 1.18; use newFromContext() instead
+ * @param $unused Unused
+ * @return ChangesList|EnhancedChangesList|OldChangesList derivative
*/
- public static function newFromUser( &$user ) {
- global $wgRequest;
+ public static function newFromUser( $unused ) {
+ return self::newFromContext( RequestContext::getMain() );
+ }
- $sk = $user->getSkin();
+ /**
+ * 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 = $wgRequest->getBool( 'enhanced', $user->getOption( 'usenewrc' ) );
- return $new ? new EnhancedChangesList( $sk ) : new OldChangesList( $sk );
+ 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
@@ -81,21 +110,19 @@ class ChangesList {
}
}
-
/**
* Returns the appropriate flags for new page, minor change and patrolling
- * @param $new Boolean
- * @param $minor Boolean
- * @param $patrolled Boolean
+ * @param $flags Array Associative array of 'flag' => Bool
* @param $nothing String to use for empty space
- * @param $bot Boolean
* @return String
*/
- protected function recentChangesFlags( $new, $minor, $patrolled, $nothing = '&#160;', $bot = false ) {
- $f = $new ? self::flag( 'newpage' ) : $nothing;
- $f .= $minor ? self::flag( 'minor' ) : $nothing;
- $f .= $bot ? self::flag( 'bot' ) : $nothing;
- $f .= $patrolled ? self::flag( 'unpatrolled' ) : $nothing;
+ 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;
}
@@ -105,28 +132,36 @@ class ChangesList {
* unpatrolled edit. By default in English it will contain "N", "m", "b",
* "!" respectively, plus it will have an appropriate title and class.
*
- * @param $key String: 'newpage', 'unpatrolled', 'minor', or 'bot'
+ * @param $flag String: 'newpage', 'unpatrolled', 'minor', or 'bot'
* @return String: Raw HTML
*/
- public static function flag( $key ) {
+ public static function flag( $flag ) {
static $messages = null;
if ( is_null( $messages ) ) {
- foreach ( explode( ' ', 'minoreditletter boteditletter newpageletter ' .
- 'unpatrolledletter recentchanges-label-minor recentchanges-label-bot ' .
- 'recentchanges-label-newpage recentchanges-label-unpatrolled' ) as $msg ) {
- $messages[$msg] = wfMsgExt( $msg, 'escapenoentities' );
+ $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] = wfMsgExt( $value[0], 'escapenoentities' );
+ $value[1] = wfMsgExt( $value[1], 'escapenoentities' );
}
}
+
# Inconsistent naming, bleh
- if ( $key == 'newpage' || $key == 'unpatrolled' ) {
- $key2 = $key;
- } else {
- $key2 = $key . 'edit';
- }
- return "<abbr class=\"$key\" title=\""
- . $messages["recentchanges-label-$key"] . "\">"
- . $messages["${key2}letter"]
- . '</abbr>';
+ $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>';
}
/**
@@ -141,12 +176,12 @@ class ChangesList {
$this->rclistOpen = false;
return '';
}
-
+
/**
* Show formatted char difference
* @param $old Integer: bytes
* @param $new Integer: bytes
- * @returns String
+ * @return String
*/
public static function showCharacterDifference( $old, $new ) {
global $wgRCChangedSizeThreshold, $wgLang, $wgMiserMode;
@@ -157,29 +192,29 @@ class ChangesList {
if ( !isset($fastCharDiff[$code]) ) {
$fastCharDiff[$code] = $wgMiserMode || wfMsgNoTrans( 'rc-change-size' ) === '$1';
}
-
+
$formatedSize = $wgLang->formatNum($szdiff);
if ( !$fastCharDiff[$code] ) {
$formatedSize = wfMsgExt( 'rc-change-size', array( 'parsemag', 'escape' ), $formatedSize );
}
-
+
if( abs( $szdiff ) > abs( $wgRCChangedSizeThreshold ) ) {
$tag = 'strong';
} else {
- $tag = 'span';
+ $tag = 'span';
}
if( $szdiff === 0 ) {
return "<$tag class='mw-plusminus-null'>($formatedSize)</$tag>";
} elseif( $szdiff > 0 ) {
return "<$tag class='mw-plusminus-pos'>(+$formatedSize)</$tag>";
- } else {
+ } else {
return "<$tag class='mw-plusminus-neg'>($formatedSize)</$tag>";
}
}
/**
- * Returns text for the end of RC
+ * Returns text for the end of RC
* @return String
*/
public function endRecentChangesList() {
@@ -190,42 +225,38 @@ class ChangesList {
}
}
+ /**
+ * @param $s
+ * @param $rc RecentChange
+ * @return void
+ */
public function insertMove( &$s, $rc ) {
# Diff
$s .= '(' . $this->message['diff'] . ') (';
# Hist
- $s .= $this->skin->link(
+ $s .= Linker::linkKnown(
$rc->getMovedToTitle(),
$this->message['hist'],
array(),
- array( 'action' => 'history' ),
- array( 'known', 'noclasses' )
+ array( 'action' => 'history' )
) . ') . . ';
# "[[x]] moved to [[y]]"
$msg = ( $rc->mAttribs['rc_type'] == RC_MOVE ) ? '1movedto2' : '1movedto2_redir';
- $s .= wfMsg(
+ $s .= wfMsgHtml(
$msg,
- $this->skin->link(
+ Linker::linkKnown(
$rc->getTitle(),
null,
array(),
- array( 'redirect' => 'no' ),
- array( 'known', 'noclasses' )
+ array( 'redirect' => 'no' )
),
- $this->skin->link(
- $rc->getMovedToTitle(),
- null,
- array(),
- array(),
- array( 'known', 'noclasses' )
- )
+ Linker::linkKnown( $rc->getMovedToTitle() )
);
}
public function insertDateHeader( &$s, $rc_timestamp ) {
- global $wgLang;
# Make date header if necessary
- $date = $wgLang->date( $rc_timestamp, true, true );
+ $date = $this->getLang()->date( $rc_timestamp, true, true );
if( $date != $this->lastdate ) {
if( $this->lastdate != '' ) {
$s .= "</ul>\n";
@@ -238,20 +269,20 @@ class ChangesList {
public function insertLog( &$s, $title, $logtype ) {
$logname = LogPage::logName( $logtype );
- $s .= '(' . $this->skin->link(
- $title,
- $logname,
- array(),
- array(),
- array( 'known', 'noclasses' )
- ) . ')';
+ $s .= '(' . Linker::linkKnown( $title, htmlspecialchars( $logname ) ) . ')';
}
+ /**
+ * @param $s
+ * @param $rc RecentChange
+ * @param $unpatrolled
+ * @return void
+ */
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'];
- } else if( !self::userCan($rc,Revision::DELETED_TEXT) ) {
+ } elseif( !self::userCan($rc,Revision::DELETED_TEXT) ) {
$diffLink = $this->message['diff'];
} else {
$query = array(
@@ -264,31 +295,35 @@ class ChangesList {
$query['rcid'] = $rc->mAttribs['rc_id'];
};
- $diffLink = $this->skin->link(
+ $diffLink = Linker::linkKnown(
$rc->getTitle(),
$this->message['diff'],
array( 'tabindex' => $rc->counter ),
- $query,
- array( 'known', 'noclasses' )
+ $query
);
}
$s .= '(' . $diffLink . $this->message['pipe-separator'];
# History link
- $s .= $this->skin->link(
+ $s .= Linker::linkKnown(
$rc->getTitle(),
$this->message['hist'],
array(),
array(
'curid' => $rc->mAttribs['rc_cur_id'],
'action' => 'history'
- ),
- array( 'known', 'noclasses' )
+ )
);
$s .= ') . . ';
}
+ /**
+ * @param $s
+ * @param $rc RecentChange
+ * @param $unpatrolled
+ * @param $watched
+ * @return void
+ */
public function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) {
- global $wgContLang;
# 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();
@@ -298,21 +333,19 @@ class ChangesList {
}
if( $this->isDeleted($rc,Revision::DELETED_TEXT) ) {
- $articlelink = $this->skin->link(
+ $articlelink = Linker::linkKnown(
$rc->getTitle(),
null,
array(),
- $params,
- array( 'known', 'noclasses' )
+ $params
);
$articlelink = '<span class="history-deleted">' . $articlelink . '</span>';
} else {
- $articlelink = ' '. $this->skin->link(
+ $articlelink = ' '. Linker::linkKnown(
$rc->getTitle(),
null,
array(),
- $params,
- array( 'known', 'noclasses' )
+ $params
);
}
# Bolden pages watched by this user
@@ -320,7 +353,7 @@ class ChangesList {
$articlelink = "<strong class=\"mw-watched\">{$articlelink}</strong>";
}
# RTL/LTR marker
- $articlelink .= $wgContLang->getDirMark();
+ $articlelink .= $this->getLang()->getDirMark();
wfRunHooks( 'ChangesListInsertArticleLink',
array(&$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched) );
@@ -328,41 +361,54 @@ class ChangesList {
$s .= " $articlelink";
}
+ /**
+ * @param $s
+ * @param $rc RecentChange
+ * @return void
+ */
public function insertTimestamp( &$s, $rc ) {
- global $wgLang;
- $s .= $this->message['semicolon-separator'] .
- $wgLang->time( $rc->mAttribs['rc_timestamp'], true, true ) . ' . . ';
+ $s .= $this->message['semicolon-separator'] .
+ $this->getLang()->time( $rc->mAttribs['rc_timestamp'], true, true ) . ' . . ';
}
- /** Insert links to user page, user talk page and eventually a blocking link */
+ /** Insert links to user page, user talk page and eventually a blocking link
+ *
+ * @param $rc RecentChange
+ */
public function insertUserRelatedLinks( &$s, &$rc ) {
if( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
- $s .= ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
+ $s .= ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
} else {
- $s .= $this->skin->userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
- $s .= $this->skin->userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+ $s .= 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 */
+ /** insert a formatted action
+ *
+ * @param $rc RecentChange
+ */
public function insertAction( &$s, &$rc ) {
if( $rc->mAttribs['rc_type'] == RC_LOG ) {
if( $this->isDeleted( $rc, LogPage::DELETED_ACTION ) ) {
$s .= ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-event' ) . '</span>';
} else {
$s .= ' '.LogPage::actionText( $rc->mAttribs['rc_log_type'], $rc->mAttribs['rc_log_action'],
- $rc->getTitle(), $this->skin, LogPage::extractParams( $rc->mAttribs['rc_params'] ), true, true );
+ $rc->getTitle(), $this->getSkin(), LogPage::extractParams( $rc->mAttribs['rc_params'] ), true, true );
}
}
}
- /** insert a formatted comment */
+ /** insert a formatted comment
+ *
+ * @param $rc RecentChange
+ */
public function insertComment( &$s, &$rc ) {
if( $rc->mAttribs['rc_type'] != RC_MOVE && $rc->mAttribs['rc_type'] != RC_MOVE_OVER_REDIRECT ) {
if( $this->isDeleted( $rc, Revision::DELETED_COMMENT ) ) {
$s .= ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-comment' ) . '</span>';
} else {
- $s .= $this->skin->commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() );
+ $s .= Linker::commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() );
}
}
}
@@ -380,12 +426,11 @@ class ChangesList {
* Returns the string which indicates the number of watching users
*/
protected function numberofWatchingusers( $count ) {
- global $wgLang;
static $cache = array();
if( $count > 0 ) {
if( !isset( $cache[$count] ) ) {
$cache[$count] = wfMsgExt( 'number_of_watching_users_RCview',
- array('parsemag', 'escape' ), $wgLang->formatNum( $count ) );
+ array('parsemag', 'escape' ), $this->getLang()->formatNum( $count ) );
}
return $cache[$count];
} else {
@@ -425,15 +470,18 @@ class ChangesList {
return '<span class="mw-rc-unwatched">' . $link . '</span>';
}
}
-
- /** Inserts a rollback link */
+
+ /** Inserts a rollback link
+ *
+ * @param $s
+ * @param $rc RecentChange
+ */
public function insertRollback( &$s, &$rc ) {
- global $wgUser;
if( !$rc->mAttribs['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 ($wgUser->isAllowed('rollback') && $rc->mAttribs['page_latest'] == $rc->mAttribs['rc_this_oldid'] )
+ if ( $this->getUser()->isAllowed('rollback') && $rc->mAttribs['page_latest'] == $rc->mAttribs['rc_this_oldid'] )
{
$rev = new Revision( array(
'id' => $rc->mAttribs['rc_this_oldid'],
@@ -442,15 +490,21 @@ class ChangesList {
'deleted' => $rc->mAttribs['rc_deleted']
) );
$rev->setTitle( $page );
- $s .= ' '.$this->skin->generateRollback( $rev );
+ $s .= ' '.Linker::generateRollback( $rev, $this->getContext() );
}
}
}
+ /**
+ * @param $s
+ * @param $rc RecentChange
+ * @param $classes
+ * @return
+ */
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;
@@ -468,12 +522,14 @@ class ChangesList {
class OldChangesList extends ChangesList {
/**
* Format a line using the old system (aka without any javascript).
+ *
+ * @param $rc RecentChange
*/
public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) {
- global $wgLang, $wgRCShowChangedSize, $wgUser;
+ global $wgRCShowChangedSize;
wfProfileIn( __METHOD__ );
# Should patrol-related stuff be shown?
- $unpatrolled = $wgUser->useRCPatrol() && !$rc->mAttribs['rc_patrolled'];
+ $unpatrolled = $this->getUser()->useRCPatrol() && !$rc->mAttribs['rc_patrolled'];
$dateheader = ''; // $s now contains only <li>...</li>, for hooks' convenience.
$this->insertDateHeader( $dateheader, $rc->mAttribs['rc_timestamp'] );
@@ -499,7 +555,7 @@ class OldChangesList extends ChangesList {
$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 ) = SpecialPage::resolveAliasWithSubpage( $rc->mAttribs['rc_title'] );
+ list( $name, $subpage ) = SpecialPageFactory::resolveAlias( $rc->mAttribs['rc_title'] );
if( $name == 'Log' ) {
$this->insertLog( $s, $rc->getTitle(), $subpage );
}
@@ -507,8 +563,15 @@ class OldChangesList extends ChangesList {
} else {
$this->insertDiffHist( $s, $rc, $unpatrolled );
# M, N, b and ! (minor, new, bot and unpatrolled)
- $s .= $this->recentChangesFlags( $rc->mAttribs['rc_new'], $rc->mAttribs['rc_minor'],
- $unpatrolled, '', $rc->mAttribs['rc_bot'] );
+ $s .= $this->recentChangesFlags(
+ array(
+ 'newpage' => $rc->mAttribs['rc_new'],
+ 'minor' => $rc->mAttribs['rc_minor'],
+ 'unpatrolled' => $unpatrolled,
+ 'bot' => $rc->mAttribs['rc_bot']
+ ),
+ ''
+ );
$this->insertArticleLink( $s, $rc, $unpatrolled, $watched );
}
# Edit/log timestamp
@@ -522,6 +585,8 @@ class OldChangesList extends ChangesList {
}
# User tool links
$this->insertUserRelatedLinks( $s, $rc );
+ # LTR/RTL direction mark
+ $s .= $this->getLang()->getDirMark();
# Log action text (if any)
$this->insertAction( $s, $rc );
# Edit or log comment
@@ -532,17 +597,17 @@ class OldChangesList extends ChangesList {
$this->insertRollback( $s, $rc );
# For subclasses
$this->insertExtra( $s, $rc, $classes );
-
+
# How many users watch this page
if( $rc->numberofWatchingusers > 0 ) {
- $s .= ' ' . wfMsgExt( 'number_of_watching_users_RCview',
- array( 'parsemag', 'escape' ), $wgLang->formatNum( $rc->numberofWatchingusers ) );
+ $s .= ' ' . wfMsgExt( 'number_of_watching_users_RCview',
+ array( 'parsemag', 'escape' ), $this->getLang()->formatNum( $rc->numberofWatchingusers ) );
}
-
+
if( $this->watchlist ) {
$classes[] = Sanitizer::escapeClass( 'watchlist-'.$rc->mAttribs['rc_namespace'].'-'.$rc->mAttribs['rc_title'] );
}
-
+
wfRunHooks( 'OldChangesListRecentChangesLine', array(&$this, &$s, $rc) );
wfProfileOut( __METHOD__ );
@@ -560,34 +625,32 @@ class EnhancedChangesList extends ChangesList {
* @return String
*/
public function beginRecentChangesList() {
- global $wgOut;
$this->rc_cache = array();
$this->rcMoveIndex = 0;
$this->rcCacheIndex = 0;
$this->lastdate = '';
$this->rclistOpen = false;
- $wgOut->addModules( 'mediawiki.legacy.enhancedchanges' );
+ $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 ) {
- global $wgLang, $wgUser;
-
wfProfileIn( __METHOD__ );
# Create a specialised object
$rc = RCCacheEntry::newFromParent( $baseRC );
- # Extract fields from DB into the function scope (rc_xxxx variables)
- // FIXME: Would be good to replace this extract() call with something
- // that explicitly initializes variables.
- extract( $rc->mAttribs );
- $curIdEq = array( 'curid' => $rc_cur_id );
+ $curIdEq = array( 'curid' => $rc->mAttribs['rc_cur_id'] );
# If it's a new day, add the headline and flush the cache
- $date = $wgLang->date( $rc_timestamp, true );
+ $date = $this->getLang()->date( $rc->mAttribs['rc_timestamp'], true );
$ret = '';
if( $date != $this->lastdate ) {
# Process current cache
@@ -598,48 +661,50 @@ class EnhancedChangesList extends ChangesList {
}
# Should patrol-related stuff be shown?
- if( $wgUser->useRCPatrol() ) {
- $rc->unpatrolled = !$rc_patrolled;
+ if( $this->getUser()->useRCPatrol() ) {
+ $rc->unpatrolled = !$rc->mAttribs['rc_patrolled'];
} else {
$rc->unpatrolled = false;
}
$showdifflinks = true;
# Make article link
+ $type = $rc->mAttribs['rc_type'];
+ $logType = $rc->mAttribs['rc_log_type'];
// Page moves
- if( $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
- $msg = ( $rc_type == RC_MOVE ) ? "1movedto2" : "1movedto2_redir";
- $clink = wfMsg( $msg, $this->skin->linkKnown( $rc->getTitle(), null,
+ if( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
+ $msg = ( $type == RC_MOVE ) ? "1movedto2" : "1movedto2_redir";
+ $clink = wfMsg( $msg, Linker::linkKnown( $rc->getTitle(), null,
array(), array( 'redirect' => 'no' ) ),
- $this->skin->linkKnown( $rc->getMovedToTitle() ) );
+ Linker::linkKnown( $rc->getMovedToTitle() ) );
// New unpatrolled pages
- } else if( $rc->unpatrolled && $rc_type == RC_NEW ) {
- $clink = $this->skin->linkKnown( $rc->getTitle(), null, array(),
- array( 'rcid' => $rc_id ) );
+ } elseif( $rc->unpatrolled && $type == RC_NEW ) {
+ $clink = Linker::linkKnown( $rc->getTitle(), null, array(),
+ array( 'rcid' => $rc->mAttribs['rc_id'] ) );
// Log entries
- } else if( $rc_type == RC_LOG ) {
- if( $rc_log_type ) {
- $logtitle = SpecialPage::getTitleFor( 'Log', $rc_log_type );
- $clink = '(' . $this->skin->linkKnown( $logtitle,
- LogPage::logName($rc_log_type) ) . ')';
+ } elseif( $type == RC_LOG ) {
+ if( $logType ) {
+ $logtitle = SpecialPage::getTitleFor( 'Log', $logType );
+ $clink = '(' . Linker::linkKnown( $logtitle,
+ LogPage::logName( $logType ) ) . ')';
} else {
- $clink = $this->skin->link( $rc->getTitle() );
+ $clink = Linker::link( $rc->getTitle() );
}
$watched = false;
// Log entries (old format) and special pages
- } elseif( $rc_namespace == NS_SPECIAL ) {
- list( $specialName, $logtype ) = SpecialPage::resolveAliasWithSubpage( $rc_title );
+ } elseif( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) {
+ list( $specialName, $logtype ) = SpecialPageFactory::resolveAlias( $rc->mAttribs['rc_title'] );
if ( $specialName == 'Log' ) {
# Log updates, etc
$logname = LogPage::logName( $logtype );
- $clink = '(' . $this->skin->linkKnown( $rc->getTitle(), $logname ) . ')';
+ $clink = '(' . Linker::linkKnown( $rc->getTitle(), $logname ) . ')';
} else {
wfDebug( "Unexpected special page in recentchanges\n" );
$clink = '';
}
// Edits
} else {
- $clink = $this->skin->linkKnown( $rc->getTitle() );
+ $clink = Linker::linkKnown( $rc->getTitle() );
}
# Don't show unusable diff links
@@ -647,7 +712,7 @@ class EnhancedChangesList extends ChangesList {
$showdifflinks = false;
}
- $time = $wgLang->time( $rc_timestamp, true, true );
+ $time = $this->getLang()->time( $rc->mAttribs['rc_timestamp'], true, true );
$rc->watched = $watched;
$rc->link = $clink;
$rc->timestamp = $time;
@@ -655,20 +720,22 @@ class EnhancedChangesList extends ChangesList {
# 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_id );
+ $rcIdQuery = array( 'rcid' => $rc->mAttribs['rc_id'] );
} else {
$rcIdQuery = array();
}
- $querycur = $curIdEq + array( 'diff' => '0', 'oldid' => $rc_this_oldid );
- $querydiff = $curIdEq + array( 'diff' => $rc_this_oldid, 'oldid' =>
- $rc_last_oldid ) + $rcIdQuery;
+ $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'];
- } else if( in_array( $rc_type, array(RC_NEW,RC_LOG,RC_MOVE,RC_MOVE_OVER_REDIRECT) ) ) {
- if ( $rc_type != RC_NEW ) {
+ } 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 ) );
@@ -683,21 +750,21 @@ class EnhancedChangesList extends ChangesList {
}
# Make "last" link
- if( !$showdifflinks || !$rc_last_oldid ) {
+ if( !$showdifflinks || !$lastOldid ) {
$lastLink = $this->message['last'];
- } else if( $rc_type == RC_LOG || $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
+ } elseif( in_array( $type, array( RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ) ) ) {
$lastLink = $this->message['last'];
} else {
- $lastLink = $this->skin->linkKnown( $rc->getTitle(), $this->message['last'],
- array(), $curIdEq + array('diff' => $rc_this_oldid, 'oldid' => $rc_last_oldid) + $rcIdQuery );
+ $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">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
+ if( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
+ $rc->userlink = ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-user' ) . '</span>';
} else {
- $rc->userlink = $this->skin->userLink( $rc_user, $rc_user_text );
- $rc->usertalklink = $this->skin->userToolLinks( $rc_user, $rc_user_text );
+ $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;
@@ -708,13 +775,13 @@ class EnhancedChangesList extends ChangesList {
# Page moves go on their own line
$title = $rc->getTitle();
$secureName = $title->getPrefixedDBkey();
- if( $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
+ 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( $rc_type == RC_LOG ){
- $secureName = SpecialPage::getTitleFor( 'Log', $rc_log_type )->getPrefixedDBkey();
+ if( $type == RC_LOG ){
+ $secureName = SpecialPage::getTitleFor( 'Log', $logType )->getPrefixedDBkey();
}
if( !isset( $this->rc_cache[$secureName] ) ) {
$this->rc_cache[$secureName] = array();
@@ -732,16 +799,16 @@ class EnhancedChangesList extends ChangesList {
* Enhanced RC group
*/
protected function recentChangesBlockGroup( $block ) {
- global $wgLang, $wgContLang, $wgRCShowChangedSize;
+ global $wgRCShowChangedSize;
wfProfileIn( __METHOD__ );
# Add the namespace and title of the block as part of the class
if ( $block[0]->mAttribs['rc_log_type'] ) {
# Log entry
- $classes = 'mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-log-' . $block[0]->mAttribs['rc_log_type'] . '-' . $block[0]->mAttribs['rc_title'] );
+ $classes = 'mw-collapsible mw-collapsed mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-log-' . $block[0]->mAttribs['rc_log_type'] . '-' . $block[0]->mAttribs['rc_title'] );
} else {
- $classes = 'mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-ns' . $block[0]->mAttribs['rc_namespace'] . '-' . $block[0]->mAttribs['rc_title'] );
+ $classes = 'mw-collapsible mw-collapsed mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-ns' . $block[0]->mAttribs['rc_namespace'] . '-' . $block[0]->mAttribs['rc_title'] );
}
$r = Html::openElement( 'table', array( 'class' => $classes ) ) .
Html::openElement( 'tr' );
@@ -794,51 +861,56 @@ class EnhancedChangesList extends ChangesList {
$users = array();
foreach( $userlinks as $userlink => $count) {
$text = $userlink;
- $text .= $wgContLang->getDirMark();
+ $text .= $this->getLang()->getDirMark();
if( $count > 1 ) {
- $text .= ' (' . $wgLang->formatNum( $count ) . '×)';
+ $text .= ' (' . $this->getLang()->formatNum( $count ) . '×)';
}
array_push( $users, $text );
}
- $users = ' <span class="changedby">[' .
+ $users = ' <span class="changedby">[' .
implode( $this->message['semicolon-separator'], $users ) . ']</span>';
- # ID for JS visibility toggle
- $jsid = $this->rcCacheIndex;
- # onclick handler to toggle hidden/expanded
- $toggleLink = "onclick='toggleVisibility($jsid); return false'";
# Title for <a> tags
$expandTitle = htmlspecialchars( wfMsg( 'rc-enhanced-expand' ) );
$closeTitle = htmlspecialchars( wfMsg( 'rc-enhanced-hide' ) );
- $tl = "<span id='mw-rc-openarrow-$jsid' class='mw-changeslist-expanded' style='visibility:hidden'><a href='#' $toggleLink title='$expandTitle'>" . $this->sideArrow() . "</a></span>";
- $tl .= "<span id='mw-rc-closearrow-$jsid' class='mw-changeslist-hidden' style='display:none'><a href='#' $toggleLink title='$closeTitle'>" . $this->downArrow() . "</a></span>";
- $r .= '<td class="mw-enhanced-rc">'.$tl.'&#160;';
+ $tl = "<span class='mw-collapsible-toggle'>"
+ . "<span class='mw-rc-openarrow'>"
+ . "<a href='#' title='$expandTitle'>{$this->sideArrow()}</a>"
+ . "</span><span class='mw-rc-closearrow'>"
+ . "<a href='#' title='$closeTitle'>{$this->downArrow()}</a>"
+ . "</span></span>";
+ $r .= "<td>$tl</td>";
# Main line
- $r .= $this->recentChangesFlags( $isnew, false, $unpatrolled, '&#160;', $bot );
+ $r .= '<td class="mw-enhanced-rc">' . $this->recentChangesFlags( array(
+ 'newpage' => $isnew,
+ 'minor' => false,
+ 'unpatrolled' => $unpatrolled,
+ 'bot' => $bot ,
+ ) );
# Timestamp
- $r .= '&#160;'.$block[0]->timestamp.'&#160;</td><td style="padding:0px;">';
+ $r .= '&#160;'.$block[0]->timestamp.'&#160;</td><td>';
# Article link
if( $namehidden ) {
$r .= ' <span class="history-deleted">' . wfMsgHtml( 'rev-deleted-event' ) . '</span>';
- } else if( $allLogs ) {
+ } elseif( $allLogs ) {
$r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
} else {
$this->insertArticleLink( $r, $block[0], $block[0]->unpatrolled, $block[0]->watched );
}
- $r .= $wgContLang->getDirMark();
+ $r .= $this->getLang()->getDirMark();
$queryParams['curid'] = $curId;
# Changes message
$n = count($block);
static $nchanges = array();
if ( !isset( $nchanges[$n] ) ) {
- $nchanges[$n] = wfMsgExt( 'nchanges', array( 'parsemag', 'escape' ), $wgLang->formatNum( $n ) );
+ $nchanges[$n] = wfMsgExt( 'nchanges', array( 'parsemag', 'escape' ), $this->getLang()->formatNum( $n ) );
}
# Total change link
$r .= ' ';
@@ -846,14 +918,14 @@ class EnhancedChangesList extends ChangesList {
$r .= '(';
if( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT ) ) {
$r .= $nchanges[$n];
- } else if( $isnew ) {
+ } elseif( $isnew ) {
$r .= $nchanges[$n];
} else {
$params = $queryParams;
$params['diff'] = $currentRevision;
$params['oldid'] = $oldid;
-
- $r .= $this->skin->link(
+
+ $r .= Linker::link(
$block[0]->getTitle(),
$nchanges[$n],
array(),
@@ -866,19 +938,18 @@ class EnhancedChangesList extends ChangesList {
# History
if( $allLogs ) {
// don't show history link for logs
- } else if( $namehidden || !$block[0]->getTitle()->exists() ) {
+ } elseif( $namehidden || !$block[0]->getTitle()->exists() ) {
$r .= $this->message['pipe-separator'] . $this->message['hist'] . ')';
} else {
$params = $queryParams;
$params['action'] = 'history';
$r .= $this->message['pipe-separator'] .
- $this->skin->link(
+ Linker::linkKnown(
$block[0]->getTitle(),
$this->message['hist'],
array(),
- $params,
- array( 'known', 'noclasses' )
+ $params
) . ')';
}
$r .= ' . . ';
@@ -908,55 +979,51 @@ class EnhancedChangesList extends ChangesList {
$r .= $users;
$r .= $this->numberofWatchingusers($block[0]->numberofWatchingusers);
- $r .= "</td></tr></table>\n";
-
# Sub-entries
- $r .= '<div id="mw-rc-subentries-'.$jsid.'" class="mw-changeslist-hidden">';
- $r .= '<table class="mw-enhanced-rc">';
foreach( $block as $rcObj ) {
- # Extract fields from DB into the function scope (rc_xxxx variables)
- // FIXME: Would be good to replace this extract() call with something
- // that explicitly initializes variables.
# Classes to apply -- TODO implement
$classes = array();
- extract( $rcObj->mAttribs );
+ $type = $rcObj->mAttribs['rc_type'];
#$r .= '<tr><td valign="top">'.$this->spacerArrow();
- $r .= '<tr><td style="vertical-align:top;font-family:monospace; padding:0px;">';
- $r .= $this->spacerIndent() . $this->spacerIndent();
- $r .= $this->recentChangesFlags( $rc_new, $rc_minor, $rcObj->unpatrolled, '&#160;', $rc_bot );
- $r .= '&#160;</td><td style="vertical-align:top; padding:0px;"><span style="font-family:monospace">';
+ $r .= '<tr><td></td><td class="mw-enhanced-rc">';
+ $r .= $this->recentChangesFlags( array(
+ 'newpage' => $rcObj->mAttribs['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( $rc_this_oldid != 0 ) {
- $params['oldid'] = $rc_this_oldid;
+ if( $rcObj->mAttribs['rc_this_oldid'] != 0 ) {
+ $params['oldid'] = $rcObj->mAttribs['rc_this_oldid'];
}
# Log timestamp
- if( $rc_type == RC_LOG ) {
+ if( $type == RC_LOG ) {
$link = $rcObj->timestamp;
# Revision link
- } else if( !ChangesList::userCan($rcObj,Revision::DELETED_TEXT) ) {
+ } elseif( !ChangesList::userCan($rcObj,Revision::DELETED_TEXT) ) {
$link = '<span class="history-deleted">'.$rcObj->timestamp.'</span> ';
} else {
- if ( $rcObj->unpatrolled && $rc_type == RC_NEW) {
+ if ( $rcObj->unpatrolled && $type == RC_NEW) {
$params['rcid'] = $rcObj->mAttribs['rc_id'];
}
- $link = $this->skin->link(
+ $link = Linker::linkKnown(
$rcObj->getTitle(),
$rcObj->timestamp,
array(),
- $params,
- array( 'known', 'noclasses' )
+ $params
);
if( $this->isDeleted($rcObj,Revision::DELETED_TEXT) )
$link = '<span class="history-deleted">'.$link.'</span> ';
}
$r .= $link . '</span>';
- if ( !$rc_type == RC_LOG || $rc_type == RC_NEW ) {
+ if ( !$type == RC_LOG || $type == RC_NEW ) {
$r .= ' (';
$r .= $rcObj->curlink;
$r .= $this->message['pipe-separator'];
@@ -966,9 +1033,10 @@ class EnhancedChangesList extends ChangesList {
$r .= ' . . ';
# Character diff
- if( $wgRCShowChangedSize ) {
- $r .= ( $rcObj->getCharacterDifference() == '' ? '' : $rcObj->getCharacterDifference() . ' . . ' ) ;
+ if( $wgRCShowChangedSize && $rcObj->getCharacterDifference() ) {
+ $r .= $rcObj->getCharacterDifference() . ' . . ' ;
}
+
# User links
$r .= $rcObj->userlink;
$r .= $rcObj->usertalklink;
@@ -983,7 +1051,7 @@ class EnhancedChangesList extends ChangesList {
$r .= "</td></tr>\n";
}
- $r .= "</table></div>\n";
+ $r .= "</table>\n";
$this->rcCacheIndex++;
@@ -1013,8 +1081,8 @@ class EnhancedChangesList extends ChangesList {
* @return String: HTML <img> tag
*/
protected function sideArrow() {
- global $wgContLang;
- $dir = $wgContLang->isRTL() ? 'l' : 'r';
+ global $wgLang;
+ $dir = $wgLang->isRTL() ? 'l' : 'r';
return $this->arrow( $dir, '+', wfMsg( 'rc-enhanced-expand' ) );
}
@@ -1036,70 +1104,58 @@ class EnhancedChangesList extends ChangesList {
}
/**
- * Add a set of spaces
- * @return String: HTML <td> tag
- */
- protected function spacerIndent() {
- return '&#160;&#160;&#160;&#160;&#160;';
- }
-
- /**
* Enhanced RC ungrouped line.
+ *
+ * @param $rcObj RecentChange
* @return String: a HTML formated line (generated using $r)
*/
protected function recentChangesBlockLine( $rcObj ) {
global $wgRCShowChangedSize;
wfProfileIn( __METHOD__ );
+ $query['curid'] = $rcObj->mAttribs['rc_cur_id'];
- # Extract fields from DB into the function scope (rc_xxxx variables)
- // FIXME: Would be good to replace this extract() call with something
- // that explicitly initializes variables.
- // TODO implement
- extract( $rcObj->mAttribs );
- $query['curid'] = $rc_cur_id;
-
- if( $rc_log_type ) {
+ $type = $rcObj->mAttribs['rc_type'];
+ $logType = $rcObj->mAttribs['rc_log_type'];
+ if( $logType ) {
# Log entry
- $classes = 'mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-log-' . $rc_log_type . '-' . $rcObj->mAttribs['rc_title'] );
+ $classes = 'mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-log-' . $logType . '-' . $rcObj->mAttribs['rc_title'] );
} else {
$classes = 'mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-ns' . $rcObj->mAttribs['rc_namespace'] . '-' . $rcObj->mAttribs['rc_title'] );
}
$r = Html::openElement( 'table', array( 'class' => $classes ) ) .
Html::openElement( 'tr' );
- $r .= '<td class="mw-enhanced-rc">' . $this->spacerArrow() . '&#160;';
+ $r .= '<td class="mw-enhanced-rc">' . $this->spacerArrow();
# Flag and Timestamp
- if( $rc_type == RC_MOVE || $rc_type == RC_MOVE_OVER_REDIRECT ) {
+ if( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
$r .= '&#160;&#160;&#160;&#160;'; // 4 flags -> 4 spaces
} else {
- $r .= $this->recentChangesFlags( $rc_type == RC_NEW, $rc_minor, $rcObj->unpatrolled, '&#160;', $rc_bot );
+ $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 style="padding:0px;">';
+ $r .= '&#160;'.$rcObj->timestamp.'&#160;</td><td>';
# Article or log link
- if( $rc_log_type ) {
- $logtitle = Title::newFromText( "Log/$rc_log_type", NS_SPECIAL );
- $logname = LogPage::logName( $rc_log_type );
- $r .= '(' . $this->skin->link(
- $logtitle,
- $logname,
- array(),
- array(),
- array( 'known', 'noclasses' )
- ) . ')';
+ if( $logType ) {
+ $logtitle = SpecialPage::getTitleFor( 'Log', $logType );
+ $logname = LogPage::logName( $logType );
+ $r .= '(' . Linker::linkKnown( $logtitle, htmlspecialchars( $logname ) ) . ')';
} else {
$this->insertArticleLink( $r, $rcObj, $rcObj->unpatrolled, $rcObj->watched );
}
# Diff and hist links
- if ( $rc_type != RC_LOG ) {
+ if ( $type != RC_LOG ) {
$r .= ' ('. $rcObj->difflink . $this->message['pipe-separator'];
$query['action'] = 'history';
- $r .= $this->skin->link(
+ $r .= Linker::linkKnown(
$rcObj->getTitle(),
$this->message['hist'],
array(),
- $query,
- array( 'known', 'noclasses' )
+ $query
) . ')';
}
$r .= ' . . ';
@@ -1110,12 +1166,12 @@ class EnhancedChangesList extends ChangesList {
# User/talk
$r .= ' '.$rcObj->userlink . $rcObj->usertalklink;
# Log action (if any)
- if( $rc_log_type ) {
+ if( $logType ) {
if( $this->isDeleted($rcObj,LogPage::DELETED_ACTION) ) {
$r .= ' <span class="history-deleted">' . wfMsgHtml('rev-deleted-event') . '</span>';
} else {
- $r .= ' ' . LogPage::actionText( $rc_log_type, $rc_log_action, $rcObj->getTitle(),
- $this->skin, LogPage::extractParams($rc_params), true, true );
+ $r .= ' ' . LogPage::actionText( $logType, $rcObj->mAttribs['rc_log_action'], $rcObj->getTitle(),
+ $this->getSkin(), LogPage::extractParams( $rcObj->mAttribs['rc_params'] ), true, true );
}
}
$this->insertComment( $r, $rcObj );
@@ -1136,6 +1192,8 @@ class EnhancedChangesList extends ChangesList {
/**
* 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 ) {
@@ -1159,7 +1217,7 @@ class EnhancedChangesList extends ChangesList {
}
/**
- * Returns text for the end of RC
+ * Returns text for the end of RC
* If enhanced RC is in use, returns pretty much all the text
*/
public function endRecentChangesList() {
diff --git a/includes/Collation.php b/includes/Collation.php
index f00b568f..0c510b78 100644
--- a/includes/Collation.php
+++ b/includes/Collation.php
@@ -3,6 +3,9 @@
abstract class Collation {
static $instance;
+ /**
+ * @return Collation
+ */
static function singleton() {
if ( !self::$instance ) {
global $wgCategoryCollation;
@@ -11,13 +14,30 @@ abstract class Collation {
return self::$instance;
}
+ /**
+ * @throws MWException
+ * @param $collationName string
+ * @return Collation
+ */
static function factory( $collationName ) {
switch( $collationName ) {
case 'uppercase':
return new UppercaseCollation;
+ case 'identity':
+ return new IdentityCollation;
case 'uca-default':
return new IcuCollation( 'root' );
default:
+ # Provide a mechanism for extensions to hook in.
+
+ $collationObject = null;
+ wfRunHooks( 'Collation::factory', array( $collationName, &$collationObject ) );
+
+ if ( $collationObject instanceof Collation ) {
+ return $collationObject;
+ }
+
+ // If all else fails...
throw new MWException( __METHOD__.": unknown collation type \"$collationName\"" );
}
}
@@ -81,6 +101,30 @@ class UppercaseCollation extends Collation {
}
}
+/**
+ * Collation class that's essentially a no-op.
+ *
+ * Does sorting based on binary value of the string.
+ * Like how things were pre 1.17.
+ */
+class IdentityCollation extends Collation {
+
+ function getSortKey( $string ) {
+ return $string;
+ }
+
+ function getFirstLetter( $string ) {
+ global $wgContLang;
+ // Copied from UppercaseCollation.
+ // I'm kind of unclear on when this could happen...
+ if ( $string[0] == "\0" ) {
+ $string = substr( $string, 1 );
+ }
+ return $wgContLang->firstChar( $string );
+ }
+}
+
+
class IcuCollation extends Collation {
var $primaryCollator, $mainCollator, $locale;
var $firstLetterData;
@@ -259,13 +303,13 @@ class IcuCollation extends Collation {
* Do a binary search, and return the index of the largest item that sorts
* less than or equal to the target value.
*
- * @param $valueCallback A function to call to get the value with
+ * @param $valueCallback array A function to call to get the value with
* a given array index.
- * @param $valueCount The number of items accessible via $valueCallback,
+ * @param $valueCount int The number of items accessible via $valueCallback,
* indexed from 0 to $valueCount - 1
- * @param $comparisonCallback A callback to compare two values, returning
+ * @param $comparisonCallback array A callback to compare two values, returning
* -1, 0 or 1 in the style of strcmp().
- * @param $target The target value to find.
+ * @param $target string The target value to find.
*
* @return The item index of the lower bound, or false if the target value
* sorts before all items.
diff --git a/includes/ConfEditor.php b/includes/ConfEditor.php
index b08b77df..42a7173d 100644
--- a/includes/ConfEditor.php
+++ b/includes/ConfEditor.php
@@ -1,6 +1,6 @@
<?php
-/**
+/**
* This is a state machine style parser with two internal stacks:
* * A next state stack, which determines the state the machine will progress to next
* * A path stack, which keeps track of the logical location in the file.
@@ -40,8 +40,8 @@ class ConfEditor {
/** The previous ConfEditorToken object */
var $prevToken;
- /**
- * The state machine stack. This is an array of strings where the topmost
+ /**
+ * The state machine stack. This is an array of strings where the topmost
* element will be popped off and become the next parser state.
*/
var $stateStack;
@@ -66,7 +66,7 @@ class ConfEditor {
var $pathStack;
/**
- * The elements of the top of the pathStack for every path encountered, indexed
+ * The elements of the top of the pathStack for every path encountered, indexed
* by slash-separated path.
*/
var $pathInfo;
@@ -77,13 +77,17 @@ class ConfEditor {
var $serial;
/**
- * Editor state. This consists of the internal copy/insert operations which
+ * Editor state. This consists of the internal copy/insert operations which
* are applied to the source string to obtain the destination string.
*/
var $edits;
/**
* Simple entry point for command-line testing
+ *
+ * @param $text string
+ *
+ * @return string
*/
static function test( $text ) {
try {
@@ -103,7 +107,7 @@ class ConfEditor {
}
/**
- * Edit the text. Returns the edited text.
+ * Edit the text. Returns the edited text.
* @param $ops Array of operations.
*
* Operations are given as an associative array, with members:
@@ -114,20 +118,20 @@ class ConfEditor {
*
* delete
* Deletes an array element or statement with the specified path.
- * e.g.
+ * e.g.
* array('type' => 'delete', 'path' => '$foo/bar/baz' )
* is equivalent to the runtime PHP code:
* unset( $foo['bar']['baz'] );
*
* set
- * Sets the value of an array element. If the element doesn't exist, it
- * is appended to the array. If it does exist, the value is set, with
+ * Sets the value of an array element. If the element doesn't exist, it
+ * is appended to the array. If it does exist, the value is set, with
* comments and indenting preserved.
*
* append
* Appends a new element to the end of the array. Adds a trailing comma.
* e.g.
- * array( 'type' => 'append', 'path', '$foo/bar',
+ * array( 'type' => 'append', 'path', '$foo/bar',
* 'key' => 'baz', 'value' => "'x'" )
* is like the PHP code:
* $foo['bar']['baz'] = 'x';
@@ -187,7 +191,7 @@ class ConfEditor {
list( $indent, ) = $this->getIndent( $start );
$textToInsert = "$indent$value,";
} else {
- list( $indent, $arrowIndent ) =
+ list( $indent, $arrowIndent ) =
$this->getIndent( $start, $key, $lastEltInfo['arrowByte'] );
$textToInsert = "$indent$key$arrowIndent=> $value,";
}
@@ -210,7 +214,7 @@ class ConfEditor {
list( $indent, ) = $this->getIndent( $start );
$textToInsert = "$indent$value,";
} else {
- list( $indent, $arrowIndent ) =
+ list( $indent, $arrowIndent ) =
$this->getIndent( $start, $key, $info['arrowByte'] );
$textToInsert = "$indent$key$arrowIndent=> $value,";
}
@@ -239,13 +243,13 @@ class ConfEditor {
try {
$this->parse();
} catch ( ConfEditorParseError $e ) {
- throw new MWException(
+ throw new MWException(
"Sorry, ConfEditor broke the file during editing and it won't parse anymore: " .
$e->getMessage() );
}
return $out;
}
-
+
/**
* Get the variables defined in the text
* @return array( varname => value )
@@ -266,7 +270,7 @@ class ConfEditor {
strlen( $trimmedPath ) - strlen( $name ) );
if( substr( $parentPath, -1 ) == '/' )
$parentPath = substr( $parentPath, 0, -1 );
-
+
$value = substr( $this->text, $data['valueStartByte'],
$data['valueEndByte'] - $data['valueStartByte']
);
@@ -275,7 +279,7 @@ class ConfEditor {
}
return $vars;
}
-
+
/**
* Set a value in an array, unless it's set already. For instance,
* setVar( $arr, 'foo/bar', 'baz', 3 ); will set
@@ -298,7 +302,7 @@ class ConfEditor {
if ( !isset( $target[$key] ) )
$target[$key] = $value;
}
-
+
/**
* Parse a scalar value in PHP
* @return mixed Parsed value
@@ -306,14 +310,14 @@ class ConfEditor {
function parseScalar( $str ) {
if ( $str !== '' && $str[0] == '\'' )
// Single-quoted string
- // @todo Fixme: trim() call is due to mystery bug where whitespace gets
+ // @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
+ // @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 ) );
@@ -364,8 +368,8 @@ class ConfEditor {
}
/**
- * Finds the source byte region which you would want to delete, if $pathName
- * was to be deleted. Includes the leading spaces and tabs, the trailing line
+ * Finds the source byte region which you would want to delete, if $pathName
+ * was to be deleted. Includes the leading spaces and tabs, the trailing line
* break, and any comments in between.
*/
function findDeletionRegion( $pathName ) {
@@ -419,9 +423,9 @@ class ConfEditor {
}
/**
- * Find the byte region in the source corresponding to the value part.
- * This includes the quotes, but does not include the trailing comma
- * or semicolon.
+ * Find the byte region in the source corresponding to the value part.
+ * This includes the quotes, but does not include the trailing comma
+ * or semicolon.
*
* The end position is the past-the-end (end + 1) value as per convention.
*/
@@ -472,7 +476,7 @@ class ConfEditor {
return $extraPath;
}
- /*
+ /**
* Find the path name of first element in the array.
* If the array is empty, this will return the \@extra interstitial element.
* If the specified path is not found or is not an array, it will return false.
@@ -519,7 +523,7 @@ class ConfEditor {
}
/**
- * Run the parser on the text. Throws an exception if the string does not
+ * Run the parser on the text. Throws an exception if the string does not
* match our defined subset of PHP syntax.
*/
public function parse() {
@@ -706,7 +710,7 @@ class ConfEditor {
}
/**
- * Set the parse position. Do not call this except from firstToken() and
+ * Set the parse position. Do not call this except from firstToken() and
* nextToken(), there is more to update than just the position.
*/
protected function setPos( $pos ) {
@@ -800,7 +804,7 @@ class ConfEditor {
if ( $this->currentToken && $this->currentToken->type == $type ) {
return $this->nextToken();
} else {
- $this->error( "expected " . $this->getTypeName( $type ) .
+ $this->error( "expected " . $this->getTypeName( $type ) .
", got " . $this->getTypeName( $this->currentToken->type ) );
}
}
@@ -875,7 +879,7 @@ class ConfEditor {
}
/**
- * Go to the next path on the same level. This ends the current path and
+ * Go to the next path on the same level. This ends the current path and
* starts a new one. If $path is \@next, the new path is set to the next
* numeric array element.
*/
@@ -889,7 +893,7 @@ class ConfEditor {
} else {
$this->pathStack[$i]['name'] = $path;
}
- $this->pathStack[$i] =
+ $this->pathStack[$i] =
array(
'startByte' => $this->byteNum,
'startToken' => $this->pos,
@@ -955,8 +959,8 @@ class ConfEditor {
}
/**
- * Looks ahead to see if the given type is the next token type, starting
- * from the current position plus the given offset. Skips any intervening
+ * Looks ahead to see if the given type is the next token type, starting
+ * from the current position plus the given offset. Skips any intervening
* whitespace.
*/
function isAhead( $type, $offset = 0 ) {
@@ -992,8 +996,8 @@ class ConfEditor {
$out = '';
foreach ( $this->tokens as $token ) {
$obj = $this->newTokenObj( $token );
- $out .= sprintf( "%-28s %s\n",
- $this->getTypeName( $obj->type ),
+ $out .= sprintf( "%-28s %s\n",
+ $this->getTypeName( $obj->type ),
addcslashes( $obj->text, "\0..\37" ) );
}
echo "<pre>" . htmlspecialchars( $out ) . "</pre>";
@@ -1008,7 +1012,7 @@ class ConfEditorParseError extends MWException {
function __construct( $editor, $msg ) {
$this->lineNum = $editor->lineNum;
$this->colNum = $editor->colNum;
- parent::__construct( "Parse error on line {$editor->lineNum} " .
+ parent::__construct( "Parse error on line {$editor->lineNum} " .
"col {$editor->colNum}: $msg" );
}
@@ -1028,7 +1032,7 @@ class ConfEditorParseError extends MWException {
*/
class ConfEditorToken {
var $type, $text;
-
+
static $scalarTypes = array( T_LNUMBER, T_DNUMBER, T_STRING, T_CONSTANT_ENCAPSED_STRING );
static $skipTypes = array( T_WHITESPACE, T_COMMENT, T_DOC_COMMENT );
diff --git a/includes/Cookie.php b/includes/Cookie.php
new file mode 100644
index 00000000..95a4599f
--- /dev/null
+++ b/includes/Cookie.php
@@ -0,0 +1,245 @@
+<?php
+/**
+ * @defgroup HTTP HTTP
+ */
+
+class Cookie {
+ protected $name;
+ protected $value;
+ protected $expires;
+ protected $path;
+ protected $domain;
+ protected $isSessionKey = true;
+ // TO IMPLEMENT protected $secure
+ // TO IMPLEMENT? protected $maxAge (add onto expires)
+ // TO IMPLEMENT? protected $version
+ // TO IMPLEMENT? protected $comment
+
+ function __construct( $name, $value, $attr ) {
+ $this->name = $name;
+ $this->set( $value, $attr );
+ }
+
+ /**
+ * Sets a cookie. Used before a request to set up any individual
+ * cookies. Used internally after a request to parse the
+ * Set-Cookie headers.
+ *
+ * @param $value String: the value of the cookie
+ * @param $attr Array: possible key/values:
+ * expires A date string
+ * path The path this cookie is used on
+ * domain Domain this cookie is used on
+ */
+ public function set( $value, $attr ) {
+ $this->value = $value;
+
+ if ( isset( $attr['expires'] ) ) {
+ $this->isSessionKey = false;
+ $this->expires = strtotime( $attr['expires'] );
+ }
+
+ if ( isset( $attr['path'] ) ) {
+ $this->path = $attr['path'];
+ } else {
+ $this->path = '/';
+ }
+
+ if ( isset( $attr['domain'] ) ) {
+ if ( self::validateCookieDomain( $attr['domain'] ) ) {
+ $this->domain = $attr['domain'];
+ }
+ } else {
+ throw new MWException( 'You must specify a domain.' );
+ }
+ }
+
+ /**
+ * Return the true if the cookie is valid is valid. Otherwise,
+ * false. The uses a method similar to IE cookie security
+ * described here:
+ * http://kuza55.blogspot.com/2008/02/understanding-cookie-security.html
+ * A better method might be to use a blacklist like
+ * http://publicsuffix.org/
+ *
+ * @fixme fails to detect 3-letter top-level domains
+ * @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 $domain String: the domain to validate
+ * @param $originDomain String: (optional) the domain the cookie originates from
+ * @return Boolean
+ */
+ public static function validateCookieDomain( $domain, $originDomain = null ) {
+ // Don't allow a trailing dot
+ if ( substr( $domain, -1 ) == '.' ) {
+ return false;
+ }
+
+ $dc = explode( ".", $domain );
+
+ // Only allow full, valid IP addresses
+ if ( preg_match( '/^[0-9.]+$/', $domain ) ) {
+ if ( count( $dc ) != 4 ) {
+ return false;
+ }
+
+ if ( ip2long( $domain ) === false ) {
+ return false;
+ }
+
+ if ( $originDomain == null || $originDomain == $domain ) {
+ return true;
+ }
+
+ }
+
+ // Don't allow cookies for "co.uk" or "gov.uk", etc, but allow "supermarket.uk"
+ if ( strrpos( $domain, "." ) - strlen( $domain ) == -3 ) {
+ if ( ( count( $dc ) == 2 && strlen( $dc[0] ) <= 2 )
+ || ( count( $dc ) == 3 && strlen( $dc[0] ) == "" && strlen( $dc[1] ) <= 2 ) ) {
+ return false;
+ }
+ if ( ( count( $dc ) == 2 || ( count( $dc ) == 3 && $dc[0] == '' ) )
+ && preg_match( '/(com|net|org|gov|edu)\...$/', $domain ) ) {
+ return false;
+ }
+ }
+
+ if ( $originDomain != null ) {
+ if ( substr( $domain, 0, 1 ) != '.' && $domain != $originDomain ) {
+ return false;
+ }
+
+ if ( substr( $domain, 0, 1 ) == '.'
+ && substr_compare( $originDomain, $domain, -strlen( $domain ),
+ strlen( $domain ), true ) != 0 ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Serialize the cookie jar into a format useful for HTTP Request headers.
+ *
+ * @param $path String: the path that will be used. Required.
+ * @param $domain String: the domain that will be used. Required.
+ * @return String
+ */
+ public function serializeToHttpRequest( $path, $domain ) {
+ $ret = '';
+
+ if ( $this->canServeDomain( $domain )
+ && $this->canServePath( $path )
+ && $this->isUnExpired() ) {
+ $ret = $this->name . '=' . $this->value;
+ }
+
+ return $ret;
+ }
+
+ protected function canServeDomain( $domain ) {
+ if ( $domain == $this->domain
+ || ( strlen( $domain ) > strlen( $this->domain )
+ && substr( $this->domain, 0, 1 ) == '.'
+ && substr_compare( $domain, $this->domain, -strlen( $this->domain ),
+ strlen( $this->domain ), true ) == 0 ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ protected function canServePath( $path ) {
+ if ( $this->path && substr_compare( $this->path, $path, 0, strlen( $this->path ) ) == 0 ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ protected function isUnExpired() {
+ if ( $this->isSessionKey || $this->expires > time() ) {
+ return true;
+ }
+
+ return false;
+ }
+}
+
+class CookieJar {
+ private $cookie = array();
+
+ /**
+ * Set a cookie in the cookie jar. Make sure only one cookie per-name exists.
+ * @see Cookie::set()
+ */
+ 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.
+ */
+ $index = strtoupper( $name );
+
+ if ( isset( $this->cookie[$index] ) ) {
+ $this->cookie[$index]->set( $value, $attr );
+ } else {
+ $this->cookie[$index] = new Cookie( $name, $value, $attr );
+ }
+ }
+
+ /**
+ * @see Cookie::serializeToHttpRequest
+ */
+ public function serializeToHttpRequest( $path, $domain ) {
+ $cookies = array();
+
+ foreach ( $this->cookie as $c ) {
+ $serialized = $c->serializeToHttpRequest( $path, $domain );
+
+ if ( $serialized ) {
+ $cookies[] = $serialized;
+ }
+ }
+
+ return implode( '; ', $cookies );
+ }
+
+ /**
+ * Parse the content of an Set-Cookie HTTP Response header.
+ *
+ * @param $cookie String
+ * @param $domain String: cookie's domain
+ */
+ public function parseCookieResponseHeader ( $cookie, $domain ) {
+ $len = strlen( 'Set-Cookie:' );
+
+ if ( substr_compare( 'Set-Cookie:', $cookie, 0, $len, true ) === 0 ) {
+ $cookie = substr( $cookie, $len );
+ }
+
+ $bit = array_map( 'trim', explode( ';', $cookie ) );
+
+ if ( count( $bit ) >= 1 ) {
+ list( $name, $value ) = explode( '=', array_shift( $bit ), 2 );
+ $attr = array();
+
+ foreach ( $bit as $piece ) {
+ $parts = explode( '=', $piece );
+ if ( count( $parts ) > 1 ) {
+ $attr[strtolower( $parts[0] )] = $parts[1];
+ } else {
+ $attr[strtolower( $parts[0] )] = true;
+ }
+ }
+
+ if ( !isset( $attr['domain'] ) ) {
+ $attr['domain'] = $domain;
+ } elseif ( !Cookie::validateCookieDomain( $attr['domain'], $domain ) ) {
+ return null;
+ }
+
+ $this->setCookie( $name, $value, $attr );
+ }
+ }
+}
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 0395633d..4248add7 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -26,58 +26,43 @@ if( !defined( 'MEDIAWIKI' ) ) {
die( 1 );
}
-# Create a site configuration object. Not used for much in a default install
-if ( !defined( 'MW_PHP4' ) ) {
- require_once( "$IP/includes/SiteConfiguration.php" );
- $wgConf = new SiteConfiguration;
-}
+# Create a site configuration object. Not used for much in a default install.
+# Note: this (and other things) will break if the autoloader is not enabled.
+# Please include includes/AutoLoader.php before including this file.
+$wgConf = new SiteConfiguration;
/** @endcond */
/** MediaWiki version number */
-$wgVersion = '1.17.1';
+$wgVersion = '1.18.0';
/** Name of the site. It must be changed in LocalSettings.php */
$wgSitename = 'MediaWiki';
/**
- * URL of the server. It will be automatically built including https mode.
+ * URL of the server.
*
* Example:
* <code>
- * $wgServer = http://example.com
+ * $wgServer = 'http://example.com';
* </code>
*
* This is usually detected correctly by MediaWiki. If MediaWiki detects the
* wrong server, it will redirect incorrectly after you save a page. In that
* case, set this variable to fix it.
+ *
+ * If you want to use protocol-relative URLs on your wiki, set this to a
+ * protocol-relative URL like '//example.com' and set $wgCanonicalServer
+ * to a fully qualified URL.
*/
-$wgServer = '';
-
-/** @cond file_level_code */
-if( isset( $_SERVER['SERVER_NAME'] ) ) {
- $serverName = $_SERVER['SERVER_NAME'];
-} elseif( isset( $_SERVER['HOSTNAME'] ) ) {
- $serverName = $_SERVER['HOSTNAME'];
-} elseif( isset( $_SERVER['HTTP_HOST'] ) ) {
- $serverName = $_SERVER['HTTP_HOST'];
-} elseif( isset( $_SERVER['SERVER_ADDR'] ) ) {
- $serverName = $_SERVER['SERVER_ADDR'];
-} else {
- $serverName = 'localhost';
-}
-
-$wgProto = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
+$wgServer = WebRequest::detectServer();
-$wgServer = $wgProto.'://' . $serverName;
-# If the port is a non-standard one, add it to the URL
-if( isset( $_SERVER['SERVER_PORT'] )
- && !strpos( $serverName, ':' )
- && ( ( $wgProto == 'http' && $_SERVER['SERVER_PORT'] != 80 )
- || ( $wgProto == 'https' && $_SERVER['SERVER_PORT'] != 443 ) ) ) {
-
- $wgServer .= ":" . $_SERVER['SERVER_PORT'];
-}
-/** @endcond */
+/**
+ * Canonical URL of the server, to use in IRC feeds and notification e-mails.
+ * Must be fully qualified, even if $wgServer is protocol-relative.
+ *
+ * Defaults to $wgServer, expanded to a fully qualified http:// URL if needed.
+ */
+$wgCanonicalServer = false;
/************************************************************************//**
* @name Script path settings
@@ -147,6 +132,7 @@ $wgRedirectScript = false; ///< defaults to
*/
$wgLoadScript = false;
+
/**@}*/
/************************************************************************//**
@@ -183,6 +169,7 @@ $wgLocalStylePath = false;
/**
* The URL path of the extensions directory.
* Defaults to "{$wgScriptPath}/extensions".
+ * @since 1.16
*/
$wgExtensionAssetsPath = false;
@@ -228,23 +215,6 @@ $wgFavicon = '/favicon.ico';
$wgAppleTouchIcon = false;
/**
- * The URL path of the math directory. Defaults to "{$wgUploadPath}/math".
- *
- * See http://www.mediawiki.org/wiki/Manual:Enable_TeX for details about how to
- * set up mathematical formula display.
- */
-$wgMathPath = false;
-
-/**
- * The filesystem path of the math directory.
- * Defaults to "{$wgUploadDirectory}/math".
- *
- * See http://www.mediawiki.org/wiki/Manual:Enable_TeX for details about how to
- * set up mathematical formula display.
- */
-$wgMathDirectory = false;
-
-/**
* The local filesystem path to a temporary directory. This is not required to
* be web accessible.
*
@@ -295,7 +265,7 @@ $wgAllowImageMoving = true;
$wgIllegalFileChars = ":";
/**
- * @deprecated use $wgDeletedDirectory
+ * @deprecated since 1.17 use $wgDeletedDirectory
*/
$wgFileStore = array();
@@ -327,7 +297,7 @@ $wgImgAuthPublicTest = true;
* - class The class name for the repository. May come from the core or an extension.
* The core repository classes are LocalRepo, ForeignDBRepo, FSRepo.
*
- * - name A unique name for the repository.
+ * - name A unique name for the repository (but $wgLocalFileRepo should be 'local').
*
* For most core repos:
* - url Base public URL
@@ -373,8 +343,12 @@ $wgImgAuthPublicTest = true;
* - apibase Use for the foreign API's URL
* - apiThumbCacheExpiry How long to locally cache thumbs for
*
- * The default is to initialise these arrays from the MW<1.11 backwards compatible settings:
- * $wgUploadPath, $wgThumbnailScriptPath, $wgSharedUploadDirectory, etc.
+ * If you leave $wgLocalFileRepo set to false, Setup will fill in appropriate values.
+ * Otherwise, set $wgLocalFileRepo to a repository structure as described above.
+ * If you set $wgUseInstantCommons to true, it will add an entry for Commons.
+ * If you set $wgForeignFileRepos to an array of repostory structures, those will
+ * be searched after the local file repo.
+ * Otherwise, you will only have access to local media files.
*/
$wgLocalFileRepo = false;
@@ -393,7 +367,7 @@ $wgUseInstantCommons = false;
* Requires PHP's EXIF extension: http://www.php.net/manual/en/ref.exif.php
*
* NOTE FOR WINDOWS USERS:
- * To enable EXIF functions, add the folloing lines to the
+ * To enable EXIF functions, add the following lines to the
* "Windows extensions" section of php.ini:
*
* extension=extensions/php_mbstring.dll
@@ -402,6 +376,13 @@ $wgUseInstantCommons = false;
$wgShowEXIF = function_exists( 'exif_read_data' );
/**
+ * If to automatically update the img_metadata field
+ * if the metadata field is outdated but compatible with the current version.
+ * Defaults to false.
+ */
+$wgUpdateCompatibleMetadata = false;
+
+/**
* If you operate multiple wikis, you can define a shared upload path here.
* Uploads to this wiki will NOT be put there - they will be put into
* $wgUploadDirectory.
@@ -434,12 +415,24 @@ $wgCacheSharedUploads = true;
$wgAllowCopyUploads = false;
/**
* Allow asynchronous copy uploads.
- * This feature is experimental.
+ * This feature is experimental and broken as of r81612.
*/
$wgAllowAsyncCopyUploads = false;
/**
- * Max size for uploads, in bytes. Applies to all uploads.
+ * 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
+ * for non-specified types.
+ *
+ * For example:
+ * $wgMaxUploadSize = array(
+ * '*' => 250 * 1024,
+ * 'url' => 500 * 1024,
+ * );
+ * 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
@@ -536,22 +529,16 @@ $wgMimeTypeBlacklist = array(
'text/scriptlet', 'application/x-msdownload',
# Windows metafile, client-side vulnerability on some systems
'application/x-msmetafile',
- # A ZIP file may be a valid Java archive containing an applet which exploits the
- # same-origin policy to steal cookies
- 'application/zip',
-
- # MS Office OpenXML and other Open Package Conventions files are zip files
- # and thus blacklisted just as other zip files. If you remove these entries
- # from the blacklist in your local configuration, a malicious file upload
- # will be able to compromise the wiki's user accounts, and the user
- # accounts of any other website in the same cookie domain.
- 'application/x-opc+zip',
- 'application/msword',
- 'application/vnd.ms-powerpoint',
- 'application/vnd.msexcel',
);
/**
+ * Allow Java archive uploads.
+ * This is not recommended for public wikis since a maliciously-constructed
+ * applet running on the same domain as the wiki can steal the user's cookies.
+ */
+$wgAllowJavaUploads = false;
+
+/**
* This is a flag to determine whether or not to check file extensions on upload.
*
* WARNING: setting this to false is insecure for public wikis.
@@ -593,7 +580,7 @@ $wgTrustedMediaFormats = array(
* Each entry in the array maps a MIME type to a class name
*/
$wgMediaHandlers = array(
- 'image/jpeg' => 'BitmapHandler',
+ 'image/jpeg' => 'JpegHandler',
'image/png' => 'PNGHandler',
'image/gif' => 'GIFHandler',
'image/tiff' => 'TiffHandler',
@@ -645,11 +632,18 @@ $wgImageMagickTempDir = false;
$wgCustomConvertCommand = false;
/**
+ * Some tests and extensions use exiv2 to manipulate the EXIF metadata in some image formats.
+ */
+$wgExiv2Command = '/usr/bin/exiv2';
+
+/**
* Scalable Vector Graphics (SVG) may be uploaded as images.
* Since SVG support is not yet standard in browsers, it is
* necessary to rasterize SVGs to PNG as a fallback format.
*
* An external program is required to perform this conversion.
+ * If set to an array, the first item is a PHP callable and any further items
+ * are passed as parameters after $srcPath, $dstPath, $width, $height
*/
$wgSVGConverters = array(
'ImageMagick' => '$path/convert -background white -thumbnail $widthx$height\! $input PNG:$output',
@@ -658,6 +652,7 @@ $wgSVGConverters = array(
'batik' => 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input',
'rsvg' => '$path/rsvg -w$width -h$height $input $output',
'imgserv' => '$path/imgserv-wrapper -i svg -o png -w$width $input $output',
+ 'ImagickExt' => array( 'SvgHandler::rasterizeImagickExt' ),
);
/** Pick a converter defined in $wgSVGConverters */
$wgSVGConverter = 'ImageMagick';
@@ -667,7 +662,7 @@ $wgSVGConverterPath = '';
$wgSVGMaxSize = 2048;
/** Don't read SVG metadata beyond this point.
* Default is 1024*256 bytes */
-$wgSVGMetadataCutoff = 262144;
+$wgSVGMetadataCutoff = 262144;
/**
* MediaWiki will reject HTMLesque tags in uploaded files due to idiotic browsers which can't
@@ -744,6 +739,12 @@ $wgShowArchiveThumbnails = true;
/** Obsolete, always true, kept for compatibility with extensions */
$wgUseImageResize = true;
+/**
+ * If set to true, images that contain certain the exif orientation tag will
+ * be rotated accordingly. If set to null, try to auto-detect whether a scaler
+ * is available that can rotate.
+ */
+$wgEnableAutoRotation = null;
/**
* Internal name of virus scanner. This servers as a key to the
@@ -907,7 +908,7 @@ $wgGalleryOptions = array (
'imagesPerRow' => 0, // Default number of images per-row in the gallery. 0 -> Adapt to screensize
'imageWidth' => 120, // Width of the cells containing images in galleries (in "px")
'imageHeight' => 120, // Height of the cells containing images in galleries (in "px")
- 'captionLength' => 20, // Length of caption to truncate (in characters)
+ 'captionLength' => 25, // Length of caption to truncate (in characters)
'showBytes' => true, // Show the filesize in bytes in categories
);
@@ -976,6 +977,8 @@ $wgDjvuOutputExtension = 'jpg';
* @{
*/
+$serverName = substr( $wgServer, strrpos( $wgServer, '/' ) + 1 );
+
/**
* Site admin email address.
*/
@@ -1037,6 +1040,11 @@ $wgPasswordReminderResendTime = 24;
$wgNewPasswordExpiry = 3600 * 24 * 7;
/**
+ * The time, in seconds, when an email confirmation email expires
+ */
+$wgUserEmailConfirmationTokenExpiry = 7 * 24 * 60 * 60;
+
+/**
* SMTP Mode
* For using a direct (authenticated) SMTP server connection.
* Default to false or fill an array :
@@ -1053,6 +1061,7 @@ $wgSMTP = false;
/**
* Additional email parameters, will be passed as the last argument to mail() call.
+ * If using safe_mode this has no effect
*/
$wgAdditionalMailParams = null;
@@ -1177,8 +1186,6 @@ $wgSQLMode = '';
/** Mediawiki schema */
$wgDBmwschema = 'mediawiki';
-/** Tsearch2 schema */
-$wgDBts2schema = 'public';
/** To override default SQLite data directory ($docroot/../data) */
$wgSQLiteDataDir = '';
@@ -1375,6 +1382,7 @@ $wgExternalServers = array();
*
* $wgDefaultExternalStore = array( 'DB://cluster1', 'DB://cluster2' );
*
+ * @var array
*/
$wgDefaultExternalStore = false;
@@ -1467,6 +1475,8 @@ $wgCacheDirectory = false;
* - CACHE_DBA: Use PHP's DBA extension to store in a DBM-style
* database. This is slow, and is not recommended for
* anything other than debugging.
+ * - (other): A string may be used which identifies a cache
+ * configuration in $wgObjectCaches.
*
* @see $wgMessageCacheType, $wgParserCacheType
*/
@@ -1489,6 +1499,37 @@ $wgMessageCacheType = CACHE_ANYTHING;
$wgParserCacheType = CACHE_ANYTHING;
/**
+ * Advanced object cache configuration.
+ *
+ * Use this to define the class names and constructor parameters which are used
+ * for the various cache types. Custom cache types may be defined here and
+ * referenced from $wgMainCacheType, $wgMessageCacheType or $wgParserCacheType.
+ *
+ * The format is an associative array where the key is a cache identifier, and
+ * the value is an associative array of parameters. The "class" parameter is the
+ * class name which will be used. Alternatively, a "factory" parameter may be
+ * given, giving a callable function which will generate a suitable cache object.
+ *
+ * The other parameters are dependent on the class used.
+ */
+$wgObjectCaches = array(
+ CACHE_NONE => array( 'class' => 'EmptyBagOStuff' ),
+ CACHE_DB => array( 'class' => 'SqlBagOStuff', 'table' => 'objectcache' ),
+ CACHE_DBA => array( 'class' => 'DBABagOStuff' ),
+
+ CACHE_ANYTHING => array( 'factory' => 'ObjectCache::newAnything' ),
+ CACHE_ACCEL => array( 'factory' => 'ObjectCache::newAccelerator' ),
+ CACHE_MEMCACHED => array( 'factory' => 'ObjectCache::newMemcached' ),
+
+ 'eaccelerator' => array( 'class' => 'eAccelBagOStuff' ),
+ 'apc' => array( 'class' => 'APCBagOStuff' ),
+ 'xcache' => array( 'class' => 'XCacheBagOStuff' ),
+ 'wincache' => array( 'class' => 'WinCacheBagOStuff' ),
+ 'memcached-php' => array( 'class' => 'MemcachedPhpBagOStuff' ),
+ 'hash' => array( 'class' => 'HashBagOStuff' ),
+);
+
+/**
* The expiry time for the parser cache, in seconds. The default is 86.4k
* seconds, otherwise known as a day.
*/
@@ -1512,7 +1553,7 @@ $wgSessionsInMemcached = false;
* 'session_mysql.' Setting to null skips setting this entirely (which might be
* useful if you're doing cross-application sessions, see bug 11381)
*/
-$wgSessionHandler = 'files';
+$wgSessionHandler = null;
/** If enabled, will send MemCached debugging information to $wgDebugLogFile */
$wgMemCachedDebug = false;
@@ -1545,6 +1586,13 @@ $wgUseLocalMessageCache = false;
$wgLocalMessageCacheSerialized = true;
/**
+ * Instead of caching everything, keep track which messages are requested and
+ * load only most used messages. This only makes sense if there is lots of
+ * interface messages customised in the wiki (like hundreds in many languages).
+ */
+$wgAdaptiveMessageCache = false;
+
+/**
* Localisation cache configuration. Associative array with keys:
* class: The class to use. May be overridden by extensions.
*
@@ -1589,7 +1637,7 @@ $wgCacheEpoch = '20030516000000';
* to ensure that client-side caches do not keep obsolete copies of global
* styles.
*/
-$wgStyleVersion = '301';
+$wgStyleVersion = '303';
/**
* This will cache static pages for non-logged-in users to reduce
@@ -1673,9 +1721,9 @@ $wgClockSkewFudge = 5;
* to setting $wgCacheEpoch to the modification time of LocalSettings.php, as
* was previously done in the default LocalSettings.php file.
*
- * On high-traffic wikis, this should be set to false, to avoid the need to
+ * On high-traffic wikis, this should be set to false, to avoid the need to
* check the file modification time, and to avoid the performance impact of
- * unnecessary cache invalidations.
+ * unnecessary cache invalidations.
*/
$wgInvalidateCacheOnLocalSettingsChange = true;
@@ -1706,13 +1754,22 @@ $wgUseESI = false;
/** 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
+ * 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
+ * pollution. This does not affect 'normal' requests to index.php other than
+ * HTTP redirects.
+ */
+$wgVaryOnXFP = false;
+
/**
* Internal server name as known to Squid, if different. Example:
* <code>
* $wgInternalServer = 'http://yourinternal.tld:8000';
* </code>
*/
-$wgInternalServer = $wgServer;
+$wgInternalServer = false;
/**
* Cache timeout for the squid, will be sent as s-maxage (without ESI) or
@@ -1807,20 +1864,15 @@ $wgDummyLanguageCodes = array(
'als',
'bat-smg',
'be-x-old',
- 'dk',
'fiu-vro',
'iu',
'nb',
'qqq',
+ 'qqx',
+ 'roa-rup',
'simple',
- 'tp',
);
-/** @deprecated Since MediaWiki 1.5, this must always be set to UTF-8. */
-$wgInputEncoding = 'UTF-8';
-/** @deprecated Since MediaWiki 1.5, this must always be set to UTF-8. */
-$wgOutputEncoding = 'UTF-8';
-
/**
* Character set for use in the article edit box. Language-specific encodings
* may be defined.
@@ -2060,17 +2112,7 @@ $wgLocaltimezone = null;
* This setting is used for most date/time displays in the software, and is
* overrideable in user preferences. It is *not* used for signature timestamps.
*
- * You can set it to match the configured server timezone like this:
- * $wgLocalTZoffset = date("Z") / 60;
- *
- * If your server is not configured for the timezone you want, you can set
- * this in conjunction with the signature timezone and override the PHP default
- * timezone like so:
- * $wgLocaltimezone="Europe/Berlin";
- * date_default_timezone_set( $wgLocaltimezone );
- * $wgLocalTZoffset = date("Z") / 60;
- *
- * Leave at NULL to show times in universal time (UTC/GMT).
+ * By default, this will be set to match $wgLocaltimezone.
*/
$wgLocalTZoffset = null;
@@ -2084,28 +2126,49 @@ $wgLocalTZoffset = null;
/** The default Content-Type header. */
$wgMimeType = 'text/html';
-/** The content type used in script tags. */
+/**
+ * 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. */
+/**
+ * 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.
+ */
$wgDocType = '-//W3C//DTD XHTML 1.0 Transitional//EN';
-/** The URL of the document type declaration. */
+/**
+ * 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.
+ */
$wgDTD = 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd';
-/** The default xmlns attribute. */
+/**
+ * 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?
+ */
$wgXhtmlDefaultNamespace = 'http://www.w3.org/1999/xhtml';
/**
* 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.
+ * to always true. If it's true, a number of other settings will be irrelevant
+ * and have no effect.
*/
$wgHtml5 = true;
/**
* Defines the value of the version attribute in the &lt;html&gt; tag, if any.
- * Will be initialized later if not set explicitly.
+ * 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.
*/
$wgHtml5Version = null;
@@ -2145,6 +2208,9 @@ $wgWellFormedXml = true;
* $wgXhtmlNamespaces['svg'] = 'http://www.w3.org/2000/svg';
* 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.
*/
$wgXhtmlNamespaces = array();
@@ -2188,12 +2254,6 @@ $wgValidateAllHtml = false;
$wgDefaultSkin = 'vector';
/**
-* Should we allow the user's to select their own skin that will override the default?
-* @deprecated in 1.16, use $wgHiddenPrefs[] = 'skin' to disable it
-*/
-$wgAllowUserSkin = true;
-
-/**
* Specify the name of a skin that should not be presented in the list of
* available skins. Use for blacklisting a skin which you do not want to
* remove from the .../skins/ directory
@@ -2269,7 +2329,7 @@ $wgEnableTooltipsAndAccesskeys = true;
$wgBreakFrames = false;
/**
- * The X-Frame-Options header to send on pages sensitive to clickjacking
+ * The X-Frame-Options header to send on pages sensitive to clickjacking
* attacks, such as edit pages. This prevents those pages from being displayed
* in a frame or iframe. The options are:
*
@@ -2279,9 +2339,9 @@ $wgBreakFrames = false;
* to allow framing within a trusted domain. This is insecure if there
* is a page on the same domain which allows framing of arbitrary URLs.
*
- * - false: Allow all framing. This opens up the wiki to XSS attacks and thus
- * full compromise of local user accounts. Private wikis behind a
- * corporate firewall are especially vulnerable. This is not
+ * - false: Allow all framing. This opens up the wiki to XSS attacks and thus
+ * full compromise of local user accounts. Private wikis behind a
+ * corporate firewall are especially vulnerable. This is not
* recommended.
*
* For extra safety, set $wgBreakFrames = true, to prevent framing on all pages,
@@ -2310,17 +2370,17 @@ $wgExperimentalHtmlIds = false;
* You can add new icons to the built in copyright or poweredby, or you can create
* a new block. Though note that you may need to add some custom css to get good styling
* of new blocks in monobook. vector and modern should work without any special css.
- *
+ *
* $wgFooterIcons itself is a key/value array.
- * The key is the name of a block that the icons will be wrapped in. The final id varies
- * by skin; Monobook and Vector will turn poweredby into f-poweredbyico while Modern
+ * The key is the name of a block that the icons will be wrapped in. The final id varies
+ * by skin; Monobook and Vector will turn poweredby into f-poweredbyico while Modern
* turns it into mw_poweredby.
* The value is either key/value array of icons or a string.
* In the key/value array the key may or may not be used by the skin but it can
* be used to find the icon and unset it or change the icon if needed.
* This is useful for disabling icons that are set by extensions.
- * 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
+ * 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
* but not required, however some skins will ignore icons without an image
@@ -2346,6 +2406,13 @@ $wgFooterIcons = array(
);
/**
+ * Login / create account link behavior when it's possible for anonymous users to create an account
+ * true = use a combined login / create account link
+ * false = split login and create account into two separate links
+ */
+$wgUseCombinedLoginLink = true;
+
+/**
* Search form behavior for Vector skin only
* true = use an icon search button
* false = use Go & Search buttons
@@ -2370,9 +2437,12 @@ $wgVectorShowVariantName = false;
$wgEdititis = false;
/**
- * Experimental better directionality support.
+ * Better directionality support (bug 6100 and related).
+ * Removed in 1.18, still kept here for LiquidThreads backwards compatibility.
+ *
+ * @deprecated since 1.18
*/
-$wgBetterDirectionality = false;
+$wgBetterDirectionality = true;
/** @} */ # End of output format settings }
@@ -2397,6 +2467,12 @@ $wgBetterDirectionality = false;
*/
$wgResourceModules = array();
+/*
+ * Default 'remoteBasePath' value for resource loader modules.
+ * If not set, then $wgScriptPath will be used as a fallback.
+ */
+$wgResourceBasePath = null;
+
/**
* Maximum time in seconds to cache resources served by the resource loader
*/
@@ -2453,6 +2529,19 @@ $wgResourceLoaderMinifierMaxLineLength = 1000;
$wgIncludeLegacyJavaScript = true;
/**
+ * Whether or not to assing configuration variables to the global window object.
+ * If this is set to false, old code using deprecated variables like:
+ * " if ( window.wgRestrictionEdit ) ..."
+ * or:
+ * " if ( wgIsArticle ) ..."
+ * will no longer work and needs to use mw.config instead. For example:
+ * " if ( mw.config.exists('wgRestrictionEdit') )"
+ * or
+ * " if ( mw.config.get('wgIsArticle') )".
+ */
+$wgLegacyJavaScriptGlobals = true;
+
+/**
* If set to a positive number, ResourceLoader will not generate URLs whose
* query string is more than this many characters long, and will instead use
* multiple requests with shorter query strings. This degrades performance,
@@ -2465,6 +2554,25 @@ $wgIncludeLegacyJavaScript = true;
*/
$wgResourceLoaderMaxQueryLength = -1;
+/**
+ * If set to true, JavaScript modules loaded from wiki pages will be parsed prior
+ * to minification to validate it.
+ *
+ * Parse errors will result in a JS exception being thrown during module load,
+ * which avoids breaking other modules loaded in the same request.
+ */
+$wgResourceLoaderValidateJS = true;
+
+/**
+ * If set to true, statically-sourced (file-backed) JavaScript resources will
+ * be parsed for validity before being bundled up into ResourceLoader modules.
+ *
+ * This can be helpful for development by providing better error messages in
+ * default (non-debug) mode, but JavaScript parsing is slow and memory hungry
+ * and may fail on large pre-bundled frameworks.
+ */
+$wgResourceLoaderValidateStaticJS = false;
+
/** @} */ # End of resource loader settings }
@@ -2511,6 +2619,14 @@ $wgMetaNamespaceTalk = false;
$wgExtraNamespaces = array();
/**
+ * Same as above, but for namespaces with gender distinction.
+ * Note: the default form for the namespace should also be set
+ * using $wgExtraNamespaces for the same index.
+ * @since 1.18
+ */
+$wgExtraGenderNamespaces = array();
+
+/**
* Namespace aliases
* These are alternate names for the primary localised namespace names, which
* are defined by $wgExtraNamespaces and the language file. If a page is
@@ -2720,6 +2836,7 @@ $wgUrlProtocols = array(
'https://',
'ftp://',
'irc://',
+ 'ircs://', // @bug 28503
'gopher://',
'telnet://', // Well if we're going to support the above.. -ævar
'nntp://', // @bug 3808 RFC 1738
@@ -2729,6 +2846,7 @@ $wgUrlProtocols = array(
'svn://',
'git://',
'mms://',
+ '//', // for protocol-relative URLs
);
/**
@@ -2781,8 +2899,9 @@ $wgAllowImageTag = false;
* - $wgTidyBin should be set to the path of the binary and
* - $wgTidyConf to the path of the configuration file.
* - $wgTidyOpts can include any number of parameters.
- * - $wgTidyInternal controls the use of the PECL extension to use an in-
- * process tidy library instead of spawning a separate program.
+ * - $wgTidyInternal controls the use of the PECL extension or the
+ * libtidy (PHP >= 5) extension to use an in-process tidy library instead
+ * of spawning a separate program.
* Normally you shouldn't need to override the setting except for
* debugging. To install, use 'pear install tidy' and add a line
* 'extension=tidy.so' to php.ini.
@@ -2862,6 +2981,7 @@ $wgExpensiveParserFunctionLimit = 100;
/**
* Preprocessor caching threshold
+ * Setting it to 'false' will disable the preprocessor cache.
*/
$wgPreprocessorCacheThreshold = 1000;
@@ -2883,14 +3003,30 @@ $wgTranscludeCacheExpiry = 3600;
*/
/**
- * Under which condition should a page in the main namespace be counted
- * as a valid article? If $wgUseCommaCount is set to true, it will be
- * counted if it contains at least one comma. If it is set to false
- * (default), it will only be counted if it contains at least one [[wiki
- * link]]. See http://www.mediawiki.org/wiki/Manual:Article_count
+ * Method used to determine if a page in a content namespace should be counted
+ * as a valid article.
+ *
+ * Redirect pages will never be counted as valid articles.
*
- * Retroactively changing this variable will not affect
- * the existing count (cf. maintenance/recount.sql).
+ * This variable can have the following values:
+ * - 'any': all pages as considered as valid articles
+ * - 'comma': the page must contain a comma to be considered valid
+ * - 'link': the page must contain a [[wiki link]] to be considered valid
+ * - null: the value will be set at run time depending on $wgUseCommaCount:
+ * if $wgUseCommaCount is false, it will be 'link', if it is true
+ * it will be 'comma'
+ *
+ * See also See http://www.mediawiki.org/wiki/Manual:Article_count
+ *
+ * Retroactively changing this variable will not affect the existing count,
+ * to update it, you will need to run the maintenance/updateArticleCount.php
+ * script.
+ */
+$wgArticleCountMethod = null;
+
+/**
+ * Backward compatibility setting, will set $wgArticleCountMethod if it is null.
+ * @deprecated since 1.18; use $wgArticleCountMethod instead
*/
$wgUseCommaCount = false;
@@ -2928,6 +3064,17 @@ $wgPasswordSalt = true;
$wgMinimalPasswordLength = 1;
/**
+ * Whether to allow password resets ("enter some identifying data, and we'll send an email
+ * with a temporary password you can use to get back into the account") identified by
+ * various bits of data. Setting all of these to false (or the whole variable to false)
+ * has the effect of disabling password resets entirely
+ */
+$wgPasswordResetRoutes = array(
+ 'username' => true,
+ 'email' => false,
+);
+
+/**
* Maximum number of Unicode characters in signature
*/
$wgMaxSigChars = 255;
@@ -2962,8 +3109,6 @@ $wgReservedUsernames = array(
$wgDefaultUserOptions = array(
'ccmeonemails' => 0,
'cols' => 80,
- 'contextchars' => 50,
- 'contextlines' => 5,
'date' => 'default',
'diffonly' => 0,
'disablemail' => 0,
@@ -2996,7 +3141,7 @@ $wgDefaultUserOptions = array(
'numberheadings' => 0,
'previewonfirst' => 0,
'previewontop' => 1,
- 'quickbar' => 1,
+ 'quickbar' => 5,
'rcdays' => 7,
'rclimit' => 50,
'rememberpassword' => 0,
@@ -3029,7 +3174,7 @@ $wgDefaultUserOptions = array(
/**
* Whether or not to allow and use real name fields.
- * @deprecated in 1.16, use $wgHiddenPrefs[] = 'realname' below to disable real
+ * @deprecated since 1.16, use $wgHiddenPrefs[] = 'realname' below to disable real
* names
*/
$wgAllowRealName = true;
@@ -3128,18 +3273,6 @@ $wgSecureLogin = false;
*/
/**
- * Allow sysops to ban logged-in users
- * @deprecated since 1.17, will be made permanently true in 1.18
- */
-$wgSysopUserBans = true;
-
-/**
- * Allow sysops to ban IP ranges
- * @deprecated since 1.17; set $wgBlockCIDRLimit to array( 'IPv4' => 32, 'IPv6 => 128 ) instead.
- */
-$wgSysopRangeBans = true;
-
-/**
* Number of seconds before autoblock entries expire. Default 86400 = 1 day.
*/
$wgAutoblockExpiry = 86400;
@@ -3180,7 +3313,7 @@ $wgBlockDisablesLogin = false;
* $wgWhitelistRead = array ( "Main Page", "Wikipedia:Help");
* </code>
*
- * Special:Userlogin and Special:Resetpass are always whitelisted.
+ * Special:Userlogin and Special:ChangePassword are always whitelisted.
*
* NOTE: This will only work if $wgGroupPermissions['*']['read'] is false --
* see below. Otherwise, ALL pages are accessible, regardless of this setting.
@@ -3278,7 +3411,6 @@ $wgGroupPermissions['sysop']['autopatrol'] = true;
$wgGroupPermissions['sysop']['protect'] = true;
$wgGroupPermissions['sysop']['proxyunbannable'] = true;
$wgGroupPermissions['sysop']['rollback'] = true;
-$wgGroupPermissions['sysop']['trackback'] = true;
$wgGroupPermissions['sysop']['upload'] = true;
$wgGroupPermissions['sysop']['reupload'] = true;
$wgGroupPermissions['sysop']['reupload-shared'] = true;
@@ -3295,6 +3427,7 @@ $wgGroupPermissions['sysop']['movefile'] = true;
$wgGroupPermissions['sysop']['unblockself'] = true;
$wgGroupPermissions['sysop']['suppressredirect'] = true;
#$wgGroupPermissions['sysop']['mergehistory'] = true;
+#$wgGroupPermissions['sysop']['trackback'] = true;
// Permission to change users' group assignments
$wgGroupPermissions['bureaucrat']['userrights'] = true;
@@ -3364,7 +3497,7 @@ $wgGroupsRemoveFromSelf = array();
* Set of available actions that can be restricted via action=protect
* You probably shouldn't change this.
* Translated through restriction-* messages.
- * Title::getRestrictionTypes() will remove restrictions that are not
+ * Title::getRestrictionTypes() will remove restrictions that are not
* applicable to a specific title (create and upload)
*/
$wgRestrictionTypes = array( 'create', 'edit', 'move', 'upload' );
@@ -3429,7 +3562,7 @@ $wgAutoConfirmCount = 0;
/**
* Automatically add a usergroup to any user who matches certain conditions.
* The format is
- * array( '&' or '|' or '^', cond1, cond2, ... )
+ * array( '&' or '|' or '^' or '!', cond1, cond2, ... )
* where cond1, cond2, ... are themselves conditions; *OR*
* APCOND_EMAILCONFIRMED, *OR*
* array( APCOND_EMAILCONFIRMED ), *OR*
@@ -3440,6 +3573,7 @@ $wgAutoConfirmCount = 0;
* array( APCOND_IPINRANGE, range ), *OR*
* array( APCOND_AGE_FROM_EDIT, seconds since first edit ), *OR*
* array( APCOND_BLOCKED ), *OR*
+ * array( APCOND_ISBOT ), *OR*
* similar constructs defined by extensions.
*
* If $wgEmailAuthentication is off, APCOND_EMAILCONFIRMED will be true for any
@@ -3453,6 +3587,31 @@ $wgAutopromote = array(
);
/**
+ * Automatically add a usergroup to any user who matches certain conditions.
+ * Does not add the user to the group again if it has been removed.
+ * Also, does not remove the group if the user no longer meets the criteria.
+ *
+ * The format is
+ * array( event => criteria, ... )
+ * where event is
+ * 'onEdit' (when user edits) or 'onView' (when user views the wiki)
+ * and criteria has the same format as $wgAutopromote
+ *
+ * @see $wgAutopromote
+ * @since 1.18
+ */
+$wgAutopromoteOnce = array(
+ 'onEdit' => array(),
+ 'onView' => array()
+);
+
+/*
+ * Put user rights log entries for autopromotion in recent changes?
+ * @since 1.18
+ */
+$wgAutopromoteOnceLogInRC = true;
+
+/**
* $wgAddGroups and $wgRemoveGroups can be used to give finer control over who
* can assign which groups at Special:Userrights. Example configuration:
*
@@ -3512,7 +3671,7 @@ $wgSummarySpamRegex = array();
* - true : block it
* - false : let it through
*
- * @deprecated Use hooks. See SpamBlacklist extension.
+ * @deprecated since 1.17 Use hooks. See SpamBlacklist extension.
*/
$wgFilterCallback = false;
@@ -3523,7 +3682,7 @@ $wgFilterCallback = false;
$wgEnableDnsBlacklist = false;
/**
- * @deprecated Use $wgEnableDnsBlacklist instead, only kept for backward
+ * @deprecated since 1.17 Use $wgEnableDnsBlacklist instead, only kept for backward
* compatibility
*/
$wgEnableSorbs = false;
@@ -3535,7 +3694,7 @@ $wgEnableSorbs = false;
$wgDnsBlacklistUrls = array( 'http.dnsbl.sorbs.net.' );
/**
- * @deprecated Use $wgDnsBlacklistUrls instead, only kept for backward
+ * @deprecated since 1.17 Use $wgDnsBlacklistUrls instead, only kept for backward
* compatibility
*/
$wgSorbsUrl = array();
@@ -3583,17 +3742,6 @@ $wgRateLimits = array(
$wgRateLimitLog = null;
/**
- * Array of groups which should never trigger the rate limiter
- *
- * @deprecated as of 1.13.0, the preferred method is using
- * $wgGroupPermissions[]['noratelimit']. However, this will still
- * work if desired.
- *
- * $wgRateLimitsExcludedGroups = array( 'sysop', 'bureaucrat' );
- */
-$wgRateLimitsExcludedGroups = array();
-
-/**
* Array of IPs which should be excluded from rate limits.
* This may be useful for whitelisting NAT gateways for conferences, etc.
*/
@@ -3631,7 +3779,7 @@ $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/includes/proxy_check.php";
+$wgProxyScriptPath = "$IP/maintenance/proxy_check.php";
/** */
$wgProxyMemcExpiry = 86400;
/** This should always be customised in LocalSettings.php */
@@ -3658,13 +3806,34 @@ $wgCookieExpiration = 30*86400;
* or ".any.subdomain.net"
*/
$wgCookieDomain = '';
+
+
+/**
+ * Set this variable if you want to restrict cookies to a certain path within
+ * the domain specified by $wgCookieDomain.
+ */
$wgCookiePath = '/';
-$wgCookieSecure = ($wgProto == 'https');
+
+/**
+ * Whether the "secure" flag should be set on the cookie. This can be:
+ * - true: Set secure flag
+ * - false: Don't set secure flag
+ * - "detect": Set the secure flag if $wgServer is set to an HTTPS URL
+ */
+$wgCookieSecure = 'detect';
+
+/**
+ * By default, MediaWiki checks if the client supports cookies during the
+ * login process, so that it can display an informative error message if
+ * cookies are disabled. Set this to true if you want to disable this cookie
+ * check.
+ */
$wgDisableCookieCheck = false;
/**
- * Set $wgCookiePrefix to use a custom one. Setting to false sets the default of
- * using the database name.
+ * Cookies generated by MediaWiki have names starting with this prefix. Set it
+ * to a string to use a custom prefix. Setting it to false causes the database
+ * name to be used as a prefix.
*/
$wgCookiePrefix = false;
@@ -3672,10 +3841,8 @@ $wgCookiePrefix = false;
* Set authentication cookies to HttpOnly to prevent access by JavaScript,
* in browsers that support this feature. This can mitigates some classes of
* XSS attack.
- *
- * Only supported on PHP 5.2 or higher.
*/
-$wgCookieHttpOnly = version_compare("5.2", PHP_VERSION, "<");
+$wgCookieHttpOnly = true;
/**
* If the requesting browser matches a regex in this blacklist, we won't
@@ -3708,28 +3875,6 @@ $wgSessionName = false;
* Please see math/README for more information.
*/
$wgUseTeX = false;
-/** Location of the texvc binary */
-$wgTexvc = $IP . '/math/texvc';
-/**
- * Texvc background color
- * use LaTeX color format as used in \special function
- * for transparent background use value 'Transparent' for alpha transparency or
- * 'transparent' for binary transparency.
- */
-$wgTexvcBackgroundColor = 'transparent';
-
-/**
- * Normally when generating math images, we double-check that the
- * directories we want to write to exist, and that files that have
- * been generated still exist when we need to bring them up again.
- *
- * This lets us give useful error messages in case of permission
- * problems, and automatically rebuild images that have been lost.
- *
- * On a big site with heavy NFS traffic this can be slow and flaky,
- * so sometimes we want to short-circuit it by setting this to false.
- */
-$wgMathCheckFiles = true;
/* @} */ # end LaTeX }
@@ -3761,7 +3906,7 @@ $wgDebugLogPrefix = '';
$wgDebugRedirects = false;
/**
- * If true, log debugging data from action=raw.
+ * If true, log debugging data from action=raw and load.php.
* This is normally false to avoid overlapping debug entries due to gen=css and
* gen=js requests.
*/
@@ -3885,7 +4030,7 @@ $wgDebugProfiling = false;
/** Output debug message on every wfProfileIn/wfProfileOut */
$wgDebugFunctionEntry = 0;
-/*
+/**
* Destination for wfIncrStats() data...
* 'cache' to go into the system cache, if enabled (memcached)
* 'udp' to be sent to the UDP profiler (see $wgUDPProfilerHost)
@@ -3893,6 +4038,14 @@ $wgDebugFunctionEntry = 0;
*/
$wgStatsMethod = 'cache';
+/**
+ * When $wgStatsMethod is 'udp', setting this to a string allows statistics to
+ * be aggregated over more than one wiki. The string will be used in place of
+ * the DB name in outgoing UDP packets. If this is set to false, the DB name
+ * will be used.
+ */
+$wgAggregateStatsID = false;
+
/** Whereas to count the number of time an article is viewed.
* Does not work if pages are cached (for example with squid).
*/
@@ -3901,6 +4054,8 @@ $wgDisableCounters = false;
/**
* Support blog-style "trackbacks" for articles. See
* http://www.sixapart.com/pronet/docs/trackback_spec for details.
+ *
+ * If enabling this, you also need to grant the 'trackback' right to a group
*/
$wgUseTrackbacks = false;
@@ -3914,8 +4069,8 @@ $wgUseTrackbacks = false;
* Use full paths.
*/
$wgParserTestFiles = array(
- "$IP/maintenance/tests/parser/parserTests.txt",
- "$IP/maintenance/tests/parser/ExtraParserTests.txt"
+ "$IP/tests/parser/parserTests.txt",
+ "$IP/tests/parser/extraParserTests.txt"
);
/**
@@ -3954,11 +4109,8 @@ $wgAdvancedSearchHighlighting = false;
/**
* Regexp to match word boundaries, defaults for non-CJK languages
* should be empty for CJK since the words are not separate
- *
- * @todo FIXME: checks for lower than required PHP version (5.1.x).
*/
-$wgSearchHighlightBoundaries = version_compare("5.1", PHP_VERSION, "<")? '[\p{Z}\p{P}\p{C}]'
- : '[ ,.;:!?~!@#$%\^&*\(\)+=\-\\|\[\]"\'<>\n\r\/{}]'; // PHP 5.0 workaround
+$wgSearchHighlightBoundaries = '[\p{Z}\p{P}\p{C}]';
/**
* Set to true to have the search engine count total
@@ -4176,12 +4328,12 @@ $wgReadOnly = null;
$wgReadOnlyFile = false;
/**
- * When you run the web-based upgrade utility, it will tell you what to set
+ * When you run the web-based upgrade utility, it will tell you what to set
* this to in order to authorize the upgrade process. It will subsequently be
* used as a password, to authorize further upgrades.
*
- * For security, do not set this to a guessable string. Use the value supplied
- * by the install/upgrade process. To cause the upgrader to generate a new key,
+ * For security, do not set this to a guessable string. Use the value supplied
+ * by the install/upgrade process. To cause the upgrader to generate a new key,
* delete the old key from LocalSettings.php.
*/
$wgUpgradeKey = false;
@@ -4289,6 +4441,16 @@ $wgFeedDiffCutoff = 32768;
$wgOverrideSiteFeed = array();
/**
+ * Available feeds objects
+ * Should probably only be defined when a page is syndicated ie when
+ * $wgOut->isSyndicated() is true
+ */
+$wgFeedClasses = array(
+ 'rss' => 'RSSFeed',
+ 'atom' => 'AtomFeed',
+);
+
+/**
* Which feed types should we provide by default? This can include 'rss',
* 'atom', neither, or both.
*/
@@ -4337,16 +4499,31 @@ $wgUseTagFilter = true;
* @{
*/
-/** RDF metadata toggles */
-$wgEnableDublinCoreRdf = false;
-$wgEnableCreativeCommonsRdf = false;
-
-/** Override for copyright metadata.
- * TODO: these options need documentation
+/**
+ * Override for copyright metadata.
+ *
+ * This is the name of the page containing information about the wiki's copyright status,
+ * which will be added as a link in the footer if it is specified. It overrides
+ * $wgRightsUrl if both are specified.
*/
$wgRightsPage = null;
+
+/**
+ * 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.
+ */
$wgRightsText = null;
+
+/**
+ * Override for copyright metadata.
+ */
$wgRightsIcon = null;
/**
@@ -4356,17 +4533,13 @@ $wgLicenseTerms = false;
/**
* Set this to some HTML to override the rights icon with an arbitrary logo
- * @deprecated Use $wgFooterIcons['copyright']['copyright']
+ * @deprecated since 1.18 Use $wgFooterIcons['copyright']['copyright']
*/
$wgCopyrightIcon = null;
/** Set this to true if you want detailed copyright information forms on Upload. */
$wgUseCopyrightUpload = false;
-/** Set this to false if you want to disable checking that detailed copyright
- * information values are not empty. */
-$wgCheckCopyrightUpload = true;
-
/**
* Set this to the number of authors that you want to be credited below an
* article text. Set it to zero to hide the attribution block, and a negative
@@ -4456,12 +4629,6 @@ $wgExportFromNamespaces = false;
$wgExtensionFunctions = array();
/**
- * Extension functions for initialisation of skins. This is called somewhat earlier
- * than $wgExtensionFunctions.
- */
-$wgSkinExtensionFunctions = array();
-
-/**
* Extension messages files.
*
* Associative array mapping extension name to the filename where messages can be
@@ -4480,7 +4647,7 @@ $wgExtensionMessagesFiles = array();
/**
* Aliases for special pages provided by extensions.
- * @deprecated Use $specialPageAliases in a file referred to by $wgExtensionMessagesFiles
+ * @deprecated since 1.16 Use $specialPageAliases in a file referred to by $wgExtensionMessagesFiles
*/
$wgExtensionAliasesFiles = array();
@@ -4500,7 +4667,10 @@ $wgParserOutputHooks = array();
/**
* List of valid skin names.
- * The key should be the name in all lower case, the value should be a display name.
+ * 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
+ * class name of the skin to load, and if the skin's class cannot be found through
+ * the autoloader it will be used to load a .php file by that name in the skins directory.
* The default skins will be added later, by Skin::getSkinNames(). Use
* Skin::getSkinNames() as an accessor if you wish to have access to the full list.
*/
@@ -4542,6 +4712,7 @@ $wgExtensionCredits = array();
/**
* Authentication plugin.
+ * @var AuthPlugin
*/
$wgAuth = null;
@@ -4572,6 +4743,17 @@ $wgJobClasses = array(
);
/**
+ * Jobs that must be explicitly requested, i.e. aren't run by job runners unless special flags are set.
+ *
+ * These can be:
+ * - Very long-running jobs.
+ * - Jobs that you would never want to run as part of a page rendering request.
+ * - Jobs that you want to run on specialized machines ( like transcoding, or a particular
+ * machine on your cluster has 'outside' web access you could restrict uploadFromUrl )
+ */
+$wgJobTypesExcludedFromDefaultQueue = array();
+
+/**
* Additional functions to be performed with updateSpecialPages.
* Expensive Querypages are already updated.
*/
@@ -4624,24 +4806,29 @@ $wgCategoryMagicGallery = true;
$wgCategoryPagingLimit = 200;
/**
- * Specify how category names should be sorted, when listed on a category page.
+ * Specify how category names should be sorted, when listed on a category page.
* A sorting scheme is also known as a collation.
*
* Available values are:
*
* - uppercase: Converts the category name to upper case, and sorts by that.
*
- * - uca-default: Provides access to the Unicode Collation Algorithm with
+ * - identity: Does no conversion. Sorts by binary value of the string.
+ *
+ * - uca-default: Provides access to the Unicode Collation Algorithm with
* the default element table. This is a compromise collation which sorts
* all languages in a mediocre way. However, it is better than "uppercase".
*
- * To use the uca-default collation, you must have PHP's intl extension
- * installed. See http://php.net/manual/en/intl.setup.php . The details of the
- * resulting collation will depend on the version of ICU installed on the
+ * To use the uca-default collation, you must have PHP's intl extension
+ * installed. See http://php.net/manual/en/intl.setup.php . The details of the
+ * resulting collation will depend on the version of ICU installed on the
* server.
*
* After you change this, you must run maintenance/updateCollation.php to fix
- * the sort keys in the database.
+ * the sort keys in the database.
+ *
+ * Extensions can define there own collations by subclassing Collation
+ * and using the Collation::factory hook.
*/
$wgCategoryCollation = 'uppercase';
@@ -4753,34 +4940,34 @@ $wgLogHeaders = array(
* Extensions with custom log types may add to this array.
*/
$wgLogActions = array(
- 'block/block' => 'blocklogentry',
- 'block/unblock' => 'unblocklogentry',
- 'block/reblock' => 'reblock-logentry',
- 'protect/protect' => 'protectedarticle',
- 'protect/modify' => 'modifiedarticleprotection',
- 'protect/unprotect' => 'unprotectedarticle',
- 'protect/move_prot' => 'movedarticleprotection',
- 'rights/rights' => 'rightslogentry',
- 'rights/disable' => 'disableaccount-logentry',
- 'delete/delete' => 'deletedarticle',
- 'delete/restore' => 'undeletedarticle',
- 'delete/revision' => 'revdelete-logentry',
- 'delete/event' => 'logdelete-logentry',
- 'upload/upload' => 'uploadedimage',
- 'upload/overwrite' => 'overwroteimage',
- 'upload/revert' => 'uploadedimage',
- 'move/move' => '1movedto2',
- 'move/move_redir' => '1movedto2_redir',
- 'import/upload' => 'import-logentry-upload',
- 'import/interwiki' => 'import-logentry-interwiki',
- 'merge/merge' => 'pagemerge-logentry',
- 'suppress/revision' => 'revdelete-logentry',
- 'suppress/file' => 'revdelete-logentry',
- 'suppress/event' => 'logdelete-logentry',
- 'suppress/delete' => 'suppressedarticle',
- 'suppress/block' => 'blocklogentry',
- 'suppress/reblock' => 'reblock-logentry',
- 'patrol/patrol' => 'patrol-log-line',
+ 'block/block' => 'blocklogentry',
+ 'block/unblock' => 'unblocklogentry',
+ 'block/reblock' => 'reblock-logentry',
+ 'protect/protect' => 'protectedarticle',
+ 'protect/modify' => 'modifiedarticleprotection',
+ 'protect/unprotect' => 'unprotectedarticle',
+ 'protect/move_prot' => 'movedarticleprotection',
+ 'rights/rights' => 'rightslogentry',
+ 'rights/autopromote' => 'rightslogentry-autopromote',
+ 'delete/delete' => 'deletedarticle',
+ 'delete/restore' => 'undeletedarticle',
+ 'delete/revision' => 'revdelete-logentry',
+ 'delete/event' => 'logdelete-logentry',
+ 'upload/upload' => 'uploadedimage',
+ 'upload/overwrite' => 'overwroteimage',
+ 'upload/revert' => 'uploadedimage',
+ 'move/move' => '1movedto2',
+ 'move/move_redir' => '1movedto2_redir',
+ 'import/upload' => 'import-logentry-upload',
+ 'import/interwiki' => 'import-logentry-interwiki',
+ 'merge/merge' => 'pagemerge-logentry',
+ 'suppress/revision' => 'revdelete-logentry',
+ 'suppress/file' => 'revdelete-logentry',
+ 'suppress/event' => 'logdelete-logentry',
+ 'suppress/delete' => 'suppressedarticle',
+ 'suppress/block' => 'blocklogentry',
+ 'suppress/reblock' => 'reblock-logentry',
+ 'patrol/patrol' => 'patrol-log-line',
);
/**
@@ -4795,11 +4982,6 @@ $wgLogActionsHandlers = array();
*/
$wgNewUserLog = true;
-/**
- * Log the automatic creations of new users accounts?
- */
-$wgLogAutocreatedAccounts = false;
-
/** @} */ # end logging }
/*************************************************************************//**
@@ -4868,16 +5050,18 @@ $wgSpecialPageGroups = array(
'Listusers' => 'users',
'Activeusers' => 'users',
'Listgrouprights' => 'users',
- 'Ipblocklist' => 'users',
+ 'BlockList' => 'users',
'Contributions' => 'users',
'Emailuser' => 'users',
'Listadmins' => 'users',
'Listbots' => 'users',
'Userrights' => 'users',
- 'Blockip' => 'users',
+ 'Block' => 'users',
+ 'Unblock' => 'users',
'Preferences' => 'users',
- 'Resetpass' => 'users',
+ 'ChangePassword' => 'users',
'DeletedContributions' => 'users',
+ 'PasswordReset' => 'users',
'Mostlinked' => 'highuse',
'Mostlinkedcategories' => 'highuse',
@@ -4926,12 +5110,6 @@ $wgSpecialPageGroups = array(
$wgSortSpecialPages = true;
/**
- * Filter for Special:Randompage. Part of a WHERE clause
- * @deprecated as of 1.16, use the SpecialRandomGetRandomTitle hook
- */
-$wgExtraRandompageSQL = false;
-
-/**
* On Special:Unusedimages, consider images "used", if they are put
* into a category. Default (false) is not to count those as used.
*/
@@ -4946,6 +5124,47 @@ $wgMaxRedirectLinksRetrieved = 500;
/** @} */ # end special pages }
/*************************************************************************//**
+ * @name Actions
+ * @{
+ */
+
+/**
+ * Array of allowed values for the title=foo&action=<action> parameter. Syntax is:
+ * 'foo' => 'ClassName' Load the specified class which subclasses Action
+ * 'foo' => true Load the class FooAction which subclasses Action
+ * If something is specified in the getActionOverrides()
+ * of the relevant Page object it will be used
+ * instead of the default class.
+ * 'foo' => false The action is disabled; show an error message
+ * Unsetting core actions will probably cause things to complain loudly.
+ */
+$wgActions = array(
+ 'credits' => true,
+ 'deletetrackback' => true,
+ 'info' => true,
+ 'markpatrolled' => true,
+ 'purge' => true,
+ 'revert' => true,
+ 'revisiondelete' => true,
+ 'rollback' => true,
+ 'unwatch' => true,
+ 'watch' => true,
+);
+
+/**
+ * Array of disabled article actions, e.g. view, edit, delete, etc.
+ * @deprecated since 1.18; just set $wgActions['action'] = false instead
+ */
+$wgDisabledActions = array();
+
+/**
+ * Allow the "info" action, very inefficient at the moment
+ */
+$wgAllowPageInfo = false;
+
+/** @} */ # end actions }
+
+/*************************************************************************//**
* @name Robot (search engine crawler) policy
* See also $wgNoFollowLinks.
* @{
@@ -5060,14 +5279,7 @@ $wgAPIMaxUncachedDiffs = 1;
$wgAPIRequestLog = false;
/**
- * Cache the API help text for up to an hour. Disable this during API
- * debugging and development
- */
-$wgAPICacheHelp = true;
-
-/**
- * Set the timeout for the API help text cache. Ignored if $wgAPICacheHelp
- * is false.
+ * Set the timeout for the API help text cache. If set to 0, caching disabled
*/
$wgAPICacheHelpTimeout = 60*60;
@@ -5080,12 +5292,11 @@ $wgUseAjax = true;
* List of Ajax-callable functions.
* Extensions acting as Ajax callbacks must register here
*/
-$wgAjaxExportList = array( 'wfAjaxGetFileUrl' );
+$wgAjaxExportList = array();
/**
* Enable watching/unwatching pages using AJAX.
* Requires $wgUseAjax to be true too.
- * Causes wfAjaxWatch to be added to $wgAjaxExportList
*/
$wgAjaxWatch = true;
@@ -5214,20 +5425,62 @@ $wgUpdateRowsPerQuery = 100;
/** @} */ # End job queue }
/************************************************************************//**
- * @name Miscellaneous
+ * @name HipHop compilation
* @{
*/
-/** Allow the "info" action, very inefficient at the moment */
-$wgAllowPageInfo = false;
+/**
+ * The build directory for HipHop compilation.
+ * Defaults to $IP/maintenance/hiphop/build.
+ */
+$wgHipHopBuildDirectory = false;
-/** Name of the external diff engine to use */
-$wgExternalDiffEngine = false;
+/**
+ * The HipHop build type. Can be either "Debug" or "Release".
+ */
+$wgHipHopBuildType = 'Debug';
/**
- * Array of disabled article actions, e.g. view, edit, dublincore, delete, etc.
+ * Number of parallel processes to use during HipHop compilation, or "detect"
+ * to guess from system properties.
*/
-$wgDisabledActions = array();
+$wgHipHopCompilerProcs = 'detect';
+
+/**
+ * Filesystem extensions directory. Defaults to $IP/../extensions.
+ *
+ * To compile extensions with HipHop, set $wgExtensionsDirectory correctly,
+ * and use code like:
+ *
+ * require( MWInit::extensionSetupPath( 'Extension/Extension.php' ) );
+ *
+ * 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;
/**
* Disable redirects to special pages and interwiki redirects, which use a 302
@@ -5296,6 +5549,8 @@ $wgUploadMaintenance = false;
$wgEnableSelenium = false;
$wgSeleniumTestConfigs = array();
$wgSeleniumConfigFile = null;
+$wgDBtestuser = ''; //db user that has permission to create and drop the test databases only
+$wgDBtestpassword = '';
/**
* For really cool vim folding this needs to be at the end:
diff --git a/includes/Defines.php b/includes/Defines.php
index 64197d9c..ff7d7980 100644
--- a/includes/Defines.php
+++ b/includes/Defines.php
@@ -1,6 +1,11 @@
<?php
/**
- * A few constants that might be needed during LocalSettings.php
+ * A few constants that might be needed during LocalSettings.php.
+ *
+ * Note: these constants must all be resolvable at compile time by HipHop,
+ * since this file will not be executed during request startup for a compiled
+ * MediaWiki.
+ *
* @file
*/
@@ -80,27 +85,6 @@ define( 'NS_IMAGE', NS_FILE );
define( 'NS_IMAGE_TALK', NS_FILE_TALK );
/**@}*/
-/**
- * Available feeds objects
- * Should probably only be defined when a page is syndicated ie when
- * $wgOut->isSyndicated() is true
- */
-$wgFeedClasses = array(
- 'rss' => 'RSSFeed',
- 'atom' => 'AtomFeed',
-);
-
-/**@{
- * Maths constants
- */
-define( 'MW_MATH_PNG', 0 );
-define( 'MW_MATH_SIMPLE', 1 );
-define( 'MW_MATH_HTML', 2 );
-define( 'MW_MATH_SOURCE', 3 );
-define( 'MW_MATH_MODERN', 4 );
-define( 'MW_MATH_MATHML', 5 );
-/**@}*/
-
/**@{
* Cache type
*/
@@ -114,7 +98,7 @@ define( 'CACHE_DBA', 4 ); // Use PHP's DBA extension to store in a DBM-st
/**@{
* Media types.
- * This defines constants for the value returned by Image::getMediaType()
+ * This defines constants for the value returned by File::getMediaType()
*/
define( 'MEDIATYPE_UNKNOWN', 'UNKNOWN' ); // unknown format
define( 'MEDIATYPE_BITMAP', 'BITMAP' ); // some bitmap image or image source (like psd, etc). Can't scale up.
@@ -254,4 +238,15 @@ define( 'APCOND_ISIP', 5 );
define( 'APCOND_IPINRANGE', 6 );
define( 'APCOND_AGE_FROM_EDIT', 7 );
define( 'APCOND_BLOCKED', 8 );
+define( 'APCOND_ISBOT', 9 );
/**@}*/
+
+/**
+ * Protocol constants for wfExpandUrl()
+ */
+define( 'PROTO_HTTP', 'http://' );
+define( 'PROTO_HTTPS', 'https://' );
+define( 'PROTO_RELATIVE', '//' );
+define( 'PROTO_CURRENT', null );
+define( 'PROTO_CANONICAL', 1 );
+define( 'PROTO_INTERNAL', 2 );
diff --git a/includes/DjVuImage.php b/includes/DjVuImage.php
index cccb070a..80b7408c 100644
--- a/includes/DjVuImage.php
+++ b/includes/DjVuImage.php
@@ -72,7 +72,7 @@ class DjVuImage {
function dump() {
$file = fopen( $this->mFilename, 'rb' );
$header = fread( $file, 12 );
- // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
+ // @todo FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack( 'a4magic/a4chunk/NchunkLength', $header ) );
echo "$chunk $chunkLength\n";
$this->dumpForm( $file, $chunkLength, 1 );
@@ -88,7 +88,7 @@ class DjVuImage {
if( $chunkHeader == '' ) {
break;
}
- // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
+ // @todo FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack( 'a4chunk/NchunkLength', $chunkHeader ) );
echo str_repeat( ' ', $indent * 4 ) . "$chunk $chunkLength\n";
@@ -119,7 +119,7 @@ class DjVuImage {
if( strlen( $header ) < 16 ) {
wfDebug( __METHOD__ . ": too short file header\n" );
} else {
- // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
+ // @todo FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack( 'a4magic/a4form/NformLength/a4subtype', $header ) );
if( $magic != 'AT&T' ) {
@@ -143,7 +143,7 @@ class DjVuImage {
if( strlen( $header ) < 8 ) {
return array( false, 0 );
} else {
- // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
+ // @todo FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack( 'a4chunk/Nlength', $header ) );
return array( $chunk, $length );
}
@@ -202,7 +202,7 @@ class DjVuImage {
return false;
}
- // FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
+ // @todo FIXME: Would be good to replace this extract() call with something that explicitly initializes local variables.
extract( unpack(
'nwidth/' .
'nheight/' .
@@ -269,7 +269,7 @@ class DjVuImage {
EOR;
$txt = preg_replace_callback( $reg, array( $this, 'pageTextCallback' ), $txt );
$txt = "<DjVuTxt>\n<HEAD></HEAD>\n<BODY>\n" . $txt . "</BODY>\n</DjVuTxt>\n";
- $xml = preg_replace( "/<DjVuXML>/", "<mw-djvu><DjVuXML>", $xml );
+ $xml = preg_replace( "/<DjVuXML>/", "<mw-djvu><DjVuXML>", $xml, 1 );
$xml = $xml . $txt. '</mw-djvu>' ;
}
}
diff --git a/includes/EditPage.php b/includes/EditPage.php
index 3e85ad10..e6e7111d 100644
--- a/includes/EditPage.php
+++ b/includes/EditPage.php
@@ -11,7 +11,7 @@
* interfaces.
*
* EditPage cares about two distinct titles:
- * $wgTitle is the page that forms submit to, links point to,
+ * $this->mContextTitle is the page that forms submit to, links point to,
* redirects go to, etc. $this->mTitle (as well as $mArticle) is the
* page in the database that is actually being edited. These are
* usually the same, but they are now allowed to be different.
@@ -42,20 +42,31 @@ class EditPage {
const AS_IMAGE_REDIRECT_ANON = 233;
const AS_IMAGE_REDIRECT_LOGGED = 234;
+ /**
+ * @var Article
+ */
var $mArticle;
+
+ /**
+ * @var Title
+ */
var $mTitle;
+ private $mContextTitle = null;
var $action;
var $isConflict = false;
var $isCssJsSubpage = false;
var $isCssSubpage = false;
var $isJsSubpage = false;
- var $deletedSinceEdit = false;
+ var $isWrongCaseCssJsPage = false;
+ var $isNew = false; // new page or new section
+ var $deletedSinceEdit;
var $formtype;
var $firsttime;
var $lastDelete;
var $mTokenOk = false;
var $mTokenOkExceptSuffix = false;
var $mTriedSave = false;
+ var $incompleteForm = false;
var $tooBig = false;
var $kblength = false;
var $missingComment = false;
@@ -64,7 +75,12 @@ class EditPage {
var $autoSumm = '';
var $hookError = '';
#var $mPreviewTemplates;
+
+ /**
+ * @var ParserOutput
+ */
var $mParserOutput;
+
var $mBaseRevision = false;
var $mShowSummaryField = true;
@@ -85,6 +101,7 @@ class EditPage {
public $editFormTextBottom;
public $editFormTextAfterContent;
public $previewTextAfterContent;
+ public $mPreloadText;
/* $didSave should be set to true whenever an article was succesfully altered. */
public $didSave = false;
@@ -94,7 +111,7 @@ class EditPage {
/**
* @todo document
- * @param $article
+ * @param $article Article
*/
function __construct( $article ) {
$this->mArticle =& $article;
@@ -113,18 +130,47 @@ class EditPage {
$this->mPreloadText = "";
}
+ /**
+ * @return Article
+ */
function getArticle() {
return $this->mArticle;
}
+ /**
+ * Set the context Title object
+ *
+ * @param $title Title object or null
+ */
+ public function setContextTitle( $title ) {
+ $this->mContextTitle = $title;
+ }
+
+ /**
+ * Get the context title object.
+ * If not set, $wgTitle will be returned. This behavior might changed in
+ * the future to return $this->mTitle instead.
+ *
+ * @return Title object
+ */
+ public function getContextTitle() {
+ if ( is_null( $this->mContextTitle ) ) {
+ global $wgTitle;
+ return $wgTitle;
+ } else {
+ return $this->mContextTitle;
+ }
+ }
/**
* Fetch initial editing page content.
+ *
+ * @param $def_text string
* @returns mixed string on success, $def_text for invalid sections
* @private
*/
function getContent( $def_text = '' ) {
- global $wgOut, $wgRequest, $wgParser, $wgContLang, $wgMessageCache;
+ global $wgOut, $wgRequest, $wgParser;
wfProfileIn( __METHOD__ );
# Get variables from query string :P
@@ -141,10 +187,10 @@ class EditPage {
if ( !$this->mTitle->exists() ) {
if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
# If this is a system message, get the default text.
- list( $message, $lang ) = $wgMessageCache->figureMessage( $wgContLang->lcfirst( $this->mTitle->getText() ) );
- $text = wfMsgGetKey( $message, false, $lang, false );
- if( wfEmptyMsg( $message, $text ) )
+ $text = $this->mTitle->getDefaultMessageText();
+ if( $text === false ) {
$text = $this->getPreloadedText( $preload );
+ }
} else {
# If requested, preload some text.
$text = $this->getPreloadedText( $preload );
@@ -198,7 +244,7 @@ class EditPage {
// was created, or we may simply have got bogus input.
$this->editFormPageTop .= $wgOut->parse( '<div class="error mw-undo-norev">' . wfMsgNoTrans( 'undo-norev' ) . '</div>' );
}
- } else if ( $section != '' ) {
+ } elseif ( $section != '' ) {
if ( $section == 'new' ) {
$text = $this->getPreloadedText( $preload );
} else {
@@ -212,7 +258,11 @@ class EditPage {
return $text;
}
- /** Use this method before edit() to preload some text into the edit box */
+ /**
+ * Use this method before edit() to preload some text into the edit box
+ *
+ * @param $text string
+ */
public function setPreloadedText( $text ) {
$this->mPreloadText = $text;
}
@@ -253,15 +303,19 @@ class EditPage {
return '';
}
- /*
+ /**
* Check if a page was deleted while the user was editing it, before submit.
* Note that we rely on the logging table, which hasn't been always there,
* but that doesn't matter, because this only applies to brand new
* deletes.
*/
protected function wasDeletedSinceLastEdit() {
- if ( $this->deletedSinceEdit )
- return true;
+ if ( $this->deletedSinceEdit !== null ) {
+ return $this->deletedSinceEdit;
+ }
+
+ $this->deletedSinceEdit = false;
+
if ( $this->mTitle->isDeletedQuick() ) {
$this->lastDelete = $this->getLastDelete();
if ( $this->lastDelete ) {
@@ -271,12 +325,15 @@ class EditPage {
}
}
}
+
return $this->deletedSinceEdit;
}
/**
* Checks whether the user entered a skin name in uppercase,
* e.g. "User:Example/Monobook.css" instead of "monobook.css"
+ *
+ * @return bool
*/
protected function isWrongCaseCssJsPage() {
if( $this->mTitle->isCssJsSubpage() ) {
@@ -335,7 +392,7 @@ class EditPage {
$this->preview = true;
}
- $wgOut->addModules( array( 'mediawiki.legacy.edit', 'mediawiki.action.edit' ) );
+ $wgOut->addModules( array( 'mediawiki.action.edit' ) );
if ( $wgUser->getOption( 'uselivepreview', false ) ) {
$wgOut->addModules( 'mediawiki.legacy.preview' );
@@ -345,6 +402,9 @@ class EditPage {
$permErrors = $this->getEditPermissionErrors();
if ( $permErrors ) {
+ // Auto-block user's IP if the account was "hard" blocked
+ $wgUser->spreadAnyEditBlock();
+
wfDebug( __METHOD__ . ": User can't edit\n" );
$content = $this->getContent( null );
$content = $content === '' ? null : $content;
@@ -354,9 +414,9 @@ class EditPage {
} else {
if ( $this->save ) {
$this->formtype = 'save';
- } else if ( $this->preview ) {
+ } elseif ( $this->preview ) {
$this->formtype = 'preview';
- } else if ( $this->diff ) {
+ } elseif ( $this->diff ) {
$this->formtype = 'diff';
} else { # First time through
$this->firsttime = true;
@@ -377,10 +437,11 @@ class EditPage {
$this->isConflict = false;
// css / js subpages of user pages get a special treatment
- $this->isCssJsSubpage = $this->mTitle->isCssJsSubpage();
- $this->isCssSubpage = $this->mTitle->isCssSubpage();
- $this->isJsSubpage = $this->mTitle->isJsSubpage();
+ $this->isCssJsSubpage = $this->mTitle->isCssJsSubpage();
+ $this->isCssSubpage = $this->mTitle->isCssSubpage();
+ $this->isJsSubpage = $this->mTitle->isJsSubpage();
$this->isWrongCaseCssJsPage = $this->isWrongCaseCssJsPage();
+ $this->is