From 08aa4418c30cfc18ccc69a0f0f9cb9e17be6c196 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Mon, 12 Aug 2013 09:28:15 +0200 Subject: Update to MediaWiki 1.21.1 --- includes/Action.php | 62 +- includes/AjaxResponse.php | 4 +- includes/ArrayUtils.php | 69 + includes/Article.php | 282 +- includes/AuthPlugin.php | 16 +- includes/AutoLoader.php | 185 +- includes/Autopromote.php | 12 +- includes/BacklinkCache.php | 419 -- includes/Block.php | 146 +- includes/CacheHelper.php | 2 +- includes/Category.php | 92 +- includes/CategoryPage.php | 4 +- includes/CategoryViewer.php | 85 +- includes/Categoryfinder.php | 39 +- includes/Cdb.php | 8 +- includes/Cdb_PHP.php | 24 +- includes/ChangeTags.php | 33 +- includes/ChangesFeed.php | 22 +- includes/ChangesList.php | 150 +- includes/Collation.php | 295 +- includes/ConfEditor.php | 12 +- includes/Cookie.php | 31 +- includes/CryptRand.php | 22 +- includes/DataUpdate.php | 10 +- includes/DefaultSettings.php | 1266 +++--- includes/DeferredUpdates.php | 6 +- includes/Defines.php | 56 +- includes/DeprecatedGlobal.php | 2 +- includes/EditPage.php | 1132 +++-- includes/Exception.php | 87 +- includes/Export.php | 119 +- includes/ExternalEdit.php | 4 +- includes/ExternalStore.php | 172 - includes/ExternalStoreDB.php | 185 - includes/ExternalStoreHttp.php | 45 - includes/ExternalUser.php | 16 +- includes/FakeTitle.php | 6 +- includes/Fallback.php | 27 +- includes/Feed.php | 28 +- includes/FeedUtils.php | 90 +- includes/FileDeleteForm.php | 18 +- includes/ForkController.php | 4 +- includes/FormOptions.php | 32 +- includes/GitInfo.php | 16 +- includes/GlobalFunctions.php | 448 +- includes/HTMLForm.php | 286 +- includes/HistoryBlob.php | 51 +- includes/Hooks.php | 120 +- includes/Html.php | 230 +- includes/HttpFunctions.old.php | 33 - includes/HttpFunctions.php | 46 +- includes/IP.php | 42 +- includes/ImageGallery.php | 30 +- includes/ImagePage.php | 190 +- includes/Import.php | 144 +- includes/Init.php | 4 +- includes/Licenses.php | 9 +- includes/LinkFilter.php | 40 +- includes/Linker.php | 454 ++- includes/LinksUpdate.php | 100 +- includes/LocalisationCache.php | 1281 ------ includes/MagicWord.php | 26 +- includes/MappedIterator.php | 96 + includes/Message.php | 165 +- includes/MessageBlobStore.php | 33 +- includes/Metadata.php | 57 +- includes/MimeMagic.php | 251 +- includes/Namespace.php | 63 +- includes/OutputHandler.php | 10 +- includes/OutputPage.php | 390 +- includes/PHPVersionError.php | 33 +- includes/Pager.php | 61 +- includes/PathRouter.php | 10 +- includes/PoolCounter.php | 14 +- includes/Preferences.php | 113 +- includes/PrefixSearch.php | 10 +- includes/ProtectionForm.php | 62 +- includes/ProxyTools.php | 6 +- includes/QueryPage.php | 86 +- includes/RecentChange.php | 235 +- includes/Revision.php | 483 ++- includes/RevisionList.php | 4 +- includes/Sanitizer.php | 207 +- includes/ScopedCallback.php | 40 + includes/SeleniumWebSettings.php | 27 +- includes/Setup.php | 36 +- includes/SiteConfiguration.php | 163 +- includes/SiteStats.php | 22 +- includes/Skin.php | 76 +- includes/SkinLegacy.php | 22 +- includes/SkinTemplate.php | 154 +- includes/SpecialPage.php | 177 +- includes/SpecialPageFactory.php | 59 +- includes/SqlDataUpdate.php | 14 +- includes/SquidPurgeClient.php | 47 +- includes/Status.php | 76 +- includes/StreamFile.php | 19 +- includes/StringUtils.php | 73 +- includes/StubObject.php | 23 +- includes/Timestamp.php | 42 +- includes/Title.php | 889 ++-- includes/TitleArray.php | 4 +- includes/UIDGenerator.php | 350 ++ includes/User.php | 903 ++-- includes/UserMailer.php | 245 +- includes/UserRightsProxy.php | 14 +- includes/ViewCountUpdate.php | 17 +- includes/WatchedItem.php | 75 +- includes/WebRequest.php | 121 +- includes/WebResponse.php | 56 +- includes/WebStart.php | 19 +- includes/Wiki.php | 88 +- includes/WikiError.php | 2 +- includes/WikiFilePage.php | 9 +- includes/WikiMap.php | 26 +- includes/WikiPage.php | 1320 +++--- includes/Xml.php | 202 +- includes/XmlTypeCheck.php | 2 +- includes/ZhClient.php | 10 +- includes/ZhConversion.php | 792 +++- includes/ZipDirectoryReader.php | 40 +- includes/actions/CachedAction.php | 36 +- includes/actions/CreditsAction.php | 13 +- includes/actions/DeleteAction.php | 11 +- includes/actions/EditAction.php | 21 +- includes/actions/HistoryAction.php | 77 +- includes/actions/InfoAction.php | 320 +- includes/actions/MarkpatrolledAction.php | 5 + includes/actions/ProtectAction.php | 20 +- includes/actions/PurgeAction.php | 15 +- includes/actions/RawAction.php | 35 +- includes/actions/RenderAction.php | 11 +- includes/actions/RevertAction.php | 6 +- includes/actions/RevisiondeleteAction.php | 5 + includes/actions/RollbackAction.php | 35 +- includes/actions/ViewAction.php | 11 +- includes/actions/WatchAction.php | 10 + includes/api/ApiBase.php | 386 +- includes/api/ApiBlock.php | 29 +- includes/api/ApiComparePages.php | 22 +- includes/api/ApiCreateAccount.php | 298 ++ includes/api/ApiDelete.php | 25 +- includes/api/ApiDisabled.php | 8 - includes/api/ApiEditPage.php | 181 +- includes/api/ApiEmailUser.php | 10 +- includes/api/ApiExpandTemplates.php | 10 +- includes/api/ApiFeedContributions.php | 26 +- includes/api/ApiFeedWatchlist.php | 20 +- includes/api/ApiFileRevert.php | 16 +- includes/api/ApiFormatBase.php | 48 +- includes/api/ApiFormatDbg.php | 8 - includes/api/ApiFormatDump.php | 8 - includes/api/ApiFormatJson.php | 6 +- includes/api/ApiFormatNone.php | 43 + includes/api/ApiFormatPhp.php | 8 - includes/api/ApiFormatRaw.php | 4 - includes/api/ApiFormatTxt.php | 8 - includes/api/ApiFormatWddx.php | 8 - includes/api/ApiFormatXml.php | 20 +- includes/api/ApiFormatYaml.php | 6 +- includes/api/ApiHelp.php | 91 +- includes/api/ApiImageRotate.php | 232 ++ includes/api/ApiImport.php | 14 +- includes/api/ApiLogin.php | 8 +- includes/api/ApiLogout.php | 8 - includes/api/ApiMain.php | 289 +- includes/api/ApiModuleManager.php | 171 + includes/api/ApiMove.php | 25 +- includes/api/ApiOpenSearch.php | 8 - includes/api/ApiOptions.php | 68 +- includes/api/ApiPageSet.php | 560 ++- includes/api/ApiParamInfo.php | 114 +- includes/api/ApiParse.php | 132 +- includes/api/ApiPatrol.php | 8 - includes/api/ApiProtect.php | 10 +- includes/api/ApiPurge.php | 142 +- includes/api/ApiQuery.php | 649 ++- includes/api/ApiQueryAllCategories.php | 16 +- includes/api/ApiQueryAllImages.php | 63 +- includes/api/ApiQueryAllLinks.php | 138 +- includes/api/ApiQueryAllMessages.php | 21 +- includes/api/ApiQueryAllPages.php | 20 +- includes/api/ApiQueryAllUsers.php | 18 +- includes/api/ApiQueryBacklinks.php | 35 +- includes/api/ApiQueryBase.php | 138 +- includes/api/ApiQueryBlocks.php | 18 +- includes/api/ApiQueryCategories.php | 11 +- includes/api/ApiQueryCategoryInfo.php | 5 +- includes/api/ApiQueryCategoryMembers.php | 17 +- includes/api/ApiQueryDeletedrevs.php | 23 +- includes/api/ApiQueryDisabled.php | 8 - includes/api/ApiQueryDuplicateFiles.php | 19 +- includes/api/ApiQueryExtLinksUsage.php | 20 +- includes/api/ApiQueryExternalLinks.php | 16 +- includes/api/ApiQueryFilearchive.php | 35 +- includes/api/ApiQueryIWBacklinks.php | 10 +- includes/api/ApiQueryIWLinks.php | 10 +- includes/api/ApiQueryImageInfo.php | 122 +- includes/api/ApiQueryImages.php | 17 +- includes/api/ApiQueryInfo.php | 149 +- includes/api/ApiQueryLangBacklinks.php | 10 +- includes/api/ApiQueryLangLinks.php | 12 +- includes/api/ApiQueryLinks.php | 17 +- includes/api/ApiQueryLogEvents.php | 51 +- includes/api/ApiQueryORM.php | 264 ++ includes/api/ApiQueryPagePropNames.php | 116 + includes/api/ApiQueryPageProps.php | 17 +- includes/api/ApiQueryPagesWithProp.php | 189 + includes/api/ApiQueryProtectedTitles.php | 9 +- includes/api/ApiQueryQueryPage.php | 8 +- includes/api/ApiQueryRandom.php | 6 +- includes/api/ApiQueryRecentChanges.php | 63 +- includes/api/ApiQueryRevisions.php | 180 +- includes/api/ApiQuerySearch.php | 8 +- includes/api/ApiQuerySiteinfo.php | 57 +- includes/api/ApiQueryStashImageInfo.php | 7 +- includes/api/ApiQueryTags.php | 6 +- includes/api/ApiQueryUserContributions.php | 13 +- includes/api/ApiQueryUserInfo.php | 10 +- includes/api/ApiQueryUsers.php | 76 +- includes/api/ApiQueryWatchlist.php | 18 +- includes/api/ApiQueryWatchlistRaw.php | 10 +- includes/api/ApiResult.php | 99 +- includes/api/ApiRollback.php | 10 +- includes/api/ApiRsd.php | 12 - includes/api/ApiSetNotificationTimestamp.php | 66 +- includes/api/ApiTokens.php | 70 +- includes/api/ApiUnblock.php | 8 - includes/api/ApiUndelete.php | 18 +- includes/api/ApiUpload.php | 275 +- includes/api/ApiUserrights.php | 8 - includes/api/ApiWatch.php | 25 +- includes/cache/BacklinkCache.php | 452 ++ includes/cache/CacheDependency.php | 12 +- includes/cache/FileCacheBase.php | 6 +- includes/cache/GenderCache.php | 8 +- includes/cache/HTMLCacheUpdate.php | 226 +- includes/cache/HTMLFileCache.php | 7 +- includes/cache/LinkBatch.php | 2 +- includes/cache/LinkCache.php | 32 +- includes/cache/LocalisationCache.php | 1288 ++++++ includes/cache/MessageCache.php | 243 +- includes/cache/ProcessCacheLRU.php | 19 +- includes/cache/SquidUpdate.php | 15 +- includes/cache/UserCache.php | 12 +- includes/clientpool/RedisConnectionPool.php | 312 ++ includes/content/AbstractContent.php | 444 ++ includes/content/Content.php | 508 +++ includes/content/ContentHandler.php | 1114 +++++ includes/content/CssContent.php | 65 + includes/content/CssContentHandler.php | 67 + includes/content/JavaScriptContent.php | 66 + includes/content/JavaScriptContentHandler.php | 67 + includes/content/MessageContent.php | 158 + includes/content/TextContent.php | 286 ++ includes/content/TextContentHandler.php | 115 + includes/content/WikitextContent.php | 322 ++ includes/content/WikitextContentHandler.php | 98 + includes/context/ContextSource.php | 22 +- includes/context/DerivativeContext.php | 24 +- includes/context/IContextSource.php | 15 +- includes/context/RequestContext.php | 137 +- includes/dao/DBAccessBase.php | 92 + includes/dao/IDBAccessObject.php | 4 +- includes/db/CloneDatabase.php | 23 +- includes/db/Database.php | 625 ++- includes/db/DatabaseError.php | 22 +- includes/db/DatabaseIbm_db2.php | 1721 -------- includes/db/DatabaseMssql.php | 105 +- includes/db/DatabaseMysql.php | 61 +- includes/db/DatabaseOracle.php | 76 +- includes/db/DatabasePostgres.php | 156 +- includes/db/DatabaseSqlite.php | 47 +- includes/db/DatabaseUtility.php | 13 +- includes/db/IORMRow.php | 5 +- includes/db/IORMTable.php | 91 +- includes/db/LBFactory.php | 22 +- includes/db/LBFactory_Multi.php | 11 +- includes/db/LBFactory_Single.php | 2 +- includes/db/LoadBalancer.php | 150 +- includes/db/LoadMonitor.php | 9 +- includes/db/ORMIterator.php | 4 +- includes/db/ORMResult.php | 2 +- includes/db/ORMRow.php | 47 +- includes/db/ORMTable.php | 295 +- includes/debug/Debug.php | 11 +- includes/diff/DairikiDiff.php | 79 +- includes/diff/DifferenceEngine.php | 191 +- includes/diff/WikiDiff3.php | 5 +- includes/extauth/MediaWiki.php | 24 +- includes/externalstore/ExternalStore.php | 178 + includes/externalstore/ExternalStoreDB.php | 181 + includes/externalstore/ExternalStoreHttp.php | 43 + includes/externalstore/ExternalStoreMedium.php | 60 + includes/externalstore/ExternalStoreMwstore.php | 72 + includes/filebackend/FSFile.php | 49 +- includes/filebackend/FSFileBackend.php | 341 +- includes/filebackend/FileBackend.php | 370 +- includes/filebackend/FileBackendGroup.php | 10 +- includes/filebackend/FileBackendMultiWrite.php | 91 +- includes/filebackend/FileBackendStore.php | 502 ++- includes/filebackend/FileOp.php | 332 +- includes/filebackend/FileOpBatch.php | 62 +- includes/filebackend/README | 208 + includes/filebackend/SwiftFileBackend.php | 483 ++- includes/filebackend/TempFSFile.php | 13 +- includes/filebackend/filejournal/DBFileJournal.php | 32 + includes/filebackend/filejournal/FileJournal.php | 65 +- includes/filebackend/lockmanager/DBLockManager.php | 227 +- includes/filebackend/lockmanager/FSLockManager.php | 42 +- includes/filebackend/lockmanager/LSLockManager.php | 8 +- includes/filebackend/lockmanager/LockManager.php | 324 +- .../filebackend/lockmanager/LockManagerGroup.php | 54 +- .../filebackend/lockmanager/MemcLockManager.php | 94 +- .../filebackend/lockmanager/QuorumLockManager.php | 230 ++ includes/filebackend/lockmanager/ScopedLock.php | 102 + includes/filerepo/FSRepo.php | 8 +- includes/filerepo/FileRepo.php | 253 +- includes/filerepo/ForeignAPIRepo.php | 18 +- includes/filerepo/ForeignDBRepo.php | 2 +- includes/filerepo/ForeignDBViaLBRepo.php | 2 +- includes/filerepo/LocalRepo.php | 53 +- includes/filerepo/README | 23 +- includes/filerepo/RepoGroup.php | 24 +- includes/filerepo/file/ArchivedFile.php | 152 +- includes/filerepo/file/File.php | 208 +- includes/filerepo/file/ForeignAPIFile.php | 8 +- includes/filerepo/file/ForeignDBFile.php | 15 +- includes/filerepo/file/LocalFile.php | 281 +- includes/filerepo/file/OldLocalFile.php | 65 +- includes/filerepo/file/UnregisteredLocalFile.php | 22 +- includes/installer/CliInstaller.php | 6 +- includes/installer/DatabaseInstaller.php | 23 +- includes/installer/DatabaseUpdater.php | 449 +- includes/installer/Ibm_db2Installer.php | 270 -- includes/installer/Ibm_db2Updater.php | 91 - includes/installer/InstallDocFormatter.php | 6 +- includes/installer/Installer.i18n.php | 3294 +++++++++++---- includes/installer/Installer.php | 67 +- includes/installer/LocalSettingsGenerator.php | 50 +- includes/installer/MysqlInstaller.php | 17 +- includes/installer/MysqlUpdater.php | 106 +- includes/installer/OracleInstaller.php | 9 +- includes/installer/OracleUpdater.php | 29 +- includes/installer/PostgresInstaller.php | 21 +- includes/installer/PostgresUpdater.php | 96 +- includes/installer/SqliteInstaller.php | 6 +- includes/installer/SqliteUpdater.php | 34 +- includes/installer/WebInstaller.php | 37 +- includes/installer/WebInstallerOutput.php | 8 +- includes/installer/WebInstallerPage.php | 49 +- includes/interwiki/Interwiki.php | 96 +- includes/job/DoubleRedirectJob.php | 207 - includes/job/EmaillingJob.php | 46 - includes/job/EnotifNotifyJob.php | 57 - includes/job/Job.php | 416 +- includes/job/JobQueue.php | 435 ++ includes/job/JobQueueAggregator.php | 139 + includes/job/JobQueueAggregatorMemc.php | 117 + includes/job/JobQueueAggregatorRedis.php | 165 + includes/job/JobQueueDB.php | 716 ++++ includes/job/JobQueueGroup.php | 351 ++ includes/job/README | 81 + includes/job/RefreshLinksJob.php | 202 - includes/job/UploadFromUrlJob.php | 179 - includes/job/jobs/AssembleUploadChunksJob.php | 118 + includes/job/jobs/DoubleRedirectJob.php | 218 + includes/job/jobs/DuplicateJob.php | 59 + includes/job/jobs/EmaillingJob.php | 47 + includes/job/jobs/EnotifNotifyJob.php | 58 + includes/job/jobs/HTMLCacheUpdateJob.php | 254 ++ includes/job/jobs/NullJob.php | 60 + includes/job/jobs/PublishStashedFileJob.php | 130 + includes/job/jobs/RefreshLinksJob.php | 226 + includes/job/jobs/UploadFromUrlJob.php | 179 + includes/json/FormatJson.php | 19 +- includes/json/Services_JSON.php | 24 +- includes/libs/CSSJanus.php | 15 +- includes/libs/CSSMin.php | 14 +- includes/libs/GenericArrayObject.php | 15 +- includes/libs/IEContentAnalyzer.php | 13 +- includes/libs/IEUrlExtension.php | 12 +- includes/libs/JavaScriptMinifier.php | 20 +- includes/libs/jsminplus.php | 6 +- includes/limit.sh | 102 + includes/logging/LogEntry.php | 78 +- includes/logging/LogEventsList.php | 84 +- includes/logging/LogFormatter.php | 234 +- includes/logging/LogPage.php | 75 +- includes/logging/LogPager.php | 44 +- includes/media/BMP.php | 2 +- includes/media/Bitmap.php | 84 +- includes/media/BitmapMetadataHandler.php | 60 +- includes/media/DjVu.php | 10 +- includes/media/DjVuImage.php | 27 +- includes/media/Exif.php | 169 +- includes/media/ExifBitmap.php | 15 +- includes/media/FormatMetadata.php | 102 +- includes/media/GIF.php | 26 +- includes/media/GIFMetadataExtractor.php | 46 +- includes/media/IPTC.php | 136 +- includes/media/ImageHandler.php | 7 +- includes/media/Jpeg.php | 35 +- includes/media/JpegMetadataExtractor.php | 57 +- includes/media/MediaHandler.php | 112 +- includes/media/MediaTransformOutput.php | 43 +- includes/media/PNG.php | 24 +- includes/media/PNGMetadataExtractor.php | 4 +- includes/media/SVG.php | 67 +- includes/media/SVGMetadataExtractor.php | 39 +- includes/media/Tiff.php | 7 +- includes/media/XCF.php | 4 +- includes/media/XMP.php | 657 ++- includes/media/XMPInfo.php | 63 +- includes/media/XMPValidate.php | 193 +- includes/mime.types | 2 +- includes/mobile/DeviceDetection.php | 459 --- includes/normal/Makefile | 2 +- includes/normal/RandomTest.php | 2 +- includes/normal/Utf8CaseGenerate.php | 6 +- includes/normal/Utf8Test.php | 7 +- includes/normal/UtfNormal.php | 32 +- includes/normal/UtfNormalBench.php | 12 +- includes/normal/UtfNormalDefines.php | 4 +- includes/normal/UtfNormalGenerate.php | 4 +- includes/normal/UtfNormalMemStress.php | 10 +- includes/normal/UtfNormalTest.php | 8 +- includes/normal/UtfNormalTest2.php | 4 +- includes/normal/UtfNormalUtil.php | 6 +- includes/objectcache/APCBagOStuff.php | 43 +- includes/objectcache/BagOStuff.php | 131 +- includes/objectcache/DBABagOStuff.php | 63 +- includes/objectcache/EhcacheBagOStuff.php | 72 +- includes/objectcache/EmptyBagOStuff.php | 25 +- includes/objectcache/HashBagOStuff.php | 28 +- includes/objectcache/MemcachedBagOStuff.php | 27 +- includes/objectcache/MemcachedClient.php | 187 +- includes/objectcache/MemcachedPeclBagOStuff.php | 27 +- includes/objectcache/MemcachedPhpBagOStuff.php | 3 +- includes/objectcache/MultiWriteBagOStuff.php | 29 +- includes/objectcache/ObjectCache.php | 8 +- includes/objectcache/ObjectCacheSessionHandler.php | 8 +- includes/objectcache/RedisBagOStuff.php | 189 +- includes/objectcache/SqlBagOStuff.php | 528 ++- includes/objectcache/WinCacheBagOStuff.php | 47 +- includes/objectcache/XCacheBagOStuff.php | 40 +- includes/parser/CacheTime.php | 2 +- includes/parser/CoreLinkFunctions.php | 12 +- includes/parser/CoreParserFunctions.php | 76 +- includes/parser/CoreTagHooks.php | 1 + includes/parser/DateFormatter.php | 54 +- includes/parser/LinkHolderArray.php | 96 +- includes/parser/Parser.php | 744 ++-- includes/parser/ParserCache.php | 7 +- includes/parser/ParserOptions.php | 86 +- includes/parser/ParserOutput.php | 208 +- includes/parser/Parser_LinkHooks.php | 118 +- includes/parser/Preprocessor.php | 7 +- includes/parser/Preprocessor_DOM.php | 77 +- includes/parser/Preprocessor_Hash.php | 76 +- includes/parser/Preprocessor_HipHop.hphp | 2013 --------- includes/parser/StripState.php | 7 +- includes/parser/Tidy.php | 58 +- includes/profiler/Profiler.php | 174 +- includes/profiler/ProfilerSimple.php | 26 +- includes/profiler/ProfilerSimpleText.php | 18 +- includes/profiler/ProfilerSimpleTrace.php | 22 +- includes/profiler/ProfilerSimpleUDP.php | 2 +- includes/resourceloader/ResourceLoader.php | 93 +- includes/resourceloader/ResourceLoaderContext.php | 18 +- .../resourceloader/ResourceLoaderFileModule.php | 139 +- .../ResourceLoaderLanguageDataModule.php | 34 +- includes/resourceloader/ResourceLoaderModule.php | 56 +- .../ResourceLoaderNoscriptModule.php | 2 +- .../resourceloader/ResourceLoaderSiteModule.php | 6 +- .../resourceloader/ResourceLoaderStartUpModule.php | 55 +- .../ResourceLoaderUserCSSPrefsModule.php | 2 +- .../ResourceLoaderUserTokensModule.php | 3 +- .../resourceloader/ResourceLoaderWikiModule.php | 36 +- includes/revisiondelete/RevisionDelete.php | 13 +- .../revisiondelete/RevisionDeleteAbstracts.php | 7 +- includes/revisiondelete/RevisionDeleter.php | 4 +- includes/search/SearchEngine.php | 83 +- includes/search/SearchIBM_DB2.php | 234 -- includes/search/SearchMssql.php | 19 +- includes/search/SearchMySQL.php | 8 +- includes/search/SearchOracle.php | 142 +- includes/search/SearchPostgres.php | 44 +- includes/search/SearchSqlite.php | 15 +- includes/search/SearchUpdate.php | 13 +- includes/site/MediaWikiSite.php | 352 ++ includes/site/Site.php | 702 ++++ includes/site/SiteList.php | 300 ++ includes/site/SiteSQLStore.php | 491 +++ includes/site/SiteStore.php | 85 + includes/specials/SpecialActiveusers.php | 37 +- includes/specials/SpecialAllmessages.php | 46 +- includes/specials/SpecialAllpages.php | 96 +- includes/specials/SpecialAncientpages.php | 8 +- includes/specials/SpecialBlankpage.php | 2 +- includes/specials/SpecialBlock.php | 100 +- includes/specials/SpecialBlockList.php | 34 +- includes/specials/SpecialBlockme.php | 6 +- includes/specials/SpecialBooksources.php | 42 +- includes/specials/SpecialBrokenRedirects.php | 63 +- includes/specials/SpecialCachedPage.php | 4 +- includes/specials/SpecialCategories.php | 8 +- includes/specials/SpecialChangeEmail.php | 14 +- includes/specials/SpecialChangePassword.php | 73 +- includes/specials/SpecialComparePages.php | 31 +- includes/specials/SpecialConfirmemail.php | 10 +- includes/specials/SpecialContributions.php | 89 +- includes/specials/SpecialDeadendpages.php | 4 + includes/specials/SpecialDeletedContributions.php | 19 +- includes/specials/SpecialDisambiguations.php | 14 +- includes/specials/SpecialDoubleRedirects.php | 81 +- includes/specials/SpecialEditWatchlist.php | 57 +- includes/specials/SpecialEmailuser.php | 13 +- includes/specials/SpecialExport.php | 17 +- includes/specials/SpecialFewestrevisions.php | 5 +- includes/specials/SpecialFileDuplicateSearch.php | 24 +- includes/specials/SpecialFilepath.php | 6 +- includes/specials/SpecialImport.php | 36 +- includes/specials/SpecialJavaScriptTest.php | 19 +- includes/specials/SpecialLinkSearch.php | 45 +- includes/specials/SpecialListfiles.php | 12 +- includes/specials/SpecialListgrouprights.php | 26 +- includes/specials/SpecialListredirects.php | 20 +- includes/specials/SpecialListusers.php | 44 +- includes/specials/SpecialLockdb.php | 4 + includes/specials/SpecialLog.php | 4 +- includes/specials/SpecialLonelypages.php | 9 +- includes/specials/SpecialLongpages.php | 4 + includes/specials/SpecialMIMEsearch.php | 19 +- includes/specials/SpecialMergeHistory.php | 53 +- includes/specials/SpecialMostcategories.php | 13 +- includes/specials/SpecialMostimages.php | 12 +- includes/specials/SpecialMostinterwikis.php | 13 +- includes/specials/SpecialMostlinked.php | 15 +- includes/specials/SpecialMostlinkedcategories.php | 12 +- includes/specials/SpecialMostlinkedtemplates.php | 5 +- includes/specials/SpecialMostrevisions.php | 4 + includes/specials/SpecialMovepage.php | 65 +- includes/specials/SpecialNewimages.php | 23 +- includes/specials/SpecialNewpages.php | 57 +- includes/specials/SpecialPagesWithProp.php | 138 + includes/specials/SpecialPasswordReset.php | 26 +- includes/specials/SpecialPopularpages.php | 10 +- includes/specials/SpecialPreferences.php | 6 +- includes/specials/SpecialPrefixindex.php | 30 +- includes/specials/SpecialProtectedpages.php | 46 +- includes/specials/SpecialProtectedtitles.php | 28 +- includes/specials/SpecialRandompage.php | 10 +- includes/specials/SpecialRandomredirect.php | 2 +- includes/specials/SpecialRecentchanges.php | 100 +- includes/specials/SpecialRecentchangeslinked.php | 46 +- includes/specials/SpecialRevisiondelete.php | 45 +- includes/specials/SpecialSearch.php | 131 +- includes/specials/SpecialShortpages.php | 4 + includes/specials/SpecialSpecialpages.php | 17 +- includes/specials/SpecialStatistics.php | 63 +- includes/specials/SpecialTags.php | 4 + includes/specials/SpecialUnblock.php | 29 +- .../specials/SpecialUncategorizedcategories.php | 13 + includes/specials/SpecialUncategorizedimages.php | 3 + includes/specials/SpecialUncategorizedpages.php | 11 +- includes/specials/SpecialUndelete.php | 352 +- includes/specials/SpecialUnlockdb.php | 4 + includes/specials/SpecialUnusedcategories.php | 8 +- includes/specials/SpecialUnusedimages.php | 3 + includes/specials/SpecialUnusedtemplates.php | 18 +- includes/specials/SpecialUnwatchedpages.php | 17 +- includes/specials/SpecialUpload.php | 98 +- includes/specials/SpecialUploadStash.php | 34 +- includes/specials/SpecialUserlogin.php | 330 +- includes/specials/SpecialUserlogout.php | 9 +- includes/specials/SpecialUserrights.php | 106 +- includes/specials/SpecialVersion.php | 137 +- includes/specials/SpecialWantedcategories.php | 4 + includes/specials/SpecialWantedfiles.php | 6 +- includes/specials/SpecialWantedpages.php | 11 +- includes/specials/SpecialWantedtemplates.php | 4 + includes/specials/SpecialWatchlist.php | 116 +- includes/specials/SpecialWhatlinkshere.php | 22 +- includes/specials/SpecialWithoutinterwiki.php | 4 + includes/templates/NoLocalSettings.php | 4 + includes/templates/Usercreate.php | 58 +- includes/templates/Userlogin.php | 4 +- includes/tidy.conf | 5 +- includes/upload/UploadBase.php | 159 +- includes/upload/UploadFromChunks.php | 56 +- includes/upload/UploadFromFile.php | 12 +- includes/upload/UploadFromStash.php | 17 +- includes/upload/UploadFromUrl.php | 22 +- includes/upload/UploadStash.php | 74 +- includes/zhtable/Makefile | 336 -- includes/zhtable/Makefile.py | 391 -- includes/zhtable/README | 33 - includes/zhtable/printutf8.c | 99 - includes/zhtable/simp2trad.manual | 372 -- includes/zhtable/simp2trad_noconvert.manual | 139 - includes/zhtable/simp2trad_supp_set.manual | 2 - includes/zhtable/simpphrases.manual | 2239 ---------- includes/zhtable/simpphrases_exclude.manual | 21 - includes/zhtable/toCN.manual | 275 -- includes/zhtable/toHK.manual | 2300 ----------- includes/zhtable/toSG.manual | 21 - includes/zhtable/toSimp.manual | 165 - includes/zhtable/toTW.manual | 411 -- includes/zhtable/toTrad.manual | 186 - includes/zhtable/trad2simp.manual | 150 - includes/zhtable/trad2simp_noconvert.manual | 5 - includes/zhtable/trad2simp_supp_set.manual | 3 - includes/zhtable/tradphrases.manual | 4310 -------------------- includes/zhtable/tradphrases_exclude.manual | 330 -- 615 files changed, 40872 insertions(+), 35489 deletions(-) create mode 100644 includes/ArrayUtils.php delete mode 100644 includes/BacklinkCache.php delete mode 100644 includes/ExternalStore.php delete mode 100644 includes/ExternalStoreDB.php delete mode 100644 includes/ExternalStoreHttp.php delete mode 100644 includes/HttpFunctions.old.php delete mode 100644 includes/LocalisationCache.php create mode 100644 includes/MappedIterator.php create mode 100644 includes/ScopedCallback.php create mode 100644 includes/UIDGenerator.php create mode 100644 includes/api/ApiCreateAccount.php create mode 100644 includes/api/ApiFormatNone.php create mode 100644 includes/api/ApiImageRotate.php create mode 100644 includes/api/ApiModuleManager.php create mode 100644 includes/api/ApiQueryORM.php create mode 100644 includes/api/ApiQueryPagePropNames.php create mode 100644 includes/api/ApiQueryPagesWithProp.php create mode 100644 includes/cache/BacklinkCache.php create mode 100644 includes/cache/LocalisationCache.php create mode 100644 includes/clientpool/RedisConnectionPool.php create mode 100644 includes/content/AbstractContent.php create mode 100644 includes/content/Content.php create mode 100644 includes/content/ContentHandler.php create mode 100644 includes/content/CssContent.php create mode 100644 includes/content/CssContentHandler.php create mode 100644 includes/content/JavaScriptContent.php create mode 100644 includes/content/JavaScriptContentHandler.php create mode 100644 includes/content/MessageContent.php create mode 100644 includes/content/TextContent.php create mode 100644 includes/content/TextContentHandler.php create mode 100644 includes/content/WikitextContent.php create mode 100644 includes/content/WikitextContentHandler.php create mode 100644 includes/dao/DBAccessBase.php delete mode 100644 includes/db/DatabaseIbm_db2.php create mode 100644 includes/externalstore/ExternalStore.php create mode 100644 includes/externalstore/ExternalStoreDB.php create mode 100644 includes/externalstore/ExternalStoreHttp.php create mode 100644 includes/externalstore/ExternalStoreMedium.php create mode 100644 includes/externalstore/ExternalStoreMwstore.php create mode 100644 includes/filebackend/README create mode 100644 includes/filebackend/lockmanager/QuorumLockManager.php create mode 100644 includes/filebackend/lockmanager/ScopedLock.php delete mode 100644 includes/installer/Ibm_db2Installer.php delete mode 100644 includes/installer/Ibm_db2Updater.php delete mode 100644 includes/job/DoubleRedirectJob.php delete mode 100644 includes/job/EmaillingJob.php delete mode 100644 includes/job/EnotifNotifyJob.php create mode 100644 includes/job/JobQueue.php create mode 100644 includes/job/JobQueueAggregator.php create mode 100644 includes/job/JobQueueAggregatorMemc.php create mode 100644 includes/job/JobQueueAggregatorRedis.php create mode 100644 includes/job/JobQueueDB.php create mode 100644 includes/job/JobQueueGroup.php create mode 100644 includes/job/README delete mode 100644 includes/job/RefreshLinksJob.php delete mode 100644 includes/job/UploadFromUrlJob.php create mode 100644 includes/job/jobs/AssembleUploadChunksJob.php create mode 100644 includes/job/jobs/DoubleRedirectJob.php create mode 100644 includes/job/jobs/DuplicateJob.php create mode 100644 includes/job/jobs/EmaillingJob.php create mode 100644 includes/job/jobs/EnotifNotifyJob.php create mode 100644 includes/job/jobs/HTMLCacheUpdateJob.php create mode 100644 includes/job/jobs/NullJob.php create mode 100644 includes/job/jobs/PublishStashedFileJob.php create mode 100644 includes/job/jobs/RefreshLinksJob.php create mode 100644 includes/job/jobs/UploadFromUrlJob.php create mode 100644 includes/limit.sh delete mode 100644 includes/mobile/DeviceDetection.php delete mode 100644 includes/parser/Preprocessor_HipHop.hphp delete mode 100644 includes/search/SearchIBM_DB2.php create mode 100644 includes/site/MediaWikiSite.php create mode 100644 includes/site/Site.php create mode 100644 includes/site/SiteList.php create mode 100644 includes/site/SiteSQLStore.php create mode 100644 includes/site/SiteStore.php create mode 100644 includes/specials/SpecialPagesWithProp.php delete mode 100644 includes/zhtable/Makefile delete mode 100644 includes/zhtable/Makefile.py delete mode 100644 includes/zhtable/README delete mode 100644 includes/zhtable/printutf8.c delete mode 100644 includes/zhtable/simp2trad.manual delete mode 100644 includes/zhtable/simp2trad_noconvert.manual delete mode 100644 includes/zhtable/simp2trad_supp_set.manual delete mode 100644 includes/zhtable/simpphrases.manual delete mode 100644 includes/zhtable/simpphrases_exclude.manual delete mode 100644 includes/zhtable/toCN.manual delete mode 100644 includes/zhtable/toHK.manual delete mode 100644 includes/zhtable/toSG.manual delete mode 100644 includes/zhtable/toSimp.manual delete mode 100644 includes/zhtable/toTW.manual delete mode 100644 includes/zhtable/toTrad.manual delete mode 100644 includes/zhtable/trad2simp.manual delete mode 100644 includes/zhtable/trad2simp_noconvert.manual delete mode 100644 includes/zhtable/trad2simp_supp_set.manual delete mode 100644 includes/zhtable/tradphrases.manual delete mode 100644 includes/zhtable/tradphrases_exclude.manual (limited to 'includes') diff --git a/includes/Action.php b/includes/Action.php index 51922251..2e0c88ba 100644 --- a/includes/Action.php +++ b/includes/Action.php @@ -32,13 +32,13 @@ * * 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). The FormAction and FormlessAction classes respresent these two groups. + * patrol, etc). The FormAction and FormlessAction classes represent these two groups. */ abstract class Action { /** * Page on which we're performing the action - * @var Page $page + * @var WikiPage|Article|ImagePage|CategoryPage|Page $page */ protected $page; @@ -61,7 +61,7 @@ abstract class Action { * @param $overrides Array * @return bool|null|string */ - private final static function getClass( $action, array $overrides ) { + final private static function getClass( $action, array $overrides ) { global $wgActions; $action = strtolower( $action ); @@ -88,7 +88,7 @@ abstract class Action { * @return Action|bool|null false if the action is disabled, null * if it is not recognised */ - public final static function factory( $action, Page $page, IContextSource $context = null ) { + final public static function factory( $action, Page $page, IContextSource $context = null ) { $class = self::getClass( $action, $page->getActionOverrides() ); if ( $class ) { $obj = new $class( $page, $context ); @@ -106,7 +106,7 @@ abstract class Action { * @param $context IContextSource * @return string: action name */ - public final static function getActionName( IContextSource $context ) { + final public static function getActionName( IContextSource $context ) { global $wgActions; $request = $context->getRequest(); @@ -147,10 +147,10 @@ abstract class Action { /** * Check if a given action is recognised, even if it's disabled * - * @param $name String: name of an action + * @param string $name name of an action * @return Bool */ - public final static function exists( $name ) { + final public static function exists( $name ) { return self::getClass( $name, array() ) !== null; } @@ -158,7 +158,7 @@ abstract class Action { * Get the IContextSource in use here * @return IContextSource */ - public final function getContext() { + final public function getContext() { if ( $this->context instanceof IContextSource ) { return $this->context; } @@ -170,7 +170,7 @@ abstract class Action { * * @return WebRequest */ - public final function getRequest() { + final public function getRequest() { return $this->getContext()->getRequest(); } @@ -179,7 +179,7 @@ abstract class Action { * * @return OutputPage */ - public final function getOutput() { + final public function getOutput() { return $this->getContext()->getOutput(); } @@ -188,7 +188,7 @@ abstract class Action { * * @return User */ - public final function getUser() { + final public function getUser() { return $this->getContext()->getUser(); } @@ -197,7 +197,7 @@ abstract class Action { * * @return Skin */ - public final function getSkin() { + final public function getSkin() { return $this->getContext()->getSkin(); } @@ -206,7 +206,7 @@ abstract class Action { * * @return Language */ - public final function getLanguage() { + final public function getLanguage() { return $this->getContext()->getLanguage(); } @@ -216,7 +216,7 @@ abstract class Action { * @deprecated 1.19 Use getLanguage instead * @return Language */ - public final function getLang() { + final public function getLang() { wfDeprecated( __METHOD__, '1.19' ); return $this->getLanguage(); } @@ -225,7 +225,7 @@ abstract class Action { * Shortcut to get the Title object from the page * @return Title */ - public final function getTitle() { + final public function getTitle() { return $this->page->getTitle(); } @@ -235,7 +235,7 @@ abstract class Action { * * @return Message object */ - public final function msg() { + final public function msg() { $params = func_get_args(); return call_user_func_array( array( $this->getContext(), 'msg' ), $params ); } @@ -255,7 +255,7 @@ abstract class Action { * Return the name of the action this object responds to * @return String lowercase */ - public abstract function getName(); + abstract public function getName(); /** * Get the permission required to perform this action. Often, but not always, @@ -272,7 +272,7 @@ abstract class Action { * must throw subclasses of ErrorPageError * * @param $user User: the user to check, or null to use the context user - * @throws ErrorPageError + * @throws UserBlockedError|ReadOnlyError|PermissionsError * @return bool True on success */ protected function checkCanExecute( User $user ) { @@ -350,13 +350,13 @@ abstract class Action { * $this->getOutput(), etc. * @throws ErrorPageError */ - public abstract function show(); + abstract public function show(); /** * Execute the action in a silent fashion: do not display anything or release any errors. * @return Bool whether execution was successful */ - public abstract function execute(); + abstract public function execute(); } /** @@ -368,7 +368,7 @@ abstract class FormAction extends Action { * Get an HTMLForm descriptor array * @return Array */ - protected abstract function getFormFields(); + abstract protected function getFormFields(); /** * Add pre- or post-text to the form @@ -388,7 +388,7 @@ abstract class FormAction extends Action { protected function alterForm( HTMLForm $form ) {} /** - * Get the HTMLForm to control behaviour + * Get the HTMLForm to control behavior * @return HTMLForm|null */ protected function getForm() { @@ -406,7 +406,7 @@ abstract class FormAction extends Action { $this->getRequest()->getQueryValues(), array( 'action' => null, 'title' => null ) ); - $form->addHiddenField( 'redirectparams', wfArrayToCGI( $params ) ); + $form->addHiddenField( 'redirectparams', wfArrayToCgi( $params ) ); $form->addPreText( $this->preText() ); $form->addPostText( $this->postText() ); @@ -425,21 +425,21 @@ abstract class FormAction extends Action { * @param $data Array * @return Bool|Array true for success, false for didn't-try, array of errors on failure */ - public abstract function onSubmit( $data ); + abstract public 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(); + abstract public 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 + * behavior, but that's what subclassing is for :D */ public function show() { $this->setHeaders(); @@ -455,9 +455,10 @@ abstract class FormAction extends Action { /** * @see Action::execute() - * @throws ErrorPageError + * * @param $data array|null * @param $captureErrors bool + * @throws ErrorPageError|Exception * @return bool */ public function execute( array $data = null, $captureErrors = true ) { @@ -507,7 +508,7 @@ abstract class FormlessAction extends Action { * @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(); + abstract public function onView(); /** * We don't want an HTMLForm @@ -544,8 +545,9 @@ abstract class FormlessAction extends Action { /** * 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 + * @param array $data values that would normally be in the GET request + * @param bool $captureErrors whether to catch exceptions and just return false + * @throws ErrorPageError|Exception * @return Bool whether execution was successful */ public function execute( array $data = null, $captureErrors = true ) { diff --git a/includes/AjaxResponse.php b/includes/AjaxResponse.php index 6bf94ccb..138f808a 100644 --- a/includes/AjaxResponse.php +++ b/includes/AjaxResponse.php @@ -172,7 +172,7 @@ class AjaxResponse { # tell the client to use a cached copy, without a way to purge it. if ( $wgUseSquid ) { - # Expect explicite purge of the proxy cache, but require end user agents + # Expect explicit purge of the proxy cache, but require end user agents # to revalidate against the proxy on each visit. # Surrogate-Control controls our Squid, Cache-Control downstream caches @@ -204,7 +204,7 @@ class AjaxResponse { /** * checkLastModified tells the client to use the client-cached response if - * possible. If sucessful, the AjaxResponse is disabled so that + * possible. If successful, the AjaxResponse is disabled so that * any future call to AjaxResponse::printText() have no effect. * * @param $timestamp string diff --git a/includes/ArrayUtils.php b/includes/ArrayUtils.php new file mode 100644 index 00000000..0b74f06a --- /dev/null +++ b/includes/ArrayUtils.php @@ -0,0 +1,69 @@ + $w ) { + $sum += $w; + # Do not return keys if they have 0 weight. + # Note that the "all 0 weight" case is handed above + if ( $w > 0 && $sum >= $rand ) { + break; + } + } + return $i; + } +} diff --git a/includes/Article.php b/includes/Article.php index 9ab4b6ba..9b4afe44 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -33,7 +33,7 @@ * * @internal documentation reviewed 15 Mar 2010 */ -class Article extends Page { +class Article implements Page { /**@{{ * @private */ @@ -57,10 +57,17 @@ class Article extends Page { public $mParserOptions; /** - * Content of the revision we are working on + * Text of the revision we are working on * @var string $mContent */ - var $mContent; // !< + var $mContent; // !< #BC cruft + + /** + * Content of the revision we are working on + * @var Content + * @since 1.21 + */ + var $mContentObject; // !< /** * Is the content ($mContent) already loaded? @@ -127,7 +134,7 @@ class Article extends Page { /** * Constructor from a page id - * @param $id Int article ID to load + * @param int $id article ID to load * @return Article|null */ public static function newFromID( $id ) { @@ -231,9 +238,32 @@ class Article extends Page { * This function has side effects! Do not use this function if you * only want the real revision text if any. * + * @deprecated in 1.21; use WikiPage::getContent() instead + * * @return string Return the text of this revision */ public function getContent() { + ContentHandler::deprecated( __METHOD__, '1.21' ); + $content = $this->getContentObject(); + return ContentHandler::getContentText( $content ); + } + + /** + * Returns a Content object representing the pages effective display content, + * not necessarily the revision's content! + * + * Note that getContent/loadContent do not follow redirects anymore. + * If you need to fetch redirectable content easily, try + * the shortcut in WikiPage::getRedirectTarget() + * + * This function has side effects! Do not use this function if you + * only want the real revision text if any. + * + * @return Content Return the content of this revision + * + * @since 1.21 + */ + protected function getContentObject() { wfProfileIn( __METHOD__ ); if ( $this->mPage->getID() === 0 ) { @@ -244,19 +274,19 @@ class Article extends Page { if ( $text === false ) { $text = ''; } + + $content = ContentHandler::makeContent( $text, $this->getTitle() ); } else { $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon'; - $text = wfMessage( $message )->text(); + $content = new MessageContent( $message, null, 'parsemag' ); } - wfProfileOut( __METHOD__ ); - - return $text; } else { - $this->fetchContent(); - wfProfileOut( __METHOD__ ); - - return $this->mContent; + $this->fetchContentObject(); + $content = $this->mContentObject; } + + wfProfileOut( __METHOD__ ); + return $content; } /** @@ -336,22 +366,60 @@ class Article extends Page { * Get text of an article from database * Does *NOT* follow redirects. * + * @protected + * @note this is really internal functionality that should really NOT be used by other functions. For accessing + * article content, use the WikiPage class, especially WikiBase::getContent(). However, a lot of legacy code + * uses this method to retrieve page text from the database, so the function has to remain public for now. + * * @return mixed string containing article contents, or false if null + * @deprecated in 1.21, use WikiPage::getContent() instead */ - function fetchContent() { - if ( $this->mContentLoaded ) { + function fetchContent() { #BC cruft! + ContentHandler::deprecated( __METHOD__, '1.21' ); + + if ( $this->mContentLoaded && $this->mContent ) { return $this->mContent; } wfProfileIn( __METHOD__ ); + $content = $this->fetchContentObject(); + + $this->mContent = ContentHandler::getContentText( $content ); #@todo: get rid of mContent everywhere! + ContentHandler::runLegacyHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) ); + + wfProfileOut( __METHOD__ ); + + return $this->mContent; + } + + /** + * Get text content object + * Does *NOT* follow redirects. + * TODO: when is this null? + * + * @note code that wants to retrieve page content from the database should use WikiPage::getContent(). + * + * @return Content|null|boolean false + * + * @since 1.21 + */ + protected function fetchContentObject() { + if ( $this->mContentLoaded ) { + return $this->mContentObject; + } + + wfProfileIn( __METHOD__ ); + $this->mContentLoaded = true; + $this->mContent = null; $oldid = $this->getOldID(); # Pre-fill content with error message so that if something # fails we'll have something telling us what we intended. - $this->mContent = wfMessage( 'missing-revision', $oldid )->plain(); + //XXX: this isn't page content but a UI message. horrible. + $this->mContentObject = new MessageContent( 'missing-revision', array( $oldid ), array() ); if ( $oldid ) { # $this->mRevision might already be fetched by getOldIDFromRequest() @@ -371,6 +439,7 @@ class Article extends Page { } $this->mRevision = $this->mPage->getRevision(); + if ( !$this->mRevision ) { wfDebug( __METHOD__ . " failed to retrieve current page, rev_id " . $this->mPage->getLatest() . "\n" ); wfProfileOut( __METHOD__ ); @@ -380,14 +449,14 @@ class Article extends Page { // @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 = $this->mRevision->getText( Revision::FOR_THIS_USER ); // Loads if user is allowed + $this->mContentObject = $this->mRevision->getContent( Revision::FOR_THIS_USER, $this->getContext()->getUser() ); // Loads if user is allowed $this->mRevIdFetched = $this->mRevision->getId(); - wfRunHooks( 'ArticleAfterFetchContent', array( &$this, &$this->mContent ) ); + wfRunHooks( 'ArticleAfterFetchContentObject', array( &$this, &$this->mContentObject ) ); wfProfileOut( __METHOD__ ); - return $this->mContent; + return $this->mContentObject; } /** @@ -420,7 +489,7 @@ class Article extends Page { * @return Revision|null */ public function getRevisionFetched() { - $this->fetchContent(); + $this->fetchContentObject(); return $this->mRevision; } @@ -443,7 +512,7 @@ class Article extends Page { * page of the given title. */ public function view() { - global $wgParser, $wgUseFileCache, $wgUseETag, $wgDebugToolbar; + global $wgUseFileCache, $wgUseETag, $wgDebugToolbar; wfProfileIn( __METHOD__ ); @@ -580,7 +649,7 @@ class Article extends Page { break; case 3: # This will set $this->mRevision if needed - $this->fetchContent(); + $this->fetchContentObject(); # Are we looking at an old revision if ( $oldid && $this->mRevision ) { @@ -604,18 +673,25 @@ class Article extends Page { wfDebug( __METHOD__ . ": showing CSS/JS source\n" ); $this->showCssOrJsPage(); $outputDone = true; - } elseif( !wfRunHooks( 'ArticleViewCustom', array( $this->mContent, $this->getTitle(), $outputPage ) ) ) { + } elseif( !wfRunHooks( 'ArticleContentViewCustom', + array( $this->fetchContentObject(), $this->getTitle(), $outputPage ) ) ) { + + # Allow extensions do their own custom view for certain pages + $outputDone = true; + } elseif( !ContentHandler::runLegacyHooks( 'ArticleViewCustom', + array( $this->fetchContentObject(), $this->getTitle(), $outputPage ) ) ) { + # Allow extensions do their own custom view for certain pages $outputDone = true; } else { - $text = $this->getContent(); - $rt = Title::newFromRedirectArray( $text ); + $content = $this->getContentObject(); + $rt = $content ? $content->getRedirectChain() : null; if ( $rt ) { wfDebug( __METHOD__ . ": showing redirect=no page\n" ); # Viewing a redirect page (e.g. with parameter redirect=no) $outputPage->addHTML( $this->viewRedirect( $rt ) ); # Parse just to get categories, displaytitle, etc. - $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(), $parserOptions ); + $this->mParserOutput = $content->getParserOutput( $this->getTitle(), $oldid, $parserOptions, false ); $outputPage->addParserOutputNoText( $this->mParserOutput ); $outputDone = true; } @@ -625,8 +701,8 @@ class Article extends Page { # Run the parse, protected by a pool counter wfDebug( __METHOD__ . ": doing uncached parse\n" ); - $poolArticleView = new PoolWorkArticleView( $this, $parserOptions, - $this->getRevIdFetched(), $useParserCache, $this->getContent() ); + $poolArticleView = new PoolWorkArticleView( $this->getPage(), $parserOptions, + $this->getRevIdFetched(), $useParserCache, $this->getContentObject() ); if ( !$poolArticleView->execute() ) { $error = $poolArticleView->getError(); @@ -690,6 +766,8 @@ class Article extends Page { $this->showViewFooter(); $this->mPage->doViewUpdates( $user ); + $outputPage->addModules( 'mediawiki.action.view.postEdit' ); + wfProfileOut( __METHOD__ ); } @@ -708,6 +786,8 @@ class Article extends Page { /** * Show a diff page according to current request variables. For use within * Article::view() only, other callers should use the DifferenceEngine class. + * + * @todo: make protected */ public function showDiffPage() { $request = $this->getContext()->getRequest(); @@ -719,7 +799,17 @@ class Article extends Page { $unhide = $request->getInt( 'unhide' ) == 1; $oldid = $this->getOldID(); - $de = new DifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide ); + $rev = $this->getRevisionFetched(); + + if ( !$rev ) { + $this->getContext()->getOutput()->setPageTitle( wfMessage( 'errorpagetitle' ) ); + $this->getContext()->getOutput()->addWikiMsg( 'difference-missing-revision', $oldid, 1 ); + return; + } + + $contentHandler = $rev->getContentHandler(); + $de = $contentHandler->createDifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide ); + // DifferenceEngine directly fetched the revision: $this->mRevIdFetched = $de->mNewid; $de->showDiffPage( $diffOnly ); @@ -736,29 +826,34 @@ class Article extends Page { * * This is hooked by SyntaxHighlight_GeSHi to do syntax highlighting of these * page views. + * + * @param bool $showCacheHint whether to show a message telling the user to clear the browser cache (default: true). */ - protected function showCssOrJsPage() { - $dir = $this->getContext()->getLanguage()->getDir(); - $lang = $this->getContext()->getLanguage()->getCode(); - + protected function showCssOrJsPage( $showCacheHint = true ) { $outputPage = $this->getContext()->getOutput(); - $outputPage->wrapWikiMsg( "
\n$1\n
", - 'clearyourcache' ); - // Give hooks a chance to customise the output - if ( wfRunHooks( 'ShowRawCssJs', array( $this->mContent, $this->getTitle(), $outputPage ) ) ) { - // Wrap the whole lot in a
 and don't parse
-			$m = array();
-			preg_match( '!\.(css|js)$!u', $this->getTitle()->getText(), $m );
-			$outputPage->addHTML( "
\n" );
-			$outputPage->addHTML( htmlspecialchars( $this->mContent ) );
-			$outputPage->addHTML( "\n
\n" ); + if ( $showCacheHint ) { + $dir = $this->getContext()->getLanguage()->getDir(); + $lang = $this->getContext()->getLanguage()->getCode(); + + $outputPage->wrapWikiMsg( "
\n$1\n
", + 'clearyourcache' ); + } + + $this->fetchContentObject(); + + if ( $this->mContentObject ) { + // Give hooks a chance to customise the output + if ( ContentHandler::runLegacyHooks( 'ShowRawCssJs', array( $this->mContentObject, $this->getTitle(), $outputPage ) ) ) { + $po = $this->mContentObject->getParserOutput( $this->getTitle() ); + $outputPage->addHTML( $po->getText() ); + } } } /** * Get the robot policy to be used for the current view - * @param $action String the action= GET parameter + * @param string $action the action= GET parameter * @param $pOutput ParserOutput * @return Array the policy that should be set * TODO: actions other than 'view' @@ -768,15 +863,21 @@ class Article extends Page { $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->getTitle()->isSubpage() ) { - if ( Block::newFromTarget( null, $this->getTitle()->getText() ) instanceof Block ) { - return array( - 'index' => 'noindex', - 'follow' => 'nofollow' - ); - } + # Don't index user and user talk pages for blocked users (bug 11443) + if ( ( $ns == NS_USER || $ns == NS_USER_TALK ) && !$this->getTitle()->isSubpage() ) { + $specificTarget = null; + $vagueTarget = null; + $titleText = $this->getTitle()->getText(); + if ( IP::isValid( $titleText ) ) { + $vagueTarget = $titleText; + } else { + $specificTarget = $titleText; + } + if ( Block::newFromTarget( $specificTarget, $vagueTarget ) instanceof Block ) { + return array( + 'index' => 'noindex', + 'follow' => 'nofollow' + ); } } @@ -892,9 +993,7 @@ class Article extends Page { } // Add a tag - $outputPage->addLink( array( 'rel' => 'canonical', - 'href' => $this->getTitle()->getLocalURL() ) - ); + $outputPage->setCanonicalUrl( $this->getTitle()->getLocalURL() ); // Tell the output object that the user arrived at this article through a redirect $outputPage->setRedirectedFrom( $this->mRedirectedFrom ); @@ -948,6 +1047,8 @@ class Article extends Page { * If patrol is possible, output a patrol UI box. This is called from the * footer section of ordinary page views. If patrol is not possible or not * desired, does nothing. + * Side effect: When the patrol link is build, this method will call + * OutputPage::preventClickjacking() and load mediawiki.page.patrol.ajax. */ public function showPatrolFooter() { $request = $this->getContext()->getRequest(); @@ -960,7 +1061,9 @@ class Article extends Page { } $token = $user->getEditToken( $rcid ); + $outputPage->preventClickjacking(); + $outputPage->addModules( 'mediawiki.page.patrol.ajax' ); $link = Linker::linkKnown( $this->getTitle(), @@ -987,6 +1090,7 @@ class Article extends Page { public function showMissingArticle() { global $wgSend404Code; $outputPage = $this->getContext()->getOutput(); + $validUserPage = false; # Show info in user (talk) namespace. Does the user exist? Is he blocked? if ( $this->getTitle()->getNamespace() == NS_USER || $this->getTitle()->getNamespace() == NS_USER_TALK ) { @@ -1013,6 +1117,9 @@ class Article extends Page { ) ) ); + $validUserPage = true; + } else { + $validUserPage = true; } } @@ -1020,13 +1127,13 @@ class Article extends Page { # Show delete and move logs LogEventsList::showLogExtract( $outputPage, array( 'delete', 'move' ), $this->getTitle(), '', - array( 'lim' => 10, + array( 'lim' => 10, 'conds' => array( "log_action != 'revision'" ), 'showIfEmpty' => false, 'msgKey' => array( 'moveddeleted-notice' ) ) ); - if ( !$this->mPage->hasViewableContent() && $wgSend404Code ) { + if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) { // If there's no backing content, send a 404 Not Found // for better machine handling of broken links. $this->getContext()->getRequest()->response()->header( "HTTP/1.1 404 Not Found" ); @@ -1104,7 +1211,7 @@ class Article extends Page { * Revision as of \; view current revision * \<- Previous version | Next Version -\> * - * @param $oldid int: revision ID of this article revision + * @param int $oldid revision ID of this article revision */ public function setOldSubtitle( $oldid = 0 ) { if ( !wfRunHooks( 'DisplayOldSubtitle', array( &$this, &$oldid ) ) ) { @@ -1166,7 +1273,7 @@ class Article extends Page { 'oldid' => $oldid ) + $extraParams ); - $prev = $this->getTitle()->getPreviousRevisionID( $oldid ) ; + $prev = $this->getTitle()->getPreviousRevisionID( $oldid ); $prevlink = $prev ? Linker::linkKnown( $this->getTitle(), @@ -1377,7 +1484,13 @@ class Article extends Page { // Generate deletion reason $hasHistory = false; if ( !$reason ) { - $reason = $this->generateReason( $hasHistory ); + try { + $reason = $this->generateReason( $hasHistory ); + } catch ( MWException $e ) { + # if a page is horribly broken, we still want to be able to delete it. so be lenient about errors here. + wfDebug( "Error while building auto delete summary: $e" ); + $reason = ''; + } } // If the page has a history, insert a warning @@ -1406,7 +1519,7 @@ class Article extends Page { /** * Output deletion confirmation dialog * @todo FIXME: Move to another file? - * @param $reason String: prefilled reason + * @param string $reason prefilled reason */ public function confirmDelete( $reason ) { wfDebug( "Article::confirmDelete\n" ); @@ -1614,9 +1727,11 @@ class Article extends Page { * * @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 + * @return ParserOutput or false if the given revision ID is not found */ public function getParserOutput( $oldid = null, User $user = null ) { + //XXX: bypasses mParserOptions and thus setParserOptions() + if ( $user === null ) { $parserOptions = $this->getParserOptions(); } else { @@ -1626,6 +1741,21 @@ class Article extends Page { return $this->mPage->getParserOutput( $parserOptions, $oldid ); } + /** + * Override the ParserOptions used to render the primary article wikitext. + * + * @param ParserOptions $options + * @throws MWException if the parser options where already initialized. + */ + public function setParserOptions( ParserOptions $options ) { + if ( $this->mParserOptions ) { + throw new MWException( "can't change parser options after they have already been set" ); + } + + // clone, so if $options is modified later, it doesn't confuse the parser cache. + $this->mParserOptions = clone $options; + } + /** * Get parser options suitable for rendering the primary article wikitext * @return ParserOptions @@ -1757,15 +1887,16 @@ class Article extends Page { * * @deprecated in 1.18; call OutputPage::redirect() directly * @param $noRedir Boolean: add redirect=no - * @param $sectionAnchor String: section to redirect to, including "#" - * @param $extraQuery String: extra query params + * @param string $sectionAnchor section to redirect to, including "#" + * @param string $extraQuery extra query params */ public function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) { wfDeprecated( __METHOD__, '1.18' ); if ( $noRedir ) { $query = 'redirect=no'; - if ( $extraQuery ) + if ( $extraQuery ) { $query .= "&$extraQuery"; + } } else { $query = $extraQuery; } @@ -1777,7 +1908,7 @@ class Article extends Page { * Use PHP's magic __get handler to handle accessing of * raw WikiPage fields for backwards compatibility. * - * @param $fname String Field name + * @param string $fname Field name */ public function __get( $fname ) { if ( property_exists( $this->mPage, $fname ) ) { @@ -1791,7 +1922,7 @@ class Article extends Page { * Use PHP's magic __set handler to handle setting of * raw WikiPage fields for backwards compatibility. * - * @param $fname String Field name + * @param string $fname Field name * @param $fvalue mixed New value */ public function __set( $fname, $fvalue ) { @@ -1810,8 +1941,8 @@ class Article extends Page { * Use PHP's magic __call handler to transform instance calls to * WikiPage functions for backwards compatibility. * - * @param $fname String Name of called method - * @param $args Array Arguments to the method + * @param string $fname Name of called method + * @param array $args Arguments to the method * @return mixed */ public function __call( $fname, $args ) { @@ -1844,7 +1975,13 @@ class Article extends Page { * @return bool */ public function updateRestrictions( $limit = array(), $reason = '', &$cascade = 0, $expiry = array() ) { - return $this->mPage->updateRestrictions( $limit, $reason, $cascade, $expiry ); + return $this->mPage->doUpdateRestrictions( + $limit, + $expiry, + $cascade, + $reason, + $this->getContext()->getUser() + ); } /** @@ -1891,7 +2028,9 @@ class Article extends Page { * @return mixed */ public function generateReason( &$hasHistory ) { - return $this->mPage->getAutoDeleteReason( $hasHistory ); + $title = $this->mPage->getTitle(); + $handler = ContentHandler::getForTitle( $title ); + return $handler->getAutoDeleteReason( $title, $hasHistory ); } // ****** B/C functions for static methods ( __callStatic is PHP>=5.3 ) ****** // @@ -1929,6 +2068,7 @@ class Article extends Page { * @param $newtext * @param $flags * @return string + * @deprecated since 1.21, use ContentHandler::getAutosummary() instead */ public static function getAutosummary( $oldtext, $newtext, $flags ) { return WikiPage::getAutosummary( $oldtext, $newtext, $flags ); diff --git a/includes/AuthPlugin.php b/includes/AuthPlugin.php index 2e42439c..a4658176 100644 --- a/includes/AuthPlugin.php +++ b/includes/AuthPlugin.php @@ -46,7 +46,7 @@ class AuthPlugin { * you might need to munge it (for instance, for lowercase initial * letters). * - * @param $username String: username. + * @param string $username username. * @return bool */ public function userExists( $username ) { @@ -60,8 +60,8 @@ class AuthPlugin { * you might need to munge it (for instance, for lowercase initial * letters). * - * @param $username String: username. - * @param $password String: user password. + * @param string $username username. + * @param string $password user password. * @return bool */ public function authenticate( $username, $password ) { @@ -73,7 +73,7 @@ class AuthPlugin { * Modify options in the login template. * * @param $template UserLoginTemplate object. - * @param $type String 'signup' or 'login'. Added in 1.16. + * @param string $type 'signup' or 'login'. Added in 1.16. */ public function modifyUITemplate( &$template, &$type ) { # Override this! @@ -83,7 +83,7 @@ class AuthPlugin { /** * Set the domain this plugin is supposed to use when authenticating. * - * @param $domain String: authentication domain. + * @param string $domain authentication domain. */ public function setDomain( $domain ) { $this->domain = $domain; @@ -105,7 +105,7 @@ class AuthPlugin { /** * Check to see if the specific domain is a valid domain. * - * @param $domain String: authentication domain. + * @param string $domain authentication domain. * @return bool */ public function validDomain( $domain ) { @@ -194,7 +194,7 @@ class AuthPlugin { * Return true if successful. * * @param $user User object. - * @param $password String: password. + * @param string $password password. * @return bool */ public function setPassword( $user, $password ) { @@ -251,7 +251,7 @@ class AuthPlugin { * Check if a user should authenticate locally if the global authentication fails. * If either this or strict() returns true, local authentication is not used. * - * @param $username String: username. + * @param string $username username. * @return Boolean */ public function strictUserAuth( $username ) { diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index a8b22027..3555e2c3 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -33,12 +33,12 @@ $wgAutoloadLocalClasses = array( 'AjaxDispatcher' => 'includes/AjaxDispatcher.php', 'AjaxResponse' => 'includes/AjaxResponse.php', 'AlphabeticPager' => 'includes/Pager.php', + 'ArrayUtils' => 'includes/ArrayUtils.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', 'BadTitleError' => 'includes/Exception.php', 'BaseTemplate' => 'includes/SkinTemplate.php', 'Block' => 'includes/Block.php', @@ -71,8 +71,6 @@ $wgAutoloadLocalClasses = array( 'DeferredUpdates' => 'includes/DeferredUpdates.php', 'DeprecatedGlobal' => 'includes/DeprecatedGlobal.php', 'DerivativeRequest' => 'includes/WebRequest.php', - 'DeviceDetection' => 'includes/mobile/DeviceDetection.php', - 'DeviceProperties' => 'includes/mobile/DeviceDetection.php', 'DiffHistoryBlob' => 'includes/HistoryBlob.php', 'DoubleReplacer' => 'includes/StringUtils.php', 'DummyLinker' => 'includes/Linker.php', @@ -93,9 +91,11 @@ $wgAutoloadLocalClasses = array( 'ErrorPageError' => 'includes/Exception.php', 'ExplodeIterator' => 'includes/StringUtils.php', 'ExternalEdit' => 'includes/ExternalEdit.php', - 'ExternalStore' => 'includes/ExternalStore.php', - 'ExternalStoreDB' => 'includes/ExternalStoreDB.php', - 'ExternalStoreHttp' => 'includes/ExternalStoreHttp.php', + 'ExternalStore' => 'includes/externalstore/ExternalStore.php', + 'ExternalStoreDB' => 'includes/externalstore/ExternalStoreDB.php', + 'ExternalStoreHttp' => 'includes/externalstore/ExternalStoreHttp.php', + 'ExternalStoreMedium' => 'includes/externalstore/ExternalStoreMedium.php', + 'ExternalStoreMwstore' => 'includes/externalstore/ExternalStoreMwstore.php', 'ExternalUser' => 'includes/ExternalUser.php', 'FakeTitle' => 'includes/FakeTitle.php', 'Fallback' => 'includes/Fallback.php', @@ -119,6 +119,7 @@ $wgAutoloadLocalClasses = array( 'Html' => 'includes/Html.php', 'HTMLApiField' => 'includes/HTMLForm.php', 'HTMLCheckField' => 'includes/HTMLForm.php', + 'HTMLCheckMatrix' => 'includes/HTMLForm.php', 'HTMLEditTools' => 'includes/HTMLForm.php', 'HTMLFloatField' => 'includes/HTMLForm.php', 'HTMLForm' => 'includes/HTMLForm.php', @@ -136,11 +137,8 @@ $wgAutoloadLocalClasses = array( 'HTMLTextField' => 'includes/HTMLForm.php', 'Http' => 'includes/HttpFunctions.php', 'HttpError' => 'includes/Exception.php', - 'HttpRequest' => 'includes/HttpFunctions.old.php', 'ICacheHelper' => 'includes/CacheHelper.php', 'IcuCollation' => 'includes/Collation.php', - 'IDeviceProperties' => 'includes/mobile/DeviceDetection.php', - 'IDeviceDetector' => 'includes/mobile/DeviceDetection.php', 'IdentityCollation' => 'includes/Collation.php', 'ImageGallery' => 'includes/ImageGallery.php', 'ImageHistoryList' => 'includes/ImagePage.php', @@ -153,10 +151,11 @@ $wgAutoloadLocalClasses = array( 'IndexPager' => 'includes/Pager.php', 'Interwiki' => 'includes/interwiki/Interwiki.php', 'IP' => 'includes/IP.php', - 'LCStore_Accel' => 'includes/LocalisationCache.php', - 'LCStore_CDB' => 'includes/LocalisationCache.php', - 'LCStore_DB' => 'includes/LocalisationCache.php', - 'LCStore_Null' => 'includes/LocalisationCache.php', + 'LCStore' => 'includes/cache/LocalisationCache.php', + 'LCStore_Accel' => 'includes/cache/LocalisationCache.php', + 'LCStore_CDB' => 'includes/cache/LocalisationCache.php', + 'LCStore_DB' => 'includes/cache/LocalisationCache.php', + 'LCStore_Null' => 'includes/cache/LocalisationCache.php', 'LegacyTemplate' => 'includes/SkinLegacy.php', 'License' => 'includes/Licenses.php', 'Licenses' => 'includes/Licenses.php', @@ -164,11 +163,12 @@ $wgAutoloadLocalClasses = array( 'LinkFilter' => 'includes/LinkFilter.php', 'LinksUpdate' => 'includes/LinksUpdate.php', 'LinksDeletionUpdate' => 'includes/LinksUpdate.php', - 'LocalisationCache' => 'includes/LocalisationCache.php', - 'LocalisationCache_BulkLoad' => 'includes/LocalisationCache.php', + 'LocalisationCache' => 'includes/cache/LocalisationCache.php', + 'LocalisationCache_BulkLoad' => 'includes/cache/LocalisationCache.php', 'MagicWord' => 'includes/MagicWord.php', 'MagicWordArray' => 'includes/MagicWord.php', 'MailAddress' => 'includes/UserMailer.php', + 'MappedIterator' => 'includes/MappedIterator.php', 'MediaWiki' => 'includes/Wiki.php', 'MediaWiki_I18N' => 'includes/SkinTemplate.php', 'Message' => 'includes/Message.php', @@ -183,7 +183,7 @@ $wgAutoloadLocalClasses = array( 'MWNamespace' => 'includes/Namespace.php', 'OldChangesList' => 'includes/ChangesList.php', 'OutputPage' => 'includes/OutputPage.php', - 'Page' => 'includes/WikiPage.php', + 'Page' => 'includes/WikiPage.php', 'PageQueryPage' => 'includes/PageQueryPage.php', 'Pager' => 'includes/Pager.php', 'PasswordError' => 'includes/User.php', @@ -201,10 +201,12 @@ $wgAutoloadLocalClasses = array( 'ProtectionForm' => 'includes/ProtectionForm.php', 'QueryPage' => 'includes/QueryPage.php', 'QuickTemplate' => 'includes/SkinTemplate.php', + 'RawMessage' => 'includes/Message.php', 'RCCacheEntry' => 'includes/ChangesList.php', 'RdfMetaData' => 'includes/Metadata.php', 'ReadOnlyError' => 'includes/Exception.php', 'RecentChange' => 'includes/RecentChange.php', + 'RedirectSpecialArticle' => 'includes/SpecialPage.php', 'RedirectSpecialPage' => 'includes/SpecialPage.php', 'RegexlikeReplacer' => 'includes/StringUtils.php', 'ReplacementArray' => 'includes/StringUtils.php', @@ -219,6 +221,7 @@ $wgAutoloadLocalClasses = array( 'Sanitizer' => 'includes/Sanitizer.php', 'DataUpdate' => 'includes/DataUpdate.php', 'SqlDataUpdate' => 'includes/SqlDataUpdate.php', + 'ScopedCallback' => 'includes/ScopedCallback.php', 'ScopedPHPTimeout' => 'includes/ScopedPHPTimeout.php', 'SiteConfiguration' => 'includes/SiteConfiguration.php', 'SiteStats' => 'includes/SiteStats.php', @@ -247,10 +250,12 @@ $wgAutoloadLocalClasses = array( 'StubUserLang' => 'includes/StubObject.php', 'TablePager' => 'includes/Pager.php', 'MWTimestamp' => 'includes/Timestamp.php', + 'TimestampException' => 'includes/Timestamp.php', 'Title' => 'includes/Title.php', 'TitleArray' => 'includes/TitleArray.php', 'TitleArrayFromResult' => 'includes/TitleArray.php', 'ThrottledError' => 'includes/Exception.php', + 'UIDGenerator' => 'includes/UIDGenerator.php', 'UnlistedSpecialPage' => 'includes/SpecialPage.php', 'UploadSourceAdapter' => 'includes/Import.php', 'UppercaseCollation' => 'includes/Collation.php', @@ -272,9 +277,9 @@ $wgAutoloadLocalClasses = array( 'WikiError' => 'includes/WikiError.php', 'WikiErrorMsg' => 'includes/WikiError.php', 'WikiExporter' => 'includes/Export.php', - 'WikiFilePage' => 'includes/WikiFilePage.php', + 'WikiFilePage' => 'includes/WikiFilePage.php', 'WikiImporter' => 'includes/Import.php', - 'WikiPage' => 'includes/WikiPage.php', + 'WikiPage' => 'includes/WikiPage.php', 'WikiRevision' => 'includes/Import.php', 'WikiMap' => 'includes/WikiMap.php', 'WikiReference' => 'includes/WikiMap.php', @@ -289,6 +294,21 @@ $wgAutoloadLocalClasses = array( 'ZipDirectoryReader' => 'includes/ZipDirectoryReader.php', 'ZipDirectoryReaderError' => 'includes/ZipDirectoryReader.php', + # content handler + 'AbstractContent' => 'includes/content/AbstractContent.php', + 'ContentHandler' => 'includes/content/ContentHandler.php', + 'Content' => 'includes/content/Content.php', + 'CssContentHandler' => 'includes/content/CssContentHandler.php', + 'CssContent' => 'includes/content/CssContent.php', + 'JavaScriptContentHandler' => 'includes/content/JavaScriptContentHandler.php', + 'JavaScriptContent' => 'includes/content/JavaScriptContent.php', + 'MessageContent' => 'includes/content/MessageContent.php', + 'MWContentSerializationException' => 'includes/content/ContentHandler.php', + 'TextContentHandler' => 'includes/content/TextContentHandler.php', + 'TextContent' => 'includes/content/TextContent.php', + 'WikitextContentHandler' => 'includes/content/WikitextContentHandler.php', + 'WikitextContent' => 'includes/content/WikitextContent.php', + # includes/actions 'CachedAction' => 'includes/actions/CachedAction.php', 'CreditsAction' => 'includes/actions/CreditsAction.php', @@ -318,6 +338,7 @@ $wgAutoloadLocalClasses = array( 'ApiBase' => 'includes/api/ApiBase.php', 'ApiBlock' => 'includes/api/ApiBlock.php', 'ApiComparePages' => 'includes/api/ApiComparePages.php', + 'ApiCreateAccount' => 'includes/api/ApiCreateAccount.php', 'ApiDelete' => 'includes/api/ApiDelete.php', 'ApiDisabled' => 'includes/api/ApiDisabled.php', 'ApiEditPage' => 'includes/api/ApiEditPage.php', @@ -331,6 +352,7 @@ $wgAutoloadLocalClasses = array( 'ApiFormatDump' => 'includes/api/ApiFormatDump.php', 'ApiFormatFeedWrapper' => 'includes/api/ApiFormatBase.php', 'ApiFormatJson' => 'includes/api/ApiFormatJson.php', + 'ApiFormatNone' => 'includes/api/ApiFormatNone.php', 'ApiFormatPhp' => 'includes/api/ApiFormatPhp.php', 'ApiFormatRaw' => 'includes/api/ApiFormatRaw.php', 'ApiFormatTxt' => 'includes/api/ApiFormatTxt.php', @@ -339,11 +361,13 @@ $wgAutoloadLocalClasses = array( 'ApiFormatXmlRsd' => 'includes/api/ApiRsd.php', 'ApiFormatYaml' => 'includes/api/ApiFormatYaml.php', 'ApiHelp' => 'includes/api/ApiHelp.php', + 'ApiImageRotate' => 'includes/api/ApiImageRotate.php', 'ApiImport' => 'includes/api/ApiImport.php', 'ApiImportReporter' => 'includes/api/ApiImport.php', 'ApiLogin' => 'includes/api/ApiLogin.php', 'ApiLogout' => 'includes/api/ApiLogout.php', 'ApiMain' => 'includes/api/ApiMain.php', + 'ApiModuleManager' => 'includes/api/ApiModuleManager.php', 'ApiMove' => 'includes/api/ApiMove.php', 'ApiOpenSearch' => 'includes/api/ApiOpenSearch.php', 'ApiOptions' => 'includes/api/ApiOptions.php', @@ -383,7 +407,10 @@ $wgAutoloadLocalClasses = array( 'ApiQueryLangLinks' => 'includes/api/ApiQueryLangLinks.php', 'ApiQueryLinks' => 'includes/api/ApiQueryLinks.php', 'ApiQueryLogEvents' => 'includes/api/ApiQueryLogEvents.php', + 'ApiQueryORM' => 'includes/api/ApiQueryORM.php', 'ApiQueryPageProps' => 'includes/api/ApiQueryPageProps.php', + 'ApiQueryPagesWithProp' => 'includes/api/ApiQueryPagesWithProp.php', + 'ApiQueryPagePropNames' => 'includes/api/ApiQueryPagePropNames.php', 'ApiQueryProtectedTitles' => 'includes/api/ApiQueryProtectedTitles.php', 'ApiQueryQueryPage' => 'includes/api/ApiQueryQueryPage.php', 'ApiQueryRandom' => 'includes/api/ApiQueryRandom.php', @@ -410,6 +437,7 @@ $wgAutoloadLocalClasses = array( 'UsageException' => 'includes/api/ApiMain.php', # includes/cache + 'BacklinkCache' => 'includes/cache/BacklinkCache.php', 'CacheDependency' => 'includes/cache/CacheDependency.php', 'ConstantDependency' => 'includes/cache/CacheDependency.php', 'DependencyWrapper' => 'includes/cache/CacheDependency.php', @@ -418,7 +446,6 @@ $wgAutoloadLocalClasses = array( 'GenderCache' => 'includes/cache/GenderCache.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', @@ -430,6 +457,10 @@ $wgAutoloadLocalClasses = array( 'TitleDependency' => 'includes/cache/CacheDependency.php', 'TitleListDependency' => 'includes/cache/CacheDependency.php', + # includes/clientpool + 'RedisConnectionPool' => 'includes/clientpool/RedisConnectionPool.php', + 'RedisConnRef' => 'includes/clientpool/RedisConnectionPool.php', + # includes/context 'ContextSource' => 'includes/context/ContextSource.php', 'DerivativeContext' => 'includes/context/DerivativeContext.php', @@ -438,14 +469,13 @@ $wgAutoloadLocalClasses = array( # includes/dao 'IDBAccessObject' => 'includes/dao/IDBAccessObject.php', + 'DBAccessBase' => 'includes/dao/DBAccessBase.php', # includes/db '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', @@ -464,10 +494,6 @@ $wgAutoloadLocalClasses = array( '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', - 'IBM_DB2Helper' => 'includes/db/DatabaseIbm_db2.php', - 'IBM_DB2Result' => 'includes/db/DatabaseIbm_db2.php', 'LBFactory' => 'includes/db/LBFactory.php', 'LBFactory_Fake' => 'includes/db/LBFactory.php', 'LBFactory_Multi' => 'includes/db/LBFactory_Multi.php', @@ -548,13 +574,14 @@ $wgAutoloadLocalClasses = array( 'NullFileJournal' => 'includes/filebackend/filejournal/FileJournal.php', 'LockManagerGroup' => 'includes/filebackend/lockmanager/LockManagerGroup.php', 'LockManager' => 'includes/filebackend/lockmanager/LockManager.php', - 'ScopedLock' => 'includes/filebackend/lockmanager/LockManager.php', + 'ScopedLock' => 'includes/filebackend/lockmanager/ScopedLock.php', 'FSLockManager' => 'includes/filebackend/lockmanager/FSLockManager.php', 'DBLockManager' => 'includes/filebackend/lockmanager/DBLockManager.php', 'LSLockManager' => 'includes/filebackend/lockmanager/LSLockManager.php', 'MemcLockManager' => 'includes/filebackend/lockmanager/MemcLockManager.php', - 'QuorumLockManager' => 'includes/filebackend/lockmanager/LockManager.php', - 'MySqlLockManager'=> 'includes/filebackend/lockmanager/DBLockManager.php', + 'QuorumLockManager' => 'includes/filebackend/lockmanager/QuorumLockManager.php', + 'MySqlLockManager' => 'includes/filebackend/lockmanager/DBLockManager.php', + 'PostgreSqlLockManager' => 'includes/filebackend/lockmanager/DBLockManager.php', 'NullLockManager' => 'includes/filebackend/lockmanager/LockManager.php', 'FileOp' => 'includes/filebackend/FileOp.php', 'FileOpBatch' => 'includes/filebackend/FileOpBatch.php', @@ -562,8 +589,8 @@ $wgAutoloadLocalClasses = array( 'CopyFileOp' => 'includes/filebackend/FileOp.php', 'MoveFileOp' => 'includes/filebackend/FileOp.php', 'DeleteFileOp' => 'includes/filebackend/FileOp.php', - 'ConcatenateFileOp' => 'includes/filebackend/FileOp.php', 'CreateFileOp' => 'includes/filebackend/FileOp.php', + 'DescribeFileOp' => 'includes/filebackend/FileOp.php', 'NullFileOp' => 'includes/filebackend/FileOp.php', # includes/filerepo @@ -594,11 +621,8 @@ $wgAutoloadLocalClasses = array( 'CliInstaller' => 'includes/installer/CliInstaller.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', 'MysqlInstaller' => 'includes/installer/MysqlInstaller.php', 'MysqlUpdater' => 'includes/installer/MysqlUpdater.php', @@ -631,13 +655,26 @@ $wgAutoloadLocalClasses = array( 'WebInstallerPage' => 'includes/installer/WebInstallerPage.php', # includes/job - 'DoubleRedirectJob' => 'includes/job/DoubleRedirectJob.php', - 'EmaillingJob' => 'includes/job/EmaillingJob.php', - 'EnotifNotifyJob' => 'includes/job/EnotifNotifyJob.php', 'Job' => 'includes/job/Job.php', - 'RefreshLinksJob' => 'includes/job/RefreshLinksJob.php', - 'RefreshLinksJob2' => 'includes/job/RefreshLinksJob.php', - 'UploadFromUrlJob' => 'includes/job/UploadFromUrlJob.php', + 'JobQueue' => 'includes/job/JobQueue.php', + 'JobQueueAggregator' => 'includes/job/JobQueueAggregator.php', + 'JobQueueAggregatorMemc' => 'includes/job/JobQueueAggregatorMemc.php', + 'JobQueueAggregatorRedis' => 'includes/job/JobQueueAggregatorRedis.php', + 'JobQueueDB' => 'includes/job/JobQueueDB.php', + 'JobQueueGroup' => 'includes/job/JobQueueGroup.php', + + # includes/job/jobs + 'DoubleRedirectJob' => 'includes/job/jobs/DoubleRedirectJob.php', + 'DuplicateJob' => 'includes/job/jobs/DuplicateJob.php', + 'EmaillingJob' => 'includes/job/jobs/EmaillingJob.php', + 'EnotifNotifyJob' => 'includes/job/jobs/EnotifNotifyJob.php', + 'HTMLCacheUpdateJob' => 'includes/job/jobs/HTMLCacheUpdateJob.php', + 'NullJob' => 'includes/job/jobs/NullJob.php', + 'RefreshLinksJob' => 'includes/job/jobs/RefreshLinksJob.php', + 'RefreshLinksJob2' => 'includes/job/jobs/RefreshLinksJob.php', + 'UploadFromUrlJob' => 'includes/job/jobs/UploadFromUrlJob.php', + 'AssembleUploadChunksJob' => 'includes/job/jobs/AssembleUploadChunksJob.php', + 'PublishStashedFileJob' => 'includes/job/jobs/PublishStashedFileJob.php', # includes/json 'FormatJson' => 'includes/json/FormatJson.php', @@ -676,6 +713,7 @@ $wgAutoloadLocalClasses = array( 'PatrolLog' => 'includes/logging/PatrolLog.php', 'PatrolLogFormatter' => 'includes/logging/LogFormatter.php', 'RCDatabaseLogEntry' => 'includes/logging/LogEntry.php', + 'RightsLogFormatter' => 'includes/logging/LogFormatter.php', # includes/media 'BitmapHandler' => 'includes/media/Bitmap.php', @@ -747,35 +785,24 @@ $wgAutoloadLocalClasses = array( 'MWTidyWrapper' => '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', @@ -785,7 +812,6 @@ $wgAutoloadLocalClasses = array( 'Preprocessor' => 'includes/parser/Preprocessor.php', 'Preprocessor_DOM' => 'includes/parser/Preprocessor_DOM.php', 'Preprocessor_Hash' => 'includes/parser/Preprocessor_Hash.php', - 'Preprocessor_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp', 'StripState' => 'includes/parser/StripState.php', # includes/profiler @@ -827,7 +853,6 @@ $wgAutoloadLocalClasses = array( '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', @@ -839,7 +864,6 @@ $wgAutoloadLocalClasses = array( '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', 'SearchMySQL' => 'includes/search/SearchMySQL.php', 'SearchNearMatchResultSet' => 'includes/search/SearchEngine.php', @@ -854,6 +878,16 @@ $wgAutoloadLocalClasses = array( 'SqliteSearchResultSet' => 'includes/search/SearchSqlite.php', 'SqlSearchResultSet' => 'includes/search/SearchEngine.php', + # includes/site + 'MediaWikiSite' => 'includes/site/MediaWikiSite.php', + 'Site' => 'includes/site/Site.php', + 'SiteObject' => 'includes/site/Site.php', + 'SiteArray' => 'includes/site/SiteList.php', + 'SiteList' => 'includes/site/SiteList.php', + 'SiteSQLStore' => 'includes/site/SiteSQLStore.php', + 'Sites' => 'includes/site/SiteSQLStore.php', + 'SiteStore' => 'includes/site/SiteStore.php', + # includes/specials 'ActiveUsersPager' => 'includes/specials/SpecialActiveusers.php', 'AllmessagesTablePager' => 'includes/specials/SpecialAllmessages.php', @@ -862,8 +896,6 @@ $wgAutoloadLocalClasses = array( 'BrokenRedirectsPage' => 'includes/specials/SpecialBrokenRedirects.php', 'CategoryPager' => 'includes/specials/SpecialCategories.php', 'ContribsPager' => 'includes/specials/SpecialContributions.php', - 'DBLockForm' => 'includes/specials/SpecialLockdb.php', - 'DBUnlockForm' => 'includes/specials/SpecialUnlockdb.php', 'DeadendPagesPage' => 'includes/specials/SpecialDeadendpages.php', 'DeletedContribsPager' => 'includes/specials/SpecialDeletedContributions.php', 'DeletedContributionsPage' => 'includes/specials/SpecialDeletedContributions.php', @@ -928,10 +960,10 @@ $wgAutoloadLocalClasses = array( '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', + 'SpecialPagesWithProp' => 'includes/specials/SpecialPagesWithProp.php', 'SpecialPermanentLink' => 'includes/SpecialPage.php', 'SpecialPreferences' => 'includes/specials/SpecialPreferences.php', 'SpecialPrefixindex' => 'includes/specials/SpecialPrefixindex.php', @@ -989,7 +1021,6 @@ $wgAutoloadLocalClasses = array( 'UploadFromUrl' => 'includes/upload/UploadFromUrl.php', 'UploadStash' => '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', @@ -1004,20 +1035,29 @@ $wgAutoloadLocalClasses = array( 'FakeConverter' => 'languages/Language.php', 'Language' => 'languages/Language.php', 'LanguageConverter' => 'languages/LanguageConverter.php', + 'CLDRPluralRuleConverter' => 'languages/utils/CLDRPluralRuleEvaluator.php', + 'CLDRPluralRuleConverter_Expression' => 'languages/utils/CLDRPluralRuleEvaluator.php', + 'CLDRPluralRuleConverter_Fragment' => 'languages/utils/CLDRPluralRuleEvaluator.php', + 'CLDRPluralRuleConverter_Operator' => 'languages/utils/CLDRPluralRuleEvaluator.php', 'CLDRPluralRuleEvaluator' => 'languages/utils/CLDRPluralRuleEvaluator.php', + 'CLDRPluralRuleEvaluator_Range' => 'languages/utils/CLDRPluralRuleEvaluator.php', 'CLDRPluralRuleError' => 'languages/utils/CLDRPluralRuleEvaluator.php', # maintenance + 'BackupDumper' => 'maintenance/backup.inc', 'ConvertLinks' => 'maintenance/convertLinks.php', 'DeleteArchivedFilesImplementation' => 'maintenance/deleteArchivedFiles.inc', 'DeleteArchivedRevisionsImplementation' => 'maintenance/deleteArchivedRevisions.inc', 'DeleteDefaultMessages' => 'maintenance/deleteDefaultMessages.php', + 'DumpDBZip2Output' => 'maintenance/backup.inc', + 'ExportProgressFilter' => 'maintenance/backup.inc', 'FakeMaintenance' => 'maintenance/Maintenance.php', + 'FixExtLinksProtocolRelative' => 'maintenance/fixExtLinksProtocolRelative.php', 'LoggedUpdateMaintenance' => 'maintenance/Maintenance.php', 'Maintenance' => 'maintenance/Maintenance.php', - 'FixExtLinksProtocolRelative' => 'maintenance/fixExtLinksProtocolRelative.php', 'PopulateCategory' => 'maintenance/populateCategory.php', 'PopulateImageSha1' => 'maintenance/populateImageSha1.php', + 'PopulateFilearchiveSha1' => 'maintenance/populateFilearchiveSha1.php', 'PopulateLogSearch' => 'maintenance/populateLogSearch.php', 'PopulateLogUsertext' => 'maintenance/populateLogUsertext.php', 'PopulateParentId' => 'maintenance/populateParentId.php', @@ -1040,40 +1080,13 @@ $wgAutoloadLocalClasses = array( 'wikiStatsOutput' => 'maintenance/language/StatOutputs.php', # maintenance/term - 'AnsiTermColorer' => 'maintenance/term/MWTerm.php', + 'AnsiTermColorer' => 'maintenance/term/MWTerm.php', 'DummyTermColorer' => 'maintenance/term/MWTerm.php', # mw-config 'InstallerOverrides' => 'mw-config/overrides.php', 'MyLocalSettingsGenerator' => 'mw-config/overrides.php', - # tests - 'DbTestPreviewer' => 'tests/testHelpers.inc', - 'DbTestRecorder' => 'tests/testHelpers.inc', - 'DelayedParserTest' => 'tests/testHelpers.inc', - 'TestFileIterator' => 'tests/testHelpers.inc', - 'TestRecorder' => 'tests/testHelpers.inc', - - # tests/phpunit/includes - 'GenericArrayObjectTest' => 'tests/phpunit/includes/libs/GenericArrayObjectTest.php', - - # tests/phpunit/includes/db - 'ORMRowTest' => 'tests/phpunit/includes/db/ORMRowTest.php', - - # tests/parser - 'ParserTest' => 'tests/parser/parserTest.inc', - 'ParserTestParserHook' => 'tests/parser/parserTestsParserHook.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', - # skins 'CologneBlueTemplate' => 'skins/CologneBlue.php', 'ModernTemplate' => 'skins/Modern.php', @@ -1096,7 +1109,7 @@ class AutoLoader { /** * autoload - take a class name and attempt to load it * - * @param $className String: name of class we're looking for. + * @param string $className name of class we're looking for. * @return bool Returning false is important on failure as * it allows Zend to try and look in other registered autoloaders * as well. diff --git a/includes/Autopromote.php b/includes/Autopromote.php index 9c77855d..604b9248 100644 --- a/includes/Autopromote.php +++ b/includes/Autopromote.php @@ -54,7 +54,7 @@ class Autopromote { * Does not return groups the user already belongs to or has once belonged. * * @param $user User The user to get the groups for - * @param $event String key in $wgAutopromoteOnce (each one has groups/criteria) + * @param string $event key in $wgAutopromoteOnce (each one has groups/criteria) * * @return array Groups the user should be promoted to. * @@ -140,8 +140,8 @@ class Autopromote { return true; } } - # If we got here, the array presumably does not contain other condi- - # tions; it's not recursive. Pass it off to self::checkCondition. + // If we got here, the array presumably does not contain other conditions; + // it's not recursive. Pass it off to self::checkCondition. if ( !is_array( $cond ) ) { $cond = array( $cond ); } @@ -152,11 +152,11 @@ class Autopromote { /** * As recCheckCondition, but *not* recursive. The only valid conditions * are those whose first element is APCOND_EMAILCONFIRMED/APCOND_EDITCOUNT/ - * APCOND_AGE. Other types will throw an exception if no extension evalu- - * ates them. + * APCOND_AGE. Other types will throw an exception if no extension evaluates them. * - * @param $cond Array: A condition, which must not contain other conditions + * @param array $cond A condition, which must not contain other conditions * @param $user User The user to check the condition against + * @throws MWException * @return bool Whether the condition is true for the user */ private static function checkCondition( $cond, User $user ) { diff --git a/includes/BacklinkCache.php b/includes/BacklinkCache.php deleted file mode 100644 index 05bf3186..00000000 --- a/includes/BacklinkCache.php +++ /dev/null @@ -1,419 +0,0 @@ -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. - * - * Introduced by r47317 - * - * @internal documentation reviewed on 18 Mar 2011 by hashar - */ -class BacklinkCache { - /** @var ProcessCacheLRU */ - protected static $cache; - - /** - * 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 - */ - public function __construct( Title $title ) { - $this->title = $title; - } - - /** - * Create a new BacklinkCache or reuse any existing one. - * Currently, only one cache instance can exist; callers that - * need multiple backlink cache objects should keep them in scope. - * - * @param Title $title : Title object to get a backlink cache for - * @return BacklinkCache - */ - public static function get( Title $title ) { - if ( !self::$cache ) { // init cache - self::$cache = new ProcessCacheLRU( 1 ); - } - $dbKey = $title->getPrefixedDBkey(); - if ( !self::$cache->has( $dbKey, 'obj' ) ) { - self::$cache->set( $dbKey, 'obj', new self( $title ) ); - } - return self::$cache->get( $dbKey, 'obj' ); - } - - /** - * Serialization handler, diasallows to serialize the database to prevent - * failures after this class is deserialized from cache with dead DB - * connection. - * - * @return array - */ - function __sleep() { - return array( 'partitionCache', 'fullResultCache', 'title' ); - } - - /** - * Clear locally stored data and database object. - */ - public function clear() { - $this->partitionCache = array(); - $this->fullResultCache = array(); - unset( $this->db ); - } - - /** - * 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 DatabaseBase object - */ - protected function getDB() { - if ( !isset( $this->db ) ) { - $this->db = wfGetDB( DB_SLAVE ); - } - - return $this->db; - } - - /** - * Get the backlinks for a given table. Cached in process memory only. - * @param $table String - * @param $startId Integer or false - * @param $endId Integer or false - * @return TitleArrayFromResult - */ - public function getLinks( $table, $startId = false, $endId = false ) { - wfProfileIn( __METHOD__ ); - - $fromField = $this->getPrefix( $table ) . '_from'; - - if ( $startId || $endId ) { - // Partial range, not cached - wfDebug( __METHOD__ . ": from DB (uncacheable range)\n" ); - $conds = $this->getConditions( $table ); - - // Use the from field in the condition rather than the joined page_id, - // because databases are stupid and don't necessarily propagate indexes. - if ( $startId ) { - $conds[] = "$fromField >= " . intval( $startId ); - } - - if ( $endId ) { - $conds[] = "$fromField <= " . intval( $endId ); - } - - $res = $this->getDB()->select( - array( $table, 'page' ), - array( 'page_namespace', 'page_title', 'page_id' ), - $conds, - __METHOD__, - array( - 'STRAIGHT_JOIN', - 'ORDER BY' => $fromField - ) ); - $ta = TitleArray::newFromResult( $res ); - - wfProfileOut( __METHOD__ ); - return $ta; - } - - // @todo FIXME: Make this a function? - if ( !isset( $this->fullResultCache[$table] ) ) { - wfDebug( __METHOD__ . ": from DB\n" ); - $res = $this->getDB()->select( - array( $table, 'page' ), - array( 'page_namespace', 'page_title', 'page_id' ), - $this->getConditions( $table ), - __METHOD__, - array( - 'STRAIGHT_JOIN', - 'ORDER BY' => $fromField, - ) ); - $this->fullResultCache[$table] = $res; - } - - $ta = TitleArray::newFromResult( $this->fullResultCache[$table] ); - - wfProfileOut( __METHOD__ ); - return $ta; - } - - /** - * Get the field name prefix for a given table - * @param $table String - * @return null|string - */ - protected function getPrefix( $table ) { - static $prefixes = array( - 'pagelinks' => 'pl', - 'imagelinks' => 'il', - 'categorylinks' => 'cl', - 'templatelinks' => 'tl', - 'redirect' => 'rd', - ); - - if ( isset( $prefixes[$table] ) ) { - return $prefixes[$table]; - } else { - $prefix = null; - wfRunHooks( 'BacklinkCacheGetPrefix', array( $table, &$prefix ) ); - if( $prefix ) { - return $prefix; - } else { - throw new MWException( "Invalid table \"$table\" in " . __CLASS__ ); - } - } - } - - /** - * Get the SQL condition array for selecting backlinks, with a join - * on the page table. - * @param $table String - * @return array|null - */ - 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(), - $this->getDb()->makeList( array( - "{$prefix}_interwiki = ''", - "{$prefix}_interwiki is null", - ), LIST_OR ), - "page_id={$prefix}_from" - ); - break; - case 'imagelinks': - $conds = array( - 'il_to' => $this->title->getDBkey(), - 'page_id=il_from' - ); - break; - case 'categorylinks': - $conds = array( - 'cl_to' => $this->title->getDBkey(), - 'page_id=cl_from', - ); - break; - default: - $conds = null; - wfRunHooks( 'BacklinkCacheGetConditions', array( $table, $this->title, &$conds ) ); - if( !$conds ) - throw new MWException( "Invalid table \"$table\" in " . __CLASS__ ); - } - - return $conds; - } - - /** - * Get the approximate number of backlinks - * @param $table String - * @return integer - */ - public function getNumLinks( $table ) { - if ( isset( $this->fullResultCache[$table] ) ) { - return $this->fullResultCache[$table]->numRows(); - } - - if ( isset( $this->partitionCache[$table] ) ) { - $entry = reset( $this->partitionCache[$table] ); - return $entry['numRows']; - } - - $titleArray = $this->getLinks( $table ); - - return $titleArray->count(); - } - - /** - * 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. - * - * @param $table String: the links table name - * @param $batchSize Integer - * @return Array - */ - public function partition( $table, $batchSize ) { - - // 1) try partition cache ... - - if ( isset( $this->partitionCache[$table][$batchSize] ) ) { - wfDebug( __METHOD__ . ": got from partition cache\n" ); - return $this->partitionCache[$table][$batchSize]['batches']; - } - - $this->partitionCache[$table][$batchSize] = false; - $cacheEntry =& $this->partitionCache[$table][$batchSize]; - - // 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" ); - - return $cacheEntry['batches']; - } - - // 3) ... fallback to memcached ... - - global $wgMemc; - - $memcKey = wfMemcKey( - 'backlinks', - md5( $this->title->getPrefixedDBkey() ), - $table, - $batchSize - ); - - $memcValue = $wgMemc->get( $memcKey ); - - if ( is_array( $memcValue ) ) { - $cacheEntry = $memcValue; - wfDebug( __METHOD__ . ": got from memcached $memcKey\n" ); - - return $cacheEntry['batches']; - } - - - // 4) ... finally fetch from the slow database :( - - $this->getLinks( $table ); - $cacheEntry = $this->partitionResult( $this->fullResultCache[$table], $batchSize ); - // Save to memcached - $wgMemc->set( $memcKey, $cacheEntry, self::CACHE_EXPIRY ); - - wfDebug( __METHOD__ . ": got from database\n" ); - return $cacheEntry['batches']; - } - - /** - * 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(); - $numRows = $res->numRows(); - $numBatches = ceil( $numRows / $batchSize ); - - for ( $i = 0; $i < $numBatches; $i++ ) { - if ( $i == 0 ) { - $start = false; - } else { - $rowNum = intval( $numRows * $i / $numBatches ); - $res->seek( $rowNum ); - $row = $res->fetchObject(); - $start = $row->page_id; - } - - if ( $i == $numBatches - 1 ) { - $end = false; - } else { - $rowNum = intval( $numRows * ( $i + 1 ) / $numBatches ); - $res->seek( $rowNum ); - $row = $res->fetchObject(); - $end = $row->page_id - 1; - } - - # Sanity check order - if ( $start && $end && $start > $end ) { - throw new MWException( __METHOD__ . ': Internal error: query result out of order' ); - } - - $batches[] = array( $start, $end ); - } - - return array( 'numRows' => $numRows, 'batches' => $batches ); - } -} diff --git a/includes/Block.php b/includes/Block.php index 732699dc..7ee36ce9 100644 --- a/includes/Block.php +++ b/includes/Block.php @@ -65,11 +65,11 @@ class Block { $timestamp = 0, $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0, $hideName = 0, $blockEmail = 0, $allowUsertalk = 0, $byText = '' ) { - if( $timestamp === 0 ){ + if( $timestamp === 0 ) { $timestamp = wfTimestampNow(); } - if( count( func_get_args() ) > 0 ){ + if( count( func_get_args() ) > 0 ) { # Soon... :D # wfDeprecated( __METHOD__ . " with arguments" ); } @@ -106,7 +106,7 @@ class Block { * user ID. Tries the user ID first, and if that doesn't work, tries * the address. * - * @param $address String: IP address of user/anon + * @param string $address IP address of user/anon * @param $user Integer: user id of user * @return Block Object * @deprecated since 1.18 @@ -164,7 +164,7 @@ class Block { /** * 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 * + * the blocking user or the block timestamp, only things which affect the blocked user * * @param $block Block * @@ -199,23 +199,23 @@ class Block { /** * 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 string $address The IP address of the user, or blank to skip IP blocks + * @param int $user The user ID, or zero for anonymous users * @return Boolean: the user is blocked from editing * @deprecated since 1.18 */ public function load( $address = '', $user = 0 ) { wfDeprecated( __METHOD__, '1.18' ); - if( $user ){ + if( $user ) { $username = User::whoIs( $user ); $block = self::newFromTarget( $username, $address ); } else { $block = self::newFromTarget( null, $address ); } - if( $block instanceof Block ){ + if( $block instanceof Block ) { # This is mildly evil, but hey, it's B/C :D - foreach( $block as $variable => $value ){ + foreach( $block as $variable => $value ) { $this->$variable = $value; } return true; @@ -227,16 +227,17 @@ class Block { /** * 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) + * 2) A rangeblock encompassing 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. + * @throws MWException * @return Bool whether a relevant block was found */ protected function newLoad( $vagueTarget = null ) { $db = wfGetDB( $this->mFromMaster ? DB_MASTER : DB_SLAVE ); - if( $this->type !== null ){ + if( $this->type !== null ) { $conds = array( 'ipb_address' => array( (string)$this->target ), ); @@ -246,11 +247,11 @@ class Block { # Be aware that the != '' check is explicit, since empty values will be # passed by some callers (bug 29116) - if( $vagueTarget != ''){ + if( $vagueTarget != '' ) { list( $target, $type ) = self::parseTarget( $vagueTarget ); switch( $type ) { case self::TYPE_USER: - # Slightly wierd, but who are we to argue? + # Slightly weird, but who are we to argue? $conds['ipb_address'][] = (string)$target; break; @@ -284,20 +285,20 @@ class Block { # This is begging for $this = $bestBlock, but that's not allowed in PHP :( $bestBlockPreventsEdit = null; - foreach( $res as $row ){ + foreach( $res as $row ) { $block = self::newFromRow( $row ); # Don't use expired blocks - if( $block->deleteIfExpired() ){ + if( $block->deleteIfExpired() ) { continue; } # Don't use anon only blocks on users - if( $this->type == self::TYPE_USER && !$block->isHardblock() ){ + if( $this->type == self::TYPE_USER && !$block->isHardblock() ) { continue; } - if( $block->getType() == self::TYPE_RANGE ){ + if( $block->getType() == self::TYPE_RANGE ) { # This is the number of bits that are allowed to vary in the block, give # or take some floating point errors $end = wfBaseconvert( $block->getRangeEnd(), 16, 10 ); @@ -306,20 +307,20 @@ class Block { # 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 ); + $score = self::TYPE_RANGE - 1 + ( $size / 128 ); } else { $score = $block->getType(); } - if( $score < $bestBlockScore ){ + if( $score < $bestBlockScore ) { $bestBlockScore = $score; $bestRow = $row; $bestBlockPreventsEdit = $block->prevents( 'edit' ); } } - if( $bestRow !== null ){ + if( $bestRow !== null ) { $this->initFromRow( $bestRow ); $this->prevents( 'edit', $bestBlockPreventsEdit ); return true; @@ -329,9 +330,9 @@ class Block { } /** - * 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 + * Get a set of SQL conditions which will select rangeblocks encompassing a given range + * @param string $start Hexadecimal IP representation + * @param string $end Hexadecimal IP representation, or null to use $start = $end * @return String */ public static function getRangeCond( $start, $end = null ) { @@ -370,9 +371,9 @@ class Block { protected static function getIpFragment( $hex ) { global $wgBlockCIDRLimit; if ( substr( $hex, 0, 3 ) == 'v6-' ) { - return 'v6-' . substr( substr( $hex, 3 ), 0, floor( $wgBlockCIDRLimit['IPv6'] / 4 ) ); + return 'v6-' . substr( substr( $hex, 3 ), 0, floor( $wgBlockCIDRLimit['IPv6'] / 4 ) ); } else { - return substr( $hex, 0, floor( $wgBlockCIDRLimit['IPv4'] / 4 ) ); + return substr( $hex, 0, floor( $wgBlockCIDRLimit['IPv4'] / 4 ) ); } } @@ -417,7 +418,7 @@ class Block { * @param $row ResultWrapper row from the ipblocks table * @return Block */ - public static function newFromRow( $row ){ + public static function newFromRow( $row ) { $block = new Block; $block->initFromRow( $row ); return $block; @@ -426,6 +427,7 @@ class Block { /** * Delete the row from the IP blocks table. * + * @throws MWException * @return Boolean */ public function delete() { @@ -463,7 +465,7 @@ class Block { Block::purgeExpired(); $row = $this->getDatabaseArray(); - $row['ipb_id'] = $dbw->nextSequenceValue("ipblocks_ipb_id_seq"); + $row['ipb_id'] = $dbw->nextSequenceValue( "ipblocks_ipb_id_seq" ); $dbw->insert( 'ipblocks', @@ -486,7 +488,7 @@ class Block { * 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 + * @return Int number of affected rows, which should probably be 1 or something has * gone slightly awry */ public function update() { @@ -508,8 +510,8 @@ class Block { * @param $db DatabaseBase * @return Array */ - protected function getDatabaseArray( $db = null ){ - if( !$db ){ + protected function getDatabaseArray( $db = null ) { + if( !$db ) { $db = wfGetDB( DB_SLAVE ); } $expiry = $db->encodeExpiry( $this->mExpiry ); @@ -534,10 +536,10 @@ class Block { 'ipb_expiry' => $expiry, 'ipb_range_start' => $this->getRangeStart(), 'ipb_range_end' => $this->getRangeEnd(), - 'ipb_deleted' => intval( $this->mHideName ), // typecast required for SQLite + 'ipb_deleted' => intval( $this->mHideName ), // typecast required for SQLite 'ipb_block_email' => $this->prevents( 'sendemail' ), 'ipb_allow_usertalk' => !$this->prevents( 'editownusertalk' ), - 'ipb_parent_block_id' => $this->mParentBlockId + 'ipb_parent_block_id' => $this->mParentBlockId ); return $a; @@ -570,10 +572,17 @@ class Block { * blocked by this Block. This will use the recentchanges table. * * @param Block $block - * @param Array &$blockIds + * @param array &$blockIds * @return Array: block IDs of retroactive autoblocks made */ protected static function defaultRetroactiveAutoblock( Block $block, array &$blockIds ) { + global $wgPutIPinRC; + + // No IPs are in recentchanges table, so nothing to select + if( !$wgPutIPinRC ) { + return; + } + $dbr = wfGetDB( DB_SLAVE ); $options = array( 'ORDER BY' => 'rc_timestamp DESC' ); @@ -583,9 +592,9 @@ class Block { $options['LIMIT'] = 1; $res = $dbr->select( 'recentchanges', array( 'rc_ip' ), $conds, - __METHOD__ , $options ); + __METHOD__, $options ); - if ( !$dbr->numRows( $res ) ) { + if ( !$res->numRows() ) { # No results, don't autoblock anything wfDebug( "No IP found to retroactively autoblock\n" ); } else { @@ -602,7 +611,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 + * @param string $ip The IP to check * @return Boolean */ public static function isWhitelistedFromAutoblocks( $ip ) { @@ -645,7 +654,7 @@ class Block { /** * Autoblocks the given IP, referring to this Block. * - * @param $autoblockIP String: the IP to autoblock. + * @param string $autoblockIP the IP to autoblock. * @return mixed: block ID if an autoblock was inserted, false if not. */ public function doAutoblock( $autoblockIP ) { @@ -687,7 +696,7 @@ class Block { wfDebug( "Autoblocking {$this->getTarget()}@" . $autoblockIP . "\n" ); $autoblock->setTarget( $autoblockIP ); $autoblock->setBlocker( $this->getBlocker() ); - $autoblock->mReason = wfMessage( 'autoblocker', $this->getTarget(), $this->mReason )->inContentLanguage()->text(); + $autoblock->mReason = wfMessage( 'autoblocker', $this->getTarget(), $this->mReason )->inContentLanguage()->plain(); $timestamp = wfTimestampNow(); $autoblock->mTimestamp = $timestamp; $autoblock->mAuto = 1; @@ -780,6 +789,7 @@ class Block { /** * Get the IP address at the start of the range in Hex form + * @throws MWException * @return String IP in Hex form */ public function getRangeStart() { @@ -797,6 +807,7 @@ class Block { /** * Get the IP address at the start of the range in Hex form + * @throws MWException * @return String IP in Hex form */ public function getRangeEnd() { @@ -934,7 +945,7 @@ class Block { /** * Encode expiry for DB * - * @param $expiry String: timestamp for expiry, or + * @param string $expiry timestamp for expiry, or * @param $db DatabaseBase object * @return String * @deprecated since 1.18; use $dbw->encodeExpiry() instead @@ -947,8 +958,8 @@ class Block { /** * Decode expiry which has come from the DB * - * @param $expiry String: Database expiry format - * @param $timestampType Int Requested timestamp format + * @param string $expiry Database expiry format + * @param int $timestampType Requested timestamp format * @return String * @deprecated since 1.18; use $wgLang->formatExpiry() instead */ @@ -971,9 +982,9 @@ class Block { } /** - * Gets rid of uneeded numbers in quad-dotted/octet IP strings + * Gets rid of unneeded numbers in quad-dotted/octet IP strings * For example, 127.111.113.151/24 -> 127.111.113.0/24 - * @param $range String: IP address to normalize + * @param string $range IP address to normalize * @return string * @deprecated since 1.18, call IP::sanitizeRange() directly */ @@ -986,9 +997,11 @@ class Block { * Purge expired blocks from the ipblocks table */ public static function purgeExpired() { - $dbw = wfGetDB( DB_MASTER ); - $dbw->delete( 'ipblocks', - array( 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ), __METHOD__ ); + if ( !wfReadOnly() ) { + $dbw = wfGetDB( DB_MASTER ); + $dbw->delete( 'ipblocks', + array( 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ), __METHOD__ ); + } } /** @@ -1005,7 +1018,7 @@ class Block { /** * 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 + * @param string $expiry whatever was typed into the form * @return String: timestamp or "infinity" string for th DB implementation * @deprecated since 1.18 moved to SpecialBlock::parseExpiryInput() */ @@ -1029,7 +1042,7 @@ class Block { * @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 + * @param bool $fromMaster 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! @@ -1037,24 +1050,24 @@ class Block { public static function newFromTarget( $specificTarget, $vagueTarget = null, $fromMaster = false ) { list( $target, $type ) = self::parseTarget( $specificTarget ); - if( $type == Block::TYPE_ID || $type == Block::TYPE_AUTO ){ + if( $type == Block::TYPE_ID || $type == Block::TYPE_AUTO ) { return Block::newFromID( $target ); - } elseif( $target === null && $vagueTarget == '' ){ + } elseif( $target === null && $vagueTarget == '' ) { # We're not going to find anything useful here # Be aware that the == '' check is explicit, since empty values will be # passed by some callers (bug 29116) return null; - } elseif( in_array( $type, array( Block::TYPE_USER, Block::TYPE_IP, Block::TYPE_RANGE ) ) ) { + } elseif( in_array( $type, array( Block::TYPE_USER, Block::TYPE_IP, Block::TYPE_RANGE, null ) ) ) { $block = new Block(); $block->fromMaster( $fromMaster ); - if( $type !== null ){ + if( $type !== null ) { $block->setTarget( $target ); } - if( $block->newLoad( $vagueTarget ) ){ + if( $block->newLoad( $vagueTarget ) ) { return $block; } } @@ -1062,22 +1075,23 @@ class Block { } /** - * 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(). + * From an existing Block, get the target and the type of target. + * Note that, except for null, it is always safe to treat the target + * 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 ) + * @param $target String|Int|User|null + * @return array( User|String|null, Block::TYPE_ constant|null ) */ public static function parseTarget( $target ) { # We may have been through this before - if( $target instanceof User ){ - if( IP::isValid( $target->getName() ) ){ + if( $target instanceof User ) { + if( IP::isValid( $target->getName() ) ) { return array( $target, self::TYPE_IP ); } else { return array( $target, self::TYPE_USER ); } - } elseif( $target === null ){ + } elseif( $target === null ) { return array( null, null ); } @@ -1098,7 +1112,7 @@ class Block { # Consider the possibility that this is not a username at all # but actually an old subpage (bug #29797) - if( strpos( $target, '/' ) !== false ){ + if( strpos( $target, '/' ) !== false ) { # An old subpage, drill down to the user behind it $parts = explode( '/', $target ); $target = $parts[0]; @@ -1165,7 +1179,7 @@ class Block { * Set the target for this block, and update $this->type accordingly * @param $target Mixed */ - public function setTarget( $target ){ + public function setTarget( $target ) { list( $this->target, $this->type ) = self::parseTarget( $target ); } @@ -1173,15 +1187,15 @@ class Block { * Get the user who implemented this block * @return User|string Local User object or string for a foreign user */ - public function getBlocker(){ + public function getBlocker() { return $this->blocker; } /** * Set the user who implemented (or will implement) this block - * @param $user User|string Local User object or username string for foriegn users + * @param $user User|string Local User object or username string for foreign users */ - public function setBlocker( $user ){ + public function setBlocker( $user ) { $this->blocker = $user; } } diff --git a/includes/CacheHelper.php b/includes/CacheHelper.php index ac46fc42..f0ae5a31 100644 --- a/includes/CacheHelper.php +++ b/includes/CacheHelper.php @@ -18,7 +18,7 @@ * http://www.gnu.org/copyleft/gpl.html * * @file - * @licence GNU GPL v2 or later + * @license GNU GPL v2 or later * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ diff --git a/includes/Category.php b/includes/Category.php index b7b12e8a..868d6c46 100644 --- a/includes/Category.php +++ b/includes/Category.php @@ -44,6 +44,7 @@ class Category { /** * Set up all member variables using a database query. + * @throws MWException * @return bool True on success, false on failure. */ protected function initialize() { @@ -57,6 +58,9 @@ class Category { # Already initialized return true; } + + wfProfileIn( __METHOD__ ); + $dbr = wfGetDB( DB_SLAVE ); $row = $dbr->selectRow( 'category', @@ -65,15 +69,17 @@ class Category { __METHOD__ ); + wfProfileOut( __METHOD__ ); + if ( !$row ) { # Okay, there were no contents. Nothing to initialize. if ( $this->mTitle ) { # If there is a title object but no record in the category table, treat this as an empty category - $this->mID = false; - $this->mName = $this->mTitle->getDBkey(); - $this->mPages = 0; + $this->mID = false; + $this->mName = $this->mTitle->getDBkey(); + $this->mPages = 0; $this->mSubcats = 0; - $this->mFiles = 0; + $this->mFiles = 0; return true; } else { @@ -81,11 +87,11 @@ class Category { } } - $this->mID = $row->cat_id; - $this->mName = $row->cat_title; - $this->mPages = $row->cat_pages; + $this->mID = $row->cat_id; + $this->mName = $row->cat_title; + $this->mPages = $row->cat_pages; $this->mSubcats = $row->cat_subcats; - $this->mFiles = $row->cat_files; + $this->mFiles = $row->cat_files; # (bug 13683) If the count is negative, then 1) it's obviously wrong # and should not be kept, and 2) we *probably* don't have to scan many @@ -100,7 +106,7 @@ class Category { /** * Factory function. * - * @param $name Array: A category name (no "Category:" prefix). It need + * @param array $name A category name (no "Category:" prefix). It need * not be normalized, with spaces replaced by underscores. * @return mixed Category, or false on a totally invalid name */ @@ -161,7 +167,7 @@ class Category { # NOTE: the row often results from a LEFT JOIN on categorylinks. This may result in # all the cat_xxx fields being null, if the category page exists, but nothing - # was ever added to the category. This case should be treated linke an empty + # was ever added to the category. This case should be treated link an empty # category, if possible. if ( $row->cat_title === null ) { @@ -173,41 +179,53 @@ class Category { $cat->mName = $title->getDBkey(); # if we have a title object, fetch the category name from there } - $cat->mID = false; + $cat->mID = false; $cat->mSubcats = 0; - $cat->mPages = 0; - $cat->mFiles = 0; + $cat->mPages = 0; + $cat->mFiles = 0; } else { - $cat->mName = $row->cat_title; - $cat->mID = $row->cat_id; + $cat->mName = $row->cat_title; + $cat->mID = $row->cat_id; $cat->mSubcats = $row->cat_subcats; - $cat->mPages = $row->cat_pages; - $cat->mFiles = $row->cat_files; + $cat->mPages = $row->cat_pages; + $cat->mFiles = $row->cat_files; } return $cat; } /** @return mixed DB key name, or false on failure */ - public function getName() { return $this->getX( 'mName' ); } + public function getName() { + return $this->getX( 'mName' ); + } /** @return mixed Category ID, or false on failure */ - public function getID() { return $this->getX( 'mID' ); } + public function getID() { + return $this->getX( 'mID' ); + } /** @return mixed Total number of member pages, or false on failure */ - public function getPageCount() { return $this->getX( 'mPages' ); } + public function getPageCount() { + return $this->getX( 'mPages' ); + } /** @return mixed Number of subcategories, or false on failure */ - public function getSubcatCount() { return $this->getX( 'mSubcats' ); } + public function getSubcatCount() { + return $this->getX( 'mSubcats' ); + } /** @return mixed Number of member files, or false on failure */ - public function getFileCount() { return $this->getX( 'mFiles' ); } + public function getFileCount() { + return $this->getX( 'mFiles' ); + } /** * @return Title|bool Title for this category, or false on failure. */ public function getTitle() { - if ( $this->mTitle ) return $this->mTitle; + if ( $this->mTitle ) { + return $this->mTitle; + } if ( !$this->initialize() ) { return false; @@ -225,20 +243,22 @@ class Category { * @return TitleArray object for category members. */ public function getMembers( $limit = false, $offset = '' ) { + wfProfileIn( __METHOD__ ); + $dbr = wfGetDB( DB_SLAVE ); $conds = array( 'cl_to' => $this->getName(), 'cl_from = page_id' ); $options = array( 'ORDER BY' => 'cl_sortkey' ); if ( $limit ) { - $options[ 'LIMIT' ] = $limit; + $options['LIMIT'] = $limit; } if ( $offset !== '' ) { $conds[] = 'cl_sortkey > ' . $dbr->addQuotes( $offset ); } - return TitleArray::newFromResult( + $result = TitleArray::newFromResult( $dbr->select( array( 'page', 'categorylinks' ), array( 'page_id', 'page_namespace', 'page_title', 'page_len', @@ -248,6 +268,10 @@ class Category { $options ) ); + + wfProfileOut( __METHOD__ ); + + return $result; } /** @@ -258,7 +282,7 @@ class Category { if ( !$this->initialize() ) { return false; } - return $this-> { $key } ; + return $this->{$key}; } /** @@ -278,8 +302,10 @@ class Category { } } + wfProfileIn( __METHOD__ ); + $dbw = wfGetDB( DB_MASTER ); - $dbw->begin( __METHOD__ ); + $dbw->begin( __METHOD__ ); # Insert the row if it doesn't exist yet (e.g., this is being run via # update.php from a pre-1.16 schema). TODO: This will cause lots and @@ -302,12 +328,12 @@ class Category { $result = $dbw->selectRow( array( 'categorylinks', 'page' ), array( 'pages' => 'COUNT(*)', - 'subcats' => "COUNT($cond1)", - 'files' => "COUNT($cond2)" + 'subcats' => "COUNT($cond1)", + 'files' => "COUNT($cond2)" ), array( 'cl_to' => $this->mName, 'page_id = cl_from' ), __METHOD__, - 'LOCK IN SHARE MODE' + array( 'LOCK IN SHARE MODE' ) ); $ret = $dbw->update( 'category', @@ -321,10 +347,12 @@ class Category { ); $dbw->commit( __METHOD__ ); + wfProfileOut( __METHOD__ ); + # Now we should update our local counts. - $this->mPages = $result->pages; + $this->mPages = $result->pages; $this->mSubcats = $result->subcats; - $this->mFiles = $result->files; + $this->mFiles = $result->files; return $ret; } diff --git a/includes/CategoryPage.php b/includes/CategoryPage.php index 32e270e8..43ab4dbd 100644 --- a/includes/CategoryPage.php +++ b/includes/CategoryPage.php @@ -40,7 +40,7 @@ class CategoryPage extends Article { /** * Constructor from a page id - * @param $id Int article ID to load + * @param int $id article ID to load * @return CategoryPage|null */ public static function newFromID( $id ) { @@ -56,7 +56,7 @@ class CategoryPage extends Article { $diffOnly = $request->getBool( 'diffonly', $this->getContext()->getUser()->getOption( 'diffonly' ) ); - if ( isset( $diff ) && $diffOnly ) { + if ( $diff !== null && $diffOnly ) { parent::view(); return; } diff --git a/includes/CategoryViewer.php b/includes/CategoryViewer.php index 3bb2bc9b..970adb53 100644 --- a/includes/CategoryViewer.php +++ b/includes/CategoryViewer.php @@ -71,11 +71,12 @@ class CategoryViewer extends ContextSource { * @since 1.19 $context is a second, required parameter * @param $title Title * @param $context IContextSource - * @param $from String - * @param $until String + * @param array $from An array with keys page, subcat, + * and file for offset of results of each section (since 1.17) + * @param array $until An array with 3 keys for until of each section (since 1.17) * @param $query Array */ - function __construct( $title, IContextSource $context, $from = '', $until = '', $query = array() ) { + function __construct( $title, IContextSource $context, $from = array(), $until = array(), $query = array() ) { global $wgCategoryPagingLimit; $this->title = $title; $this->setContext( $context ); @@ -173,7 +174,7 @@ class CategoryViewer extends ContextSource { /** * Add a subcategory to the internal lists, using a title object - * @deprecated since 1.17 kept for compatibility, please use addSubcategoryObject instead + * @deprecated since 1.17 kept for compatibility, use addSubcategoryObject instead */ function addSubcategory( Title $title, $sortkey, $pageLength ) { wfDeprecated( __METHOD__, '1.17' ); @@ -181,14 +182,14 @@ class CategoryViewer extends ContextSource { } /** - * Get the character to be used for sorting subcategories. - * If there's a link from Category:A to Category:B, the sortkey of the resulting - * entry in the categorylinks table is Category:A, not A, which it SHOULD be. - * Workaround: If sortkey == "Category:".$title, than use $title for sorting, - * else use sortkey... - * - * @param Title $title - * @param string $sortkey The human-readable sortkey (before transforming to icu or whatever). + * Get the character to be used for sorting subcategories. + * If there's a link from Category:A to Category:B, the sortkey of the resulting + * entry in the categorylinks table is Category:A, not A, which it SHOULD be. + * Workaround: If sortkey == "Category:".$title, than use $title for sorting, + * else use sortkey... + * + * @param Title $title + * @param string $sortkey The human-readable sortkey (before transforming to icu or whatever). * @return string */ function getSubcategorySortChar( $title, $sortkey ) { @@ -248,7 +249,7 @@ class CategoryViewer extends ContextSource { $link = Linker::link( $title ); if ( $isRedirect ) { // This seems kind of pointless given 'mw-redirect' class, - // but keeping for back-compatiability with user css. + // but keeping for back-compatibility with user css. $link = '' . $link . ''; } $this->articles[] = $link; @@ -259,15 +260,15 @@ class CategoryViewer extends ContextSource { function finaliseCategoryState() { if ( $this->flip['subcat'] ) { - $this->children = array_reverse( $this->children ); + $this->children = array_reverse( $this->children ); $this->children_start_char = array_reverse( $this->children_start_char ); } if ( $this->flip['page'] ) { - $this->articles = array_reverse( $this->articles ); + $this->articles = array_reverse( $this->articles ); $this->articles_start_char = array_reverse( $this->articles_start_char ); } if ( !$this->showGallery && $this->flip['file'] ) { - $this->imgsNoGallery = array_reverse( $this->imgsNoGallery ); + $this->imgsNoGallery = array_reverse( $this->imgsNoGallery ); $this->imgsNoGallery_start_char = array_reverse( $this->imgsNoGallery_start_char ); } } @@ -287,10 +288,10 @@ class CategoryViewer extends ContextSource { # the collation in the database differs from the one # set in $wgCategoryCollation, pagination might go totally haywire. $extraConds = array( 'cl_type' => $type ); - if ( $this->from[$type] !== null ) { + if ( isset( $this->from[$type] ) && $this->from[$type] !== null ) { $extraConds[] = 'cl_sortkey >= ' . $dbr->addQuotes( $this->collation->getSortKey( $this->from[$type] ) ); - } elseif ( $this->until[$type] !== null ) { + } elseif ( isset( $this->until[$type] ) && $this->until[$type] !== null ) { $extraConds[] = 'cl_sortkey < ' . $dbr->addQuotes( $this->collation->getSortKey( $this->until[$type] ) ); $this->flip[$type] = true; @@ -302,7 +303,7 @@ class CategoryViewer extends ContextSource { 'page_is_redirect', 'cl_sortkey', 'cat_id', 'cat_title', 'cat_subcats', 'cat_pages', 'cat_files', 'cl_sortkey_prefix', 'cl_collation' ), - array_merge( array( 'cl_to' => $this->title->getDBkey() ), $extraConds ), + array_merge( array( 'cl_to' => $this->title->getDBkey() ), $extraConds ), __METHOD__, array( 'USE INDEX' => array( 'categorylinks' => 'cl_sortkey' ), @@ -310,8 +311,11 @@ class CategoryViewer extends ContextSource { 'ORDER BY' => $this->flip[$type] ? 'cl_sortkey DESC' : 'cl_sortkey', ), array( - 'categorylinks' => array( 'INNER JOIN', 'cl_from = page_id' ), - 'category' => array( 'LEFT JOIN', 'cat_title = page_title AND page_namespace = ' . NS_CATEGORY ) + 'categorylinks' => array( 'INNER JOIN', 'cl_from = page_id' ), + 'category' => array( 'LEFT JOIN', array( + 'cat_title = page_title', + 'page_namespace' => NS_CATEGORY + )) ) ); @@ -388,7 +392,7 @@ class CategoryViewer extends ContextSource { $r = ''; # @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 + # with this rigmarole 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. $dbcnt = $this->cat->getPageCount() - $this->cat->getSubcatCount() @@ -437,13 +441,13 @@ class CategoryViewer extends ContextSource { * Get the paging links for a section (subcats/pages/files), to go at the top and bottom * of the output. * - * @param $type String: 'page', 'subcat', or 'file' + * @param string $type 'page', 'subcat', or 'file' * @return String: HTML output, possibly empty if there are no other pages */ private function getSectionPagingLinks( $type ) { - if ( $this->until[$type] !== null ) { + if ( isset( $this->until[$type] ) && $this->until[$type] !== null ) { return $this->pagingLinks( $this->nextPage[$type], $this->until[$type], $type ); - } elseif ( $this->nextPage[$type] !== null || $this->from[$type] !== null ) { + } elseif ( $this->nextPage[$type] !== null || ( isset( $this->from[$type] ) && $this->from[$type] !== null ) ) { return $this->pagingLinks( $this->from[$type], $this->nextPage[$type], $type ); } else { return ''; @@ -469,7 +473,7 @@ class CategoryViewer extends ContextSource { */ function formatList( $articles, $articles_start_char, $cutoff = 6 ) { $list = ''; - if ( count ( $articles ) > $cutoff ) { + if ( count( $articles ) > $cutoff ) { $list = self::columnList( $articles, $articles_start_char ); } elseif ( count( $articles ) > 0 ) { // for short lists of articles in categories. @@ -478,7 +482,7 @@ class CategoryViewer extends ContextSource { $pageLang = $this->title->getPageLanguage(); $attribs = array( 'lang' => $pageLang->getCode(), 'dir' => $pageLang->getDir(), - 'class' => 'mw-content-'.$pageLang->getDir() ); + 'class' => 'mw-content-' . $pageLang->getDir() ); $list = Html::rawElement( 'div', $attribs, $list ); return $list; @@ -569,9 +573,9 @@ class CategoryViewer extends ContextSource { /** * Create paging links, as a helper method to getSectionPagingLinks(). * - * @param $first String The 'until' parameter for the generated URL - * @param $last String The 'from' parameter for the genererated URL - * @param $type String A prefix for parameters, 'page' or 'subcat' or + * @param string $first The 'until' parameter for the generated URL + * @param string $last The 'from' parameter for the generated URL + * @param string $type A prefix for parameters, 'page' or 'subcat' or * 'file' * @return String HTML */ @@ -604,7 +608,7 @@ class CategoryViewer extends ContextSource { ); } - return $this->msg('categoryviewer-pagedlinks')->rawParams($prevLink, $nextLink)->escaped(); + return $this->msg( 'categoryviewer-pagedlinks' )->rawParams( $prevLink, $nextLink )->escaped(); } /** @@ -612,7 +616,8 @@ class CategoryViewer extends ContextSource { * corresponds to the correct segment of the category. * * @param Title $title: The title (usually $this->title) - * @param String $section: Which section + * @param string $section: Which section + * @throws MWException * @return Title */ private function addFragmentToTitle( $title, $section ) { @@ -634,6 +639,7 @@ class CategoryViewer extends ContextSource { 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 independently @@ -644,9 +650,9 @@ class CategoryViewer extends ContextSource { * category-subcat-count-limited, category-file-count, * category-file-count-limited. * - * @param $rescnt Int: The number of items returned by our database query. - * @param $dbcnt Int: The number of items according to the category table. - * @param $type String: 'subcat', 'article', or 'file' + * @param int $rescnt The number of items returned by our database query. + * @param int $dbcnt The number of items according to the category table. + * @param string $type 'subcat', 'article', or 'file' * @return String: A message giving the number of items, to output to HTML. */ private function getCountMessage( $rescnt, $dbcnt, $type ) { @@ -671,12 +677,15 @@ class CategoryViewer extends ContextSource { } $fromOrUntil = false; - if ( $this->from[$pagingType] !== null || $this->until[$pagingType] !== null ) { + if ( ( isset( $this->from[$pagingType] ) && $this->from[$pagingType] !== null ) || + ( isset( $this->until[$pagingType] ) && $this->until[$pagingType] !== null ) + ) { $fromOrUntil = true; } - if ( $dbcnt == $rescnt || ( ( $rescnt == $this->limit || $fromOrUntil ) - && $dbcnt > $rescnt ) ) { + if ( $dbcnt == $rescnt || + ( ( $rescnt == $this->limit || $fromOrUntil ) && $dbcnt > $rescnt ) + ) { # Case 1: seems sane. $totalcnt = $dbcnt; } elseif ( $rescnt < $this->limit && !$fromOrUntil ) { diff --git a/includes/Categoryfinder.php b/includes/Categoryfinder.php index e2b6a0ca..6ef224b6 100644 --- a/includes/Categoryfinder.php +++ b/includes/Categoryfinder.php @@ -28,17 +28,17 @@ * * Example use : * - * # Determines whether the article with the page_id 12345 is in both - * # "Category 1" and "Category 2" or their subcategories, respectively + * # Determines whether the article with the page_id 12345 is in both + * # "Category 1" and "Category 2" or their subcategories, respectively * - * $cf = new Categoryfinder; - * $cf->seed( - * array( 12345 ), - * array( 'Category 1', 'Category 2' ), - * 'AND' - * ); - * $a = $cf->run(); - * print implode( ',' , $a ); + * $cf = new Categoryfinder; + * $cf->seed( + * array( 12345 ), + * array( 'Category 1', 'Category 2' ), + * 'AND' + * ); + * $a = $cf->run(); + * print implode( ',' , $a ); * * */ @@ -66,7 +66,7 @@ class Categoryfinder { * Initializes the instance. Do this prior to calling run(). * @param $article_ids Array of article IDs * @param $categories FIXME - * @param $mode String: FIXME, default 'AND'. + * @param string $mode FIXME, default 'AND'. * @todo FIXME: $categories/$mode */ function seed( $article_ids, $categories, $mode = 'AND' ) { @@ -111,9 +111,9 @@ class Categoryfinder { /** * This functions recurses through the parent representation, trying to match the conditions - * @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 + * @param int $id The article/category to check + * @param array $conds The array of categories to match + * @param array $path used to check for recursion loops * @return bool Does this match the conditions? */ function check( $id, &$conds, $path = array() ) { @@ -124,7 +124,7 @@ class Categoryfinder { $path[] = $id; - # Shortcut (runtime paranoia): No contitions=all matched + # Shortcut (runtime paranoia): No conditions=all matched if ( count( $conds ) == 0 ) { return true; } @@ -135,7 +135,7 @@ class Categoryfinder { # iterate through the parents foreach ( $this->parents[$id] as $p ) { - $pname = $p->cl_to ; + $pname = $p->cl_to; # Is this a condition? if ( isset( $conds[$pname] ) ) { @@ -172,6 +172,8 @@ class Categoryfinder { * Scans a "parent layer" of the articles/categories in $this->next */ function scan_next_layer() { + wfProfileIn( __METHOD__ ); + # Find all parents of the article currently in $this->next $layer = array(); $res = $this->dbr->select( @@ -209,7 +211,7 @@ class Categoryfinder { $res = $this->dbr->select( /* FROM */ 'page', /* SELECT */ array( 'page_id', 'page_title' ), - /* WHERE */ array( 'page_namespace' => NS_CATEGORY , 'page_title' => $layer ), + /* WHERE */ array( 'page_namespace' => NS_CATEGORY, 'page_title' => $layer ), __METHOD__ . '-2' ); foreach ( $res as $o ) { @@ -225,6 +227,7 @@ class Categoryfinder { foreach ( $layer as $v ) { $this->deadend[$v] = $v; } - } + wfProfileOut( __METHOD__ ); + } } diff --git a/includes/Cdb.php b/includes/Cdb.php index ae2e5b18..a142c7cb 100644 --- a/includes/Cdb.php +++ b/includes/Cdb.php @@ -133,8 +133,9 @@ class CdbReader_DBA { } function close() { - if( isset($this->handle) ) + if( isset( $this->handle ) ) { dba_close( $this->handle ); + } unset( $this->handle ); } @@ -143,7 +144,6 @@ class CdbReader_DBA { } } - /** * Writer class which uses the DBA extension */ @@ -164,8 +164,9 @@ class CdbWriter_DBA { } function close() { - if( isset($this->handle) ) + if( isset( $this->handle ) ) { dba_close( $this->handle ); + } if ( wfIsWindows() ) { unlink( $this->realFileName ); } @@ -181,4 +182,3 @@ class CdbWriter_DBA { } } } - diff --git a/includes/Cdb_PHP.php b/includes/Cdb_PHP.php index 02be65f3..71b55f87 100644 --- a/includes/Cdb_PHP.php +++ b/includes/Cdb_PHP.php @@ -126,6 +126,7 @@ class CdbReader_PHP extends CdbReader { /** * @param $fileName string + * @throws MWException */ function __construct( $fileName ) { $this->fileName = $fileName; @@ -179,7 +180,7 @@ class CdbReader_PHP extends CdbReader { protected function read( $length, $pos ) { if ( fseek( $this->handle, $pos ) == -1 ) { // This can easily happen if the internal pointers are incorrect - throw new MWException( + throw new MWException( 'Seek failed, file "' . $this->fileName . '" may be corrupted.' ); } @@ -198,12 +199,13 @@ class CdbReader_PHP extends CdbReader { /** * Unpack an unsigned integer and throw an exception if it needs more than 31 bits * @param $s - * @return + * @throws MWException + * @return mixed */ protected function unpack31( $s ) { $data = unpack( 'V', $s ); if ( $data[1] > 0x7fffffff ) { - throw new MWException( + throw new MWException( 'Error in CDB file "' . $this->fileName . '", integer too big.' ); } return $data[1]; @@ -330,10 +332,10 @@ class CdbWriter_PHP extends CdbWriter { */ public function close() { $this->finish(); - if( isset($this->handle) ) { + if( isset( $this->handle ) ) { fclose( $this->handle ); } - if ( wfIsWindows() && file_exists($this->realFileName) ) { + if ( wfIsWindows() && file_exists( $this->realFileName ) ) { unlink( $this->realFileName ); } if ( !rename( $this->tmpFileName, $this->realFileName ) ) { @@ -349,7 +351,7 @@ class CdbWriter_PHP extends CdbWriter { protected function write( $buf ) { $len = fwrite( $this->handle, $buf ); if ( $len !== strlen( $buf ) ) { - $this->throwException( 'Error writing to CDB file "'.$this->tmpFileName.'".' ); + $this->throwException( 'Error writing to CDB file "' . $this->tmpFileName . '".' ); } } @@ -361,7 +363,7 @@ class CdbWriter_PHP extends CdbWriter { $newpos = $this->pos + $len; if ( $newpos > 0x7fffffff ) { $this->throwException( - 'A value in the CDB file "'.$this->tmpFileName.'" is too large.' ); + 'A value in the CDB file "' . $this->tmpFileName . '" is too large.' ); } $this->pos = $newpos; } @@ -390,10 +392,10 @@ class CdbWriter_PHP extends CdbWriter { */ protected function addbegin( $keylen, $datalen ) { if ( $keylen > 0x7fffffff ) { - $this->throwException( 'Key length too long in file "'.$this->tmpFileName.'".' ); + $this->throwException( 'Key length too long in file "' . $this->tmpFileName . '".' ); } if ( $datalen > 0x7fffffff ) { - $this->throwException( 'Data length too long in file "'.$this->tmpFileName.'".' ); + $this->throwException( 'Data length too long in file "' . $this->tmpFileName . '".' ); } $buf = pack( 'VV', $keylen, $datalen ); $this->write( $buf ); @@ -468,14 +470,14 @@ class CdbWriter_PHP extends CdbWriter { // Write the pointer array at the start of the file rewind( $this->handle ); if ( ftell( $this->handle ) != 0 ) { - $this->throwException( 'Error rewinding to start of file "'.$this->tmpFileName.'".' ); + $this->throwException( 'Error rewinding to start of file "' . $this->tmpFileName . '".' ); } $this->write( $final ); } /** * Clean up the temp file and throw an exception - * + * * @param $msg string * @throws MWException */ diff --git a/includes/ChangeTags.php b/includes/ChangeTags.php index 0ebc926f..3adf58f8 100644 --- a/includes/ChangeTags.php +++ b/includes/ChangeTags.php @@ -25,8 +25,8 @@ class ChangeTags { /** * Creates HTML for the given tags * - * @param $tags String: Comma-separated list of tags - * @param $page String: A label for the type of action which is being displayed, + * @param string $tags Comma-separated list of tags + * @param string $page A label for the type of action which is being displayed, * for example: 'history', 'contributions' or 'newpages' * * @return Array with two items: (html, classes) @@ -62,7 +62,7 @@ class ChangeTags { /** * Get a short description for a tag * - * @param $tag String: tag + * @param string $tag tag * * @return String: Short description of the tag from "mediawiki:tag-$tag" if this message exists, * html-escaped version of $tag otherwise @@ -75,12 +75,13 @@ class ChangeTags { /** * Add tags to a change given its rc_id, rev_id and/or log_id * - * @param $tags String|Array: Tags to add to the change + * @param string|array $tags Tags to add to the change * @param $rc_id int: rc_id of the change to add the tags to * @param $rev_id int: rev_id of the change to add the tags to * @param $log_id int: log_id of the change to add the tags to - * @param $params String: params to put in the ct_params field of tabel 'change_tag' + * @param string $params params to put in the ct_params field of table 'change_tag' * + * @throws MWException * @return bool: false if no changes are made, otherwise true * * @exception MWException when $rc_id, $rev_id and $log_id are all null @@ -159,17 +160,16 @@ class ChangeTags { * Handles selecting tags, and filtering. * Needs $tables to be set up properly, so we can figure out which join conditions to use. * - * @param $tables String|Array: Tabel names, see DatabaseBase::select - * @param $fields String|Array: Fields used in query, see DatabaseBase::select - * @param $conds String|Array: conditions used in query, see DatabaseBase::select + * @param string|array $tables Table names, see DatabaseBase::select + * @param string|array $fields Fields used in query, see DatabaseBase::select + * @param string|array $conds conditions used in query, see DatabaseBase::select * @param $join_conds Array: join conditions, see DatabaseBase::select - * @param $options Array: options, see Database::select - * @param $filter_tag String: tag to select on - * - * @exception MWException when unable to determine appropriate JOIN condition for tagging + * @param array $options options, see Database::select + * @param bool|string $filter_tag Tag to select on * + * @throws MWException When unable to determine appropriate JOIN condition for tagging */ - static function modifyDisplayQuery( &$tables, &$fields, &$conds, + static function modifyDisplayQuery( &$tables, &$fields, &$conds, &$join_conds, &$options, $filter_tag = false ) { global $wgRequest, $wgUseTagFilter; @@ -211,7 +211,7 @@ class ChangeTags { /** * Build a text box to select a change tag * - * @param $selected String: tag to select by default + * @param string $selected 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. @@ -221,11 +221,12 @@ class ChangeTags { * - if $fullForm is false: Array with * - if $fullForm is true: String, html fragment */ - public static function buildTagFilterSelector( $selected='', $fullForm = false, Title $title = null ) { + public static function buildTagFilterSelector( $selected = '', $fullForm = false, Title $title = null ) { global $wgUseTagFilter; - if ( !$wgUseTagFilter || !count( self::listDefinedTags() ) ) + if ( !$wgUseTagFilter || !count( self::listDefinedTags() ) ) { return $fullForm ? '' : array(); + } $data = array( Html::rawElement( 'label', array( 'for' => 'tagfilter' ), wfMessage( 'tag-filter' )->parse() ), Xml::input( 'tagfilter', 20, $selected, array( 'class' => 'mw-tagfilter-input' ) ) ); diff --git a/includes/ChangesFeed.php b/includes/ChangesFeed.php index ee4c2d64..476de5bd 100644 --- a/includes/ChangesFeed.php +++ b/includes/ChangesFeed.php @@ -31,8 +31,8 @@ class ChangesFeed { /** * Constructor * - * @param $format String: feed's format (either 'rss' or 'atom') - * @param $type String: type of feed (for cache keys) + * @param string $format feed's format (either 'rss' or 'atom') + * @param string $type type of feed (for cache keys) */ public function __construct( $format, $type ) { $this->format = $format; @@ -42,9 +42,9 @@ class ChangesFeed { /** * Get a ChannelFeed subclass object to use * - * @param $title String: feed's title - * @param $description String: feed's description - * @param $url String: url of origin page + * @param string $title feed's title + * @param string $description feed's description + * @param string $url url of origin page * @return ChannelFeed subclass or false on failure */ public function getFeedObject( $title, $description, $url ) { @@ -110,9 +110,9 @@ class ChangesFeed { /** * Save to feed result to $messageMemc * - * @param $feed String: feed's content - * @param $timekey String: memcached key of the last modification - * @param $key String: memcached key of the content + * @param string $feed feed's content + * @param string $timekey memcached key of the last modification + * @param string $key memcached key of the content */ public function saveToCache( $feed, $timekey, $key ) { global $messageMemc; @@ -125,8 +125,8 @@ class ChangesFeed { * Try to load the feed result from $messageMemc * * @param $lastmod Integer: timestamp of the last item in the recentchanges table - * @param $timekey String: memcached key of the last modification - * @param $key String: memcached key of the content + * @param string $timekey memcached key of the last modification + * @param string $key memcached key of the content * @return string|bool feed's content on cache hit or false on cache miss */ public function loadFromCache( $lastmod, $timekey, $key ) { @@ -135,7 +135,7 @@ 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 diff --git a/includes/ChangesList.php b/includes/ChangesList.php index 84677124..8461001e 100644 --- a/includes/ChangesList.php +++ b/includes/ChangesList.php @@ -30,7 +30,7 @@ */ class RCCacheEntry extends RecentChange { var $secureName, $link; - var $curlink , $difflink, $lastlink, $usertalklink, $versionlink; + var $curlink, $difflink, $lastlink, $usertalklink, $versionlink; var $userlink, $timestamp, $watched; /** @@ -60,7 +60,7 @@ class ChangesList extends ContextSource { protected $message; /** - * Changeslist contructor + * Changeslist constructor * * @param $obj Skin or IContextSource */ @@ -80,7 +80,7 @@ class ChangesList extends ContextSource { * This first argument used to be an User object. * * @deprecated in 1.18; use newFromContext() instead - * @param $unused string|User Unused + * @param string|User $unused Unused * @return ChangesList|EnhancedChangesList|OldChangesList derivative */ public static function newFromUser( $unused ) { @@ -130,13 +130,13 @@ class ChangesList extends ContextSource { /** * Returns the appropriate flags for new page, minor change and patrolling - * @param $flags Array Associative array of 'flag' => Bool - * @param $nothing String to use for empty space + * @param array $flags Associative array of 'flag' => Bool + * @param string $nothing to use for empty space * @return String */ protected function recentChangesFlags( $flags, $nothing = ' ' ) { $f = ''; - foreach( array( 'newpage', 'minor', 'bot', 'unpatrolled' ) as $flag ){ + foreach( array( 'newpage', 'minor', 'bot', 'unpatrolled' ) as $flag ) { $f .= isset( $flags[$flag] ) && $flags[$flag] ? self::flag( $flag ) : $nothing; @@ -150,7 +150,7 @@ class ChangesList extends ContextSource { * unpatrolled edit. By default in English it will contain "N", "m", "b", * "!" respectively, plus it will have an appropriate title and class. * - * @param $flag String: 'newpage', 'unpatrolled', 'minor', or 'bot' + * @param string $flag 'newpage', 'unpatrolled', 'minor', or 'bot' * @return String: Raw HTML */ public static function flag( $flag ) { @@ -217,7 +217,7 @@ class ChangesList extends ContextSource { $lang = $context->getLanguage(); $code = $lang->getCode(); static $fastCharDiff = array(); - if ( !isset($fastCharDiff[$code]) ) { + if ( !isset( $fastCharDiff[$code] ) ) { $fastCharDiff[$code] = $wgMiserMode || $context->msg( 'rc-change-size' )->plain() === '$1'; } @@ -286,6 +286,10 @@ class ChangesList extends ContextSource { } } + /** + * @param string $s HTML to update + * @param $rc_timestamp mixed + */ public function insertDateHeader( &$s, $rc_timestamp ) { # Make date header if necessary $date = $this->getLanguage()->userDate( $rc_timestamp, $this->getUser() ); @@ -299,6 +303,11 @@ class ChangesList extends ContextSource { } } + /** + * @param string $s HTML to update + * @param $title Title + * @param $logtype string + */ public function insertLog( &$s, $title, $logtype ) { $page = new LogPage( $logtype ); $logname = $page->getName()->escaped(); @@ -306,7 +315,7 @@ class ChangesList extends ContextSource { } /** - * @param $s + * @param string $s HTML to update * @param $rc RecentChange * @param $unpatrolled */ @@ -319,7 +328,7 @@ class ChangesList extends ContextSource { } else { $query = array( 'curid' => $rc->mAttribs['rc_cur_id'], - 'diff' => $rc->mAttribs['rc_this_oldid'], + 'diff' => $rc->mAttribs['rc_this_oldid'], 'oldid' => $rc->mAttribs['rc_last_oldid'] ); @@ -349,7 +358,7 @@ class ChangesList extends ContextSource { } /** - * @param $s + * @param string $s HTML to update * @param $rc RecentChange * @param $unpatrolled * @param $watched @@ -369,7 +378,7 @@ class ChangesList extends ContextSource { array( 'class' => 'mw-changeslist-title' ), $params ); - if( $this->isDeleted($rc,Revision::DELETED_TEXT) ) { + if( $this->isDeleted( $rc, Revision::DELETED_TEXT ) ) { $articlelink = '' . $articlelink . ''; } # To allow for boldening pages watched by this user @@ -378,20 +387,33 @@ class ChangesList extends ContextSource { $articlelink .= $this->getLanguage()->getDirMark(); wfRunHooks( 'ChangesListInsertArticleLink', - array(&$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched) ); + array( &$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched ) ); $s .= " $articlelink"; } /** - * @param $s + * Get the timestamp from $rc formatted with current user's settings + * and a separator + * * @param $rc RecentChange + * @return string HTML fragment */ - public function insertTimestamp( &$s, $rc ) { - $s .= $this->message['semicolon-separator'] . '' . + public function getTimestamp( $rc ) { + return $this->message['semicolon-separator'] . '' . $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() ) . ' . . '; } + /** + * Insert time timestamp string from $rc into $s + * + * @param string $s HTML to update + * @param $rc RecentChange + */ + public function insertTimestamp( &$s, $rc ) { + $s .= $this->getTimestamp( $rc ); + } + /** * Insert links to user page, user talk page and eventually a blocking link * @@ -435,6 +457,7 @@ class ChangesList extends ContextSource { return Linker::commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() ); } } + return ''; } /** @@ -511,15 +534,15 @@ class ChangesList extends ContextSource { $page = $rc->getTitle(); /** Check for rollback and edit permissions, disallow special pages, and only * show a link on the top-most revision */ - if ( $this->getUser()->isAllowed('rollback') && $rc->mAttribs['page_latest'] == $rc->mAttribs['rc_this_oldid'] ) + if ( $this->getUser()->isAllowed( 'rollback' ) && $rc->mAttribs['page_latest'] == $rc->mAttribs['rc_this_oldid'] ) { $rev = new Revision( array( - 'id' => $rc->mAttribs['rc_this_oldid'], - 'user' => $rc->mAttribs['rc_user'], + 'title' => $page, + 'id' => $rc->mAttribs['rc_this_oldid'], + 'user' => $rc->mAttribs['rc_user'], 'user_text' => $rc->mAttribs['rc_user_text'], - 'deleted' => $rc->mAttribs['rc_deleted'] + 'deleted' => $rc->mAttribs['rc_deleted'] ) ); - $rev->setTitle( $page ); $s .= ' '.Linker::generateRollback( $rev, $this->getContext() ); } } @@ -531,16 +554,16 @@ class ChangesList extends ContextSource { * @param $classes */ public function insertTags( &$s, &$rc, &$classes ) { - if ( empty($rc->mAttribs['ts_tags']) ) + if ( empty( $rc->mAttribs['ts_tags'] ) ) return; - list($tagSummary, $newClasses) = ChangeTags::formatSummaryRow( $rc->mAttribs['ts_tags'], 'changeslist' ); + list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow( $rc->mAttribs['ts_tags'], 'changeslist' ); $classes = array_merge( $classes, $newClasses ); $s .= ' ' . $tagSummary; } public function insertExtra( &$s, &$rc, &$classes ) { - ## Empty, used for subclassers to add anything special. + // Empty, used for subclasses to add anything special. } protected function showAsUnpatrolled( RecentChange $rc ) { @@ -556,7 +579,6 @@ class ChangesList extends ContextSource { } } - /** * Generate a list of changes using the good old system (no javascript) */ @@ -565,9 +587,10 @@ class OldChangesList extends ChangesList { * Format a line using the old system (aka without any javascript). * * @param $rc RecentChange, passed by reference - * @param $watched Bool (default false) - * @param $linenumber Int (default null) - * @return string + * @param bool $watched (default false) + * @param int $linenumber (default null) + * + * @return string|bool */ public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) { global $wgRCShowChangedSize; @@ -655,17 +678,19 @@ class OldChangesList extends ChangesList { } if( $this->watchlist ) { - $classes[] = Sanitizer::escapeClass( 'watchlist-'.$rc->mAttribs['rc_namespace'].'-'.$rc->mAttribs['rc_title'] ); + $classes[] = Sanitizer::escapeClass( 'watchlist-' . $rc->mAttribs['rc_namespace'] . '-' . $rc->mAttribs['rc_title'] ); } - wfRunHooks( 'OldChangesListRecentChangesLine', array(&$this, &$s, $rc) ); + if ( !wfRunHooks( 'OldChangesListRecentChangesLine', array( &$this, &$s, $rc, &$classes ) ) ) { + wfProfileOut( __METHOD__ ); + return false; + } wfProfileOut( __METHOD__ ); - return "$dateheader
  • ".$s."
  • \n"; + return "$dateheader
  • " . $s . "
  • \n"; } } - /** * Generate a list of changes using an Enhanced system (uses javascript). */ @@ -795,7 +820,7 @@ class EnhancedChangesList extends ChangesList { $lastLink = $this->message['last']; } else { $lastLink = Linker::linkKnown( $rc->getTitle(), $this->message['last'], - array(), $curIdEq + array('diff' => $thisOldid, 'oldid' => $lastOldid) + $rcIdQuery ); + array(), $curIdEq + array( 'diff' => $thisOldid, 'oldid' => $lastOldid ) + $rcIdQuery ); } # Make user links @@ -807,7 +832,7 @@ class EnhancedChangesList extends ChangesList { } $rc->lastlink = $lastLink; - $rc->curlink = $curLink; + $rc->curlink = $curLink; $rc->difflink = $diffLink; # Put accumulated information into the cache, for later display @@ -816,10 +841,10 @@ class EnhancedChangesList extends ChangesList { $secureName = $title->getPrefixedDBkey(); if( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) { # Use an @ character to prevent collision with page names - $this->rc_cache['@@' . ($this->rcMoveIndex++)] = array($rc); + $this->rc_cache['@@' . ($this->rcMoveIndex++)] = array( $rc ); } else { # Logs are grouped by type - if( $type == RC_LOG ){ + if( $type == RC_LOG ) { $secureName = SpecialPage::getTitleFor( 'Log', $logType )->getPrefixedDBkey(); } if( !isset( $this->rc_cache[$secureName] ) ) { @@ -862,6 +887,8 @@ class EnhancedChangesList extends ChangesList { # Other properties $unpatrolled = false; $isnew = false; + $allBots = true; + $allMinors = true; $curId = $currentRevision = 0; # Some catalyst variables... $namehidden = true; @@ -895,7 +922,13 @@ class EnhancedChangesList extends ChangesList { $currentRevision = $rcObj->mAttribs['rc_this_oldid']; } - $bot = $rcObj->mAttribs['rc_bot']; + if( !$rcObj->mAttribs['rc_bot'] ) { + $allBots = false; + } + if( !$rcObj->mAttribs['rc_minor'] ) { + $allMinors = false; + } + $userlinks[$u]++; } @@ -917,19 +950,19 @@ class EnhancedChangesList extends ChangesList { implode( $this->message['semicolon-separator'], $users ) )->escaped() . '
    '; - $tl = ''; + $tl = ''; $r .= "$tl"; # Main line $r .= '' . $this->recentChangesFlags( array( - 'newpage' => $isnew, - 'minor' => false, - 'unpatrolled' => $unpatrolled, - 'bot' => $bot , + 'newpage' => $isnew, # show, when one have this flag + 'minor' => $allMinors, # show only, when all have this flag + 'unpatrolled' => $unpatrolled, # show, when one have this flag + 'bot' => $allBots, # show only, when all have this flag ) ); # Timestamp - $r .= ' '.$block[0]->timestamp.' '; + $r .= ' ' . $block[0]->timestamp . ' '; # Article link if( $namehidden ) { @@ -944,7 +977,7 @@ class EnhancedChangesList extends ChangesList { $queryParams['curid'] = $curId; # Changes message - $n = count($block); + $n = count( $block ); static $nchanges = array(); if ( !isset( $nchanges[$n] ) ) { $nchanges[$n] = $this->msg( 'nchanges' )->numParams( $n )->escaped(); @@ -999,7 +1032,7 @@ class EnhancedChangesList extends ChangesList { # Character difference (does not apply if only log items) if( $wgRCShowChangedSize && !$allLogs ) { $last = 0; - $first = count($block) - 1; + $first = count( $block ) - 1; # Some events (like logs) have an "empty" size, so we need to skip those... while( $last < $first && $block[$last]->mAttribs['rc_new_len'] === null ) { $last++; @@ -1018,7 +1051,7 @@ class EnhancedChangesList extends ChangesList { } $r .= $users; - $r .= $this->numberofWatchingusers($block[0]->numberofWatchingusers); + $r .= $this->numberofWatchingusers( $block[0]->numberofWatchingusers ); # Sub-entries foreach( $block as $rcObj ) { @@ -1046,7 +1079,7 @@ class EnhancedChangesList extends ChangesList { $link = $rcObj->timestamp; # Revision link } elseif( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) { - $link = ''.$rcObj->timestamp.' '; + $link = '' . $rcObj->timestamp . ' '; } else { if ( $rcObj->unpatrolled && $type == RC_NEW) { $params['rcid'] = $rcObj->mAttribs['rc_id']; @@ -1058,8 +1091,9 @@ class EnhancedChangesList extends ChangesList { array(), $params ); - if( $this->isDeleted($rcObj,Revision::DELETED_TEXT) ) - $link = ''.$link.' '; + if( $this->isDeleted( $rcObj, Revision::DELETED_TEXT ) ) { + $link = '' . $link . ' '; + } } $r .= $link . ''; @@ -1103,12 +1137,12 @@ class EnhancedChangesList extends ChangesList { /** * Generate HTML for an arrow or placeholder graphic - * @param $dir String: one of '', 'd', 'l', 'r' - * @param $alt String: text - * @param $title String: text + * @param string $dir one of '', 'd', 'l', 'r' + * @param string $alt text + * @param string $title text * @return String: HTML "" tag */ - protected function arrow( $dir, $alt='', $title='' ) { + protected function arrow( $dir, $alt = '', $title = '' ) { global $wgStylePath; $encUrl = htmlspecialchars( $wgStylePath . '/common/images/Arr_' . $dir . '.png' ); $encAlt = htmlspecialchars( $alt ); @@ -1170,7 +1204,7 @@ class EnhancedChangesList extends ChangesList { $r = Html::openElement( 'table', array( 'class' => $classes ) ) . Html::openElement( 'tr' ); - $r .= ''; + $r .= ''; # Flag and Timestamp if( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) { $r .= '    '; // 4 flags -> 4 spaces @@ -1182,7 +1216,7 @@ class EnhancedChangesList extends ChangesList { 'bot' => $rcObj->mAttribs['rc_bot'], ) ); } - $r .= ' '.$rcObj->timestamp.' '; + $r .= ' ' . $rcObj->timestamp . ' '; # Article or log link if( $logType ) { $logPage = new LogPage( $logType ); @@ -1214,7 +1248,7 @@ class EnhancedChangesList extends ChangesList { if ( $type == RC_LOG ) { $r .= $this->insertLogEntry( $rcObj ); } else { - $r .= ' '.$rcObj->userlink . $rcObj->usertalklink; + $r .= ' ' . $rcObj->userlink . $rcObj->usertalklink; $r .= $this->insertComment( $rcObj ); $this->insertRollback( $r, $rcObj ); } @@ -1222,7 +1256,7 @@ class EnhancedChangesList extends ChangesList { # Tags $this->insertTags( $r, $rcObj, $classes ); # Show how many people are watching this if enabled - $r .= $this->numberofWatchingusers($rcObj->numberofWatchingusers); + $r .= $this->numberofWatchingusers( $rcObj->numberofWatchingusers ); $r .= "\n"; @@ -1255,7 +1289,7 @@ class EnhancedChangesList extends ChangesList { wfProfileOut( __METHOD__ ); - return '
    '.$blockOut.'
    '; + return '
    ' . $blockOut . '
    '; } /** diff --git a/includes/Collation.php b/includes/Collation.php index ad2b94b1..6bba019b 100644 --- a/includes/Collation.php +++ b/includes/Collation.php @@ -48,8 +48,12 @@ abstract class Collation { case 'uca-default': return new IcuCollation( 'root' ); default: - # Provide a mechanism for extensions to hook in. + $match = array(); + if ( preg_match( '/^uca-([a-z-]+)$/', $collationName, $match ) ) { + return new IcuCollation( $match[1] ); + } + # Provide a mechanism for extensions to hook in. $collationObject = null; wfRunHooks( 'Collation::factory', array( $collationName, &$collationObject ) ); @@ -144,18 +148,19 @@ class IdentityCollation extends Collation { } } - class IcuCollation extends Collation { + const FIRST_LETTER_VERSION = 1; + var $primaryCollator, $mainCollator, $locale; var $firstLetterData; /** * Unified CJK blocks. * - * The same definition of a CJK block must be used for both Collation and - * generateCollationData.php. These blocks are omitted from the first - * letter data, as an optimisation measure and because the default UCA table - * is pretty useless for sorting Chinese text anyway. Japanese and Korean + * The same definition of a CJK block must be used for both Collation and + * generateCollationData.php. These blocks are omitted from the first + * letter data, as an optimisation measure and because the default UCA table + * is pretty useless for sorting Chinese text anyway. Japanese and Korean * blocks are not included here, because they are smaller and more useful. */ static $cjkBlocks = array( @@ -176,11 +181,105 @@ class IcuCollation extends Collation { array( 0x2F800, 0x2FA1F ), // CJK Compatibility Ideographs Supplement ); + /** + * Additional characters (or character groups) to be considered separate + * letters for given languages, or to be removed from the list of such + * letters (denoted by keys starting with '-'). + * + * These are additions to (or subtractions from) the data stored in the + * first-letters-root.ser file (which among others includes full basic latin, + * cyrillic and greek alphabets). + * + * "Separate letter" is a letter that would have a separate heading/section + * for it in a dictionary or a phone book in this language. This data isn't + * used for sorting (the ICU library handles that), only for deciding which + * characters (or character groups) to use as headings. + * + * Initially generated based on the primary level of Unicode collation + * tailorings available at http://developer.mimer.com/charts/tailorings.htm , + * later modified. + * + * Empty arrays are intended; this signifies that the data for the language is + * available and that there are, in fact, no additional letters to consider. + */ + static $tailoringFirstLetters = array( + // Verified by native speakers + 'be' => array( "Ё" ), + 'be-tarask' => array( "Ё" ), + 'en' => array(), + 'fi' => array( "Å", "Ä", "Ö" ), + 'hu' => array( "Cs", "Dz", "Dzs", "Gy", "Ly", "Ny", "Ö", "Sz", "Ty", "Ü", "Zs" ), + 'it' => array(), + 'pl' => array( "Ą", "Ć", "Ę", "Ł", "Ń", "Ó", "Ś", "Ź", "Ż" ), + 'pt' => array(), + 'ru' => array(), + 'sv' => array( "Å", "Ä", "Ö" ), + 'uk' => array( "Ґ", "Ь" ), + 'vi' => array( "Ă", "Â", "Đ", "Ê", "Ô", "Ơ", "Ư" ), + // Not verified, but likely correct + 'af' => array(), + 'ast' => array( "Ch", "Ll", "Ñ" ), + 'az' => array( "Ç", "Ə", "Ğ", "İ", "Ö", "Ş", "Ü" ), + 'bg' => array(), + 'br' => array( "Ch", "C'h" ), + 'bs' => array( "Č", "Ć", "Dž", "Đ", "Lj", "Nj", "Š", "Ž" ), + 'ca' => array(), + 'co' => array(), + 'cs' => array( "Č", "Ch", "Ř", "Š", "Ž" ), + 'cy' => array( "Ch", "Dd", "Ff", "Ng", "Ll", "Ph", "Rh", "Th" ), + 'da' => array( "Æ", "Ø", "Å" ), + 'de' => array(), + 'dsb' => array( "Č", "Ć", "Dź", "Ě", "Ch", "Ł", "Ń", "Ŕ", "Š", "Ś", "Ž", "Ź" ), + 'el' => array(), + 'eo' => array( "Ĉ", "Ĝ", "Ĥ", "Ĵ", "Ŝ", "Ŭ" ), + 'es' => array( "Ñ" ), + 'et' => array( "Š", "Ž", "Õ", "Ä", "Ö", "Ü" ), + 'eu' => array( "Ñ" ), + 'fo' => array( "Á", "Ð", "Í", "Ó", "Ú", "Ý", "Æ", "Ø", "Å" ), + 'fr' => array(), + 'fur' => array( "À", "Á", "Â", "È", "Ì", "Ò", "Ù" ), + 'fy' => array(), + 'ga' => array(), + 'gd' => array(), + 'gl' => array( "Ch", "Ll", "Ñ" ), + 'hr' => array( "Č", "Ć", "Dž", "Đ", "Lj", "Nj", "Š", "Ž" ), + 'hsb' => array( "Č", "Dź", "Ě", "Ch", "Ł", "Ń", "Ř", "Š", "Ć", "Ž" ), + 'is' => array( "Á", "Ð", "É", "Í", "Ó", "Ú", "Ý", "Þ", "Æ", "Ö", "Å" ), + 'kk' => array( "Ү", "І" ), + 'kl' => array( "Æ", "Ø", "Å" ), + 'ku' => array( "Ç", "Ê", "Î", "Ş", "Û" ), + 'ky' => array( "Ё" ), + 'la' => array(), + 'lb' => array(), + 'lt' => array( "Č", "Š", "Ž" ), + 'lv' => array( "Č", "Ģ", "Ķ", "Ļ", "Ņ", "Š", "Ž" ), + 'mk' => array(), + 'mo' => array( "Ă", "Â", "Î", "Ş", "Ţ" ), + 'mt' => array( "Ċ", "Ġ", "Għ", "Ħ", "Ż" ), + 'nl' => array(), + 'no' => array( "Æ", "Ø", "Å" ), + 'oc' => array(), + 'rm' => array(), + 'ro' => array( "Ă", "Â", "Î", "Ş", "Ţ" ), + 'rup' => array( "Ă", "Â", "Î", "Ľ", "Ń", "Ş", "Ţ" ), + 'sco' => array(), + 'sk' => array( "Ä", "Č", "Ch", "Ô", "Š", "Ž" ), + 'sl' => array( "Č", "Š", "Ž" ), + 'smn' => array( "Á", "Č", "Đ", "Ŋ", "Š", "Ŧ", "Ž", "Æ", "Ø", "Å", "Ä", "Ö" ), + 'sq' => array( "Ç", "Dh", "Ë", "Gj", "Ll", "Nj", "Rr", "Sh", "Th", "Xh", "Zh" ), + 'sr' => array(), + 'tk' => array( "Ç", "Ä", "Ž", "Ň", "Ö", "Ş", "Ü", "Ý" ), + 'tl' => array( "Ñ", "Ng" ), + 'tr' => array( "Ç", "Ğ", "İ", "Ö", "Ş", "Ü" ), + 'tt' => array( "Ә", "Ө", "Ү", "Җ", "Ң", "Һ" ), + 'uz' => array( "Ch", "G'", "Ng", "O'", "Sh" ), + ); + const RECORD_LENGTH = 14; function __construct( $locale ) { if ( !extension_loaded( 'intl' ) ) { - throw new MWException( 'An ICU collation was requested, ' . + throw new MWException( 'An ICU collation was requested, ' . 'but the intl extension is not available.' ); } $this->locale = $locale; @@ -218,8 +317,8 @@ class IcuCollation extends Collation { // Check for CJK $firstChar = mb_substr( $string, 0, 1, 'UTF-8' ); - if ( ord( $firstChar ) > 0x7f - && self::isCjk( utf8ToCodepoint( $firstChar ) ) ) + if ( ord( $firstChar ) > 0x7f + && self::isCjk( utf8ToCodepoint( $firstChar ) ) ) { return $firstChar; } @@ -249,25 +348,37 @@ class IcuCollation extends Collation { $cacheKey = wfMemcKey( 'first-letters', $this->locale ); $cacheEntry = $cache->get( $cacheKey ); - if ( $cacheEntry ) { + if ( $cacheEntry && isset( $cacheEntry['version'] ) + && $cacheEntry['version'] == self::FIRST_LETTER_VERSION ) + { $this->firstLetterData = $cacheEntry; return $this->firstLetterData; } // Generate data from serialized data file - $letters = wfGetPrecompiledData( "first-letters-{$this->locale}.ser" ); - if ( $letters === false ) { - throw new MWException( "MediaWiki does not support ICU locale " . - "\"{$this->locale}\"" ); + if ( isset ( self::$tailoringFirstLetters[$this->locale] ) ) { + $letters = wfGetPrecompiledData( "first-letters-root.ser" ); + // Append additional characters + $letters = array_merge( $letters, self::$tailoringFirstLetters[$this->locale] ); + // Remove unnecessary ones, if any + if ( isset( self::$tailoringFirstLetters[ '-' . $this->locale ] ) ) { + $letters = array_diff( $letters, self::$tailoringFirstLetters[ '-' . $this->locale ] ); + } + } else { + $letters = wfGetPrecompiledData( "first-letters-{$this->locale}.ser" ); + if ( $letters === false ) { + throw new MWException( "MediaWiki does not support ICU locale " . + "\"{$this->locale}\"" ); + } } // Sort the letters. // // It's impossible to have the precompiled data file properly sorted, - // because the sort order changes depending on ICU version. If the - // array is not properly sorted, the binary search will return random - // results. + // because the sort order changes depending on ICU version. If the + // array is not properly sorted, the binary search will return random + // results. // // We also take this opportunity to remove primary collisions. $letterMap = array(); @@ -284,9 +395,76 @@ class IcuCollation extends Collation { } } ksort( $letterMap, SORT_STRING ); + // Remove duplicate prefixes. Basically if something has a sortkey + // which is a prefix of some other sortkey, then it is an + // expansion and probably should not be considered a section + // header. + // + // For example 'þ' is sometimes sorted as if it is the letters + // 'th'. Other times it is its own primary element. Another + // example is '₨'. Sometimes its a currency symbol. Sometimes it + // is an 'R' followed by an 's'. + // + // Additionally an expanded element should always sort directly + // after its first element due to they way sortkeys work. + // + // UCA sortkey elements are of variable length but no collation + // element should be a prefix of some other element, so I think + // this is safe. See: + // * https://ssl.icu-project.org/repos/icu/icuhtml/trunk/design/collation/ICU_collation_design.htm + // * http://site.icu-project.org/design/collation/uca-weight-allocation + // + // Additionally, there is something called primary compression to + // worry about. Basically, if you have two primary elements that + // are more than one byte and both start with the same byte then + // the first byte is dropped on the second primary. Additionally + // either \x03 or \xFF may be added to mean that the next primary + // does not start with the first byte of the first primary. + // + // This shouldn't matter much, as the first primary is not + // changed, and that is what we are comparing against. + // + // tl;dr: This makes some assumptions about how icu implements + // collations. It seems incredibly unlikely these assumptions + // will change, but nonetheless they are assumptions. + + $prev = false; + $duplicatePrefixes = array(); + foreach( $letterMap as $key => $value ) { + // Remove terminator byte. Otherwise the prefix + // comparison will get hung up on that. + $trimmedKey = rtrim( $key, "\0" ); + if ( $prev === false || $prev === '' ) { + $prev = $trimmedKey; + // We don't yet have a collation element + // to compare against, so continue. + continue; + } + + // Due to the fact the array is sorted, we only have + // to compare with the element directly previous + // to the current element (skipping expansions). + // An element "X" will always sort directly + // before "XZ" (Unless we have "XY", but we + // do not update $prev in that case). + if ( substr( $trimmedKey, 0, strlen( $prev ) ) === $prev ) { + $duplicatePrefixes[] = $key; + // If this is an expansion, we don't want to + // compare the next element to this element, + // but to what is currently $prev + continue; + } + $prev = $trimmedKey; + } + foreach( $duplicatePrefixes as $badKey ) { + wfDebug( "Removing '{$letterMap[$badKey]}' from first letters." ); + unset( $letterMap[$badKey] ); + // This code assumes that unsetting does not change sort order. + } $data = array( 'chars' => array_values( $letterMap ), - 'keys' => array_keys( $letterMap ) + 'keys' => array_keys( $letterMap ), + 'version' => self::FIRST_LETTER_VERSION, ); // Reduce memory usage before caching @@ -320,23 +498,27 @@ class IcuCollation extends Collation { } /** - * Do a binary search, and return the index of the largest item that sorts + * Do a binary search, and return the index of the largest item that sorts * less than or equal to the target value. * - * @param $valueCallback array A function to call to get the value with + * @param array $valueCallback A function to call to get the value with * a given array index. - * @param $valueCount int The number of items accessible via $valueCallback, + * @param int $valueCount The number of items accessible via $valueCallback, * indexed from 0 to $valueCount - 1 - * @param $comparisonCallback array A callback to compare two values, returning + * @param array $comparisonCallback A callback to compare two values, returning * -1, 0 or 1 in the style of strcmp(). - * @param $target string The target value to find. + * @param string $target The target value to find. * * @return int|bool The item index of the lower bound, or false if the target value * sorts before all items. */ function findLowerBound( $valueCallback, $valueCount, $comparisonCallback, $target ) { + if ( $valueCount === 0 ) { + return false; + } + $min = 0; - $max = $valueCount - 1; + $max = $valueCount; do { $mid = $min + ( ( $max - $min ) >> 1 ); $item = call_user_func( $valueCallback, $mid ); @@ -351,12 +533,15 @@ class IcuCollation extends Collation { } } while ( $min < $max - 1 ); - if ( $min == 0 && $max == 0 && $comparison > 0 ) { - // Before the first item - return false; - } else { - return $min; + if ( $min == 0 ) { + $item = call_user_func( $valueCallback, $min ); + $comparison = call_user_func( $comparisonCallback, $target, $item ); + if ( $comparison < 0 ) { + // Before the first item + return false; + } } + return $min; } static function isCjk( $codepoint ) { @@ -367,5 +552,55 @@ class IcuCollation extends Collation { } return false; } -} + /** + * Return the version of ICU library used by PHP's intl extension, + * or false when the extension is not installed of the version + * can't be determined. + * + * The constant INTL_ICU_VERSION this function refers to isn't really + * documented. It is available since PHP 5.3.7 (see PHP bug 54561). + * This function will return false on older PHPs. + * + * @since 1.21 + * @return string|false + */ + static function getICUVersion() { + return defined( 'INTL_ICU_VERSION' ) ? INTL_ICU_VERSION : false; + } + + /** + * Return the version of Unicode appropriate for the version of ICU library + * currently in use, or false when it can't be determined. + * + * @since 1.21 + * @return string|false + */ + static function getUnicodeVersionForICU() { + $icuVersion = IcuCollation::getICUVersion(); + if ( !$icuVersion ) { + return false; + } + + $versionPrefix = substr( $icuVersion, 0, 3 ); + // Source: http://site.icu-project.org/download + $map = array( + '50.' => '6.2', + '49.' => '6.1', + '4.8' => '6.0', + '4.6' => '6.0', + '4.4' => '5.2', + '4.2' => '5.1', + '4.0' => '5.1', + '3.8' => '5.0', + '3.6' => '5.0', + '3.4' => '4.1', + ); + + if ( isset( $map[$versionPrefix] ) ) { + return $map[$versionPrefix]; + } else { + return false; + } + } +} diff --git a/includes/ConfEditor.php b/includes/ConfEditor.php index b68fc762..1d9ca921 100644 --- a/includes/ConfEditor.php +++ b/includes/ConfEditor.php @@ -66,7 +66,6 @@ class ConfEditor { */ var $stateStack; - /** * The path stack is a stack of associative arrays with the following elements: * name The name of top level of the path @@ -128,7 +127,7 @@ class ConfEditor { /** * Edit the text. Returns the edited text. - * @param $ops Array of operations. + * @param array $ops of operations. * * Operations are given as an associative array, with members: * type: One of delete, set, append or insert (required) @@ -159,6 +158,7 @@ class ConfEditor { * insert * Insert a new element at the start of the array. * + * @throws MWException * @return string */ public function edit( $ops ) { @@ -306,7 +306,7 @@ class ConfEditor { * setVar( $arr, 'foo/bar', 'baz', 3 ); will set * $arr['foo']['bar']['baz'] = 3; * @param $array array - * @param $path string slash-delimited path + * @param string $path slash-delimited path * @param $key mixed Key * @param $value mixed Value */ @@ -392,6 +392,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 * break, and any comments in between. + * @param $pathName + * @throws MWException * @return array */ function findDeletionRegion( $pathName ) { @@ -450,6 +452,8 @@ class ConfEditor { * or semicolon. * * The end position is the past-the-end (end + 1) value as per convention. + * @param $pathName + * @throws MWException * @return array */ function findValueRegion( $pathName ) { @@ -1055,6 +1059,7 @@ class ConfEditorParseError extends MWException { return "$line\n" .str_repeat( ' ', $this->colNum - 1 ) . "^\n"; } } + return ''; } } @@ -1089,4 +1094,3 @@ class ConfEditorToken { return $this->type == 'END'; } } - diff --git a/includes/Cookie.php b/includes/Cookie.php index 7984d63e..27a85072 100644 --- a/includes/Cookie.php +++ b/includes/Cookie.php @@ -40,14 +40,15 @@ class Cookie { /** * Sets a cookie. Used before a request to set up any individual - * cookies. Used internally after a request to parse the + * 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 + * @param string $value the value of the cookie + * @param array $attr possible key/values: + * expires A date string + * path The path this cookie is used on + * domain Domain this cookie is used on + * @throws MWException */ public function set( $value, $attr ) { $this->value = $value; @@ -83,8 +84,8 @@ class Cookie { * @todo fixme fails to detect 3-letter top-level domains * @todo fixme fails to detect 2-letter top-level domains for single-domain use (probably not a big problem in practice, but there are test cases) * - * @param $domain String: the domain to validate - * @param $originDomain String: (optional) the domain the cookie originates from + * @param string $domain the domain to validate + * @param string $originDomain (optional) the domain the cookie originates from * @return Boolean */ public static function validateCookieDomain( $domain, $originDomain = null ) { @@ -112,7 +113,7 @@ class Cookie { } // Don't allow cookies for "co.uk" or "gov.uk", etc, but allow "supermarket.uk" - if ( strrpos( $domain, "." ) - strlen( $domain ) == -3 ) { + 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; @@ -141,8 +142,8 @@ class Cookie { /** * 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. + * @param string $path the path that will be used. Required. + * @param string $domain the domain that will be used. Required. * @return String */ public function serializeToHttpRequest( $path, $domain ) { @@ -164,8 +165,8 @@ class Cookie { 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 ), + && substr( $this->domain, 0, 1 ) == '.' + && substr_compare( $domain, $this->domain, -strlen( $this->domain ), strlen( $this->domain ), true ) == 0 ) ) { return true; } @@ -193,7 +194,7 @@ class CookieJar { private $cookie = array(); /** - * Set a cookie in the cookie jar. Make sure only one cookie per-name exists. + * Set a cookie in the cookie jar. Make sure only one cookie per-name exists. * @see Cookie::set() */ public function setCookie ( $name, $value, $attr ) { @@ -231,7 +232,7 @@ class CookieJar { * Parse the content of an Set-Cookie HTTP Response header. * * @param $cookie String - * @param $domain String: cookie's domain + * @param string $domain cookie's domain * @return null */ public function parseCookieResponseHeader ( $cookie, $domain ) { diff --git a/includes/CryptRand.php b/includes/CryptRand.php index 858eebf2..d0305d8b 100644 --- a/includes/CryptRand.php +++ b/includes/CryptRand.php @@ -79,7 +79,7 @@ class MWCryptRand { // Include some information about the filesystem's current state in the random state $files = array(); - // We know this file is here so grab some info about ourself + // We know this file is here so grab some info about ourselves $files[] = __FILE__; // We must also have a parent folder, and with the usual file structure, a grandparent @@ -106,7 +106,11 @@ class MWCryptRand { } } // The absolute filename itself will differ from install to install so don't leave it out - $state .= realpath( $file ); + if( ( $path = realpath( $file ) ) !== false ) { + $state .= $path; + } else { + $state .= $file; + } $state .= implode( '', $stat ); } else { // The fact that the file isn't there is worth at least a @@ -144,7 +148,7 @@ class MWCryptRand { /** * Randomly hash data while mixing in clock drift data for randomness * - * @param $data string The data to randomly hash. + * @param string $data The data to randomly hash. * @return String The hashed bytes * @author Tim Starling */ @@ -158,7 +162,7 @@ class MWCryptRand { $buffer = str_repeat( ' ', $bufLength ); $bufPos = 0; - // Iterate for $duration seconds or at least $minIerations number of iterations + // Iterate for $duration seconds or at least $minIterations number of iterations $iterations = 0; $startTime = microtime( true ); $currentTime = $startTime; @@ -391,7 +395,7 @@ class MWCryptRand { // We hash the random state with more salt to avoid the state from leaking // out and being used to predict the /randomness/ that follows. if ( strlen( $buffer ) < $bytes ) { - wfDebug( __METHOD__ . ": Falling back to using a pseudo random state to generate randomness.\n" ); + wfDebug( __METHOD__ . ": Falling back to using a pseudo random state to generate randomness.\n" ); } while ( strlen( $buffer ) < $bytes ) { wfProfileIn( __METHOD__ . '-fallback' ); @@ -464,8 +468,8 @@ class MWCryptRand { * You can use MWCryptRand::wasStrong() if you wish to know if the source used * was cryptographically strong. * - * @param $bytes int the number of bytes of random data to generate - * @param $forceStrong bool Pass true if you want generate to prefer cryptographically + * @param int $bytes the number of bytes of random data to generate + * @param bool $forceStrong Pass true if you want generate to prefer cryptographically * strong sources of entropy even if reading from them may steal * more entropy from the system than optimal. * @return String Raw binary random data @@ -480,8 +484,8 @@ class MWCryptRand { * You can use MWCryptRand::wasStrong() if you wish to know if the source used * was cryptographically strong. * - * @param $chars int the number of hex chars of random data to generate - * @param $forceStrong bool Pass true if you want generate to prefer cryptographically + * @param int $chars the number of hex chars of random data to generate + * @param bool $forceStrong Pass true if you want generate to prefer cryptographically * strong sources of entropy even if reading from them may steal * more entropy from the system than optimal. * @return String Hexadecimal random data diff --git a/includes/DataUpdate.php b/includes/DataUpdate.php index 377b64c0..c1076b23 100644 --- a/includes/DataUpdate.php +++ b/includes/DataUpdate.php @@ -34,7 +34,7 @@ abstract class DataUpdate implements DeferrableUpdate { /** * Constructor */ - public function __construct( ) { + public function __construct() { # noop } @@ -67,15 +67,15 @@ abstract class DataUpdate implements DeferrableUpdate { * * This methods supports transactions logic by first calling beginTransaction() * on all updates in the array, then calling doUpdate() on each, and, if all goes well, - * then calling commitTransaction() on each update. If an error occurrs, - * rollbackTransaction() will be called on any update object that had beginTranscation() + * then calling commitTransaction() on each update. If an error occurs, + * rollbackTransaction() will be called on any update object that had beginTransaction() * called but not yet commitTransaction(). * * This allows for limited transactional logic across multiple backends for storing * secondary data. * - * @static - * @param $updates array a list of DataUpdate instances + * @param array $updates a list of DataUpdate instances + * @throws Exception|null */ public static function runUpdates( $updates ) { if ( empty( $updates ) ) return; # nothing to do diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index 710605ad..9d024c86 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -47,7 +47,7 @@ * This is not a valid entry point, perform no further processing unless * MEDIAWIKI is defined */ -if( !defined( 'MEDIAWIKI' ) ) { +if ( !defined( 'MEDIAWIKI' ) ) { echo "This file is part of MediaWiki and is not a valid entry point\n"; die( 1 ); } @@ -55,13 +55,19 @@ if( !defined( 'MEDIAWIKI' ) ) { /** * wgConf hold the site configuration. * Not used for much in a default install. + * @since 1.5 */ $wgConf = new SiteConfiguration; -/** MediaWiki version number */ -$wgVersion = '1.20.6'; +/** + * MediaWiki version number + * @since 1.2 + */ +$wgVersion = '1.21.1'; -/** Name of the site. It must be changed in LocalSettings.php */ +/** + * Name of the site. It must be changed in LocalSettings.php + */ $wgSitename = 'MediaWiki'; /** @@ -87,6 +93,7 @@ $wgServer = WebRequest::detectServer(); * Must be fully qualified, even if $wgServer is protocol-relative. * * Defaults to $wgServer, expanded to a fully qualified http:// URL if needed. + * @since 1.18 */ $wgCanonicalServer = false; @@ -104,7 +111,7 @@ $wgCanonicalServer = false; * Other paths will be set to defaults based on it unless they are directly * set in LocalSettings.php */ -$wgScriptPath = '/wiki'; +$wgScriptPath = '/wiki'; /** * Whether to support URLs like index.php/Page_title These often break when PHP @@ -121,11 +128,11 @@ $wgScriptPath = '/wiki'; * The default $wgArticlePath will be set based on this value at runtime, but if * you have customized it, having this incorrectly set to true can cause * redirect loops when "pretty URLs" are used. + * @since 1.2.1 */ -$wgUsePathInfo = - ( strpos( php_sapi_name(), 'cgi' ) === false ) && - ( strpos( php_sapi_name(), 'apache2filter' ) === false ) && - ( strpos( php_sapi_name(), 'isapi' ) === false ); +$wgUsePathInfo = ( strpos( PHP_SAPI, 'cgi' ) === false ) && + ( strpos( PHP_SAPI, 'apache2filter' ) === false ) && + ( strpos( PHP_SAPI, 'isapi' ) === false ); /** * The extension to append to script names by default. This can either be .php @@ -133,9 +140,9 @@ $wgUsePathInfo = * * Some hosting providers use PHP 4 for *.php files, and PHP 5 for *.php5. This * variable is provided to support those providers. + * @since 1.11 */ -$wgScriptExtension = '.php'; - +$wgScriptExtension = '.php'; /**@}*/ @@ -176,12 +183,14 @@ $wgRedirectScript = false; * The URL path to load.php. * * Defaults to "{$wgScriptPath}/load{$wgScriptExtension}". + * @since 1.17 */ $wgLoadScript = false; /** * The URL path of the skins directory. * Defaults to "{$wgScriptPath}/skins". + * @since 1.3 */ $wgStylePath = false; $wgStyleSheetPath = &$wgStylePath; @@ -189,6 +198,7 @@ $wgStyleSheetPath = &$wgStylePath; /** * The URL path of the skins directory. Should not point to an external domain. * Defaults to "{$wgScriptPath}/skins". + * @since 1.17 */ $wgLocalStylePath = false; @@ -202,6 +212,7 @@ $wgExtensionAssetsPath = false; /** * Filesystem stylesheets directory. * Defaults to "{$IP}/skins". + * @since 1.3 */ $wgStyleDirectory = false; @@ -239,12 +250,14 @@ $wgLogo = false; /** * The URL path of the shortcut icon. + * @since 1.6 */ $wgFavicon = '/favicon.ico'; /** * The URL path of the icon for iPhone and iPod Touch web app bookmarks. * Defaults to no icon. + * @since 1.12 */ $wgAppleTouchIcon = false; @@ -268,6 +281,7 @@ $wgTmpDirectory = false; /** * If set, this URL is added to the start of $wgUploadPath to form a complete * upload URL. + * @since 1.4 */ $wgUploadBaseUrl = ''; @@ -276,6 +290,7 @@ $wgUploadBaseUrl = ''; * Full thumbnail URL will be like $wgUploadStashScalerBaseUrl/e/e6/Foo.jpg/123px-Foo.jpg * where 'e6' are the first two characters of the MD5 hash of the file name. * If $wgUploadStashScalerBaseUrl is set to false, thumbs are rendered locally as needed. + * @since 1.17 */ $wgUploadStashScalerBaseUrl = false; @@ -291,6 +306,7 @@ $wgUploadStashScalerBaseUrl = false; * * There must be an appropriate script or rewrite rule in place to handle these * URLs. + * @since 1.5 */ $wgActionPaths = array(); @@ -312,6 +328,20 @@ $wgUploadStashMaxAge = 6 * 3600; // 6 hours /** Allows to move images and other media files */ $wgAllowImageMoving = true; +/** + * Enable deferred upload tasks that use the job queue. + * Only enable this if job runners are set up for both the + * 'AssembleUploadChunks' and 'PublishStashedFile' job types. + */ +$wgEnableAsyncUploads = false; + +/** + * Allow chunked uploads. This should only really be needed if you + * use the UploadWizard extension or allow huge file uploads. + * https://www.mediawiki.org/wiki/API:Upload#Chunked_uploading + */ +$wgAllowChunkedUploads = false; + /** * These are additional characters that should be replaced with '-' in filenames */ @@ -353,7 +383,7 @@ $wgImgAuthPublicTest = true; * FSRepo is also supported for backwards compatibility. * * - name A unique name for the repository (but $wgLocalFileRepo should be 'local'). - * The name should consist of alpha-numberic characters. + * The name should consist of alpha-numeric characters. * - backend A file backend name (see $wgFileBackends). * * For most core repos: @@ -361,7 +391,9 @@ $wgImgAuthPublicTest = true; * container : backend container name the zone is in * directory : root path within container for the zone * url : base URL to the root of the zone - * handlerUrl : base script handled URL to the root of the zone + * urlsByExt : map of file extension types to base URLs + * (useful for using a different cache for videos) + * handlerUrl : base script-handled URL to the root of the zone * (see FileRepo::getZoneHandlerUrl() function) * Zones default to using "-" as the container name * and default to using the container root as the zone's root directory. @@ -405,7 +437,7 @@ $wgImgAuthPublicTest = true; * * ForeignDBRepo: * - dbType, dbServer, dbUser, dbPassword, dbName, dbFlags - * equivalent to the corresponding member of $wgDBservers + * equivalent to the corresponding member of $wgDBservers * - tablePrefix Table prefix, the foreign wiki's $wgDBprefix * - hasSharedCache True if the wiki's shared cache is accessible via the local $wgMemc * @@ -416,7 +448,7 @@ $wgImgAuthPublicTest = true; * 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 + * If you set $wgForeignFileRepos to an array of repository structures, those will * be searched after the local file repo. * Otherwise, you will only have access to local media files. * @@ -436,14 +468,34 @@ $wgUseInstantCommons = false; /** * File backend structure configuration. + * * This is an array of file backend configuration arrays. * Each backend configuration has the following parameters: - * - 'name' : A unique name for the backend - * - 'class' : The file backend class to use - * - 'wikiId' : A unique string that identifies the wiki (container prefix) - * - 'lockManager' : The name of a lock manager (see $wgLockManagers) - * - * Additional parameters are specific to the class used. + * - 'name' : A unique name for the backend + * - 'class' : The file backend class to use + * - 'wikiId' : A unique string that identifies the wiki (container prefix) + * - 'lockManager' : The name of a lock manager (see $wgLockManagers) + * + * See FileBackend::__construct() for more details. + * Additional parameters are specific to the file backend class used. + * These settings should be global to all wikis when possible. + * + * There are two particularly important aspects about each backend: + * - a) Whether it is fully qualified or wiki-relative. + * By default, the paths of files are relative to the current wiki, + * which works via prefixing them with the current wiki ID when accessed. + * Setting 'wikiId' forces the backend to be fully qualified by prefixing + * all paths with the specified value instead. This can be useful if + * multiple wikis need to share the same data. Note that 'name' is *not* + * part of any prefix and thus should not be relied upon for namespacing. + * - b) Whether it is only defined for some wikis or is defined on all + * wikis in the wiki farm. Defining a backend globally is useful + * if multiple wikis need to share the same data. + * One should be aware of these aspects when configuring a backend for use with + * any basic feature or plugin. For example, suppose an extension stores data for + * different wikis in different directories and sometimes needs to access data from + * a foreign wiki's directory in order to render a page on given wiki. The extension + * would need a fully qualified backend that is defined on all wikis in the wiki farm. */ $wgFileBackends = array(); @@ -452,7 +504,10 @@ $wgFileBackends = array(); * Each backend configuration has the following parameters: * - 'name' : A unique name for the lock manager * - 'class' : The lock manger class to use - * Additional parameters are specific to the class used. + * + * See LockManager::__construct() for more details. + * Additional parameters are specific to the lock manager class used. + * These settings should be global to all wikis. */ $wgLockManagers = array(); @@ -530,6 +585,13 @@ $wgAllowAsyncCopyUploads = false; */ $wgCopyUploadsDomains = array(); +/** + * Enable copy uploads from Special:Upload. $wgAllowCopyUploads must also be + * true. If $wgAllowCopyUploads is true, but this is false, you will only be + * able to perform copy uploads from the API or extensions (e.g. UploadWizard). + */ +$wgCopyUploadsFromSpecialUpload = false; + /** * Proxy to use for copy upload requests. * @since 1.20 @@ -553,7 +615,7 @@ $wgCopyUploadProxy = false; * will have a maximum of 500 kB. * */ -$wgMaxUploadSize = 1024*1024*100; # 100MB +$wgMaxUploadSize = 1024 * 1024 * 100; # 100MB /** * Point the upload navigation link to an external URL @@ -654,7 +716,7 @@ $wgFileBlacklist = array( */ $wgMimeTypeBlacklist = array( # HTML may contain cookie-stealing JavaScript and web bugs - 'text/html', 'text/javascript', 'text/x-javascript', 'application/x-shellscript', + 'text/html', 'text/javascript', 'text/x-javascript', 'application/x-shellscript', # PHP scripts may execute arbitrary code on the server 'application/x-php', 'text/x-php', # Other types that may be interpreted by some servers @@ -712,10 +774,10 @@ $wgUploadSizeWarning = false; */ $wgTrustedMediaFormats = array( MEDIATYPE_BITMAP, //all bitmap formats - MEDIATYPE_AUDIO, //all audio formats - MEDIATYPE_VIDEO, //all plain video formats - "image/svg+xml", //svg (only needed if inline rendering of svg is not supported) - "application/pdf", //PDF files + MEDIATYPE_AUDIO, //all audio formats + MEDIATYPE_VIDEO, //all plain video formats + "image/svg+xml", //svg (only needed if inline rendering of svg is not supported) + "application/pdf", //PDF files #"application/x-shockwave-flash", //flash/shockwave movie ); @@ -724,18 +786,35 @@ $wgTrustedMediaFormats = array( * Each entry in the array maps a MIME type to a class name */ $wgMediaHandlers = array( - 'image/jpeg' => 'JpegHandler', - 'image/png' => 'PNGHandler', - 'image/gif' => 'GIFHandler', - 'image/tiff' => 'TiffHandler', + 'image/jpeg' => 'JpegHandler', + 'image/png' => 'PNGHandler', + 'image/gif' => 'GIFHandler', + 'image/tiff' => 'TiffHandler', 'image/x-ms-bmp' => 'BmpHandler', - 'image/x-bmp' => 'BmpHandler', - 'image/x-xcf' => 'XCFHandler', - 'image/svg+xml' => 'SvgHandler', // official - 'image/svg' => 'SvgHandler', // compat + 'image/x-bmp' => 'BmpHandler', + 'image/x-xcf' => 'XCFHandler', + 'image/svg+xml' => 'SvgHandler', // official + 'image/svg' => 'SvgHandler', // compat 'image/vnd.djvu' => 'DjVuHandler', // official - 'image/x.djvu' => 'DjVuHandler', // compat - 'image/x-djvu' => 'DjVuHandler', // compat + 'image/x.djvu' => 'DjVuHandler', // compat + 'image/x-djvu' => 'DjVuHandler', // compat +); + +/** + * Plugins for page content model handling. + * Each entry in the array maps a model id to a class name. + * + * @since 1.21 + */ +$wgContentHandlers = array( + // the usual case + CONTENT_MODEL_WIKITEXT => 'WikitextContentHandler', + // dumb version, no syntax highlighting + CONTENT_MODEL_JAVASCRIPT => 'JavaScriptContentHandler', + // dumb version, no syntax highlighting + CONTENT_MODEL_CSS => 'CssContentHandler', + // plain text, for use by extensions etc + CONTENT_MODEL_TEXT => 'TextContentHandler', ); /** @@ -778,6 +857,13 @@ $wgImageMagickTempDir = false; */ $wgCustomConvertCommand = false; +/** used for lossless jpeg rotation + * + * @since 1.21 + * **/ +$wgJpegTran = '/usr/bin/jpegtran'; + + /** * Some tests and extensions use exiv2 to manipulate the EXIF metadata in some * image formats. @@ -798,10 +884,10 @@ $wgSVGConverters = array( 'sodipodi' => '$path/sodipodi -z -w $width -f $input -e $output', 'inkscape' => '$path/inkscape -z -w $width -f $input -e $output', 'batik' => 'java -Djava.awt.headless=true -jar $path/batik-rasterizer.jar -w $width -d $output $input', - 'rsvg' => '$path/rsvg -w$width -h$height $input $output', + 'rsvg' => '$path/rsvg -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'; @@ -864,7 +950,7 @@ $wgMaxAnimatedGifArea = 1.25e7; * $wgTiffThumbnailType = array( 'jpg', 'image/jpeg' ); * @endcode */ - $wgTiffThumbnailType = false; +$wgTiffThumbnailType = false; /** * If rendered thumbnail files are older than this timestamp, they @@ -897,8 +983,8 @@ $wgIgnoreImageErrors = false; $wgGenerateThumbnailOnParse = true; /** -* Show thumbnails for old images on the image description page -*/ + * Show thumbnails for old images on the image description page + */ $wgShowArchiveThumbnails = true; /** Obsolete, always true, kept for compatibility with extensions */ @@ -912,7 +998,7 @@ $wgUseImageResize = true; $wgEnableAutoRotation = null; /** - * Internal name of virus scanner. This servers as a key to the + * Internal name of virus scanner. This serves as a key to the * $wgAntivirusSetup array. Set this to NULL to disable virus scanning. If not * null, every file uploaded will be scanned for viruses. */ @@ -930,7 +1016,7 @@ $wgAntivirus = null; * "command" is the full command to call the virus scanner - %f will be * replaced with the name of the file to scan. If not present, the filename * will be appended to the command. Note that this must be overwritten if the - * scanner is not in the system path; in that case, plase set + * scanner is not in the system path; in that case, please set * $wgAntivirusSetup[$wgAntivirus]['command'] to the desired command with full * path. * @@ -940,8 +1026,8 @@ $wgAntivirus = null; * the scan to be failed. This will pass the file if $wgAntivirusRequired * is not set. * - An exit code mapped to AV_SCAN_ABORTED causes the function to consider - * the file to have an usupported format, which is probably imune to - * virusses. This causes the file to pass. + * the file to have an unsupported format, which is probably immune to + * viruses. This causes the file to pass. * - An exit code mapped to AV_NO_VIRUS will cause the file to pass, meaning * no virus was found. * - All other codes (like AV_VIRUS_FOUND) will cause the function to report @@ -955,21 +1041,18 @@ $wgAntivirus = null; $wgAntivirusSetup = array( #setup for clamav - 'clamav' => array ( - 'command' => "clamscan --no-summary ", - - 'codemap' => array ( - "0" => AV_NO_VIRUS, # no virus - "1" => AV_VIRUS_FOUND, # virus found - "52" => AV_SCAN_ABORTED, # unsupported file format (probably imune) - "*" => AV_SCAN_FAILED, # else scan failed + 'clamav' => array( + 'command' => 'clamscan --no-summary ', + 'codemap' => array( + "0" => AV_NO_VIRUS, # no virus + "1" => AV_VIRUS_FOUND, # virus found + "52" => AV_SCAN_ABORTED, # unsupported file format (probably immune) + "*" => AV_SCAN_FAILED, # else scan failed ), - 'messagepattern' => '/.*?:(.*)/sim', ), ); - /** Determines if a failed virus scan (AV_SCAN_FAILED) will cause the file to be rejected. */ $wgAntivirusRequired = true; @@ -977,13 +1060,13 @@ $wgAntivirusRequired = true; $wgVerifyMimeType = true; /** Sets the mime type definition file to use by MimeMagic.php. */ -$wgMimeTypeFile = "includes/mime.types"; -#$wgMimeTypeFile= "/etc/mime.types"; -#$wgMimeTypeFile= null; #use built-in defaults only. +$wgMimeTypeFile = 'includes/mime.types'; +#$wgMimeTypeFile = '/etc/mime.types'; +#$wgMimeTypeFile = null; #use built-in defaults only. /** Sets the mime type info file to use by MimeMagic.php. */ -$wgMimeInfoFile= "includes/mime.info"; -#$wgMimeInfoFile= null; #use built-in defaults only. +$wgMimeInfoFile = 'includes/mime.info'; +#$wgMimeInfoFile = null; #use built-in defaults only. /** * Switch for loading the FileInfo extension by PECL at runtime. @@ -1016,11 +1099,11 @@ $wgTrivialMimeDetection = false; * array = ( 'rootElement' => 'associatedMimeType' ) */ $wgXMLMimeTypes = array( - 'http://www.w3.org/2000/svg:svg' => 'image/svg+xml', - 'svg' => 'image/svg+xml', + 'http://www.w3.org/2000/svg:svg' => 'image/svg+xml', + 'svg' => 'image/svg+xml', 'http://www.lysator.liu.se/~alla/dia/:diagram' => 'application/x-dia-diagram', - 'http://www.w3.org/1999/xhtml:html' => 'text/html', // application/xhtml+xml? - 'html' => 'text/html', // application/xhtml+xml? + 'http://www.w3.org/1999/xhtml:html' => 'text/html', // application/xhtml+xml? + 'html' => 'text/html', // application/xhtml+xml? ); /** @@ -1056,7 +1139,7 @@ $wgThumbLimits = array( /** * Default parameters for the "" tag */ -$wgGalleryOptions = array ( +$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") @@ -1076,6 +1159,16 @@ $wgThumbUpright = 0.75; */ $wgDirectoryMode = 0777; +/** + * Generate and use thumbnails suitable for screens with 1.5 and 2.0 pixel densities. + * + * This means a 320x240 use of an image on the wiki will also generate 480x360 and 640x480 + * thumbnails, output via data-src-1-5 and data-src-2-0. Runtime JavaScript switches the + * images in after loading the original low-resolution versions depending on the reported + * window.devicePixelRatio. + */ +$wgResponsiveImages = true; + /** * @name DJVU settings * @{ @@ -1182,7 +1275,7 @@ $wgEnableUserEmail = true; * instead of From. ($wgEmergencyContact will be used as From.) * * Some mailers (eg sSMTP) set the SMTP envelope sender to the From value, - * which can cause problems with SPF validation and leak recipient addressses + * which can cause problems with SPF validation and leak recipient addresses * when bounces are sent to the sender. */ $wgUserEmailUseReplyTo = false; @@ -1211,12 +1304,12 @@ $wgUserEmailConfirmationTokenExpiry = 7 * 24 * 60 * 60; * * @code * $wgSMTP = array( - * 'host' => 'SMTP domain', - * 'IDHost' => 'domain for MessageID', - * 'port' => '25', - * 'auth' => [true|false], - * 'username' => [SMTP username], - * 'password' => [SMTP password], + * 'host' => 'SMTP domain', + * 'IDHost' => 'domain for MessageID', + * 'port' => '25', + * 'auth' => [true|false], + * 'username' => [SMTP username], + * 'password' => [SMTP password], * ); * @endcode */ @@ -1228,6 +1321,12 @@ $wgSMTP = false; */ $wgAdditionalMailParams = null; +/** + * For parts of the system that have been updated to provide HTML email content, send + * both text and HTML parts as the body of the email + */ +$wgAllowHTMLEmail = false; + /** * True: from page editor if s/he opted-in. False: Enotif mails appear to come * from $wgEmergencyContact @@ -1375,12 +1474,16 @@ $wgAllDBsAreLocalhost = false; * preferences shared (preferences were stored in the user table prior to 1.16) * * $wgSharedTables may be customized with a list of tables to share in the shared - * datbase. However it is advised to limit what tables you do share as many of + * database. However it is advised to limit what tables you do share as many of * MediaWiki's tables may have side effects if you try to share them. - * EXPERIMENTAL * * $wgSharedPrefix is the table prefix for the shared database. It defaults to * $wgDBprefix. + * + * @deprecated In new code, use the $wiki parameter to wfGetLB() to access + * remote databases. Using wfGetLB() allows the shared database to reside on + * separate servers to the wiki's own database, with suitable configuration + * of $wgLBFactoryConf. */ $wgSharedDB = null; @@ -1459,7 +1562,7 @@ $wgDBerrorLog = false; * Timezone to use in the error log. * Defaults to the wiki timezone ($wgLocaltimezone). * - * A list of useable timezones can found at: + * A list of usable timezones can found at: * http://php.net/manual/en/timezones.php * * @par Examples: @@ -1526,7 +1629,6 @@ $wgOldChangeTagsIndex = false; /**@}*/ # End of DB settings } - /************************************************************************//** * @name Text storage * @{ @@ -1560,7 +1662,7 @@ $wgExternalStores = false; * Create a cluster named 'cluster1' containing three servers: * @code * $wgExternalServers = array( - * 'cluster1' => array( 'srv28', 'srv29', 'srv30' ) + * 'cluster1' => array( 'srv28', 'srv29', 'srv30' ) * ); * @endcode * @@ -1758,13 +1860,13 @@ $wgDBAhandler = 'db3'; /** * Deprecated alias for $wgSessionsInObjectCache. * - * @deprecated Use $wgSessionsInObjectCache + * @deprecated since 1.20; Use $wgSessionsInObjectCache */ $wgSessionsInMemcached = false; /** * Store sessions in an object cache, configured by $wgSessionCacheType. This - * can be useful to improve performance, or to avoid the locking behaviour of + * can be useful to improve performance, or to avoid the locking behavior of * PHP's default session handler, which tends to prevent multiple requests for * the same user from acting concurrently. */ @@ -1815,9 +1917,10 @@ $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). + * Instead of caching everything, only cache those messages which have + * been customised in the site content language. This means that + * MediaWiki:Foo/ja is ignored if MediaWiki:Foo doesn't exist. + * This option is probably only useful for translatewiki.net. */ $wgAdaptiveMessageCache = false; @@ -2031,6 +2134,27 @@ $wgSquidServersNoPurge = array(); /** Maximum number of titles to purge in any one client operation */ $wgMaxSquidPurgeTitles = 400; +/** + * Whether to use a Host header in purge requests sent to the proxy servers + * configured in $wgSquidServers. Set this to false to support Squid + * configured in forward-proxy mode. + * + * If this is set to true, a Host header will be sent, and only the path + * component of the URL will appear on the request line, as if the request + * were a non-proxy HTTP 1.1 request. Varnish only supports this style of + * request. Squid supports this style of request only if reverse-proxy mode + * (http_port ... accel) is enabled. + * + * If this is set to false, no Host header will be sent, and the absolute URL + * will be sent in the request line, as is the standard for an HTTP proxy + * request in both HTTP 1.0 and 1.1. This style of request is not supported + * by Varnish, but is supported by Squid in either configuration (forward or + * reverse). + * + * @since 1.21 + */ +$wgSquidPurgeUseHostHeader = true; + /** * Routing configuration for HTCP multicast purging. Add elements here to * enable HTCP and determine which purges are sent where. If set to an empty @@ -2073,13 +2197,13 @@ $wgHTCPMulticastRouting = array(); * setting is ignored. If $wgHTCPMulticastRouting is not set and this setting * is, it is used to populate $wgHTCPMulticastRouting. * - * @deprecated in favor of $wgHTCPMulticastRouting + * @deprecated since 1.20 in favor of $wgHTCPMulticastRouting */ $wgHTCPMulticastAddress = false; /** * HTCP multicast port. - * @deprecated in favor of $wgHTCPMulticastRouting + * @deprecated since 1.20 in favor of $wgHTCPMulticastRouting * @see $wgHTCPMulticastAddress */ $wgHTCPPort = 4827; @@ -2100,9 +2224,29 @@ $wgUsePrivateIPs = false; * @{ */ -/** Site language code, should be one of ./languages/Language(.*).php */ +/** + * Site language code. See languages/Names.php for languages supported by + * MediaWiki out of the box. Not all languages listed there have translations, + * see languages/messages/ for the list of languages with some localisation. + * + * Warning: Don't use language codes listed in $wgDummyLanguageCodes like "no" + * for Norwegian (use "nb" instead), or things will break unexpectedly. + * + * This defines the default interface language for all users, but users can + * change it in their preferences. + * + * This also defines the language of pages in the wiki. The content is wrapped + * in a html element with lang=XX attribute. This behavior can be overridden + * via hooks, see Title::getPageLanguage. + */ $wgLanguageCode = 'en'; +/** + * Language cache size, or really how many languages can we handle + * simultaneously without degrading to crawl speed. + */ +$wgLangObjCacheSize = 10; + /** * Some languages need different word forms, usually for different cases. * Used in Language::convertGrammar(). @@ -2125,7 +2269,7 @@ $wgExtraLanguageNames = array(); /** * List of language codes that don't correspond to an actual language. - * These codes are mostly leftoffs from renames, or other legacy things. + * These codes are mostly left-offs from renames, or other legacy things. * This array makes them not appear as a selectable language on the installer, * and excludes them when running the transstat.php script. */ @@ -2249,24 +2393,16 @@ $wgBrowserBlackList = array( * requires that the cur table be kept around for those revisions * to remain viewable. * - * maintenance/migrateCurStubs.php can be used to complete the - * migration in the background once the wiki is back online. - * * This option affects the updaters *only*. Any present cur stub * revisions will be readable at runtime regardless of this setting. */ $wgLegacySchemaConversion = false; -/** - * Enable to allow rewriting dates in page text. - * DOES NOT FORMAT CORRECTLY FOR MOST LANGUAGES. - */ -$wgUseDynamicDates = false; /** * Enable dates like 'May 12' instead of '12 May', this only takes effect if * the interface is set to English. */ -$wgAmericanDates = false; +$wgAmericanDates = false; /** * For Hindi and Arabic use local numerals instead of Western style (0-9) * numerals in interface. @@ -2295,7 +2431,7 @@ $wgDisableLangConversion = false; /** Whether to enable language variant conversion for links. */ $wgDisableTitleConversion = false; -/** Whether to enable cononical language links in meta data. */ +/** Whether to enable canonical language links in meta data. */ $wgCanonicalLanguageLinks = true; /** Default variant code, if false, the default will be the language code */ @@ -2319,9 +2455,9 @@ $wgDisabledVariants = array(); * * @par Example: * @code - * $wgLanguageCode = 'sr'; - * $wgVariantArticlePath = '/$2/$1'; - * $wgArticlePath = '/wiki/$1'; + * $wgLanguageCode = 'sr'; + * $wgVariantArticlePath = '/$2/$1'; + * $wgArticlePath = '/wiki/$1'; * @endcode * * A link to /wiki/ would be redirected to /sr/Главна_страна @@ -2354,7 +2490,7 @@ $wgLoginLanguageSelector = false; * To allow language-specific main page and community * portal: * @code - * $wgForceUIMsgAsContentMsg = array( 'mainpage', 'portal-url' ); + * $wgForceUIMsgAsContentMsg = array( 'mainpage', 'portal-url' ); * @endcode */ $wgForceUIMsgAsContentMsg = array(); @@ -2370,7 +2506,7 @@ $wgForceUIMsgAsContentMsg = array(); * Timezones can be translated by editing MediaWiki messages of type * timezone-nameinlowercase like timezone-utc. * - * A list of useable timezones can found at: + * A list of usable timezones can found at: * http://php.net/manual/en/timezones.php * * @par Examples: @@ -2389,23 +2525,12 @@ $wgLocaltimezone = null; * for anonymous users and new user accounts. * * 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. + * overridable in user preferences. It is *not* used for signature timestamps. * * By default, this will be set to match $wgLocaltimezone. */ $wgLocalTZoffset = null; -/** - * If set to true, this will roll back a few bug fixes introduced in 1.19, - * emulating the 1.18 behaviour, to avoid introducing bug 34832. In 1.19, - * language variant conversion is disabled in interface messages. Setting this - * to true re-enables it. - * - * @todo This variable should be removed (implicitly false) in 1.20 or earlier. - */ -$wgBug34832TransitionalRollback = true; - - /** @} */ # End of language/charset settings /*************************************************************************//** @@ -2499,7 +2624,7 @@ $wgWellFormedXml = true; * @par Example: * @code * $wgXhtmlNamespaces['svg'] = 'http://www.w3.org/2000/svg'; - * @endCode + * @endcode * Normally we wouldn't have to define this in the root "" * element, but IE needs it there in some circumstances. * @@ -2527,12 +2652,12 @@ $wgSiteNotice = ''; /** * A subtitle to add to the tagline, for skins that have it/ */ -$wgExtraSubtitle = ''; +$wgExtraSubtitle = ''; /** * If this is set, a "donate" link will appear in the sidebar. Set it to a URL. */ -$wgSiteSupportPage = ''; +$wgSiteSupportPage = ''; /** * Validate the overall output using tidy and refuse @@ -2684,7 +2809,7 @@ $wgExperimentalHtmlIds = false; * for the icon, the following keys are used: * - src: An absolute url to the image to use for the icon, this is recommended * but not required, however some skins will ignore icons without an image - * - url: The url to use in the a element arround the text or icon, if not set an a element will not be outputted + * - url: The url to use in the a element around the text or icon, if not set an a element will not be outputted * - alt: This is the text form of the icon, it will be displayed without an image in * skins like Modern or if src is not set, and will otherwise be used as * the alt="" for the image. This key is required. @@ -2719,7 +2844,7 @@ $wgUseCombinedLoginLink = false; * - true = use an icon search button * - false = use Go & Search buttons */ -$wgVectorUseSimpleSearch = false; +$wgVectorUseSimpleSearch = true; /** * Watch and unwatch as an icon rather than a link for Vector skin only. @@ -2754,16 +2879,24 @@ $wgBetterDirectionality = true; */ $wgSend404Code = true; - /** * The $wgShowRollbackEditCount variable is used to show how many edits will be - * rollback. The numeric value of the varible are the limit up to are counted. - * If the value is false or 0, the edits are not counted. + * rollback. The numeric value of the variable are the limit up to are counted. + * If the value is false or 0, the edits are not counted. Disabling this will + * furthermore prevent MediaWiki from hiding some useless rollback links. * * @since 1.20 */ $wgShowRollbackEditCount = 10; +/** + * Output a tag on every page indicating the canonical + * server which should be used, i.e. $wgServer or $wgCanonicalServer. Since + * detection of the current server is unreliable, the link is sent + * unconditionally. + */ +$wgEnableCanonicalServerLink = false; + /** @} */ # End of output format settings } /*************************************************************************//** @@ -2890,21 +3023,21 @@ $wgPreloadJavaScriptMwUtil = false; * * @par Example of legacy code: * @code{,js} - * if ( window.wgRestrictionEdit ) { ... } + * if ( window.wgRestrictionEdit ) { ... } * @endcode * or: * @code{,js} - * if ( wgIsArticle ) { ... } + * if ( wgIsArticle ) { ... } * @endcode * * Instead, one needs to use mw.config. * @par Example using mw.config global configuration: * @code{,js} - * if ( mw.config.exists('wgRestrictionEdit') ) { ... } + * if ( mw.config.exists('wgRestrictionEdit') ) { ... } * @endcode * or: * @code{,js} - * if ( mw.config.get('wgIsArticle') ) { ... } + * if ( mw.config.get('wgIsArticle') ) { ... } * @endcode */ $wgLegacyJavaScriptGlobals = true; @@ -2950,7 +3083,6 @@ $wgResourceLoaderExperimentalAsyncLoading = false; /** @} */ # End of resource loader settings } - /*************************************************************************//** * @name Page title and interwiki link settings * @{ @@ -3085,13 +3217,13 @@ $wgInterwikiExpiry = 10800; $wgInterwikiCache = false; /** * Specify number of domains to check for messages. - * - 1: Just wiki(db)-level - * - 2: wiki and global levels - * - 3: site levels + * - 1: Just wiki(db)-level + * - 2: wiki and global levels + * - 3: site levels */ $wgInterwikiScopes = 3; /** - * $wgInterwikiFallbackSite - if unable to resolve from cache + * $wgInterwikiFallbackSite - if unable to resolve from cache */ $wgInterwikiFallbackSite = 'wiki'; /** @} */ # end of Interwiki caching settings. @@ -3133,7 +3265,7 @@ $wgCapitalLinks = true; * * @par Example: * @code - * $wgCapitalLinkOverrides[ NS_FILE ] = false; + * $wgCapitalLinkOverrides[ NS_FILE ] = false; * @endcode */ $wgCapitalLinkOverrides = array(); @@ -3142,16 +3274,18 @@ $wgCapitalLinkOverrides = array(); * See Language.php for a list of namespaces. */ $wgNamespacesWithSubpages = array( - NS_TALK => true, - NS_USER => true, - NS_USER_TALK => true, - NS_PROJECT_TALK => true, - NS_FILE_TALK => true, - NS_MEDIAWIKI => true, + NS_TALK => true, + NS_USER => true, + NS_USER_TALK => true, + NS_PROJECT => true, + NS_PROJECT_TALK => true, + NS_FILE_TALK => true, + NS_MEDIAWIKI => true, NS_MEDIAWIKI_TALK => true, - NS_TEMPLATE_TALK => true, - NS_HELP_TALK => true, - NS_CATEGORY_TALK => true + NS_TEMPLATE_TALK => true, + NS_HELP => true, + NS_HELP_TALK => true, + NS_CATEGORY_TALK => true ); /** @@ -3195,7 +3329,7 @@ $wgInvalidRedirectTargets = array( 'Filepath', 'Mypage', 'Mytalk' ); * class The class name * * preprocessorClass The preprocessor class. Two classes are currently available: - * Preprocessor_Hash, which uses plain PHP arrays for tempoarary + * Preprocessor_Hash, which uses plain PHP arrays for temporary * storage, and Preprocessor_DOM, which uses the DOM module for * temporary storage. Preprocessor_DOM generally uses less memory; * the speed of the two is roughly the same. @@ -3225,12 +3359,16 @@ $wgMaxTocLevel = 999; $wgMaxPPNodeCount = 1000000; /** - * A complexity limit on template expansion: the maximum number of nodes - * generated by Preprocessor::preprocessToObj() + * A complexity limit on template expansion: the maximum number of elements + * generated by Preprocessor::preprocessToObj(). This allows you to limit the + * amount of memory used by the Preprocessor_DOM node cache: testing indicates + * that each element uses about 160 bytes of memory on a 64-bit processor, so + * this default corresponds to about 155 MB. + * + * When the limit is exceeded, an exception is thrown. */ $wgMaxGeneratedPPNodeCount = 1000000; - /** * Maximum recursion depth for templates within templates. * The current parser adds two levels to the PHP call stack for each template, @@ -3248,7 +3386,7 @@ $wgUrlProtocols = array( 'https://', 'ftp://', 'irc://', - 'ircs://', // @bug 28503 + 'ircs://', // @bug 28503 'gopher://', 'telnet://', // Well if we're going to support the above.. -ævar 'nntp://', // @bug 3808 RFC 1738 @@ -3324,7 +3462,7 @@ $wgAlwaysUseTidy = false; /** @see $wgUseTidy */ $wgTidyBin = 'tidy'; /** @see $wgUseTidy */ -$wgTidyConf = $IP.'/includes/tidy.conf'; +$wgTidyConf = $IP . '/includes/tidy.conf'; /** @see $wgUseTidy */ $wgTidyOpts = ''; /** @see $wgUseTidy */ @@ -3520,77 +3658,70 @@ $wgReservedUsernames = array( * */ $wgDefaultUserOptions = array( - 'ccmeonemails' => 0, - 'cols' => 80, - 'date' => 'default', - 'diffonly' => 0, - 'disablemail' => 0, - 'disablesuggest' => 0, - 'editfont' => 'default', - 'editondblclick' => 0, - 'editsection' => 1, + 'ccmeonemails' => 0, + 'cols' => 80, + 'date' => 'default', + 'diffonly' => 0, + 'disablemail' => 0, + 'disablesuggest' => 0, + 'editfont' => 'default', + 'editondblclick' => 0, + 'editsection' => 1, 'editsectiononrightclick' => 0, - 'enotifminoredits' => 0, - 'enotifrevealaddr' => 0, - 'enotifusertalkpages' => 1, - 'enotifwatchlistpages' => 0, - 'extendwatchlist' => 0, - 'externaldiff' => 0, - 'externaleditor' => 0, - 'fancysig' => 0, - 'forceeditsummary' => 0, - 'gender' => 'unknown', - 'hideminor' => 0, - 'hidepatrolled' => 0, - 'imagesize' => 2, - 'justify' => 0, - 'math' => 1, - 'minordefault' => 0, - 'newpageshidepatrolled' => 0, - 'nocache' => 0, - 'noconvertlink' => 0, - 'norollbackdiff' => 0, - 'numberheadings' => 0, - 'previewonfirst' => 0, - 'previewontop' => 1, - 'quickbar' => 5, - 'rcdays' => 7, - 'rclimit' => 50, - 'rememberpassword' => 0, - 'rows' => 25, - 'searchlimit' => 20, - 'showhiddencats' => 0, - 'showjumplinks' => 1, - 'shownumberswatching' => 1, - 'showtoc' => 1, - 'showtoolbar' => 1, - 'skin' => false, - 'stubthreshold' => 0, - 'thumbsize' => 2, - 'underline' => 2, - 'uselivepreview' => 0, - 'usenewrc' => 0, - 'watchcreations' => 0, - 'watchdefault' => 0, - 'watchdeletion' => 0, - 'watchlistdays' => 3.0, - 'watchlisthideanons' => 0, - 'watchlisthidebots' => 0, - 'watchlisthideliu' => 0, - 'watchlisthideminor' => 0, - 'watchlisthideown' => 0, - 'watchlisthidepatrolled' => 0, - 'watchmoves' => 0, - 'wllimit' => 250, + 'enotifminoredits' => 0, + 'enotifrevealaddr' => 0, + 'enotifusertalkpages' => 1, + 'enotifwatchlistpages' => 0, + 'extendwatchlist' => 0, + 'externaldiff' => 0, + 'externaleditor' => 0, + 'fancysig' => 0, + 'forceeditsummary' => 0, + 'gender' => 'unknown', + 'hideminor' => 0, + 'hidepatrolled' => 0, + 'imagesize' => 2, + 'justify' => 0, + 'math' => 1, + 'minordefault' => 0, + 'newpageshidepatrolled' => 0, + 'nocache' => 0, + 'noconvertlink' => 0, + 'norollbackdiff' => 0, + 'numberheadings' => 0, + 'previewonfirst' => 0, + 'previewontop' => 1, + 'quickbar' => 5, + 'rcdays' => 7, + 'rclimit' => 50, + 'rememberpassword' => 0, + 'rows' => 25, + 'searchlimit' => 20, + 'showhiddencats' => 0, + 'showjumplinks' => 1, + 'shownumberswatching' => 1, + 'showtoc' => 1, + 'showtoolbar' => 1, + 'skin' => false, + 'stubthreshold' => 0, + 'thumbsize' => 2, + 'underline' => 2, + 'uselivepreview' => 0, + 'usenewrc' => 0, + 'watchcreations' => 0, + 'watchdefault' => 0, + 'watchdeletion' => 0, + 'watchlistdays' => 3.0, + 'watchlisthideanons' => 0, + 'watchlisthidebots' => 0, + 'watchlisthideliu' => 0, + 'watchlisthideminor' => 0, + 'watchlisthideown' => 0, + 'watchlisthidepatrolled' => 0, + 'watchmoves' => 0, + 'wllimit' => 250, ); -/** - * Whether or not to allow and use real name fields. - * @deprecated since 1.16, use $wgHiddenPrefs[] = 'realname' below to disable real - * names - */ -$wgAllowRealName = true; - /** An array of preferences to not show for the user */ $wgHiddenPrefs = array(); @@ -3672,11 +3803,18 @@ $wgAllowPrefChange = array(); /** * This is to let user authenticate using https when they come from http. * Based on an idea by George Herbert on wikitech-l: - * http://lists.wikimedia.org/pipermail/wikitech-l/2010-October/050065.html + * http://lists.wikimedia.org/pipermail/wikitech-l/2010-October/050039.html * @since 1.17 */ $wgSecureLogin = false; +/** + * By default, keep users logged in via HTTPS when $wgSecureLogin is also + * true. Users opt-out of HTTPS when they login by de-selecting the checkbox. + * @since 1.21 + */ +$wgSecureLoginDefaultHTTPS = true; + /** @} */ # end user accounts } /************************************************************************//** @@ -3744,6 +3882,34 @@ $wgBlockDisablesLogin = false; */ $wgWhitelistRead = false; +/** + * Pages anonymous user may see, set as an array of regular expressions. + * + * This function will match the regexp against the title name, which + * is without underscore. + * + * @par Example: + * To whitelist [[Main Page]]: + * @code + * $wgWhitelistReadRegexp = array( "/Main Page/" ); + * @endcode + * + * @note Unless ^ and/or $ is specified, a regular expression might match + * pages not intended to be whitelisted. The above example will also + * whitelist a page named 'Security Main Page'. + * + * @par Example: + * To allow reading any page starting with 'User' regardless of the case: + * @code + * $wgWhitelistReadRegexp = array( "@^UsEr.*@i" ); + * @endcode + * Will allow both [[User is banned]] and [[User:JohnDoe]] + * + * @note This will only work if $wgGroupPermissions['*']['read'] is false -- + * see below. Otherwise, ALL pages are accessible, regardless of this setting. + */ +$wgWhitelistReadRegexp = false; + /** * Should editors be required to have a validated e-mail * address before being allowed to edit? @@ -3778,93 +3944,93 @@ $wgGroupPermissions = array(); /** @cond file_level_code */ // Implicit group for all visitors -$wgGroupPermissions['*']['createaccount'] = true; -$wgGroupPermissions['*']['read'] = true; -$wgGroupPermissions['*']['edit'] = true; -$wgGroupPermissions['*']['createpage'] = true; -$wgGroupPermissions['*']['createtalk'] = true; -$wgGroupPermissions['*']['writeapi'] = true; -//$wgGroupPermissions['*']['patrolmarks'] = false; // let anons see what was patrolled +$wgGroupPermissions['*']['createaccount'] = true; +$wgGroupPermissions['*']['read'] = true; +$wgGroupPermissions['*']['edit'] = true; +$wgGroupPermissions['*']['createpage'] = true; +$wgGroupPermissions['*']['createtalk'] = true; +$wgGroupPermissions['*']['writeapi'] = true; +#$wgGroupPermissions['*']['patrolmarks'] = false; // let anons see what was patrolled // Implicit group for all logged-in accounts -$wgGroupPermissions['user']['move'] = true; -$wgGroupPermissions['user']['move-subpages'] = true; +$wgGroupPermissions['user']['move'] = true; +$wgGroupPermissions['user']['move-subpages'] = true; $wgGroupPermissions['user']['move-rootuserpages'] = true; // can move root userpages -$wgGroupPermissions['user']['movefile'] = true; -$wgGroupPermissions['user']['read'] = true; -$wgGroupPermissions['user']['edit'] = true; -$wgGroupPermissions['user']['createpage'] = true; -$wgGroupPermissions['user']['createtalk'] = true; -$wgGroupPermissions['user']['writeapi'] = true; -$wgGroupPermissions['user']['upload'] = true; -$wgGroupPermissions['user']['reupload'] = true; -$wgGroupPermissions['user']['reupload-shared'] = true; -$wgGroupPermissions['user']['minoredit'] = true; -$wgGroupPermissions['user']['purge'] = true; // can use ?action=purge without clicking "ok" -$wgGroupPermissions['user']['sendemail'] = true; +$wgGroupPermissions['user']['movefile'] = true; +$wgGroupPermissions['user']['read'] = true; +$wgGroupPermissions['user']['edit'] = true; +$wgGroupPermissions['user']['createpage'] = true; +$wgGroupPermissions['user']['createtalk'] = true; +$wgGroupPermissions['user']['writeapi'] = true; +$wgGroupPermissions['user']['upload'] = true; +$wgGroupPermissions['user']['reupload'] = true; +$wgGroupPermissions['user']['reupload-shared'] = true; +$wgGroupPermissions['user']['minoredit'] = true; +$wgGroupPermissions['user']['purge'] = true; // can use ?action=purge without clicking "ok" +$wgGroupPermissions['user']['sendemail'] = true; // Implicit group for accounts that pass $wgAutoConfirmAge $wgGroupPermissions['autoconfirmed']['autoconfirmed'] = true; // Users with bot privilege can have their edits hidden // from various log pages by default -$wgGroupPermissions['bot']['bot'] = true; -$wgGroupPermissions['bot']['autoconfirmed'] = true; -$wgGroupPermissions['bot']['nominornewtalk'] = true; -$wgGroupPermissions['bot']['autopatrol'] = true; +$wgGroupPermissions['bot']['bot'] = true; +$wgGroupPermissions['bot']['autoconfirmed'] = true; +$wgGroupPermissions['bot']['nominornewtalk'] = true; +$wgGroupPermissions['bot']['autopatrol'] = true; $wgGroupPermissions['bot']['suppressredirect'] = true; -$wgGroupPermissions['bot']['apihighlimits'] = true; -$wgGroupPermissions['bot']['writeapi'] = true; -#$wgGroupPermissions['bot']['editprotected'] = true; // can edit all protected pages without cascade protection enabled +$wgGroupPermissions['bot']['apihighlimits'] = true; +$wgGroupPermissions['bot']['writeapi'] = true; +#$wgGroupPermissions['bot']['editprotected'] = true; // can edit all protected pages without cascade protection enabled // Most extra permission abilities go to this group -$wgGroupPermissions['sysop']['block'] = true; -$wgGroupPermissions['sysop']['createaccount'] = true; -$wgGroupPermissions['sysop']['delete'] = true; -$wgGroupPermissions['sysop']['bigdelete'] = true; // can be separately configured for pages with > $wgDeleteRevisionsLimit revs -$wgGroupPermissions['sysop']['deletedhistory'] = true; // can view deleted history entries, but not see or restore the text -$wgGroupPermissions['sysop']['deletedtext'] = true; // can view deleted revision text -$wgGroupPermissions['sysop']['undelete'] = true; -$wgGroupPermissions['sysop']['editinterface'] = true; -$wgGroupPermissions['sysop']['editusercss'] = true; -$wgGroupPermissions['sysop']['edituserjs'] = true; -$wgGroupPermissions['sysop']['import'] = true; -$wgGroupPermissions['sysop']['importupload'] = true; -$wgGroupPermissions['sysop']['move'] = true; -$wgGroupPermissions['sysop']['move-subpages'] = true; +$wgGroupPermissions['sysop']['block'] = true; +$wgGroupPermissions['sysop']['createaccount'] = true; +$wgGroupPermissions['sysop']['delete'] = true; +$wgGroupPermissions['sysop']['bigdelete'] = true; // can be separately configured for pages with > $wgDeleteRevisionsLimit revs +$wgGroupPermissions['sysop']['deletedhistory'] = true; // can view deleted history entries, but not see or restore the text +$wgGroupPermissions['sysop']['deletedtext'] = true; // can view deleted revision text +$wgGroupPermissions['sysop']['undelete'] = true; +$wgGroupPermissions['sysop']['editinterface'] = true; +$wgGroupPermissions['sysop']['editusercss'] = true; +$wgGroupPermissions['sysop']['edituserjs'] = true; +$wgGroupPermissions['sysop']['import'] = true; +$wgGroupPermissions['sysop']['importupload'] = true; +$wgGroupPermissions['sysop']['move'] = true; +$wgGroupPermissions['sysop']['move-subpages'] = true; $wgGroupPermissions['sysop']['move-rootuserpages'] = true; -$wgGroupPermissions['sysop']['patrol'] = true; -$wgGroupPermissions['sysop']['autopatrol'] = true; -$wgGroupPermissions['sysop']['protect'] = true; -$wgGroupPermissions['sysop']['proxyunbannable'] = true; -$wgGroupPermissions['sysop']['rollback'] = true; -$wgGroupPermissions['sysop']['upload'] = true; -$wgGroupPermissions['sysop']['reupload'] = true; -$wgGroupPermissions['sysop']['reupload-shared'] = true; -$wgGroupPermissions['sysop']['unwatchedpages'] = true; -$wgGroupPermissions['sysop']['autoconfirmed'] = true; -$wgGroupPermissions['sysop']['ipblock-exempt'] = true; -$wgGroupPermissions['sysop']['blockemail'] = true; -$wgGroupPermissions['sysop']['markbotedits'] = true; -$wgGroupPermissions['sysop']['apihighlimits'] = true; -$wgGroupPermissions['sysop']['browsearchive'] = true; -$wgGroupPermissions['sysop']['noratelimit'] = true; -$wgGroupPermissions['sysop']['movefile'] = true; -$wgGroupPermissions['sysop']['unblockself'] = true; +$wgGroupPermissions['sysop']['patrol'] = true; +$wgGroupPermissions['sysop']['autopatrol'] = true; +$wgGroupPermissions['sysop']['protect'] = true; +$wgGroupPermissions['sysop']['proxyunbannable'] = true; +$wgGroupPermissions['sysop']['rollback'] = true; +$wgGroupPermissions['sysop']['upload'] = true; +$wgGroupPermissions['sysop']['reupload'] = true; +$wgGroupPermissions['sysop']['reupload-shared'] = true; +$wgGroupPermissions['sysop']['unwatchedpages'] = true; +$wgGroupPermissions['sysop']['autoconfirmed'] = true; +$wgGroupPermissions['sysop']['ipblock-exempt'] = true; +$wgGroupPermissions['sysop']['blockemail'] = true; +$wgGroupPermissions['sysop']['markbotedits'] = true; +$wgGroupPermissions['sysop']['apihighlimits'] = true; +$wgGroupPermissions['sysop']['browsearchive'] = true; +$wgGroupPermissions['sysop']['noratelimit'] = true; +$wgGroupPermissions['sysop']['movefile'] = true; +$wgGroupPermissions['sysop']['unblockself'] = true; $wgGroupPermissions['sysop']['suppressredirect'] = true; -#$wgGroupPermissions['sysop']['upload_by_url'] = true; -#$wgGroupPermissions['sysop']['mergehistory'] = true; +#$wgGroupPermissions['sysop']['upload_by_url'] = true; +#$wgGroupPermissions['sysop']['mergehistory'] = true; // Permission to change users' group assignments -$wgGroupPermissions['bureaucrat']['userrights'] = true; +$wgGroupPermissions['bureaucrat']['userrights'] = true; $wgGroupPermissions['bureaucrat']['noratelimit'] = true; // Permission to change users' groups assignments across wikis #$wgGroupPermissions['bureaucrat']['userrights-interwiki'] = true; // Permission to export pages including linked pages regardless of $wgExportMaxLinkDepth #$wgGroupPermissions['bureaucrat']['override-export-depth'] = true; -#$wgGroupPermissions['sysop']['deletelogentry'] = true; -#$wgGroupPermissions['sysop']['deleterevision'] = true; +#$wgGroupPermissions['sysop']['deletelogentry'] = true; +#$wgGroupPermissions['sysop']['deleterevision'] = true; // To hide usernames from users and Sysops #$wgGroupPermissions['suppress']['hideuser'] = true; // To hide revisions/log items from users and Sysops @@ -3962,7 +4128,7 @@ $wgNamespaceProtection = array(); * namespaces constants (NS_USER, NS_MAIN...). * * Among other things, this may be useful to enforce read-restrictions - * which may otherwise be bypassed by using the template machanism. + * which may otherwise be bypassed by using the template mechanism. */ $wgNonincludableNamespaces = array(); @@ -4037,11 +4203,11 @@ $wgAutopromote = array( * * The format is: * @code - * array( event => criteria, ... ) + * array( event => criteria, ... ) * @endcode * Where event is either: - * - 'onEdit' (when user edits) - * - 'onView' (when user views the wiki) + * - 'onEdit' (when user edits) + * - 'onView' (when user views the wiki) * * Criteria has the same format as $wgAutopromote * @@ -4100,7 +4266,8 @@ $wgDeleteRevisionsLimit = 0; /** * Number of accounts each IP address may create, 0 to disable. * - * @warning Requires memcached */ + * @warning Requires memcached + */ $wgAccountCreationThrottle = 0; /** @@ -4191,25 +4358,25 @@ $wgProxyWhitelist = array(); */ $wgRateLimits = array( 'edit' => array( - 'anon' => null, // for any and all anonymous edits (aggregate) - 'user' => null, // for each logged-in user + 'anon' => null, // for any and all anonymous edits (aggregate) + 'user' => null, // for each logged-in user 'newbie' => null, // for each recent (autoconfirmed) account; overrides 'user' - 'ip' => null, // for each anon and recent account + 'ip' => null, // for each anon and recent account 'subnet' => null, // ... with final octet removed - ), + ), 'move' => array( - 'user' => null, + 'user' => null, 'newbie' => null, - 'ip' => null, + 'ip' => null, 'subnet' => null, - ), + ), 'mailpassword' => array( 'anon' => null, - ), + ), 'emailuser' => array( 'user' => null, - ), - ); + ), +); /** * Set to a filename to log rate limiter hits. @@ -4225,6 +4392,7 @@ $wgRateLimitsExcludedIPs = array(); /** * Log IP addresses in the recentchanges table; can be accessed only by * extensions (e.g. CheckUser) or a DB admin + * Used for retroactive autoblocks */ $wgPutIPinRC = true; @@ -4261,14 +4429,26 @@ $wgBlockOpenProxies = false; /** Port we want to scan for a proxy */ $wgProxyPorts = array( 80, 81, 1080, 3128, 6588, 8000, 8080, 8888, 65506 ); /** Script used to scan */ -$wgProxyScriptPath = "$IP/maintenance/proxy_check.php"; +$wgProxyScriptPath = "$IP/maintenance/proxyCheck.php"; /** */ $wgProxyMemcExpiry = 86400; /** This should always be customised in LocalSettings.php */ $wgSecretKey = false; -/** big list of banned IP addresses, in the keys not the values */ + +/** + * Big list of banned IP addresses. + * + * This can have the following formats: + * - An array of addresses, either in the values + * or the keys (for backward compatibility) + * - A string, in that case this is the path to a file + * containing the list of IP addresses, one per line + */ $wgProxyList = array(); -/** deprecated */ + +/** + * @deprecated since 1.14 + */ $wgProxyKey = false; /** @} */ # end of proxy scanner settings @@ -4281,7 +4461,7 @@ $wgProxyKey = false; /** * Default cookie expiration time. Setting to 0 makes all cookies session-only. */ -$wgCookieExpiration = 180*86400; +$wgCookieExpiration = 180 * 86400; /** * Set to set an explicit domain on the login cookies eg, "justthis.domain.org" @@ -4289,7 +4469,6 @@ $wgCookieExpiration = 180*86400; */ $wgCookieDomain = ''; - /** * Set this variable if you want to restrict cookies to a certain path within * the domain specified by $wgCookieDomain. @@ -4343,7 +4522,7 @@ $wgCacheVaryCookies = array(); /** Override to customise the session name */ $wgSessionName = false; -/** @} */ # end of cookie settings } +/** @} */ # end of cookie settings } /************************************************************************//** * @name LaTeX (mathematical formulas) @@ -4507,7 +4686,9 @@ $wgProfileOnly = false; * Log sums from profiling into "profiling" table in db. * * You have to create a 'profiling' table in your database before using - * this feature, see maintenance/archives/patch-profiling.sql + * this feature. Run set $wgProfileToDatabase to true in + * LocalSettings.php and run maintenance/update.php or otherwise + * manually add patch-profiling.sql to your database. * * To enable profiling, edit StartProfiler.php */ @@ -4560,6 +4741,13 @@ $wgAggregateStatsID = false; */ $wgDisableCounters = false; +/** + * InfoAction retrieves a list of transclusion links (both to and from). + * This number puts a limit on that query in the case of highly transcluded + * templates. + */ +$wgPageInfoTransclusionLimit = 50; + /** * Set this to an integer to only do synchronous site_stats updates * one every *this many* updates. The other requests go into pending @@ -4621,7 +4809,6 @@ $wgJavaScriptTestConfig = array( ), ); - /** * Overwrite the caching key prefix with custom value. * @since 1.19 @@ -4650,7 +4837,7 @@ $wgDebugToolbar = false; $wgDisableTextSearch = false; /** - * Set to true to have nicer highligted text in search results, + * Set to true to have nicer highlighted text in search results, * by default off due to execution overhead */ $wgAdvancedSearchHighlighting = false; @@ -4676,7 +4863,7 @@ $wgCountTotalSearchHits = false; /** * Template for OpenSearch suggestions, defaults to API action=opensearch * - * Sites with heavy load would tipically have these point to a custom + * Sites with heavy load would typically have these point to a custom * PHP wrapper to avoid firing up mediawiki for every keystroke * * Placeholders: {searchTerms} @@ -4724,7 +4911,7 @@ $wgNamespacesToBeSearchedDefault = array( */ $wgNamespacesToBeSearchedHelp = array( NS_PROJECT => true, - NS_HELP => true, + NS_HELP => true, ); /** @@ -4751,10 +4938,10 @@ $wgDisableInternalSearch = false; * To forward to Google you'd have something like: * @code * $wgSearchForwardUrl = - * 'http://www.google.com/search?q=$1' . - * '&domains=http://example.com' . - * '&sitesearch=http://example.com' . - * '&ie=utf-8&oe=utf-8'; + * 'http://www.google.com/search?q=$1' . + * '&domains=http://example.com' . + * '&sitesearch=http://example.com' . + * '&ie=utf-8&oe=utf-8'; * @endcode */ $wgSearchForwardUrl = null; @@ -4768,14 +4955,14 @@ $wgUseTwoButtonsSearchForm = true; /** * Array of namespaces to generate a Google sitemap for when the - * maintenance/generateSitemap.php script is run, or false if one is to be ge- - * nerated for all namespaces. + * maintenance/generateSitemap.php script is run, or false if one is to be + * generated for all namespaces. */ $wgSitemapNamespaces = false; /** * Custom namespace priorities for sitemaps. Setting this will allow you to - * set custom priorities to namsepaces when sitemaps are generated using the + * set custom priorities to namespaces when sitemaps are generated using the * maintenance/generateSitemap.php script. * * This should be a map of namespace IDs to priority @@ -4805,7 +4992,7 @@ $wgEnableSearchContributorsByIP = true; /** * Path to the GNU diff3 utility. If the file doesn't exist, edit conflicts will - * fall back to the old behaviour (no merging). + * fall back to the old behavior (no merging). */ $wgDiff3 = '/usr/bin/diff3'; @@ -4816,7 +5003,7 @@ $wgDiff = '/usr/bin/diff'; /** * Which namespaces have special treatment where they should be preview-on-open - * Internaly only Category: pages apply, but using this extensions (e.g. Semantic MediaWiki) + * Internally only Category: pages apply, but using this extensions (e.g. Semantic MediaWiki) * can specify namespaces of pages they have special treatment for */ $wgPreviewOnOpenNamespaces = array( @@ -4858,7 +5045,7 @@ $wgUseAutomaticEditSummaries = true; * @cond file_level_code * Set $wgCommandLineMode if it's not set already, to avoid notices */ -if( !isset( $wgCommandLineMode ) ) { +if ( !isset( $wgCommandLineMode ) ) { $wgCommandLineMode = false; } /** @endcond */ @@ -5032,7 +5219,7 @@ $wgOverrideSiteFeed = array(); * $wgOut->isSyndicated() is true. */ $wgFeedClasses = array( - 'rss' => 'RSSFeed', + 'rss' => 'RSSFeed', 'atom' => 'AtomFeed', ); @@ -5078,6 +5265,15 @@ $wgAllowCategorizedRecentChanges = false; */ $wgUseTagFilter = true; +/** + * If set to an integer, pages that are watched by this many users or more + * will not require the unwatchedpages permission to view the number of + * watchers. + * + * @since 1.21 + */ +$wgUnwatchedPageThreshold = false; + /** @} */ # end RC/watchlist } /************************************************************************//** @@ -5180,8 +5376,8 @@ $wgExportAllowHistory = true; $wgExportMaxHistory = 0; /** -* Return distinct author list (when not returning full history) -*/ + * Return distinct author list (when not returning full history) + */ $wgExportAllowListContributors = false; /** @@ -5198,13 +5394,13 @@ $wgExportAllowListContributors = false; $wgExportMaxLinkDepth = 0; /** -* Whether to allow the "export all pages in namespace" option -*/ + * Whether to allow the "export all pages in namespace" option + */ $wgExportFromNamespaces = false; /** -* Whether to allow exporting the entire wiki into a single file -*/ + * Whether to allow exporting the entire wiki into a single file + */ $wgExportAllowAll = false; /** @} */ # end of import/export } @@ -5287,7 +5483,7 @@ $wgAutoloadClasses = array(); * 'version' => 1.9, * 'path' => __FILE__, * 'author' => 'Foo Barstein', - * 'url' => 'http://wwww.example.com/Example%20Extension/', + * 'url' => 'http://www.example.com/Example%20Extension/', * 'description' => 'An example extension', * 'descriptionmsg' => 'exampleextension-desc', * ); @@ -5296,6 +5492,11 @@ $wgAutoloadClasses = array(); * Where $type is 'specialpage', 'parserhook', 'variable', 'media' or 'other'. * Where 'descriptionmsg' can be an array with message key and parameters: * 'descriptionmsg' => array( 'exampleextension-desc', param1, param2, ... ), + * + * author can be a string or an array of strings. Authors can be linked using + * the regular wikitext link syntax. To have an internationalized version of + * "and others" show, add an element "...". This element can also be linked, + * for instance "[http://example ...]". */ $wgExtensionCredits = array(); @@ -5347,11 +5548,14 @@ $wgJobClasses = array( 'enotifNotify' => 'EnotifNotifyJob', 'fixDoubleRedirect' => 'DoubleRedirectJob', 'uploadFromUrl' => 'UploadFromUrlJob', + 'AssembleUploadChunks' => 'AssembleUploadChunksJob', + 'PublishStashedFile' => 'PublishStashedFileJob', + 'null' => 'NullJob' ); /** - - * Jobs that must be explicitly requested, i.e. aren't run by job runners unless special flags are set. + * Jobs that must be explicitly requested, i.e. aren't run by job runners unless + * special flags are set. The values here are keys of $wgJobClasses. * * These can be: * - Very long-running jobs. @@ -5359,7 +5563,25 @@ $wgJobClasses = array( * - 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(); +$wgJobTypesExcludedFromDefaultQueue = array( 'AssembleUploadChunks', 'PublishStashedFile' ); + +/** + * Map of job types to configuration arrays. + * This determines which queue class and storage system is used for each job type. + * Job types that do not have explicit configuration will use the 'default' config. + * These settings should be global to all wikis. + */ +$wgJobTypeConf = array( + 'default' => array( 'class' => 'JobQueueDB', 'order' => 'random' ), +); + +/** + * Which aggregator to use for tracking which queues have jobs. + * These settings should be global to all wikis. + */ +$wgJobQueueAggregator = array( + 'class' => 'JobQueueAggregatorMemc' +); /** * Additional functions to be performed with updateSpecialPages. @@ -5486,7 +5708,7 @@ $wgLogRestrictions = array( * * @par Example: * @code - * $wgFilterLogTypes => array( + * $wgFilterLogTypes = array( * 'move' => true, * 'import' => false, * ); @@ -5513,16 +5735,16 @@ $wgFilterLogTypes = array( * where TYPE is your log type, yoy don't need to use this array. */ $wgLogNames = array( - '' => 'all-logs-page', - 'block' => 'blocklogpage', + '' => 'all-logs-page', + 'block' => 'blocklogpage', 'protect' => 'protectlogpage', - 'rights' => 'rightslog', - 'delete' => 'dellogpage', - 'upload' => 'uploadlogpage', - 'move' => 'movelogpage', - 'import' => 'importlogpage', - 'patrol' => 'patrol-log-page', - 'merge' => 'mergelog', + 'rights' => 'rightslog', + 'delete' => 'dellogpage', + 'upload' => 'uploadlogpage', + 'move' => 'movelogpage', + 'import' => 'importlogpage', + 'patrol' => 'patrol-log-page', + 'merge' => 'mergelog', 'suppress' => 'suppressionlog', ); @@ -5536,16 +5758,16 @@ $wgLogNames = array( * where TYPE is your log type, yoy don't need to use this array. */ $wgLogHeaders = array( - '' => 'alllogstext', - 'block' => 'blocklogtext', + '' => 'alllogstext', + 'block' => 'blocklogtext', 'protect' => 'protectlogtext', - 'rights' => 'rightslogtext', - 'delete' => 'dellogpagetext', - 'upload' => 'uploadlogpagetext', - 'move' => 'movelogpagetext', - 'import' => 'importlogpagetext', - 'patrol' => 'patrol-log-header', - 'merge' => 'mergelogpagetext', + 'rights' => 'rightslogtext', + 'delete' => 'dellogpagetext', + 'upload' => 'uploadlogpagetext', + 'move' => 'movelogpagetext', + 'import' => 'importlogpagetext', + 'patrol' => 'patrol-log-header', + 'merge' => 'mergelogpagetext', 'suppress' => 'suppressionlogtext', ); @@ -5556,23 +5778,21 @@ $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/autopromote' => 'rightslogentry-autopromote', - 'upload/upload' => 'uploadedimage', - 'upload/overwrite' => 'overwroteimage', - 'upload/revert' => 'uploadedimage', - 'import/upload' => 'import-logentry-upload', - 'import/interwiki' => 'import-logentry-interwiki', - 'merge/merge' => 'pagemerge-logentry', - 'suppress/block' => 'blocklogentry', - 'suppress/reblock' => 'reblock-logentry', + 'block/block' => 'blocklogentry', + 'block/unblock' => 'unblocklogentry', + 'block/reblock' => 'reblock-logentry', + 'protect/protect' => 'protectedarticle', + 'protect/modify' => 'modifiedarticleprotection', + 'protect/unprotect' => 'unprotectedarticle', + 'protect/move_prot' => 'movedarticleprotection', + 'upload/upload' => 'uploadedimage', + 'upload/overwrite' => 'overwroteimage', + 'upload/revert' => 'uploadedimage', + 'import/upload' => 'import-logentry-upload', + 'import/interwiki' => 'import-logentry-interwiki', + 'merge/merge' => 'pagemerge-logentry', + 'suppress/block' => 'blocklogentry', + 'suppress/reblock' => 'reblock-logentry', ); /** @@ -5582,16 +5802,18 @@ $wgLogActions = array( * @see LogFormatter */ $wgLogActionsHandlers = array( - 'move/move' => 'MoveLogFormatter', - 'move/move_redir' => 'MoveLogFormatter', - 'delete/delete' => 'DeleteLogFormatter', - 'delete/restore' => 'DeleteLogFormatter', - 'delete/revision' => 'DeleteLogFormatter', - 'delete/event' => 'DeleteLogFormatter', + 'move/move' => 'MoveLogFormatter', + 'move/move_redir' => 'MoveLogFormatter', + 'delete/delete' => 'DeleteLogFormatter', + 'delete/restore' => 'DeleteLogFormatter', + 'delete/revision' => 'DeleteLogFormatter', + 'delete/event' => 'DeleteLogFormatter', 'suppress/revision' => 'DeleteLogFormatter', - 'suppress/event' => 'DeleteLogFormatter', - 'suppress/delete' => 'DeleteLogFormatter', - 'patrol/patrol' => 'PatrolLogFormatter', + 'suppress/event' => 'DeleteLogFormatter', + 'suppress/delete' => 'DeleteLogFormatter', + 'patrol/patrol' => 'PatrolLogFormatter', + 'rights/rights' => 'RightsLogFormatter', + 'rights/autopromote' => 'RightsLogFormatter', ); /** @@ -5620,110 +5842,10 @@ $wgDisableQueryPageUpdate = false; /** * List of special pages, followed by what subtitle they should go under * at Special:SpecialPages + * + * @deprecated 1.21 Override SpecialPage::getGroupName instead */ -$wgSpecialPageGroups = array( - 'DoubleRedirects' => 'maintenance', - 'BrokenRedirects' => 'maintenance', - 'Lonelypages' => 'maintenance', - 'Uncategorizedpages' => 'maintenance', - 'Uncategorizedcategories' => 'maintenance', - 'Uncategorizedimages' => 'maintenance', - 'Uncategorizedtemplates' => 'maintenance', - 'Unusedcategories' => 'maintenance', - 'Unusedimages' => 'maintenance', - 'Protectedpages' => 'maintenance', - 'Protectedtitles' => 'maintenance', - 'Unusedtemplates' => 'maintenance', - 'Withoutinterwiki' => 'maintenance', - 'Longpages' => 'maintenance', - 'Shortpages' => 'maintenance', - 'Ancientpages' => 'maintenance', - 'Deadendpages' => 'maintenance', - 'Wantedpages' => 'maintenance', - 'Wantedcategories' => 'maintenance', - 'Wantedfiles' => 'maintenance', - 'Wantedtemplates' => 'maintenance', - 'Unwatchedpages' => 'maintenance', - 'Fewestrevisions' => 'maintenance', - - 'Userlogin' => 'login', - 'Userlogout' => 'login', - 'CreateAccount' => 'login', - - 'Recentchanges' => 'changes', - 'Recentchangeslinked' => 'changes', - 'Watchlist' => 'changes', - 'Newimages' => 'changes', - 'Newpages' => 'changes', - 'Log' => 'changes', - 'Tags' => 'changes', - - 'Upload' => 'media', - 'Listfiles' => 'media', - 'MIMEsearch' => 'media', - 'FileDuplicateSearch' => 'media', - 'Filepath' => 'media', - - 'Listusers' => 'users', - 'Activeusers' => 'users', - 'Listgrouprights' => 'users', - 'BlockList' => 'users', - 'Contributions' => 'users', - 'Emailuser' => 'users', - 'Listadmins' => 'users', - 'Listbots' => 'users', - 'Userrights' => 'users', - 'Block' => 'users', - 'Unblock' => 'users', - 'Preferences' => 'users', - 'ChangeEmail' => 'users', - 'ChangePassword' => 'users', - 'DeletedContributions' => 'users', - 'PasswordReset' => 'users', - - 'Mostlinked' => 'highuse', - 'Mostlinkedcategories' => 'highuse', - 'Mostlinkedtemplates' => 'highuse', - 'Mostcategories' => 'highuse', - 'Mostimages' => 'highuse', - 'Mostinterwikis' => 'highuse', - 'Mostrevisions' => 'highuse', - - 'Allpages' => 'pages', - 'Prefixindex' => 'pages', - 'Listredirects' => 'pages', - 'Categories' => 'pages', - 'Disambiguations' => 'pages', - - 'Randompage' => 'redirects', - 'Randomredirect' => 'redirects', - 'Mypage' => 'redirects', - 'Mytalk' => 'redirects', - 'Mycontributions' => 'redirects', - 'Search' => 'redirects', - 'LinkSearch' => 'redirects', - - 'ComparePages' => 'pagetools', - 'Movepage' => 'pagetools', - 'MergeHistory' => 'pagetools', - 'Revisiondelete' => 'pagetools', - 'Undelete' => 'pagetools', - 'Export' => 'pagetools', - 'Import' => 'pagetools', - 'Whatlinkshere' => 'pagetools', - - 'Statistics' => 'wiki', - 'Version' => 'wiki', - 'Lockdb' => 'wiki', - 'Unlockdb' => 'wiki', - 'Allmessages' => 'wiki', - 'Popularpages' => 'wiki', - - 'Specialpages' => 'other', - 'Blockme' => 'other', - 'Booksources' => 'other', - 'JavaScriptTest' => 'other', -); +$wgSpecialPageGroups = array(); /** Whether or not to sort special pages in Special:Specialpages */ @@ -5759,24 +5881,24 @@ $wgMaxRedirectLinksRetrieved = 500; * Unsetting core actions will probably cause things to complain loudly. */ $wgActions = array( - 'credits' => true, - 'delete' => true, - 'edit' => true, - 'history' => true, - 'info' => true, - 'markpatrolled' => true, - 'protect' => true, - 'purge' => true, - 'raw' => true, - 'render' => true, - 'revert' => true, + 'credits' => true, + 'delete' => true, + 'edit' => true, + 'history' => true, + 'info' => true, + 'markpatrolled' => true, + 'protect' => true, + 'purge' => true, + 'raw' => true, + 'render' => true, + 'revert' => true, 'revisiondelete' => true, - 'rollback' => true, - 'submit' => true, - 'unprotect' => true, - 'unwatch' => true, - 'view' => true, - 'watch' => true, + 'rollback' => true, + 'submit' => true, + 'unprotect' => true, + 'unwatch' => true, + 'view' => true, + 'watch' => true, ); /** @@ -5818,14 +5940,14 @@ $wgNamespaceRobotPolicies = array(); /** * Robot policies per article. These override the per-namespace robot policies. - * Must be in the form of an array where the key part is a properly canonical- - * ised text form title and the value is a robot policy. + * Must be in the form of an array where the key part is a properly canonicalised + * text form title and the value is a robot policy. * * @par Example: * @code * $wgArticleRobotPolicies = array( - * 'Main Page' => 'noindex,follow', - * 'User:Bob' => 'index,follow', + * 'Main Page' => 'noindex,follow', + * 'User:Bob' => 'index,follow', * ); * @endcode * @@ -5881,6 +6003,22 @@ $wgEnableAPI = true; */ $wgEnableWriteAPI = true; +/** + * + * WARNING: SECURITY THREAT - debug use only + * + * Disables many security checks in the API for debugging purposes. + * This flag should never be used on the production servers, as it introduces + * a number of potential security holes. Even when enabled, the validation + * will still be performed, but instead of failing, API will return a warning. + * Also, there will always be a warning notifying that this flag is set. + * At this point, the flag allows GET requests to go through for modules + * requiring POST. + * + * @since 1.21 + */ +$wgDebugAPI = false; + /** * API module extensions. * Associative array mapping module name to class name. @@ -5892,6 +6030,12 @@ $wgAPIMetaModules = array(); $wgAPIPropModules = array(); $wgAPIListModules = array(); +/** + * This variable is ignored. To add your module to the API, please add it to $wgAPI*Modules + * @deprecated since 1.21 + */ +$wgAPIGeneratorModules = array(); + /** * Maximum amount of rows to scan in a DB query in the API * The default value is generally fine @@ -5919,7 +6063,7 @@ $wgAPIRequestLog = false; /** * Set the timeout for the API help text cache. If set to 0, caching disabled */ -$wgAPICacheHelpTimeout = 60*60; +$wgAPICacheHelpTimeout = 60 * 60; /** * Enable AJAX framework @@ -5961,10 +6105,10 @@ $wgAjaxLicensePreview = true; * @par Example: * @code * $wgCrossSiteAJAXdomains = array( - * 'www.mediawiki.org', - * '*.wikipedia.org', - * '*.wikimedia.org', - * '*.wiktionary.org', + * 'www.mediawiki.org', + * '*.wikipedia.org', + * '*.wikimedia.org', + * '*.wiktionary.org', * ); * @endcode */ @@ -5997,10 +6141,41 @@ $wgMaxShellMemory = 102400; $wgMaxShellFileSize = 102400; /** - * Maximum CPU time in seconds for shell processes under linux + * Maximum CPU time in seconds for shell processes under Linux */ $wgMaxShellTime = 180; +/** + * Maximum wall clock time (i.e. real time, of the kind the clock on the wall + * would measure) in seconds for shell processes under Linux + */ +$wgMaxShellWallClockTime = 180; + +/** + * Under Linux: a cgroup directory used to constrain memory usage of shell + * commands. The directory must be writable by the user which runs MediaWiki. + * + * If specified, this is used instead of ulimit, which is inaccurate, and + * causes malloc() to return NULL, which exposes bugs in C applications, making + * them segfault or deadlock. + * + * A wrapper script will create a cgroup for each shell command that runs, as + * a subgroup of the specified cgroup. If the memory limit is exceeded, the + * kernel will send a SIGKILL signal to a process in the subgroup. + * + * @par Example: + * @code + * mkdir -p /sys/fs/cgroup/memory/mediawiki + * mkdir -m 0777 /sys/fs/cgroup/memory/mediawiki/job + * echo '$wgShellCgroup = "/sys/fs/cgroup/memory/mediawiki/job";' >> LocalSettings.php + * @endcode + * + * The reliability of cgroup cleanup can be improved by installing a + * notify_on_release script in the root cgroup, see e.g. + * https://gerrit.wikimedia.org/r/#/c/40784 + */ +$wgShellCgroup = false; + /** * Executable path of the PHP cli binary (php/php5). Should be set up on install. */ @@ -6061,6 +6236,15 @@ $wgUpdateRowsPerJob = 500; */ $wgUpdateRowsPerQuery = 100; +/** + * Do not purge all the pages that use a page when it is edited + * if there are more than this many such pages. This is used to + * avoid invalidating a large portion of the squid/parser cache. + * + * This setting should factor in any squid/parser cache expiry settings. + */ +$wgMaxBacklinksInvalidate = false; + /** @} */ # End job queue } /************************************************************************//** @@ -6113,20 +6297,6 @@ $wgCompiledFiles = array(); /** @} */ # End of HipHop compilation } - -/************************************************************************//** - * @name Mobile support - * @{ - */ - -/** - * Name of the class used for mobile device detection, must be inherited from - * IDeviceDetector. - */ -$wgDeviceDetectionClass = 'DeviceDetection'; - -/** @} */ # End of Mobile support } - /************************************************************************//** * @name Miscellaneous * @{ @@ -6210,6 +6380,57 @@ $wgSeleniumConfigFile = null; $wgDBtestuser = ''; //db user that has permission to create and drop the test databases only $wgDBtestpassword = ''; +/** + * Associative array mapping namespace IDs to the name of the content model pages in that namespace should have by + * default (use the CONTENT_MODEL_XXX constants). If no special content type is defined for a given namespace, + * pages in that namespace will use the CONTENT_MODEL_WIKITEXT (except for the special case of JS and CS pages). + * + * @since 1.21 + */ +$wgNamespaceContentModels = array(); + +/** + * How to react if a plain text version of a non-text Content object is requested using ContentHandler::getContentText(): + * + * * 'ignore': return null + * * 'fail': throw an MWException + * * 'serialize': serialize to default format + * + * @since 1.21 + */ +$wgContentHandlerTextFallback = 'ignore'; + +/** + * Set to false to disable use of the database fields introduced by the ContentHandler facility. + * This way, the ContentHandler facility can be used without any additional information in the database. + * A page's content model is then derived solely from the page's title. This however means that changing + * a page's default model (e.g. using $wgNamespaceContentModels) will break the page and/or make the content + * inaccessible. This also means that pages can not be moved to a title that would default to a different + * content model. + * + * Overall, with $wgContentHandlerUseDB = false, no database updates are needed, but content handling + * is less robust and less flexible. + * + * @since 1.21 + */ +$wgContentHandlerUseDB = true; + +/** + * Determines which types of text are parsed as wikitext. This does not imply that these kinds + * of texts are also rendered as wikitext, it only means that links, magic words, etc will have + * the effect on the database they would have on a wikitext page. + * + * @todo: On the long run, it would be nice to put categories etc into a separate structure, + * or at least parse only the contents of comments in the scripts. + * + * @since 1.21 + */ +$wgTextModelsToParse = array( + CONTENT_MODEL_WIKITEXT, // Just for completeness, wikitext will always be parsed. + CONTENT_MODEL_JAVASCRIPT, // Make categories etc work, people put them into comments. + CONTENT_MODEL_CSS, // Make categories etc work, people put them into comments. +); + /** * Whether the user must enter their password to change their e-mail address * @@ -6217,6 +6438,15 @@ $wgDBtestpassword = ''; */ $wgRequirePasswordforEmailChange = true; +/** + * Register handlers for specific types of sites. + * + * @since 1.20 + */ +$wgSiteTypes = array( + 'mediawiki' => 'MediaWikiSite', +); + /** * For really cool vim folding this needs to be at the end: * vim: foldmarker=@{,@} foldmethod=marker diff --git a/includes/DeferredUpdates.php b/includes/DeferredUpdates.php index b4989a69..89c4df68 100644 --- a/includes/DeferredUpdates.php +++ b/includes/DeferredUpdates.php @@ -34,7 +34,7 @@ interface DeferrableUpdate { } /** - * Class for mananging the deferred updates. + * Class for managing the deferred updates. * * @since 1.19 */ @@ -66,7 +66,7 @@ class DeferredUpdates { /** * Do any deferred updates and clear the list * - * @param $commit String: set to 'commit' to commit after every update to + * @param string $commit set to 'commit' to commit after every update to * prevent lock contention */ public static function doUpdates( $commit = '' ) { @@ -92,7 +92,7 @@ class DeferredUpdates { $update->doUpdate(); if ( $doCommit && $dbw->trxLevel() ) { - $dbw->commit( __METHOD__ ); + $dbw->commit( __METHOD__, 'flush' ); } } catch ( MWException $e ) { // We don't want exceptions thrown during deferred updates to diff --git a/includes/Defines.php b/includes/Defines.php index be9f9816..c4a86335 100644 --- a/includes/Defines.php +++ b/includes/Defines.php @@ -39,7 +39,7 @@ define( 'MW_SPECIALPAGE_VERSION', 2 ); define( 'DBO_DEBUG', 1 ); define( 'DBO_NOBUFFER', 2 ); define( 'DBO_IGNORE', 4 ); -define( 'DBO_TRX', 8 ); +define( 'DBO_TRX', 8 ); // automatically start transaction on first query define( 'DBO_DEFAULT', 16 ); define( 'DBO_PERSISTENT', 32 ); define( 'DBO_SYSDBA', 64 ); //for oracle maintenance @@ -54,13 +54,12 @@ define( 'DBO_COMPRESS', 512 ); */ define( 'DB_SLAVE', -1 ); # Read from the slave (or only server) define( 'DB_MASTER', -2 ); # Write to master (or only server) -define( 'DB_LAST', -3 ); # Whatever database was used last /**@}*/ # Obsolete aliases define( 'DB_READ', -1 ); define( 'DB_WRITE', -2 ); - +define( 'DB_LAST', -3 ); # deprecated since 2008, usage throws exception /**@{ * Virtual namespaces; don't appear in the page database @@ -138,7 +137,7 @@ define( 'MEDIATYPE_ARCHIVE', 'ARCHIVE' ); // archive file (zip, tar, etc) */ define( 'AV_NO_VIRUS', 0 ); #scan ok, no virus found define( 'AV_VIRUS_FOUND', 1 ); #virus found! -define( 'AV_SCAN_ABORTED', -1 ); #scan aborted, the file is probably imune +define( 'AV_SCAN_ABORTED', -1 ); #scan aborted, the file is probably immune define( 'AV_SCAN_FAILED', false ); #scan failed (scanner not found or error in scanner) /**@}*/ @@ -171,11 +170,12 @@ define( 'MW_DATE_ISO', 'ISO 8601' ); /**@{ * RecentChange type identifiers */ -define( 'RC_EDIT', 0); -define( 'RC_NEW', 1); -define( 'RC_MOVE', 2); // obsolete -define( 'RC_LOG', 3); -define( 'RC_MOVE_OVER_REDIRECT', 4); // obsolete +define( 'RC_EDIT', 0 ); +define( 'RC_NEW', 1 ); +define( 'RC_MOVE', 2 ); // obsolete +define( 'RC_LOG', 3 ); +define( 'RC_MOVE_OVER_REDIRECT', 4 ); // obsolete +define( 'RC_EXTERNAL', 5 ); /**@}*/ /**@{ @@ -199,7 +199,6 @@ define( 'LIST_AND', 1 ); define( 'LIST_SET', 2 ); define( 'LIST_NAMES', 3); define( 'LIST_OR', 4); -define( 'LIST_SET_PREPARED', 8); // List of (?, ?, ?) for DatabaseIbm_db2 /**@}*/ /** @@ -213,6 +212,7 @@ require_once __DIR__.'/normal/UtfNormalDefines.php'; define( 'MW_SUPPORTS_EDITFILTERMERGED', 1 ); define( 'MW_SUPPORTS_PARSERFIRSTCALLINIT', 1 ); define( 'MW_SUPPORTS_LOCALISATIONCACHE', 1 ); +define( 'MW_SUPPORTS_CONTENTHANDLER', 1 ); /**@}*/ /** Support for $wgResourceModules */ @@ -225,7 +225,7 @@ define( 'MW_SUPPORTS_RESOURCE_MODULES', 1 ); define( 'OT_HTML', 1 ); define( 'OT_WIKI', 2 ); define( 'OT_PREPROCESS', 3 ); -define( 'OT_MSG' , 3 ); // b/c alias for OT_PREPROCESS +define( 'OT_MSG', 3 ); // b/c alias for OT_PREPROCESS define( 'OT_PLAIN', 4 ); /**@}*/ @@ -261,7 +261,7 @@ define( 'APCOND_BLOCKED', 8 ); define( 'APCOND_ISBOT', 9 ); /**@}*/ -/** +/** @{ * Protocol constants for wfExpandUrl() */ define( 'PROTO_HTTP', 'http://' ); @@ -270,3 +270,35 @@ define( 'PROTO_RELATIVE', '//' ); define( 'PROTO_CURRENT', null ); define( 'PROTO_CANONICAL', 1 ); define( 'PROTO_INTERNAL', 2 ); +/**@}*/ + +/**@{ + * Content model ids, used by Content and ContentHandler. + * These IDs will be exposed in the API and XML dumps. + * + * Extensions that define their own content model IDs should take + * care to avoid conflicts. Using the extension name as a prefix is recommended, + * for example 'myextension-somecontent'. + */ +define( 'CONTENT_MODEL_WIKITEXT', 'wikitext' ); +define( 'CONTENT_MODEL_JAVASCRIPT', 'javascript' ); +define( 'CONTENT_MODEL_CSS', 'css' ); +define( 'CONTENT_MODEL_TEXT', 'text' ); +/**@}*/ + +/**@{ + * Content formats, used by Content and ContentHandler. + * These should be MIME types, and will be exposed in the API and XML dumps. + * + * Extensions are free to use the below formats, or define their own. + * It is recommended to stick with the conventions for MIME types. + */ +define( 'CONTENT_FORMAT_WIKITEXT', 'text/x-wiki' ); // wikitext +define( 'CONTENT_FORMAT_JAVASCRIPT', 'text/javascript' ); // for js pages +define( 'CONTENT_FORMAT_CSS', 'text/css' ); // for css pages +define( 'CONTENT_FORMAT_TEXT', 'text/plain' ); // for future use, e.g. with some plain-html messages. +define( 'CONTENT_FORMAT_HTML', 'text/html' ); // for future use, e.g. with some plain-html messages. +define( 'CONTENT_FORMAT_SERIALIZED', 'application/vnd.php.serialized' ); // for future use with the api and for extensions +define( 'CONTENT_FORMAT_JSON', 'application/json' ); // for future use with the api, and for use by extensions +define( 'CONTENT_FORMAT_XML', 'application/xml' ); // for future use with the api, and for use by extensions +/**@}*/ diff --git a/includes/DeprecatedGlobal.php b/includes/DeprecatedGlobal.php index 4d7b9689..d48bd0b0 100644 --- a/includes/DeprecatedGlobal.php +++ b/includes/DeprecatedGlobal.php @@ -27,7 +27,7 @@ */ class DeprecatedGlobal extends StubObject { - // The m's are to stay consistent with parent class. + // The m's are to stay consistent with parent class. protected $mRealValue, $mVersion; function __construct( $name, $realValue, $version = false ) { diff --git a/includes/EditPage.php b/includes/EditPage.php index b762cad1..8b2dbb5f 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -40,90 +40,90 @@ class EditPage { /** * Status: Article successfully updated */ - const AS_SUCCESS_UPDATE = 200; + const AS_SUCCESS_UPDATE = 200; /** * Status: Article successfully created */ - const AS_SUCCESS_NEW_ARTICLE = 201; + const AS_SUCCESS_NEW_ARTICLE = 201; /** * Status: Article update aborted by a hook function */ - const AS_HOOK_ERROR = 210; + const AS_HOOK_ERROR = 210; /** * Status: A hook function returned an error */ - const AS_HOOK_ERROR_EXPECTED = 212; + const AS_HOOK_ERROR_EXPECTED = 212; /** - * Status: User is blocked from editting this page + * Status: User is blocked from editing this page */ - const AS_BLOCKED_PAGE_FOR_USER = 215; + const AS_BLOCKED_PAGE_FOR_USER = 215; /** * Status: Content too big (> $wgMaxArticleSize) */ - const AS_CONTENT_TOO_BIG = 216; + const AS_CONTENT_TOO_BIG = 216; /** * Status: User cannot edit? (not used) */ - const AS_USER_CANNOT_EDIT = 217; + const AS_USER_CANNOT_EDIT = 217; /** * Status: this anonymous user is not allowed to edit this page */ - const AS_READ_ONLY_PAGE_ANON = 218; + const AS_READ_ONLY_PAGE_ANON = 218; /** * Status: this logged in user is not allowed to edit this page */ - const AS_READ_ONLY_PAGE_LOGGED = 219; + const AS_READ_ONLY_PAGE_LOGGED = 219; /** * Status: wiki is in readonly mode (wfReadOnly() == true) */ - const AS_READ_ONLY_PAGE = 220; + const AS_READ_ONLY_PAGE = 220; /** * Status: rate limiter for action 'edit' was tripped */ - const AS_RATE_LIMITED = 221; + const AS_RATE_LIMITED = 221; /** - * Status: article was deleted while editting and param wpRecreate == false or form + * Status: article was deleted while editing and param wpRecreate == false or form * was not posted */ - const AS_ARTICLE_WAS_DELETED = 222; + const AS_ARTICLE_WAS_DELETED = 222; /** * Status: user tried to create this page, but is not allowed to do that * ( Title->usercan('create') == false ) */ - const AS_NO_CREATE_PERMISSION = 223; + const AS_NO_CREATE_PERMISSION = 223; /** * Status: user tried to create a blank page */ - const AS_BLANK_ARTICLE = 224; + const AS_BLANK_ARTICLE = 224; /** * Status: (non-resolvable) edit conflict */ - const AS_CONFLICT_DETECTED = 225; + const AS_CONFLICT_DETECTED = 225; /** * Status: no edit summary given and the user has forceeditsummary set and the user is not - * editting in his own userspace or talkspace and wpIgnoreBlankSummary == false + * editing in his own userspace or talkspace and wpIgnoreBlankSummary == false */ - const AS_SUMMARY_NEEDED = 226; + const AS_SUMMARY_NEEDED = 226; /** * Status: user tried to create a new section without content */ - const AS_TEXTBOX_EMPTY = 228; + const AS_TEXTBOX_EMPTY = 228; /** * Status: article is too big (> $wgMaxArticleSize), after merging in the new section @@ -133,32 +133,57 @@ class EditPage { /** * not used */ - const AS_OK = 230; + const AS_OK = 230; /** - * Status: WikiPage::doEdit() was unsuccessfull + * Status: WikiPage::doEdit() was unsuccessful */ - const AS_END = 231; + const AS_END = 231; /** * Status: summary contained spam according to one of the regexes in $wgSummarySpamRegex */ - const AS_SPAM_ERROR = 232; + const AS_SPAM_ERROR = 232; /** * Status: anonymous user is not allowed to upload (User::isAllowed('upload') == false) */ - const AS_IMAGE_REDIRECT_ANON = 233; + const AS_IMAGE_REDIRECT_ANON = 233; /** * Status: logged in user is not allowed to upload (User::isAllowed('upload') == false) */ - const AS_IMAGE_REDIRECT_LOGGED = 234; + const AS_IMAGE_REDIRECT_LOGGED = 234; + + /** + * Status: can't parse content + */ + const AS_PARSE_ERROR = 240; /** * HTML id and name for the beginning of the edit form. */ - const EDITFORM_ID = 'editform'; + const EDITFORM_ID = 'editform'; + + /** + * Prefix of key for cookie used to pass post-edit state. + * The revision id edited is added after this + */ + const POST_EDIT_COOKIE_KEY_PREFIX = 'PostEditRevision'; + + /** + * Duration of PostEdit cookie, in seconds. + * The cookie will be removed instantly if the JavaScript runs. + * + * Otherwise, though, we don't want the cookies to accumulate. + * RFC 2109 ( https://www.ietf.org/rfc/rfc2109.txt ) specifies a possible limit of only 20 cookies per domain. + * This still applies at least to some versions of IE without full updates: + * https://blogs.msdn.com/b/ieinternals/archive/2009/08/20/wininet-ie-cookie-internals-faq.aspx + * + * A value of 20 minutes should be enough to take into account slow loads and minor + * clock skew while still avoiding cookie accumulation when JavaScript is turned off. + */ + const POST_EDIT_COOKIE_DURATION = 1200; /** * @var Article @@ -214,6 +239,7 @@ class EditPage { var $textbox1 = '', $textbox2 = '', $summary = '', $nosummary = false; var $edittime = '', $section = '', $sectiontitle = '', $starttime = ''; var $oldid = 0, $editintro = '', $scrolltop = null, $bot = true; + var $contentModel = null, $contentFormat = null; # Placeholders for text injection by hooks (must be HTML) # extensions should take care to _append_ to the present value @@ -225,20 +251,32 @@ class EditPage { public $editFormTextBottom = ''; public $editFormTextAfterContent = ''; public $previewTextAfterContent = ''; - public $mPreloadText = ''; + public $mPreloadContent = null; - /* $didSave should be set to true whenever an article was succesfully altered. */ + /* $didSave should be set to true whenever an article was successfully altered. */ public $didSave = false; public $undidRev = 0; public $suppressIntro = false; + /** + * Set to true to allow editing of non-text content types. + * + * @var bool + */ + public $allowNonTextContent = false; + /** * @param $article Article */ public function __construct( Article $article ) { $this->mArticle = $article; $this->mTitle = $article->getTitle(); + + $this->contentModel = $this->mTitle->getContentModel(); + + $handler = ContentHandler::getForModelID( $this->contentModel ); + $this->contentFormat = $handler->getDefaultFormat(); } /** @@ -267,7 +305,7 @@ class EditPage { /** * Get the context title object. - * If not set, $wgTitle will be returned. This behavior might changed in + * If not set, $wgTitle will be returned. This behavior might change in * the future to return $this->mTitle instead. * * @return Title object @@ -359,11 +397,10 @@ 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->isNew = !$this->mTitle->exists() || $this->section == 'new'; # Show applicable editing introductions if ( $this->formtype == 'initial' || $this->firsttime ) { @@ -392,10 +429,13 @@ class EditPage { wfProfileOut( __METHOD__ ); return; } - if ( !$this->mTitle->getArticleID() ) + + if ( !$this->mTitle->getArticleID() ) { wfRunHooks( 'EditFormPreloadText', array( &$this->textbox1, &$this->mTitle ) ); - else + } else { wfRunHooks( 'EditFormInitialText', array( $this ) ); + } + } $this->showEditForm(); @@ -436,8 +476,9 @@ class EditPage { * "View source for ..." page displaying the source code after the error message. * * @since 1.19 - * @param $permErrors Array of permissions errors, as returned by + * @param array $permErrors of permissions errors, as returned by * Title::getUserPermissionsErrors(). + * @throws PermissionsError */ protected function displayPermissionsError( array $permErrors ) { global $wgRequest, $wgOut; @@ -450,15 +491,16 @@ class EditPage { return; } - $content = $this->getContent(); + $content = $this->getContentObject(); # Use the normal message if there's nothing to display - if ( $this->firsttime && $content === '' ) { + if ( $this->firsttime && ( !$content || $content->isEmpty() ) ) { $action = $this->mTitle->exists() ? 'edit' : ( $this->mTitle->isTalkPage() ? 'createtalk' : 'createpage' ); throw new PermissionsError( $action, $permErrors ); } + $wgOut->setRobotPolicy( 'noindex,nofollow' ); $wgOut->setPageTitle( wfMessage( 'viewsource-title', $this->getContextTitle()->getPrefixedText() ) ); $wgOut->addBacklinkSubtitle( $this->getContextTitle() ); $wgOut->addWikiText( $wgOut->formatPermissionsErrorMessage( $permErrors, 'edit' ) ); @@ -467,13 +509,14 @@ class EditPage { # If the user made changes, preserve them when showing the markup # (This happens when a user is blocked during edit, for instance) if ( !$this->firsttime ) { - $content = $this->textbox1; + $text = $this->textbox1; $wgOut->addWikiMsg( 'viewyourtext' ); } else { + $text = $this->toEditText( $content ); $wgOut->addWikiMsg( 'viewsourcetext' ); } - $this->showTextbox( $content, 'wpTextbox1', array( 'readonly' ) ); + $this->showTextbox( $text, 'wpTextbox1', array( 'readonly' ) ); $wgOut->addHTML( Html::rawElement( 'div', array( 'class' => 'templatesUsed' ), Linker::formatTemplates( $this->getTemplates() ) ) ); @@ -520,11 +563,11 @@ class EditPage { // Nothing *to* preview for new sections return false; } elseif ( ( $wgRequest->getVal( 'preload' ) !== null || $this->mTitle->exists() ) && $wgUser->getOption( 'previewonfirst' ) ) { - // Standard preference behaviour + // Standard preference behavior return true; } elseif ( !$this->mTitle->exists() && - isset( $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] ) && - $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] ) + isset( $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] ) && + $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] ) { // Categories are special return true; @@ -554,13 +597,15 @@ class EditPage { } /** - * Does this EditPage class support section editing? - * This is used by EditPage subclasses to indicate their ui cannot handle section edits + * Returns whether section editing is supported for the current page. + * Subclasses may override this to replace the default behavior, which is + * to check ContentHandler::supportsSections. * - * @return bool + * @return bool true if this edit page supports sections, false otherwise. */ protected function isSectionEditSupported() { - return true; + $contentHandler = ContentHandler::getForTitle( $this->mTitle ); + return $contentHandler->supportsSections(); } /** @@ -568,13 +613,19 @@ class EditPage { * @param $request WebRequest */ function importFormData( &$request ) { - global $wgLang, $wgUser; + global $wgContLang, $wgUser; wfProfileIn( __METHOD__ ); # Section edit can come from either the form or a link $this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) ); + if ( $this->section !== null && $this->section !== '' && !$this->isSectionEditSupported() ) { + throw new ErrorPageError( 'sectioneditnotsupported-title', 'sectioneditnotsupported-text' ); + } + + $this->isNew = !$this->mTitle->exists() || $this->section == 'new'; + if ( $request->wasPosted() ) { # These fields need to be checked for encoding. # Also remove trailing whitespace, but don't remove _initial_ @@ -586,13 +637,15 @@ class EditPage { // modified by subclasses wfProfileIn( get_class( $this ) . "::importContentFormData" ); $textbox1 = $this->importContentFormData( $request ); - if ( isset( $textbox1 ) ) + if ( $textbox1 !== null ) { $this->textbox1 = $textbox1; + } + wfProfileOut( get_class( $this ) . "::importContentFormData" ); } # Truncate for whole multibyte characters - $this->summary = $wgLang->truncate( $request->getText( 'wpSummary' ), 255 ); + $this->summary = $wgContLang->truncate( $request->getText( 'wpSummary' ), 255 ); # If the summary consists of a heading, e.g. '==Foobar==', extract the title from the # header syntax, e.g. 'Foobar'. This is mainly an issue when we are using wpSummary for @@ -604,7 +657,7 @@ class EditPage { # currently doing double duty as both edit summary and section title. Right now this # is just to allow API edits to work around this limitation, but this should be # incorporated into the actual edit form when EditPage is rewritten (Bugs 18654, 26312). - $this->sectiontitle = $wgLang->truncate( $request->getText( 'wpSectionTitle' ), 255 ); + $this->sectiontitle = $wgContLang->truncate( $request->getText( 'wpSectionTitle' ), 255 ); $this->sectiontitle = preg_replace( '/^\s*=+\s*(.*?)\s*=+\s*$/', '$1', $this->sectiontitle ); $this->edittime = $request->getVal( 'wpEdittime' ); @@ -661,7 +714,7 @@ class EditPage { $this->starttime = null; } - $this->recreate = $request->getCheck( 'wpRecreate' ); + $this->recreate = $request->getCheck( 'wpRecreate' ); $this->minoredit = $request->getCheck( 'wpMinoredit' ); $this->watchthis = $request->getCheck( 'wpWatchthis' ); @@ -679,18 +732,18 @@ class EditPage { } else { # Not a posted form? Start with nothing. wfDebug( __METHOD__ . ": Not a posted form.\n" ); - $this->textbox1 = ''; - $this->summary = ''; + $this->textbox1 = ''; + $this->summary = ''; $this->sectiontitle = ''; - $this->edittime = ''; - $this->starttime = wfTimestampNow(); - $this->edit = false; - $this->preview = false; - $this->save = false; - $this->diff = false; - $this->minoredit = false; - $this->watchthis = $request->getBool( 'watchthis', false ); // Watch may be overriden by request parameters - $this->recreate = false; + $this->edittime = ''; + $this->starttime = wfTimestampNow(); + $this->edit = false; + $this->preview = false; + $this->save = false; + $this->diff = false; + $this->minoredit = false; + $this->watchthis = $request->getBool( 'watchthis', false ); // Watch may be overridden by request parameters + $this->recreate = false; // When creating a new section, we can preload a section title by passing it as the // preloadtitle parameter in the URL (Bug 13100) @@ -711,10 +764,17 @@ class EditPage { } } + $this->oldid = $request->getInt( 'oldid' ); + $this->bot = $request->getBool( 'bot', true ); $this->nosummary = $request->getBool( 'nosummary' ); - $this->oldid = $request->getInt( 'oldid' ); + $content_handler = ContentHandler::getForTitle( $this->mTitle ); + $this->contentModel = $request->getText( 'model', $content_handler->getModelID() ); #may be overridden by revision + $this->contentFormat = $request->getText( 'format', $content_handler->getDefaultFormat() ); #may be overridden by revision + + #TODO: check if the desired model is allowed in this namespace, and if a transition from the page's current model to the new model is allowed + #TODO: check if the desired content model supports the given content format! $this->live = $request->getCheck( 'live' ); $this->editintro = $request->getText( 'editintro', @@ -730,7 +790,7 @@ class EditPage { /** * Subpage overridable method for extracting the page content data from the * posted form to be placed in $this->textbox1, if using customized input - * this method should be overrided and return the page text that will be used + * this method should be overridden and return the page text that will be used * for saving, preview parsing and so on... * * @param $request WebRequest @@ -747,7 +807,13 @@ class EditPage { function initialiseForm() { global $wgUser; $this->edittime = $this->mArticle->getTimestamp(); - $this->textbox1 = $this->getContent( false ); + + $content = $this->getContentObject( false ); #TODO: track content object?! + if ( $content === false ) { + return false; + } + $this->textbox1 = $this->toEditText( $content ); + // activate checkboxes if user wants them to be always active # Sort out the "watch" checkbox if ( $wgUser->getOption( 'watchdefault' ) ) { @@ -773,36 +839,65 @@ class EditPage { /** * Fetch initial editing page content. * - * @param $def_text string + * @param $def_text string|bool * @return mixed string on success, $def_text for invalid sections * @private + * @deprecated since 1.21, get WikiPage::getContent() instead. */ - function getContent( $def_text = '' ) { - global $wgOut, $wgRequest, $wgParser; + function getContent( $def_text = false ) { + ContentHandler::deprecated( __METHOD__, '1.21' ); + + if ( $def_text !== null && $def_text !== false && $def_text !== '' ) { + $def_content = $this->toEditContent( $def_text ); + } else { + $def_content = false; + } + + $content = $this->getContentObject( $def_content ); + + // Note: EditPage should only be used with text based content anyway. + return $this->toEditText( $content ); + } + + /** + * @param Content|null $def_content The default value to return + * + * @return mixed Content on success, $def_content for invalid sections + * + * @since 1.21 + */ + protected function getContentObject( $def_content = null ) { + global $wgOut, $wgRequest; wfProfileIn( __METHOD__ ); - $text = false; + $content = false; // For message page not locally set, use the i18n message. // For other non-existent articles, use preload text if any. if ( !$this->mTitle->exists() || $this->section == 'new' ) { if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI && $this->section != 'new' ) { # If this is a system message, get the default text. - $text = $this->mTitle->getDefaultMessageText(); + $msg = $this->mTitle->getDefaultMessageText(); + + $content = $this->toEditContent( $msg ); } - if ( $text === false ) { + if ( $content === false ) { # If requested, preload some text. $preload = $wgRequest->getVal( 'preload', // Custom preload text for new sections $this->section === 'new' ? 'MediaWiki:addsection-preload' : '' ); - $text = $this->getPreloadedText( $preload ); + + $content = $this->getPreloadedContent( $preload ); } // For existing pages, get text based on "undo" or section parameters. } else { if ( $this->section != '' ) { // Get section edit text (returns $def_text for invalid sections) - $text = $wgParser->getSection( $this->getOriginalContent(), $this->section, $def_text ); + $orig = $this->getOriginalContent(); + $content = $orig ? $orig->getSection( $this->section ) : null; + + if ( !$content ) $content = $def_content; } else { $undoafter = $wgRequest->getInt( 'undoafter' ); $undo = $wgRequest->getInt( 'undo' ); @@ -818,15 +913,16 @@ class EditPage { # Sanity check, make sure it's the right page, # the revisions exist and they were not deleted. - # Otherwise, $text will be left as-is. + # Otherwise, $content will be left as-is. if ( !is_null( $undorev ) && !is_null( $oldrev ) && $undorev->getPage() == $oldrev->getPage() && $undorev->getPage() == $this->mTitle->getArticleID() && !$undorev->isDeleted( Revision::DELETED_TEXT ) && !$oldrev->isDeleted( Revision::DELETED_TEXT ) ) { - $text = $this->mArticle->getUndoText( $undorev, $oldrev ); - if ( $text === false ) { + $content = $this->mArticle->getUndoContent( $undorev, $oldrev ); + + if ( $content === false ) { # Warn the user that something went wrong $undoMsg = 'failure'; } else { @@ -859,14 +955,14 @@ class EditPage { wfMessage( 'undo-' . $undoMsg )->plain() . '', true, /* interface */true ); } - if ( $text === false ) { - $text = $this->getOriginalContent(); + if ( $content === false ) { + $content = $this->getOriginalContent(); } } } wfProfileOut( __METHOD__ ); - return $text; + return $content; } /** @@ -876,38 +972,51 @@ class EditPage { * section replaced in its context (using WikiPage::replaceSection()) * to the original text of the edit. * - * This difers from Article::getContent() that when a missing revision is - * encountered the result will be an empty string and not the + * This differs from Article::getContent() that when a missing revision is + * encountered the result will be null and not the * 'missing-revision' message. * * @since 1.19 - * @return string + * @return Content|null */ private function getOriginalContent() { if ( $this->section == 'new' ) { - return $this->getCurrentText(); + return $this->getCurrentContent(); } $revision = $this->mArticle->getRevisionFetched(); if ( $revision === null ) { - return ''; + if ( !$this->contentModel ) $this->contentModel = $this->getTitle()->getContentModel(); + $handler = ContentHandler::getForModelID( $this->contentModel ); + + return $handler->makeEmptyContent(); } - return $this->mArticle->getContent(); + $content = $revision->getContent(); + return $content; } /** - * Get the actual text of the page. This is basically similar to - * WikiPage::getRawText() except that when the page doesn't exist an empty - * string is returned instead of false. + * Get the current content of the page. This is basically similar to + * WikiPage::getContent( Revision::RAW ) except that when the page doesn't exist an empty + * content object is returned instead of null. * - * @since 1.19 - * @return string + * @since 1.21 + * @return Content */ - private function getCurrentText() { - $text = $this->mArticle->getRawText(); - if ( $text === false ) { - return ''; + protected function getCurrentContent() { + $rev = $this->mArticle->getRevision(); + $content = $rev ? $rev->getContent( Revision::RAW ) : null; + + if ( $content === false || $content === null ) { + if ( !$this->contentModel ) $this->contentModel = $this->getTitle()->getContentModel(); + $handler = ContentHandler::getForModelID( $this->contentModel ); + + return $handler->makeEmptyContent(); } else { - return $text; + # nasty side-effect, but needed for consistency + $this->contentModel = $rev->getContentModel(); + $this->contentFormat = $rev->getContentFormat(); + + return $content; } } @@ -915,47 +1024,111 @@ class EditPage { * Use this method before edit() to preload some text into the edit box * * @param $text string + * @deprecated since 1.21, use setPreloadedContent() instead. */ public function setPreloadedText( $text ) { - $this->mPreloadText = $text; + ContentHandler::deprecated( __METHOD__, "1.21" ); + + $content = $this->toEditContent( $text ); + + $this->setPreloadedContent( $content ); + } + + /** + * Use this method before edit() to preload some content into the edit box + * + * @param $content Content + * + * @since 1.21 + */ + public function setPreloadedContent( Content $content ) { + $this->mPreloadContent = $content; } /** * Get the contents to be preloaded into the box, either set by * an earlier setPreloadText() or by loading the given page. * - * @param $preload String: representing the title to preload from. + * @param string $preload representing the title to preload from. + * * @return String + * + * @deprecated since 1.21, use getPreloadedContent() instead */ protected function getPreloadedText( $preload ) { - global $wgUser, $wgParser; + ContentHandler::deprecated( __METHOD__, "1.21" ); + + $content = $this->getPreloadedContent( $preload ); + $text = $this->toEditText( $content ); - if ( !empty( $this->mPreloadText ) ) { - return $this->mPreloadText; + return $text; + } + + /** + * Get the contents to be preloaded into the box, either set by + * an earlier setPreloadText() or by loading the given page. + * + * @param string $preload representing the title to preload from. + * + * @return Content + * + * @since 1.21 + */ + protected function getPreloadedContent( $preload ) { + global $wgUser; + + if ( !empty( $this->mPreloadContent ) ) { + return $this->mPreloadContent; } + $handler = ContentHandler::getForTitle( $this->getTitle() ); + if ( $preload === '' ) { - return ''; + return $handler->makeEmptyContent(); } $title = Title::newFromText( $preload ); # Check for existence to avoid getting MediaWiki:Noarticletext - if ( $title === null || !$title->exists() || !$title->userCan( 'read' ) ) { - return ''; + if ( $title === null || !$title->exists() || !$title->userCan( 'read', $wgUser ) ) { + //TODO: somehow show a warning to the user! + return $handler->makeEmptyContent(); } $page = WikiPage::factory( $title ); if ( $page->isRedirect() ) { $title = $page->getRedirectTarget(); # Same as before - if ( $title === null || !$title->exists() || !$title->userCan( 'read' ) ) { - return ''; + if ( $title === null || !$title->exists() || !$title->userCan( 'read', $wgUser ) ) { + //TODO: somehow show a warning to the user! + return $handler->makeEmptyContent(); } $page = WikiPage::factory( $title ); } $parserOptions = ParserOptions::newFromUser( $wgUser ); - return $wgParser->getPreloadText( $page->getRawText(), $title, $parserOptions ); + $content = $page->getContent( Revision::RAW ); + + if ( !$content ) { + //TODO: somehow show a warning to the user! + return $handler->makeEmptyContent(); + } + + if ( $content->getModel() !== $handler->getModelID() ) { + $converted = $content->convert( $handler->getModelID() ); + + if ( !$converted ) { + //TODO: somehow show a warning to the user! + wfDebug( "Attempt to preload incompatible content: " + . "can't convert " . $content->getModel() + . " to " . $handler->getModelID() ); + + return $handler->makeEmptyContent(); + } + + $content = $converted; + } + + return $content->preloadTransform( $title, $parserOptions ); } /** @@ -973,8 +1146,36 @@ class EditPage { return $this->mTokenOk; } + /** + * Sets post-edit cookie indicating the user just saved a particular revision. + * + * This uses a temporary cookie for each revision ID so separate saves will never + * interfere with each other. + * + * The cookie is deleted in the mediawiki.action.view.postEdit JS module after + * the redirect. It must be clearable by JavaScript code, so it must not be + * marked HttpOnly. The JavaScript code converts the cookie to a wgPostEdit config + * variable. + * + * Since WebResponse::setcookie does not allow forcing HttpOnly for a single + * cookie, we have to use PHP's setcookie() directly. + * + * We use a path of '/' since wgCookiePath is not exposed to JS + * + * If the variable were set on the server, it would be cached, which is unwanted + * since the post-edit state should only apply to the load right after the save. + */ + protected function setPostEditCookie() { + global $wgCookiePrefix, $wgCookieDomain; + $revisionId = $this->mArticle->getLatest(); + $postEditKey = self::POST_EDIT_COOKIE_KEY_PREFIX . $revisionId; + + setcookie( $wgCookiePrefix . $postEditKey, '1', time() + self::POST_EDIT_COOKIE_DURATION, '/', $wgCookieDomain ); + } + /** * Attempt submission + * @throws UserBlockedError|ReadOnlyError|ThrottledError|PermissionsError * @return bool false if output is done, true if the rest of the form should be displayed */ function attemptSave() { @@ -987,6 +1188,9 @@ class EditPage { // FIXME: once the interface for internalAttemptSave() is made nicer, this should use the message in $status if ( $status->value == self::AS_SUCCESS_UPDATE || $status->value == self::AS_SUCCESS_NEW_ARTICLE ) { $this->didSave = true; + if ( !$resultDetails['nullEdit'] ) { + $this->setPostEditCookie(); + } } switch ( $status->value ) { @@ -1003,6 +1207,10 @@ class EditPage { case self::AS_HOOK_ERROR: return false; + case self::AS_PARSE_ERROR: + $wgOut->addWikiText( '
    ' . $status->getWikiText() . '
    ' ); + return true; + case self::AS_SUCCESS_NEW_ARTICLE: $query = $resultDetails['redirect'] ? 'redirect=no' : ''; $anchor = isset ( $resultDetails['sectionanchor'] ) ? $resultDetails['sectionanchor'] : ''; @@ -1066,11 +1274,63 @@ class EditPage { } } + /** + * Run hooks that can filter edits just before they get saved. + * + * @param Content $content the Content to filter. + * @param Status $status for reporting the outcome to the caller + * @param User $user the user performing the edit + * + * @return bool + */ + protected function runPostMergeFilters( Content $content, Status $status, User $user ) { + // Run old style post-section-merge edit filter + if ( !ContentHandler::runLegacyHooks( 'EditFilterMerged', + array( $this, $content, &$this->hookError, $this->summary ) ) ) { + + # Error messages etc. could be handled within the hook... + $status->fatal( 'hookaborted' ); + $status->value = self::AS_HOOK_ERROR; + return false; + } elseif ( $this->hookError != '' ) { + # ...or the hook could be expecting us to produce an error + $status->fatal( 'hookaborted' ); + $status->value = self::AS_HOOK_ERROR_EXPECTED; + return false; + } + + // Run new style post-section-merge edit filter + if ( !wfRunHooks( 'EditFilterMergedContent', + array( $this->mArticle->getContext(), $content, $status, $this->summary, + $user, $this->minoredit ) ) ) { + + # Error messages etc. could be handled within the hook... + // XXX: $status->value may already be something informative... + $this->hookError = $status->getWikiText(); + $status->fatal( 'hookaborted' ); + $status->value = self::AS_HOOK_ERROR; + return false; + } elseif ( !$status->isOK() ) { + # ...or the hook could be expecting us to produce an error + // FIXME this sucks, we should just use the Status object throughout + $this->hookError = $status->getWikiText(); + $status->fatal( 'hookaborted' ); + $status->value = self::AS_HOOK_ERROR_EXPECTED; + return false; + } + + return true; + } + /** * Attempt submission (no UI) * - * @param $result - * @param $bot bool + * @param array $result array to add statuses to, currently with the possible keys: + * spam - string - Spam string from content if any spam is detected by matchSpamRegex + * sectionanchor - string - Section anchor for a section save + * nullEdit - boolean - Set if doEditContent is OK. True if null edit, false otherwise. + * redirect - boolean - Set if doEditContent is OK. True if resulting revision is a redirect + * @param bool $bot True if edit is being made under the bot right. * * @return Status object, possibly with a message, but always with one of the AS_* constants in $status->value, * @@ -1083,7 +1343,7 @@ class EditPage { $status = Status::newGood(); - wfProfileIn( __METHOD__ ); + wfProfileIn( __METHOD__ ); wfProfileIn( __METHOD__ . '-checks' ); if ( !wfRunHooks( 'EditPage::attemptSave', array( $this ) ) ) { @@ -1091,19 +1351,30 @@ class EditPage { $status->fatal( 'hookaborted' ); $status->value = self::AS_HOOK_ERROR; wfProfileOut( __METHOD__ . '-checks' ); - wfProfileOut( __METHOD__ ); + wfProfileOut( __METHOD__ ); + return $status; + } + + try { + # Construct Content object + $textbox_content = $this->toEditContent( $this->textbox1 ); + } catch ( MWContentSerializationException $ex ) { + $status->fatal( 'content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage() ); + $status->value = self::AS_PARSE_ERROR; + wfProfileOut( __METHOD__ . '-checks' ); + wfProfileOut( __METHOD__ ); return $status; } # Check image redirect if ( $this->mTitle->getNamespace() == NS_FILE && - Title::newFromRedirect( $this->textbox1 ) instanceof Title && + $textbox_content->isRedirect() && !$wgUser->isAllowed( 'upload' ) ) { $code = $wgUser->isAnon() ? self::AS_IMAGE_REDIRECT_ANON : self::AS_IMAGE_REDIRECT_LOGGED; $status->setResult( false, $code ); wfProfileOut( __METHOD__ . '-checks' ); - wfProfileOut( __METHOD__ ); + wfProfileOut( __METHOD__ ); return $status; } @@ -1209,7 +1480,7 @@ class EditPage { if ( $new ) { // Late check for create permission, just in case *PARANOIA* - if ( !$this->mTitle->userCan( 'create' ) ) { + if ( !$this->mTitle->userCan( 'create', $wgUser ) ) { $status->fatal( 'nocreatetext' ); $status->value = self::AS_NO_CREATE_PERMISSION; wfDebug( __METHOD__ . ": no create permission\n" ); @@ -1224,28 +1495,18 @@ class EditPage { return $status; } - // Run post-section-merge edit filter - if ( !wfRunHooks( 'EditFilterMerged', array( $this, $this->textbox1, &$this->hookError, $this->summary ) ) ) { - # Error messages etc. could be handled within the hook... - $status->fatal( 'hookaborted' ); - $status->value = self::AS_HOOK_ERROR; - wfProfileOut( __METHOD__ ); - return $status; - } elseif ( $this->hookError != '' ) { - # ...or the hook could be expecting us to produce an error - $status->fatal( 'hookaborted' ); - $status->value = self::AS_HOOK_ERROR_EXPECTED; + if ( !$this->runPostMergeFilters( $textbox_content, $status, $wgUser ) ) { wfProfileOut( __METHOD__ ); return $status; } - $text = $this->textbox1; + $content = $textbox_content; + $result['sectionanchor'] = ''; if ( $this->section == 'new' ) { if ( $this->sectiontitle !== '' ) { // Insert the section title above the content. - $text = wfMessage( 'newsectionheaderdefaultlevel', $this->sectiontitle ) - ->inContentLanguage()->text() . "\n\n" . $text; + $content = $content->addSectionHeader( $this->sectiontitle ); // Jump to the new section $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle ); @@ -1260,8 +1521,7 @@ class EditPage { } } elseif ( $this->summary !== '' ) { // Insert the section title above the content. - $text = wfMessage( 'newsectionheaderdefaultlevel', $this->summary ) - ->inContentLanguage()->text() . "\n\n" . $text; + $content = $content->addSectionHeader( $this->summary ); // Jump to the new section $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText( $this->summary ); @@ -1275,10 +1535,13 @@ class EditPage { $status->value = self::AS_SUCCESS_NEW_ARTICLE; - } else { + } else { # not $new # Article exists. Check for edit conflict. + + $this->mArticle->clear(); # Force reload of dates, etc. $timestamp = $this->mArticle->getTimestamp(); + wfDebug( "timestamp: {$timestamp}, edittime: {$this->edittime}\n" ); if ( $timestamp != $this->edittime ) { @@ -1295,7 +1558,8 @@ class EditPage { $this->isConflict = false; wfDebug( __METHOD__ . ": conflict suppressed; new section\n" ); } - } elseif ( $this->section == '' && Revision::userWasLastToEdit( DB_MASTER, $this->mTitle->getArticleID(), $wgUser->getId(), $this->edittime ) ) { + } elseif ( $this->section == '' && Revision::userWasLastToEdit( DB_MASTER, $this->mTitle->getArticleID(), + $wgUser->getId(), $this->edittime ) ) { # Suppress edit conflict with self, except for section edits where merging is required. wfDebug( __METHOD__ . ": Suppressing edit conflict, same user.\n" ); $this->isConflict = false; @@ -1310,26 +1574,31 @@ class EditPage { $sectionTitle = $this->summary; } + $content = null; + if ( $this->isConflict ) { - wfDebug( __METHOD__ . ": conflict! getting section '$this->section' for time '$this->edittime' (article time '{$timestamp}')\n" ); - $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $sectionTitle, $this->edittime ); + wfDebug( __METHOD__ . ": conflict! getting section '{$this->section}' for time '{$this->edittime}'" + . " (article time '{$timestamp}')\n" ); + + $content = $this->mArticle->replaceSectionContent( $this->section, $textbox_content, $sectionTitle, $this->edittime ); } else { - wfDebug( __METHOD__ . ": getting section '$this->section'\n" ); - $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $sectionTitle ); + wfDebug( __METHOD__ . ": getting section '{$this->section}'\n" ); + $content = $this->mArticle->replaceSectionContent( $this->section, $textbox_content, $sectionTitle ); } - if ( is_null( $text ) ) { + + if ( is_null( $content ) ) { wfDebug( __METHOD__ . ": activating conflict; section replace failed.\n" ); $this->isConflict = true; - $text = $this->textbox1; // do not try to merge here! + $content = $textbox_content; // do not try to merge here! } elseif ( $this->isConflict ) { # Attempt merge - if ( $this->mergeChangesInto( $text ) ) { + if ( $this->mergeChangesIntoContent( $content ) ) { // Successful merge! Maybe we should tell the user the good news? $this->isConflict = false; wfDebug( __METHOD__ . ": Suppressing edit conflict, successful merge.\n" ); } else { $this->section = ''; - $this->textbox1 = $text; + $this->textbox1 = ContentHandler::getContentText( $content ); wfDebug( __METHOD__ . ": Keeping edit conflict, failed merge.\n" ); } } @@ -1340,58 +1609,45 @@ class EditPage { return $status; } - // Run post-section-merge edit filter - if ( !wfRunHooks( 'EditFilterMerged', array( $this, $text, &$this->hookError, $this->summary ) ) ) { - # Error messages etc. could be handled within the hook... - $status->fatal( 'hookaborted' ); - $status->value = self::AS_HOOK_ERROR; - wfProfileOut( __METHOD__ ); - return $status; - } elseif ( $this->hookError != '' ) { - # ...or the hook could be expecting us to produce an error - $status->fatal( 'hookaborted' ); - $status->value = self::AS_HOOK_ERROR_EXPECTED; + if ( !$this->runPostMergeFilters( $content, $status, $wgUser ) ) { wfProfileOut( __METHOD__ ); return $status; } - # Handle the user preference to force summaries here, but not for null edits - if ( $this->section != 'new' && !$this->allowBlankSummary - && $this->getOriginalContent() != $text - && !Title::newFromRedirect( $text ) ) # check if it's not a redirect - { - if ( md5( $this->summary ) == $this->autoSumm ) { + if ( $this->section == 'new' ) { + // Handle the user preference to force summaries here + if ( !$this->allowBlankSummary && trim( $this->summary ) == '' ) { $this->missingSummary = true; - $status->fatal( 'missingsummary' ); + $status->fatal( 'missingsummary' ); // or 'missingcommentheader' if $section == 'new'. Blegh $status->value = self::AS_SUMMARY_NEEDED; wfProfileOut( __METHOD__ ); return $status; } - } - # And a similar thing for new sections - if ( $this->section == 'new' && !$this->allowBlankSummary ) { - if ( trim( $this->summary ) == '' ) { - $this->missingSummary = true; - $status->fatal( 'missingsummary' ); // or 'missingcommentheader' if $section == 'new'. Blegh - $status->value = self::AS_SUMMARY_NEEDED; + // Do not allow the user to post an empty comment + if ( $this->textbox1 == '' ) { + $this->missingComment = true; + $status->fatal( 'missingcommenttext' ); + $status->value = self::AS_TEXTBOX_EMPTY; wfProfileOut( __METHOD__ ); return $status; } + } elseif ( !$this->allowBlankSummary + && !$content->equals( $this->getOriginalContent() ) + && !$content->isRedirect() + && md5( $this->summary ) == $this->autoSumm + ) { + $this->missingSummary = true; + $status->fatal( 'missingsummary' ); + $status->value = self::AS_SUMMARY_NEEDED; + wfProfileOut( __METHOD__ ); + return $status; } # All's well wfProfileIn( __METHOD__ . '-sectionanchor' ); $sectionanchor = ''; if ( $this->section == 'new' ) { - if ( $this->textbox1 == '' ) { - $this->missingComment = true; - $status->fatal( 'missingcommenttext' ); - $status->value = self::AS_TEXTBOX_EMPTY; - wfProfileOut( __METHOD__ . '-sectionanchor' ); - wfProfileOut( __METHOD__ ); - return $status; - } if ( $this->sectiontitle !== '' ) { $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle ); // If no edit summary was specified, create one automatically from the section @@ -1428,14 +1684,14 @@ class EditPage { // merged the section into full text. Clear the section field // so that later submission of conflict forms won't try to // replace that into a duplicated mess. - $this->textbox1 = $text; + $this->textbox1 = $this->toEditText( $content ); $this->section = ''; $status->value = self::AS_SUCCESS_UPDATE; } // Check for length errors again now that the section is merged in - $this->kblength = (int)( strlen( $text ) / 1024 ); + $this->kblength = (int)( strlen( $this->toEditText( $content ) ) / 1024 ); if ( $this->kblength > $wgMaxArticleSize ) { $this->tooBig = true; $status->setResult( false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED ); @@ -1448,14 +1704,10 @@ class EditPage { ( ( $this->minoredit && !$this->isNew ) ? EDIT_MINOR : 0 ) | ( $bot ? EDIT_FORCE_BOT : 0 ); - $doEditStatus = $this->mArticle->doEdit( $text, $this->summary, $flags ); + $doEditStatus = $this->mArticle->doEditContent( $content, $this->summary, $flags, + false, null, $this->contentFormat ); - if ( $doEditStatus->isOK() ) { - $result['redirect'] = Title::newFromRedirect( $text ) !== null; - $this->commitWatch(); - wfProfileOut( __METHOD__ ); - return $status; - } else { + if ( !$doEditStatus->isOK() ) { // Failure from doEdit() // Show the edit conflict page for certain recognized errors from doEdit(), // but don't show it for errors from extension hooks @@ -1470,63 +1722,107 @@ class EditPage { wfProfileOut( __METHOD__ ); return $doEditStatus; } + + $result['nullEdit'] = $doEditStatus->hasMessage( 'edit-no-change' ); + $result['redirect'] = $content->isRedirect(); + $this->updateWatchlist(); + wfProfileOut( __METHOD__ ); + return $status; } /** - * Commit the change of watch status + * Register the change of watch status */ - protected function commitWatch() { + protected function updateWatchlist() { global $wgUser; + if ( $wgUser->isLoggedIn() && $this->watchthis != $wgUser->isWatched( $this->mTitle ) ) { + $fname = __METHOD__; + $title = $this->mTitle; + $watch = $this->watchthis; + + // Do this in its own transaction to reduce contention... $dbw = wfGetDB( DB_MASTER ); - $dbw->begin( __METHOD__ ); - if ( $this->watchthis ) { - WatchAction::doWatch( $this->mTitle, $wgUser ); - } else { - WatchAction::doUnwatch( $this->mTitle, $wgUser ); - } - $dbw->commit( __METHOD__ ); + $dbw->onTransactionIdle( function() use ( $dbw, $title, $watch, $wgUser, $fname ) { + $dbw->begin( $fname ); + if ( $watch ) { + WatchAction::doWatch( $title, $wgUser ); + } else { + WatchAction::doUnwatch( $title, $wgUser ); + } + $dbw->commit( $fname ); + } ); } } /** - * @private - * @todo document + * Attempts to merge text content with base and current revisions * * @param $editText string * * @return bool + * @deprecated since 1.21, use mergeChangesIntoContent() instead */ function mergeChangesInto( &$editText ) { + ContentHandler::deprecated( __METHOD__, "1.21" ); + + $editContent = $this->toEditContent( $editText ); + + $ok = $this->mergeChangesIntoContent( $editContent ); + + if ( $ok ) { + $editText = $this->toEditText( $editContent ); + return true; + } + return false; + } + + /** + * Attempts to do 3-way merge of edit content with a base revision + * and current content, in case of edit conflict, in whichever way appropriate + * for the content type. + * + * @since 1.21 + * + * @param $editContent + * + * @return bool + */ + private function mergeChangesIntoContent( &$editContent ) { wfProfileIn( __METHOD__ ); $db = wfGetDB( DB_MASTER ); // This is the revision the editor started from $baseRevision = $this->getBaseRevision(); - if ( is_null( $baseRevision ) ) { + $baseContent = $baseRevision ? $baseRevision->getContent() : null; + + if ( is_null( $baseContent ) ) { wfProfileOut( __METHOD__ ); return false; } - $baseText = $baseRevision->getText(); // The current state, we want to merge updates into it $currentRevision = Revision::loadFromTitle( $db, $this->mTitle ); - if ( is_null( $currentRevision ) ) { + $currentContent = $currentRevision ? $currentRevision->getContent() : null; + + if ( is_null( $currentContent ) ) { wfProfileOut( __METHOD__ ); return false; } - $currentText = $currentRevision->getText(); - $result = ''; - if ( wfMerge( $baseText, $editText, $currentText, $result ) ) { - $editText = $result; + $handler = ContentHandler::getForModelID( $baseContent->getModel() ); + + $result = $handler->merge3( $baseContent, $editContent, $currentContent ); + + if ( $result ) { + $editContent = $result; wfProfileOut( __METHOD__ ); return true; - } else { - wfProfileOut( __METHOD__ ); - return false; } + + wfProfileOut( __METHOD__ ); + return false; } /** @@ -1690,10 +1986,13 @@ class EditPage { # Give a notice if the user is editing a deleted/moved page... if ( !$this->mTitle->exists() ) { LogEventsList::showLogExtract( $wgOut, array( 'delete', 'move' ), $this->mTitle, - '', array( 'lim' => 10, - 'conds' => array( "log_action != 'revision'" ), - 'showIfEmpty' => false, - 'msgKey' => array( 'recreate-moveddeleted-warn' ) ) + '', + array( + 'lim' => 10, + 'conds' => array( "log_action != 'revision'" ), + 'showIfEmpty' => false, + 'msgKey' => array( 'recreate-moveddeleted-warn' ) + ) ); } } @@ -1711,17 +2010,77 @@ class EditPage { // Added using template syntax, to take 's into account. $wgOut->addWikiTextTitleTidy( '{{:' . $title->getFullText() . '}}', $this->mTitle ); return true; - } else { - return false; } - } else { - return false; } + return false; + } + + /** + * Gets an editable textual representation of $content. + * The textual representation can be turned by into a Content object by the + * toEditContent() method. + * + * If $content is null or false or a string, $content is returned unchanged. + * + * If the given Content object is not of a type that can be edited using the text base EditPage, + * an exception will be raised. Set $this->allowNonTextContent to true to allow editing of non-textual + * content. + * + * @param Content|null|bool|string $content + * @return String the editable text form of the content. + * + * @throws MWException if $content is not an instance of TextContent and $this->allowNonTextContent is not true. + */ + protected function toEditText( $content ) { + if ( $content === null || $content === false ) { + return $content; + } + + if ( is_string( $content ) ) { + return $content; + } + + if ( !$this->allowNonTextContent && !( $content instanceof TextContent ) ) { + throw new MWException( "This content model can not be edited as text: " + . ContentHandler::getLocalizedName( $content->getModel() ) ); + } + + return $content->serialize( $this->contentFormat ); + } + + /** + * Turns the given text into a Content object by unserializing it. + * + * If the resulting Content object is not of a type that can be edited using the text base EditPage, + * an exception will be raised. Set $this->allowNonTextContent to true to allow editing of non-textual + * content. + * + * @param string|null|bool $text Text to unserialize + * @return Content The content object created from $text. If $text was false or null, false resp. null will be + * returned instead. + * + * @throws MWException if unserializing the text results in a Content object that is not an instance of TextContent + * and $this->allowNonTextContent is not true. + */ + protected function toEditContent( $text ) { + if ( $text === false || $text === null ) { + return $text; + } + + $content = ContentHandler::makeContent( $text, $this->getTitle(), + $this->contentModel, $this->contentFormat ); + + if ( !$this->allowNonTextContent && !( $content instanceof TextContent ) ) { + throw new MWException( "This content model can not be edited as text: " + . ContentHandler::getLocalizedName( $content->getModel() ) ); + } + + return $content; } /** * Send the edit form and related headers to $wgOut - * @param $formCallback Callback that takes an OutputPage parameter; will be called + * @param $formCallback Callback|null that takes an OutputPage parameter; will be called * during form output near the top, for captchas and the like. */ function showEditForm( $formCallback = null ) { @@ -1767,6 +2126,8 @@ class EditPage { } } + //@todo: add EditForm plugin interface and use it here! + // search for textarea1 and textares2, and allow EditForm to override all uses. $wgOut->addHTML( Html::openElement( 'form', array( 'id' => self::EDITFORM_ID, 'name' => self::EDITFORM_ID, 'method' => 'post', 'action' => $this->getActionURL( $this->getContextTitle() ), 'enctype' => 'multipart/form-data' ) ) ); @@ -1820,7 +2181,7 @@ class EditPage { } if ( $this->hasPresetSummary ) { - // If a summary has been preset using &summary= we dont want to prompt for + // If a summary has been preset using &summary= we don't want to prompt for // a different summary. Only prompt for a summary if the summary is blanked. // (Bug 17416) $this->autoSumm = md5( '' ); @@ -1831,6 +2192,9 @@ class EditPage { $wgOut->addHTML( Html::hidden( 'oldid', $this->oldid ) ); + $wgOut->addHTML( Html::hidden( 'format', $this->contentFormat ) ); + $wgOut->addHTML( Html::hidden( 'model', $this->contentModel ) ); + if ( $this->section == 'new' ) { $this->showSummaryInput( true, $this->summary ); $wgOut->addHTML( $this->getSummaryPreview( true, $this->summary ) ); @@ -1843,12 +2207,14 @@ class EditPage { } if ( $this->isConflict ) { - // In an edit conflict bypass the overrideable content form method + // In an edit conflict bypass the overridable content form method // and fallback to the raw wpTextbox1 since editconflicts can't be // resolved between page source edits and custom ui edits using the // custom edit ui. $this->textbox2 = $this->textbox1; - $this->textbox1 = $this->getCurrentText(); + + $content = $this->getCurrentContent(); + $this->textbox1 = $this->toEditText( $content ); $this->showTextbox1(); } else { @@ -1874,7 +2240,13 @@ class EditPage { Linker::formatHiddenCategories( $this->mArticle->getHiddenCategories() ) ) ); if ( $this->isConflict ) { - $this->showConflict(); + try { + $this->showConflict(); + } catch ( MWContentSerializationException $ex ) { + // this can't really happen, but be nice if it does. + $msg = wfMessage( 'content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage() ); + $wgOut->addWikiText( '
    ' . $msg->text() . '
    ' ); + } } $wgOut->addHTML( $this->editFormTextBottom . "\n\n" ); @@ -1909,30 +2281,8 @@ class EditPage { $wgOut->addWikiMsg( 'talkpagetext' ); } - # Optional notices on a per-namespace and per-page basis - $editnotice_ns = 'editnotice-' . $this->mTitle->getNamespace(); - $editnotice_ns_message = wfMessage( $editnotice_ns ); - if ( $editnotice_ns_message->exists() ) { - $wgOut->addWikiText( $editnotice_ns_message->plain() ); - } - if ( MWNamespace::hasSubpages( $this->mTitle->getNamespace() ) ) { - $parts = explode( '/', $this->mTitle->getDBkey() ); - $editnotice_base = $editnotice_ns; - while ( count( $parts ) > 0 ) { - $editnotice_base .= '-' . array_shift( $parts ); - $editnotice_base_msg = wfMessage( $editnotice_base ); - if ( $editnotice_base_msg->exists() ) { - $wgOut->addWikiText( $editnotice_base_msg->plain() ); - } - } - } else { - # Even if there are no subpages in namespace, we still don't want / in MW ns. - $editnoticeText = $editnotice_ns . '-' . str_replace( '/', '-', $this->mTitle->getDBkey() ); - $editnoticeMsg = wfMessage( $editnoticeText ); - if ( $editnoticeMsg->exists() ) { - $wgOut->addWikiText( $editnoticeMsg->plain() ); - } - } + // Add edit notices + $wgOut->addHTML( implode( "\n", $this->mTitle->getEditNotices() ) ); if ( $this->isConflict ) { $wgOut->wrapWikiMsg( "
    \n$1\n
    ", 'explainconflict' ); @@ -1948,7 +2298,7 @@ class EditPage { if ( $this->section != '' && $this->section != 'new' ) { if ( !$this->summary && !$this->preview && !$this->diff ) { - $sectionTitle = self::extractSectionTitle( $this->textbox1 ); + $sectionTitle = self::extractSectionTitle( $this->textbox1 ); //FIXME: use Content object if ( $sectionTitle !== false ) { $this->summary = "/* $sectionTitle */ "; } @@ -1980,7 +2330,7 @@ class EditPage { if ( $revision ) { // Let sysop know that this will make private content public if saved - if ( !$revision->userCan( Revision::DELETED_TEXT ) ) { + if ( !$revision->userCan( Revision::DELETED_TEXT, $wgUser ) ) { $wgOut->wrapWikiMsg( "\n", 'rev-deleted-text-permission' ); } elseif ( $revision->isDeleted( Revision::DELETED_TEXT ) ) { $wgOut->wrapWikiMsg( "\n", 'rev-deleted-text-view' ); @@ -2014,10 +2364,13 @@ class EditPage { $wgOut->wrapWikiMsg( "
    \n$1\n
    ", array( 'userinvalidcssjstitle', $this->mTitle->getSkinFromCssJsSubpage() ) ); } if ( $this->formtype !== 'preview' ) { - if ( $this->isCssSubpage ) + if ( $this->isCssSubpage ) { $wgOut->wrapWikiMsg( "
    \n$1\n
    ", array( 'usercssyoucanpreview' ) ); - if ( $this->isJsSubpage ) + } + + if ( $this->isJsSubpage ) { $wgOut->wrapWikiMsg( "
    \n$1\n
    ", array( 'userjsyoucanpreview' ) ); + } } } } @@ -2073,7 +2426,6 @@ class EditPage { $this->showHeaderCopyrightWarning(); } - /** * Standard summary input and label (wgSummary), abstracted so EditPage * subclasses may reorganize the form. @@ -2081,15 +2433,15 @@ class EditPage { * inferred by the id given to the input. You can remove them both by * passing array( 'id' => false ) to $userInputAttrs. * - * @param $summary string The value of the summary input - * @param $labelText string The html to place inside the label - * @param $inputAttrs array of attrs to use on the input - * @param $spanLabelAttrs array of attrs to use on the span inside the label + * @param string $summary The value of the summary input + * @param string $labelText The html to place inside the label + * @param array $inputAttrs of attrs to use on the input + * @param array $spanLabelAttrs of attrs to use on the span inside the label * * @return array An array in the format array( $label, $input ) */ function getSummaryInput( $summary = "", $labelText = null, $inputAttrs = null, $spanLabelAttrs = null ) { - // Note: the maxlength is overriden in JS to 255 and to make it use UTF-8 bytes, not characters. + // Note: the maxlength is overridden in JS to 255 and to make it use UTF-8 bytes, not characters. $inputAttrs = ( is_array( $inputAttrs ) ? $inputAttrs : array() ) + array( 'id' => 'wpSummary', 'maxlength' => '200', @@ -2118,7 +2470,7 @@ class EditPage { * @param $isSubjectPreview Boolean: true if this is the section subject/title * up top, or false if this is the comment summary * down below the textarea - * @param $summary String: The text of the summary to display + * @param string $summary The text of the summary to display * @return String */ protected function showSummaryInput( $isSubjectPreview, $summary = "" ) { @@ -2144,18 +2496,22 @@ class EditPage { * @param $isSubjectPreview Boolean: true if this is the section subject/title * up top, or false if this is the comment summary * down below the textarea - * @param $summary String: the text of the summary to display + * @param string $summary the text of the summary to display * @return String */ protected function getSummaryPreview( $isSubjectPreview, $summary = "" ) { - if ( !$summary || ( !$this->preview && !$this->diff ) ) + // avoid spaces in preview, gets always trimmed on save + $summary = trim( $summary ); + if ( !$summary || ( !$this->preview && !$this->diff ) ) { return ""; + } global $wgParser; - if ( $isSubjectPreview ) + if ( $isSubjectPreview ) { $summary = wfMessage( 'newsectionsummary', $wgParser->stripSectionName( $summary ) ) ->inContentLanguage()->text(); + } $message = $isSubjectPreview ? 'subject-preview' : 'summary-preview'; @@ -2174,8 +2530,9 @@ class EditPage { HTML ); - if ( !$this->checkUnicodeCompliantBrowser() ) + if ( !$this->checkUnicodeCompliantBrowser() ) { $wgOut->addHTML( Html::hidden( 'safemode', '1' ) ); + } } protected function showFormAfterText() { @@ -2212,8 +2569,8 @@ HTML * The $textoverride method can be used by subclasses overriding showContentForm * to pass back to this method. * - * @param $customAttribs array of html attributes to use in the textarea - * @param $textoverride String: optional text to override $this->textarea1 with + * @param array $customAttribs of html attributes to use in the textarea + * @param string $textoverride optional text to override $this->textarea1 with */ protected function showTextbox1( $customAttribs = null, $textoverride = null ) { if ( $this->wasDeletedSinceLastEdit() && $this->formtype == 'save' ) { @@ -2255,10 +2612,10 @@ HTML $this->showTextbox( $this->textbox2, 'wpTextbox2', array( 'tabindex' => 6, 'readonly' ) ); } - protected function showTextbox( $content, $name, $customAttribs = array() ) { + protected function showTextbox( $text, $name, $customAttribs = array() ) { global $wgOut, $wgUser; - $wikitext = $this->safeUnicodeOutput( $content ); + $wikitext = $this->safeUnicodeOutput( $text ); if ( strval( $wikitext ) !== '' ) { // Ensure there's a newline at the end, otherwise adding lines // is awkward. @@ -2285,13 +2642,15 @@ HTML protected function displayPreviewArea( $previewOutput, $isOnTop = false ) { global $wgOut; $classes = array(); - if ( $isOnTop ) + if ( $isOnTop ) { $classes[] = 'ontop'; + } $attribs = array( 'id' => 'wikiPreview', 'class' => implode( ' ', $classes ) ); - if ( $this->formtype != 'preview' ) + if ( $this->formtype != 'preview' ) { $attribs['style'] = 'display: none;'; + } $wgOut->addHTML( Xml::openElement( 'div', $attribs ) ); @@ -2302,7 +2661,12 @@ HTML $wgOut->addHTML( '' ); if ( $this->formtype == 'diff' ) { - $this->showDiff(); + try { + $this->showDiff(); + } catch ( MWContentSerializationException $ex ) { + $msg = wfMessage( 'content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage() ); + $wgOut->addWikiText( '
    ' . $msg->text() . '
    ' ); + } } } @@ -2310,7 +2674,7 @@ HTML * Append preview output to $wgOut. * Includes category rendering if this is a category page. * - * @param $text String: the HTML to be output for the preview. + * @param string $text the HTML to be output for the preview. */ protected function showPreview( $text ) { global $wgOut; @@ -2334,7 +2698,7 @@ HTML * save and then make a comparison. */ function showDiff() { - global $wgUser, $wgContLang, $wgParser, $wgOut; + global $wgUser, $wgContLang, $wgOut; $oldtitlemsg = 'currentrev'; # if message does not exist, show diff against the preloaded default @@ -2342,24 +2706,43 @@ HTML $oldtext = $this->mTitle->getDefaultMessageText(); if( $oldtext !== false ) { $oldtitlemsg = 'defaultmessagetext'; + $oldContent = $this->toEditContent( $oldtext ); + } else { + $oldContent = null; } } else { - $oldtext = $this->mArticle->getRawText(); + $oldContent = $this->getCurrentContent(); } - $newtext = $this->mArticle->replaceSection( - $this->section, $this->textbox1, $this->summary, $this->edittime ); - wfRunHooks( 'EditPageGetDiffText', array( $this, &$newtext ) ); + $textboxContent = $this->toEditContent( $this->textbox1 ); + + $newContent = $this->mArticle->replaceSectionContent( + $this->section, $textboxContent, + $this->summary, $this->edittime ); - $popts = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang ); - $newtext = $wgParser->preSaveTransform( $newtext, $this->mTitle, $wgUser, $popts ); + if ( $newContent ) { + ContentHandler::runLegacyHooks( 'EditPageGetDiffText', array( $this, &$newContent ) ); + wfRunHooks( 'EditPageGetDiffContent', array( $this, &$newContent ) ); + + $popts = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang ); + $newContent = $newContent->preSaveTransform( $this->mTitle, $wgUser, $popts ); + } - if ( $oldtext !== false || $newtext != '' ) { + if ( ( $oldContent && !$oldContent->isEmpty() ) || ( $newContent && !$newContent->isEmpty() ) ) { $oldtitle = wfMessage( $oldtitlemsg )->parse(); $newtitle = wfMessage( 'yourtext' )->parse(); - $de = new DifferenceEngine( $this->mArticle->getContext() ); - $de->setText( $oldtext, $newtext ); + if ( !$oldContent ) { + $oldContent = $newContent->getContentHandler()->makeEmptyContent(); + } + + if ( !$newContent ) { + $newContent = $oldContent->getContentHandler()->makeEmptyContent(); + } + + $de = $oldContent->getContentHandler()->createDifferenceEngine( $this->mArticle->getContext() ); + $de->setContent( $oldContent, $newContent ); + $difftext = $de->getDiff( $oldtitle, $newtitle ); $de->showDiffStyle(); } else { @@ -2463,7 +2846,9 @@ HTML wfMessage( 'newwindow' )->parse(); $wgOut->addHTML( " {$cancel}\n" ); $wgOut->addHTML( " {$edithelp}\n" ); - $wgOut->addHTML( "\n\n" ); + $wgOut->addHTML( "\n" ); + wfRunHooks( 'EditPage::showStandardInputs:options', array( $this, $wgOut, &$tabindex ) ); + $wgOut->addHTML( "\n" ); } /** @@ -2476,8 +2861,12 @@ HTML if ( wfRunHooks( 'EditPageBeforeConflictDiff', array( &$this, &$wgOut ) ) ) { $wgOut->wrapWikiMsg( '

    $1

    ', "yourdiff" ); - $de = new DifferenceEngine( $this->mArticle->getContext() ); - $de->setText( $this->textbox2, $this->textbox1 ); + $content1 = $this->toEditContent( $this->textbox1 ); + $content2 = $this->toEditContent( $this->textbox2 ); + + $handler = ContentHandler::getForModelID( $this->contentModel ); + $de = $handler->createDifferenceEngine( $this->mArticle->getContext() ); + $de->setContent( $content2, $content1 ); $de->showDiff( wfMessage( 'yourtext' )->parse(), wfMessage( 'storedversion' )->text() @@ -2548,40 +2937,47 @@ HTML $dbr = wfGetDB( DB_SLAVE ); $data = $dbr->selectRow( array( 'logging', 'user' ), - array( 'log_type', - 'log_action', - 'log_timestamp', - 'log_user', - 'log_namespace', - 'log_title', - 'log_comment', - 'log_params', - 'log_deleted', - 'user_name' ), - array( 'log_namespace' => $this->mTitle->getNamespace(), - 'log_title' => $this->mTitle->getDBkey(), - 'log_type' => 'delete', - 'log_action' => 'delete', - 'user_id=log_user' ), + array( + 'log_type', + 'log_action', + 'log_timestamp', + 'log_user', + 'log_namespace', + 'log_title', + 'log_comment', + 'log_params', + 'log_deleted', + 'user_name' + ), array( + 'log_namespace' => $this->mTitle->getNamespace(), + 'log_title' => $this->mTitle->getDBkey(), + 'log_type' => 'delete', + 'log_action' => 'delete', + 'user_id=log_user' + ), __METHOD__, array( 'LIMIT' => 1, 'ORDER BY' => 'log_timestamp DESC' ) ); // Quick paranoid permission checks... if ( is_object( $data ) ) { - if ( $data->log_deleted & LogPage::DELETED_USER ) + if ( $data->log_deleted & LogPage::DELETED_USER ) { $data->user_name = wfMessage( 'rev-deleted-user' )->escaped(); - if ( $data->log_deleted & LogPage::DELETED_COMMENT ) + } + + if ( $data->log_deleted & LogPage::DELETED_COMMENT ) { $data->log_comment = wfMessage( 'rev-deleted-comment' )->escaped(); + } } return $data; } /** * Get the rendered text for previewing. + * @throws MWException * @return string */ function getPreviewText() { - global $wgOut, $wgUser, $wgParser, $wgRawHtml, $wgLang; + global $wgOut, $wgUser, $wgRawHtml, $wgLang; wfProfileIn( __METHOD__ ); @@ -2600,82 +2996,96 @@ HTML return $parsedNote; } - if ( $this->mTriedSave && !$this->mTokenOk ) { - if ( $this->mTokenOkExceptSuffix ) { - $note = wfMessage( 'token_suffix_mismatch' )->plain(); - } else { - $note = wfMessage( 'session_fail_preview' )->plain(); - } - } elseif ( $this->incompleteForm ) { - $note = wfMessage( 'edit_form_incomplete' )->plain(); - } else { - $note = wfMessage( 'previewnote' )->plain() . - ' [[#' . self::EDITFORM_ID . '|' . $wgLang->getArrow() . ' ' . wfMessage( 'continue-editing' )->text() . ']]'; - } + $note = ''; - $parserOptions = $this->mArticle->makeParserOptions( $this->mArticle->getContext() ); + try { + $content = $this->toEditContent( $this->textbox1 ); - $parserOptions->setEditSection( false ); - $parserOptions->setIsPreview( true ); - $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !== '' ); + $previewHTML = ''; + if ( !wfRunHooks( 'AlternateEditPreview', array( $this, &$content, &$previewHTML, &$this->mParserOutput ) ) ) { + wfProfileOut( __METHOD__ ); + return $previewHTML; + } + + if ( $this->mTriedSave && !$this->mTokenOk ) { + if ( $this->mTokenOkExceptSuffix ) { + $note = wfMessage( 'token_suffix_mismatch' )->plain(); - # don't parse non-wikitext pages, show message about preview - if ( $this->mTitle->isCssJsSubpage() || !$this->mTitle->isWikitextPage() ) { - if ( $this->mTitle->isCssJsSubpage() ) { - $level = 'user'; - } elseif ( $this->mTitle->isCssOrJsPage() ) { - $level = 'site'; - } else { - $level = false; - } - - # Used messages to make sure grep find them: - # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview - $class = 'mw-code'; - if ( $level ) { - if ( preg_match( "/\\.css$/", $this->mTitle->getText() ) ) { - $previewtext = "
    \n" . wfMessage( "{$level}csspreview" )->text() . "\n
    "; - $class .= " mw-css"; - } elseif ( preg_match( "/\\.js$/", $this->mTitle->getText() ) ) { - $previewtext = "
    \n" . wfMessage( "{$level}jspreview" )->text() . "\n
    "; - $class .= " mw-js"; } else { - throw new MWException( 'A CSS/JS (sub)page but which is not css nor js!' ); + $note = wfMessage( 'session_fail_preview' )->plain(); } - $parserOutput = $wgParser->parse( $previewtext, $this->mTitle, $parserOptions ); - $previewHTML = $parserOutput->getText(); + } elseif ( $this->incompleteForm ) { + $note = wfMessage( 'edit_form_incomplete' )->plain(); } else { - $previewHTML = ''; + $note = wfMessage( 'previewnote' )->plain() . + ' [[#' . self::EDITFORM_ID . '|' . $wgLang->getArrow() . ' ' . wfMessage( 'continue-editing' )->text() . ']]'; } - $previewHTML .= "
    \n" . htmlspecialchars( $this->textbox1 ) . "\n
    \n"; - } else { - $toparse = $this->textbox1; + $parserOptions = $this->mArticle->makeParserOptions( $this->mArticle->getContext() ); + $parserOptions->setEditSection( false ); + $parserOptions->setIsPreview( true ); + $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !== '' ); - # If we're adding a comment, we need to show the - # summary as the headline - if ( $this->section == "new" && $this->summary != "" ) { - $toparse = wfMessage( 'newsectionheaderdefaultlevel', $this->summary )->inContentLanguage()->text() . "\n\n" . $toparse; - } + # don't parse non-wikitext pages, show message about preview + if ( $this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage() ) { + if( $this->mTitle->isCssJsSubpage() ) { + $level = 'user'; + } elseif( $this->mTitle->isCssOrJsPage() ) { + $level = 'site'; + } else { + $level = false; + } - wfRunHooks( 'EditPageGetPreviewText', array( $this, &$toparse ) ); + if ( $content->getModel() == CONTENT_MODEL_CSS ) { + $format = 'css'; + } elseif ( $content->getModel() == CONTENT_MODEL_JAVASCRIPT ) { + $format = 'js'; + } else { + $format = false; + } - $toparse = $wgParser->preSaveTransform( $toparse, $this->mTitle, $wgUser, $parserOptions ); - $parserOutput = $wgParser->parse( $toparse, $this->mTitle, $parserOptions ); + # Used messages to make sure grep find them: + # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview + if( $level && $format ) { + $note = "
    " . wfMessage( "{$level}{$format}preview" )->text() . "
    "; + } + } - $rt = Title::newFromRedirectArray( $this->textbox1 ); + $rt = $content->getRedirectChain(); if ( $rt ) { $previewHTML = $this->mArticle->viewRedirect( $rt, false ); } else { - $previewHTML = $parserOutput->getText(); - } - $this->mParserOutput = $parserOutput; - $wgOut->addParserOutputNoText( $parserOutput ); + # If we're adding a comment, we need to show the + # summary as the headline + if ( $this->section === "new" && $this->summary !== "" ) { + $content = $content->addSectionHeader( $this->summary ); + } + + $hook_args = array( $this, &$content ); + ContentHandler::runLegacyHooks( 'EditPageGetPreviewText', $hook_args ); + wfRunHooks( 'EditPageGetPreviewContent', $hook_args ); + + $parserOptions->enableLimitReport(); + + # For CSS/JS pages, we should have called the ShowRawCssJs hook here. + # But it's now deprecated, so never mind - if ( count( $parserOutput->getWarnings() ) ) { - $note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() ); + $content = $content->preSaveTransform( $this->mTitle, $wgUser, $parserOptions ); + $parserOutput = $content->getParserOutput( $this->getArticle()->getTitle(), null, $parserOptions ); + + $previewHTML = $parserOutput->getText(); + $this->mParserOutput = $parserOutput; + $wgOut->addParserOutputNoText( $parserOutput ); + + if ( count( $parserOutput->getWarnings() ) ) { + $note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() ); + } } + } catch ( MWContentSerializationException $ex ) { + $m = wfMessage( 'content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage() ); + $note .= "\n\n" . $m->parse(); + $previewHTML = ''; } if ( $this->isConflict ) { @@ -2887,8 +3297,8 @@ HTML * Returns an array of html code of the following checkboxes: * minor and watch * - * @param $tabindex int Current tabindex - * @param $checked Array of checkbox => bool, where bool indicates the checked + * @param int $tabindex Current tabindex + * @param array $checked of checkbox => bool, where bool indicates the checked * status of the checkbox * * @return array @@ -2938,7 +3348,7 @@ HTML * Returns an array of html code of the following buttons: * save, diff, preview and live * - * @param $tabindex int Current tabindex + * @param int $tabindex Current tabindex * * @return array */ @@ -3067,7 +3477,7 @@ HTML /** * Produce the stock "your edit contains spam" page * - * @param $match string Text which triggered one or more filters + * @param string|bool $match Text which triggered one or more filters * @deprecated since 1.17 Use method spamPageWithContent() instead */ static function spamPage( $match = false ) { @@ -3096,7 +3506,7 @@ HTML global $wgOut, $wgLang; $this->textbox2 = $this->textbox1; - if( is_array( $match ) ){ + if( is_array( $match ) ) { $match = $wgLang->listToText( $match ); } $wgOut->prepareErrorPage( wfMessage( 'spamprotectiontitle' ) ); @@ -3210,7 +3620,7 @@ HTML * @private */ function makesafe( $invalue ) { - // Armor existing references for reversability. + // Armor existing references for reversibility. $invalue = strtr( $invalue, array( "&#x" => "�" ) ); $bytesleft = 0; @@ -3262,7 +3672,7 @@ HTML $i++; } while ( ctype_xdigit( $invalue[$i] ) && ( $i < strlen( $invalue ) ) ); - // Do some sanity checks. These aren't needed for reversability, + // Do some sanity checks. These aren't needed for reversibility, // but should help keep the breakage down if the editor // breaks one of the entities whilst editing. if ( ( substr( $invalue, $i, 1 ) == ";" ) and ( strlen( $hexstring ) <= 6 ) ) { @@ -3275,7 +3685,7 @@ HTML $result .= substr( $invalue, $i, 1 ); } } - // reverse the transform that we made for reversability reasons. + // reverse the transform that we made for reversibility reasons. return strtr( $result, array( "�" => "&#x" ) ); } } diff --git a/includes/Exception.php b/includes/Exception.php index 714f73e8..0bd7a2a7 100644 --- a/includes/Exception.php +++ b/includes/Exception.php @@ -64,8 +64,8 @@ class MWException extends Exception { /** * Run hook to allow extensions to modify the text of the exception * - * @param $name string: class name of the exception - * @param $args array: arguments to pass to the callback functions + * @param string $name class name of the exception + * @param array $args arguments to pass to the callback functions * @return string|null string to output or null if any hook has been called */ function runHooks( $name, $args = array() ) { @@ -83,7 +83,7 @@ class MWException extends Exception { $callargs = array_merge( array( $this ), $args ); foreach ( $hooks as $hook ) { - if ( is_string( $hook ) || ( is_array( $hook ) && count( $hook ) >= 2 && is_string( $hook[0] ) ) ) { // 'function' or array( 'class', hook' ) + if ( is_string( $hook ) || ( is_array( $hook ) && count( $hook ) >= 2 && is_string( $hook[0] ) ) ) { // 'function' or array( 'class', hook' ) $result = call_user_func_array( $hook, $callargs ); } else { $result = null; @@ -99,8 +99,8 @@ class MWException extends Exception { /** * Get a message from i18n * - * @param $key string: message name - * @param $fallback string: default message if the message cache can't be + * @param string $key message name + * @param string $fallback default message if the message cache can't be * called by the exception * The function also has other parameters that are arguments for the message * @return string message with arguments replaced @@ -171,7 +171,7 @@ class MWException extends Exception { /** * Get a random ID for this error. - * This allows to link the exception to its correspoding log entry when + * This allows to link the exception to its corresponding log entry when * $wgShowExceptionDetails is set to false. * * @return string @@ -262,7 +262,7 @@ class MWException extends Exception { if ( defined( 'MW_API' ) ) { // Unhandled API exception, we can't be sure that format printer is alive header( 'MediaWiki-API-Error: internal_api_error_' . get_class( $this ) ); - wfHttpError(500, 'Internal Server Error', $this->getText() ); + wfHttpError( 500, 'Internal Server Error', $this->getText() ); } elseif ( self::isCommandLine() ) { MWExceptionHandler::printError( $this->getText() ); } else { @@ -320,16 +320,16 @@ class ErrorPageError extends MWException { /** * Note: these arguments are keys into wfMessage(), not text! * - * @param $title string|Message Message key (string) for page title, or a Message object - * @param $msg string|Message Message key (string) for error text, or a Message object - * @param $params array with parameters to wfMessage() + * @param string|Message $title Message key (string) for page title, or a Message object + * @param string|Message $msg Message key (string) for error text, or a Message object + * @param array $params with parameters to wfMessage() */ function __construct( $title, $msg, $params = null ) { $this->title = $title; $this->msg = $msg; $this->params = $params; - if( $msg instanceof Message ){ + if( $msg instanceof Message ) { parent::__construct( $msg ); } else { parent::__construct( wfMessage( $msg )->text() ); @@ -354,8 +354,8 @@ class ErrorPageError extends MWException { */ class BadTitleError extends ErrorPageError { /** - * @param $msg string|Message A message key (default: 'badtitletext') - * @param $params Array parameter to wfMessage() + * @param string|Message $msg A message key (default: 'badtitletext') + * @param array $params parameter to wfMessage() */ function __construct( $msg = 'badtitletext', $params = null ) { parent::__construct( 'badtitle', $msg, $params ); @@ -423,7 +423,7 @@ class PermissionsError extends ErrorPageError { * @ingroup Exception */ class ReadOnlyError extends ErrorPageError { - public function __construct(){ + public function __construct() { parent::__construct( 'readonly', 'readonlytext', @@ -439,14 +439,14 @@ class ReadOnlyError extends ErrorPageError { * @ingroup Exception */ class ThrottledError extends ErrorPageError { - public function __construct(){ + public function __construct() { parent::__construct( 'actionthrottled', 'actionthrottledtext' ); } - public function report(){ + public function report() { global $wgOut; $wgOut->setStatusCode( 503 ); parent::report(); @@ -460,7 +460,7 @@ class ThrottledError extends ErrorPageError { * @ingroup Exception */ class UserBlockedError extends ErrorPageError { - public function __construct( Block $block ){ + public function __construct( Block $block ) { global $wgLang, $wgRequest; $blocker = $block->getBlocker(); @@ -500,25 +500,24 @@ class UserBlockedError extends ErrorPageError { /** * Shows a generic "user is not logged in" error page. * - * This is essentially an ErrorPageError exception which by default use the + * This is essentially an ErrorPageError exception which by default uses the * 'exception-nologin' as a title and 'exception-nologin-text' for the message. * @see bug 37627 * @since 1.20 * * @par Example: * @code - * if( $user->isAnon ) { + * if( $user->isAnon() ) { * throw new UserNotLoggedIn(); * } * @endcode * - * Please note the parameters are mixed up compared to ErrorPageError, this - * is done to be able to simply specify a reason whitout overriding the default - * title. + * Note the parameter order differs from ErrorPageError, this allows you to + * simply specify a reason without overriding the default title. * * @par Example: * @code - * if( $user->isAnon ) { + * if( $user->isAnon() ) { * throw new UserNotLoggedIn( 'action-require-loggedin' ); * } * @endcode @@ -533,11 +532,11 @@ class UserNotLoggedIn extends ErrorPageError { * @param $titleMsg A message key to set the page title. * Optional, default: 'exception-nologin' * @param $params Parameters to wfMessage(). - * Optiona, default: null + * Optional, default: null */ public function __construct( $reasonMsg = 'exception-nologin-text', - $titleMsg = 'exception-nologin', + $titleMsg = 'exception-nologin', $params = null ) { parent::__construct( $titleMsg, $reasonMsg, $params ); @@ -558,24 +557,48 @@ class HttpError extends MWException { * Constructor * * @param $httpCode Integer: HTTP status code to send to the client - * @param $content String|Message: content of the message - * @param $header String|Message: content of the header (\ and \) + * @param string|Message $content content of the message + * @param string|Message $header content of the header (\ and \) */ - public function __construct( $httpCode, $content, $header = null ){ + public function __construct( $httpCode, $content, $header = null ) { parent::__construct( $content ); $this->httpCode = (int)$httpCode; $this->header = $header; $this->content = $content; } + /** + * Returns the HTTP status code supplied to the constructor. + * + * @return int + */ + public function getStatusCode() { + return $this->httpCode; + } + + /** + * Report the HTTP error. + * Sends the appropriate HTTP status code and outputs an + * HTML page with an error message. + */ public function report() { $httpMessage = HttpStatus::getMessage( $this->httpCode ); - header( "Status: {$this->httpCode} {$httpMessage}" ); + header( "Status: {$this->httpCode} {$httpMessage}", true, $this->httpCode ); header( 'Content-type: text/html; charset=utf-8' ); + print $this->getHTML(); + } + + /** + * Returns HTML for reporting the HTTP error. + * This will be a minimal but complete HTML document. + * + * @return string HTML + */ + public function getHTML() { if ( $this->header === null ) { - $header = $httpMessage; + $header = HttpStatus::getMessage( $this->httpCode ); } elseif ( $this->header instanceof Message ) { $header = $this->header->escaped(); } else { @@ -588,7 +611,7 @@ class HttpError extends MWException { $content = htmlspecialchars( $this->content ); } - print "\n". + return "\n". "$header\n" . "

    $header

    $content

    \n"; } @@ -661,7 +684,7 @@ class MWExceptionHandler { * Print a message, if possible to STDERR. * Use this in command line mode only (see isCommandLine) * - * @param $message string Failure text + * @param string $message Failure text */ public static function printError( $message ) { # NOTE: STDERR may not be available, especially if php-cgi is used from the command line (bug #15602). diff --git a/includes/Export.php b/includes/Export.php index f01fb237..d8cc0242 100644 --- a/includes/Export.php +++ b/includes/Export.php @@ -31,8 +31,8 @@ * @ingroup SpecialPage Dump */ class WikiExporter { - var $list_authors = false ; # Return distinct author list (when not returning full history) - var $author_list = "" ; + var $list_authors = false; # Return distinct author list (when not returning full history) + var $author_list = ""; var $dumpUploads = false; var $dumpUploadFileContents = false; @@ -63,7 +63,7 @@ class WikiExporter { * @return string */ public static function schemaVersion() { - return "0.7"; + return "0.8"; } /** @@ -80,17 +80,17 @@ class WikiExporter { * offset: non-inclusive offset at which to start the query * limit: maximum number of rows to return * dir: "asc" or "desc" timestamp order - * @param $buffer Int: one of WikiExporter::BUFFER or WikiExporter::STREAM - * @param $text Int: one of WikiExporter::TEXT or WikiExporter::STUB + * @param int $buffer one of WikiExporter::BUFFER or WikiExporter::STREAM + * @param int $text one of WikiExporter::TEXT or WikiExporter::STUB */ - function __construct( &$db, $history = WikiExporter::CURRENT, + function __construct( $db, $history = WikiExporter::CURRENT, $buffer = WikiExporter::BUFFER, $text = WikiExporter::TEXT ) { - $this->db =& $db; + $this->db = $db; $this->history = $history; - $this->buffer = $buffer; - $this->writer = new XmlDumpWriter(); - $this->sink = new DumpOutput(); - $this->text = $text; + $this->buffer = $buffer; + $this->writer = new XmlDumpWriter(); + $this->sink = new DumpOutput(); + $this->text = $text; } /** @@ -126,7 +126,7 @@ class WikiExporter { /** * Dumps a series of page and revision records for those pages * in the database falling within the page_id range given. - * @param $start Int: inclusive lower limit (this id is included) + * @param int $start inclusive lower limit (this id is included) * @param $end Int: Exclusive upper limit (this id is not included) * If 0, no upper limit. */ @@ -141,7 +141,7 @@ class WikiExporter { /** * Dumps a series of page and revision records for those pages * in the database with revisions falling within the rev_id range given. - * @param $start Int: inclusive lower limit (this id is included) + * @param int $start inclusive lower limit (this id is included) * @param $end Int: Exclusive upper limit (this id is not included) * If 0, no upper limit. */ @@ -226,7 +226,7 @@ class WikiExporter { foreach ( $res as $row ) { $this->author_list .= "" . "" . - htmlentities( $row->rev_user_text ) . + htmlentities( $row->rev_user_text ) . "" . "" . $row->rev_user . @@ -330,7 +330,7 @@ class WikiExporter { $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page' ); } elseif ( $this->history & WikiExporter::CURRENT ) { # Latest revision dumps... - if ( $this->list_authors && $cond != '' ) { // List authors, if so desired + if ( $this->list_authors && $cond != '' ) { // List authors, if so desired $this->do_list_authors( $cond ); } $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' ); @@ -348,7 +348,7 @@ class WikiExporter { $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page' ); $opts['ORDER BY'] = array( 'rev_page ASC', 'rev_id ASC' ); } else { - # Uknown history specification parameter? + # Unknown history specification parameter? wfProfileOut( __METHOD__ ); throw new MWException( __METHOD__ . " given invalid history dump type." ); } @@ -427,10 +427,10 @@ class WikiExporter { protected function outputPageStream( $resultset ) { $last = null; foreach ( $resultset as $row ) { - if ( is_null( $last ) || + if ( $last === null || $last->page_namespace != $row->page_namespace || - $last->page_title != $row->page_title ) { - if ( isset( $last ) ) { + $last->page_title != $row->page_title ) { + if ( $last !== null ) { $output = ''; if ( $this->dumpUploads ) { $output .= $this->writer->writeUploads( $last, $this->dumpUploadFileContents ); @@ -445,7 +445,7 @@ class WikiExporter { $output = $this->writer->writeRevision( $row ); $this->sink->writeRevision( $row, $output ); } - if ( isset( $last ) ) { + if ( $last !== null ) { $output = ''; if ( $this->dumpUploads ) { $output .= $this->writer->writeUploads( $last, $this->dumpUploadFileContents ); @@ -498,7 +498,7 @@ class XmlDumpWriter { 'xmlns' => "http://www.mediawiki.org/xml/export-$ver/", 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance", 'xsi:schemaLocation' => "http://www.mediawiki.org/xml/export-$ver/ " . - "http://www.mediawiki.org/xml/export-$ver.xsd", + "http://www.mediawiki.org/xml/export-$ver.xsd", #TODO: how do we get a new version up there? 'version' => $ver, 'xml:lang' => $wgLanguageCode ), null ) . @@ -634,37 +634,31 @@ class XmlDumpWriter { function writeRevision( $row ) { wfProfileIn( __METHOD__ ); - $out = " \n"; + $out = " \n"; $out .= " " . Xml::element( 'id', null, strval( $row->rev_id ) ) . "\n"; - if( $row->rev_parent_id ) { + if( isset( $row->rev_parent_id ) && $row->rev_parent_id ) { $out .= " " . Xml::element( 'parentid', null, strval( $row->rev_parent_id ) ) . "\n"; } $out .= $this->writeTimestamp( $row->rev_timestamp ); - if ( $row->rev_deleted & Revision::DELETED_USER ) { + if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_USER ) ) { $out .= " " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n"; } else { $out .= $this->writeContributor( $row->rev_user, $row->rev_user_text ); } - if ( $row->rev_minor_edit ) { - $out .= " \n"; + if ( isset( $row->rev_minor_edit ) && $row->rev_minor_edit ) { + $out .= " \n"; } - if ( $row->rev_deleted & Revision::DELETED_COMMENT ) { + if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_COMMENT ) ) { $out .= " " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n"; } elseif ( $row->rev_comment != '' ) { $out .= " " . Xml::elementClean( 'comment', array(), strval( $row->rev_comment ) ) . "\n"; } - if ( $row->rev_sha1 && !( $row->rev_deleted & Revision::DELETED_TEXT ) ) { - $out .= " " . Xml::element('sha1', null, strval( $row->rev_sha1 ) ) . "\n"; - } else { - $out .= " \n"; - } - $text = ''; - if ( $row->rev_deleted & Revision::DELETED_TEXT ) { + if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_TEXT ) ) { $out .= " " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n"; } elseif ( isset( $row->old_text ) ) { // Raw text from the database may have invalid chars @@ -679,6 +673,34 @@ class XmlDumpWriter { "" ) . "\n"; } + if ( isset( $row->rev_sha1 ) && $row->rev_sha1 && !( $row->rev_deleted & Revision::DELETED_TEXT ) ) { + $out .= " " . Xml::element( 'sha1', null, strval( $row->rev_sha1 ) ) . "\n"; + } else { + $out .= " \n"; + } + + if ( isset( $row->rev_content_model ) && !is_null( $row->rev_content_model ) ) { + $content_model = strval( $row->rev_content_model ); + } else { + // probably using $wgContentHandlerUseDB = false; + // @todo: test! + $title = Title::makeTitle( $row->page_namespace, $row->page_title ); + $content_model = ContentHandler::getDefaultModelFor( $title ); + } + + $out .= " " . Xml::element( 'model', null, strval( $content_model ) ) . "\n"; + + if ( isset( $row->rev_content_format ) && !is_null( $row->rev_content_format ) ) { + $content_format = strval( $row->rev_content_format ); + } else { + // probably using $wgContentHandlerUseDB = false; + // @todo: test! + $content_handler = ContentHandler::getForModelID( $content_model ); + $content_format = $content_handler->getDefaultFormat(); + } + + $out .= " " . Xml::element( 'format', null, strval( $content_format ) ) . "\n"; + wfRunHooks( 'XmlDumpWriterWriteRevision', array( &$this, &$out, $row, $text ) ); $out .= " \n"; @@ -698,7 +720,7 @@ class XmlDumpWriter { function writeLogItem( $row ) { wfProfileIn( __METHOD__ ); - $out = " \n"; + $out = " \n"; $out .= " " . Xml::element( 'id', null, strval( $row->log_id ) ) . "\n"; $out .= $this->writeTimestamp( $row->log_timestamp, " " ); @@ -736,7 +758,7 @@ class XmlDumpWriter { /** * @param $timestamp string - * @param $indent string Default to six spaces + * @param string $indent Default to six spaces * @return string */ function writeTimestamp( $timestamp, $indent = " " ) { @@ -747,7 +769,7 @@ class XmlDumpWriter { /** * @param $id * @param $text string - * @param $indent string Default to six spaces + * @param string $indent Default to six spaces * @return string */ function writeContributor( $id, $text, $indent = " " ) { @@ -849,9 +871,8 @@ class XmlDumpWriter { } } - /** - * Base class for output stream; prints to stdout or buffer or whereever. + * Base class for output stream; prints to stdout or buffer or wherever. * @ingroup Dump */ class DumpOutput { @@ -918,7 +939,6 @@ class DumpOutput { * @param $newname mixed File name. May be a string or an array with one element */ function closeRenameAndReopen( $newname ) { - return; } /** @@ -926,10 +946,9 @@ class DumpOutput { * Use this for the last piece of a file written out * at specified checkpoints (e.g. every n hours). * @param $newname mixed File name. May be a string or an array with one element - * @param $open bool If true, a new file with the old filename will be opened again for writing (default: false) + * @param bool $open If true, a new file with the old filename will be opened again for writing (default: false) */ function closeAndRename( $newname, $open = false ) { - return; } /** @@ -938,7 +957,7 @@ class DumpOutput { * @return null */ function getFilenames() { - return NULL; + return null; } } @@ -987,7 +1006,7 @@ class DumpFileOutput extends DumpOutput { * @throws MWException */ function renameOrException( $newname ) { - if (! rename( $this->filename, $newname ) ) { + if ( !rename( $this->filename, $newname ) ) { throw new MWException( __METHOD__ . ": rename of file {$this->filename} to $newname failed\n" ); } } @@ -1050,7 +1069,7 @@ class DumpPipeOutput extends DumpFileOutput { */ function __construct( $command, $file = null ) { if ( !is_null( $file ) ) { - $command .= " > " . wfEscapeShellArg( $file ); + $command .= " > " . wfEscapeShellArg( $file ); } $this->startCommand( $command ); @@ -1106,7 +1125,7 @@ class DumpPipeOutput extends DumpFileOutput { $this->renameOrException( $newname ); if ( $open ) { $command = $this->command; - $command .= " > " . wfEscapeShellArg( $this->filename ); + $command .= " > " . wfEscapeShellArg( $this->filename ); $this->startCommand( $command ); } } @@ -1325,6 +1344,7 @@ class DumpNamespaceFilter extends DumpFilter { /** * @param $sink DumpOutput * @param $param + * @throws MWException */ function __construct( &$sink, $param ) { parent::__construct( $sink ); @@ -1338,7 +1358,7 @@ class DumpNamespaceFilter extends DumpFilter { "NS_PROJECT_TALK" => NS_PROJECT_TALK, "NS_FILE" => NS_FILE, "NS_FILE_TALK" => NS_FILE_TALK, - "NS_IMAGE" => NS_IMAGE, // NS_IMAGE is an alias for NS_FILE + "NS_IMAGE" => NS_IMAGE, // NS_IMAGE is an alias for NS_FILE "NS_IMAGE_TALK" => NS_IMAGE_TALK, "NS_MEDIAWIKI" => NS_MEDIAWIKI, "NS_MEDIAWIKI_TALK" => NS_MEDIAWIKI_TALK, @@ -1378,7 +1398,6 @@ class DumpNamespaceFilter extends DumpFilter { } } - /** * Dump output filter to include only the last revision in each page sequence. * @ingroup Dump @@ -1423,7 +1442,7 @@ class DumpLatestFilter extends DumpFilter { } /** - * Base class for output stream; prints to stdout or buffer or whereever. + * Base class for output stream; prints to stdout or buffer or wherever. * @ingroup Dump */ class DumpMultiWriter { @@ -1506,7 +1525,7 @@ class DumpMultiWriter { function getFilenames() { $filenames = array(); for ( $i = 0; $i < $this->count; $i++ ) { - $filenames[] = $this->sinks[$i]->getFilenames(); + $filenames[] = $this->sinks[$i]->getFilenames(); } return $filenames; } diff --git a/includes/ExternalEdit.php b/includes/ExternalEdit.php index 34683253..11e94230 100644 --- a/includes/ExternalEdit.php +++ b/includes/ExternalEdit.php @@ -40,7 +40,7 @@ class ExternalEdit extends ContextSource { * Check whether external edit or diff should be used. * * @param $context IContextSource context to use - * @param $type String can be either 'edit' or 'diff' + * @param string $type can be either 'edit' or 'diff' * @return Bool */ public static function useExternalEngine( IContextSource $context, $type ) { @@ -87,7 +87,7 @@ class ExternalEdit extends ContextSource { 'URL' => $image->getCanonicalURL() ) ); - } else{ + } else { $urls = array(); } } else { diff --git a/includes/ExternalStore.php b/includes/ExternalStore.php deleted file mode 100644 index 61d4ef7c..00000000 --- a/includes/ExternalStore.php +++ /dev/null @@ -1,172 +0,0 @@ -mParams = $params; - } - - /** - * Fetch data from given URL - * - * @param $url String: The URL of the text to get - * @param $params Array: associative array of parameters for the ExternalStore object. - * @return string|bool The text stored or false on error - */ - static function fetchFromURL( $url, $params = array() ) { - global $wgExternalStores; - - if( !$wgExternalStores ) - return false; - - $parts = explode( '://', $url, 2 ); - - if ( count( $parts ) != 2 ) { - return false; - } - - list( $proto, $path ) = $parts; - - if ( $path == '' ) { // Bad URL - return false; - } - - $store = self::getStoreObject( $proto, $params ); - if ( $store === false ) - return false; - return $store->fetchFromURL( $url ); - } - - /** - * Get an external store object of the given type, with the given parameters - * - * @param $proto String: type of external storage, should be a value in $wgExternalStores - * @param $params Array: associative array of parameters for the ExternalStore object. - * @return ExternalStore subclass or false on error - */ - static function getStoreObject( $proto, $params = array() ) { - global $wgExternalStores; - if( !$wgExternalStores ) - return false; - /* Protocol not enabled */ - if( !in_array( $proto, $wgExternalStores ) ) - return false; - - $class = 'ExternalStore' . ucfirst( $proto ); - /* Any custom modules should be added to $wgAutoLoadClasses for on-demand loading */ - if( !MWInit::classExists( $class ) ) { - return false; - } - - return new $class($params); - } - - /** - * Store a data item to an external store, identified by a partial URL - * The protocol part is used to identify the class, the rest is passed to the - * class itself as a parameter. - * @param $url - * @param $data - * @param $params array - * @return string|bool The URL of the stored data item, or false on error - */ - static function insert( $url, $data, $params = array() ) { - list( $proto, $params ) = explode( '://', $url, 2 ); - $store = self::getStoreObject( $proto, $params ); - if ( $store === false ) { - return false; - } else { - return $store->store( $params, $data ); - } - } - - /** - * Like insert() above, but does more of the work for us. - * This function does not need a url param, it builds it by - * itself. It also fails-over to the next possible clusters. - * - * @param $data String - * @param $storageParams Array: associative array of parameters for the ExternalStore object. - * @return string The URL of the stored data item, or false on error - */ - public static function insertToDefault( $data, $storageParams = array() ) { - global $wgDefaultExternalStore; - $tryStores = (array)$wgDefaultExternalStore; - $error = false; - while ( count( $tryStores ) > 0 ) { - $index = mt_rand(0, count( $tryStores ) - 1); - $storeUrl = $tryStores[$index]; - wfDebug( __METHOD__.": trying $storeUrl\n" ); - list( $proto, $params ) = explode( '://', $storeUrl, 2 ); - $store = self::getStoreObject( $proto, $storageParams ); - if ( $store === false ) { - throw new MWException( "Invalid external storage protocol - $storeUrl" ); - } - try { - $url = $store->store( $params, $data ); // Try to save the object - } catch ( DBConnectionError $error ) { - $url = false; - } catch( DBQueryError $error ) { - $url = false; - } - if ( $url ) { - return $url; // Done! - } else { - unset( $tryStores[$index] ); // Don't try this one again! - $tryStores = array_values( $tryStores ); // Must have consecutive keys - wfDebugLog( 'ExternalStorage', "Unable to store text to external storage $storeUrl" ); - } - } - // All stores failed - if ( $error ) { - // Rethrow the last connection error - throw $error; - } else { - throw new MWException( "Unable to store text to external storage" ); - } - } - - /** - * @param $data - * @param $wiki - * - * @return string - */ - public static function insertToForeignDefault( $data, $wiki ) { - return self::insertToDefault( $data, array( 'wiki' => $wiki ) ); - } -} diff --git a/includes/ExternalStoreDB.php b/includes/ExternalStoreDB.php deleted file mode 100644 index 6f2b33e1..00000000 --- a/includes/ExternalStoreDB.php +++ /dev/null @@ -1,185 +0,0 @@ -mParams = $params; - } - - /** - * Get a LoadBalancer for the specified cluster - * - * @param $cluster String: cluster name - * @return LoadBalancer object - */ - function &getLoadBalancer( $cluster ) { - $wiki = isset($this->mParams['wiki']) ? $this->mParams['wiki'] : false; - - return wfGetLBFactory()->getExternalLB( $cluster, $wiki ); - } - - /** - * Get a slave database connection for the specified cluster - * - * @param $cluster String: cluster name - * @return DatabaseBase object - */ - function &getSlave( $cluster ) { - global $wgDefaultExternalStore; - - $wiki = isset($this->mParams['wiki']) ? $this->mParams['wiki'] : false; - $lb =& $this->getLoadBalancer( $cluster ); - - if ( !in_array( "DB://" . $cluster, (array)$wgDefaultExternalStore ) ) { - wfDebug( "read only external store" ); - $lb->allowLagged(true); - } else { - wfDebug( "writable external store" ); - } - - return $lb->getConnection( DB_SLAVE, array(), $wiki ); - } - - /** - * Get a master database connection for the specified cluster - * - * @param $cluster String: cluster name - * @return DatabaseBase object - */ - function &getMaster( $cluster ) { - $wiki = isset($this->mParams['wiki']) ? $this->mParams['wiki'] : false; - $lb =& $this->getLoadBalancer( $cluster ); - return $lb->getConnection( DB_MASTER, array(), $wiki ); - } - - /** - * Get the 'blobs' table name for this database - * - * @param $db DatabaseBase - * @return String: table name ('blobs' by default) - */ - function getTable( &$db ) { - $table = $db->getLBInfo( 'blobs table' ); - if ( is_null( $table ) ) { - $table = 'blobs'; - } - return $table; - } - - /** - * Fetch data from given URL - * @param $url String: an url of the form DB://cluster/id or DB://cluster/id/itemid for concatened storage. - * @return mixed - */ - function fetchFromURL( $url ) { - $path = explode( '/', $url ); - $cluster = $path[2]; - $id = $path[3]; - if ( isset( $path[4] ) ) { - $itemID = $path[4]; - } else { - $itemID = false; - } - - $ret =& $this->fetchBlob( $cluster, $id, $itemID ); - - if ( $itemID !== false && $ret !== false ) { - return $ret->getItem( $itemID ); - } - return $ret; - } - - /** - * Fetch a blob item out of the database; a cache of the last-loaded - * blob will be kept so that multiple loads out of a multi-item blob - * can avoid redundant database access and decompression. - * @param $cluster - * @param $id - * @param $itemID - * @return mixed - * @private - */ - function &fetchBlob( $cluster, $id, $itemID ) { - /** - * One-step cache variable to hold base blobs; operations that - * pull multiple revisions may often pull multiple times from - * the same blob. By keeping the last-used one open, we avoid - * redundant unserialization and decompression overhead. - */ - static $externalBlobCache = array(); - - $cacheID = ( $itemID === false ) ? "$cluster/$id" : "$cluster/$id/"; - if( isset( $externalBlobCache[$cacheID] ) ) { - wfDebug( "ExternalStoreDB::fetchBlob cache hit on $cacheID\n" ); - return $externalBlobCache[$cacheID]; - } - - wfDebug( "ExternalStoreDB::fetchBlob cache miss on $cacheID\n" ); - - $dbr =& $this->getSlave( $cluster ); - $ret = $dbr->selectField( $this->getTable( $dbr ), 'blob_text', array( 'blob_id' => $id ), __METHOD__ ); - if ( $ret === false ) { - wfDebugLog( 'ExternalStoreDB', "ExternalStoreDB::fetchBlob master fallback on $cacheID\n" ); - // Try the master - $dbw =& $this->getMaster( $cluster ); - $ret = $dbw->selectField( $this->getTable( $dbw ), 'blob_text', array( 'blob_id' => $id ), __METHOD__ ); - if( $ret === false) { - wfDebugLog( 'ExternalStoreDB', "ExternalStoreDB::fetchBlob master failed to find $cacheID\n" ); - } - } - if( $itemID !== false && $ret !== false ) { - // Unserialise object; caller extracts item - $ret = unserialize( $ret ); - } - - $externalBlobCache = array( $cacheID => &$ret ); - return $ret; - } - - /** - * Insert a data item into a given cluster - * - * @param $cluster String: the cluster name - * @param $data String: the data item - * @return string URL - */ - function store( $cluster, $data ) { - $dbw = $this->getMaster( $cluster ); - $id = $dbw->nextSequenceValue( 'blob_blob_id_seq' ); - $dbw->insert( $this->getTable( $dbw ), - array( 'blob_id' => $id, 'blob_text' => $data ), - __METHOD__ ); - $id = $dbw->insertId(); - if ( !$id ) { - throw new MWException( __METHOD__.': no insert ID' ); - } - if ( $dbw->getFlag( DBO_TRX ) ) { - $dbw->commit( __METHOD__ ); - } - return "DB://$cluster/$id"; - } -} diff --git a/includes/ExternalStoreHttp.php b/includes/ExternalStoreHttp.php deleted file mode 100644 index 311e32b3..00000000 --- a/includes/ExternalStoreHttp.php +++ /dev/null @@ -1,45 +0,0 @@ -replace( 'external_user', array( 'eu_local_id', 'eu_external_id' ), array( 'eu_local_id' => $id, - 'eu_external_id' => $this->getId() ), + 'eu_external_id' => $this->getId() ), __METHOD__ ); } - + /** * Check whether this external user id is already linked with * a local user. * @return Mixed User if the account is linked, Null otherwise. */ - public final function getLocalUser(){ + final public function getLocalUser() { $dbr = wfGetDB( DB_SLAVE ); $row = $dbr->selectRow( 'external_user', @@ -305,5 +305,5 @@ abstract class ExternalUser { ? User::newFromId( $row->eu_local_id ) : null; } - + } diff --git a/includes/FakeTitle.php b/includes/FakeTitle.php index 60f7600d..efa213fb 100644 --- a/includes/FakeTitle.php +++ b/includes/FakeTitle.php @@ -114,9 +114,9 @@ class FakeTitle extends Title { function getParentCategories() { $this->error(); } function getParentCategoryTree( $children = array() ) { $this->error(); } function pageCond() { $this->error(); } - function getPreviousRevisionID( $revId, $flags=0 ) { $this->error(); } - function getNextRevisionID( $revId, $flags=0 ) { $this->error(); } - function getFirstRevision( $flags=0 ) { $this->error(); } + function getPreviousRevisionID( $revId, $flags = 0 ) { $this->error(); } + function getNextRevisionID( $revId, $flags = 0 ) { $this->error(); } + function getFirstRevision( $flags = 0 ) { $this->error(); } function isNewPage() { $this->error(); } function getEarliestRevTime( $flags = 0 ) { $this->error(); } function countRevisionsBetween( $old, $new ) { $this->error(); } diff --git a/includes/Fallback.php b/includes/Fallback.php index 4b138c11..2e19a095 100644 --- a/includes/Fallback.php +++ b/includes/Fallback.php @@ -149,13 +149,12 @@ class Fallback { return $total; } - /** * Fallback implementation of mb_strpos, hardcoded to UTF-8. * @param $haystack String * @param $needle String - * @param $offset String: optional start position - * @param $encoding String: optional encoding; ignored + * @param string $offset optional start position + * @param string $encoding optional encoding; ignored * @return int */ public static function mb_strpos( $haystack, $needle, $offset = 0, $encoding = '' ) { @@ -175,8 +174,8 @@ class Fallback { * Fallback implementation of mb_strrpos, hardcoded to UTF-8. * @param $haystack String * @param $needle String - * @param $offset String: optional start position - * @param $encoding String: optional encoding; ignored + * @param string $offset optional start position + * @param string $encoding optional encoding; ignored * @return int */ public static function mb_strrpos( $haystack, $needle, $offset = 0, $encoding = '' ) { @@ -192,22 +191,4 @@ class Fallback { return false; } } - - /** - * Fallback implementation of stream_resolve_include_path() - * Native stream_resolve_include_path is available for PHP 5 >= 5.3.2 - * @param $filename String - * @return String - */ - public static function stream_resolve_include_path( $filename ) { - $pathArray = explode( PATH_SEPARATOR, get_include_path() ); - foreach ( $pathArray as $path ) { - $fullFilename = $path . DIRECTORY_SEPARATOR . $filename; - if ( file_exists( $fullFilename ) ) { - return $fullFilename; - } - } - return false; - } - } diff --git a/includes/Feed.php b/includes/Feed.php index f9dbf5ba..caf2e571 100644 --- a/includes/Feed.php +++ b/includes/Feed.php @@ -52,11 +52,11 @@ class FeedItem { /** * Constructor * - * @param $title String|Title Item's title + * @param string|Title $title Item's title * @param $description String - * @param $url String: URL uniquely designating the item. - * @param $date String: Item's date - * @param $author String: Author's user name + * @param string $url URL uniquely designating the item. + * @param string $date Item's date + * @param string $author Author's user name * @param $comments String */ function __construct( $title, $description, $url, $date = '', $author = '', $comments = '' ) { @@ -72,7 +72,7 @@ class FeedItem { /** * Encode $string so that it can be safely embedded in a XML document * - * @param $string String: string to encode + * @param string $string string to encode * @return String */ public function xmlEncode( $string ) { @@ -95,7 +95,7 @@ class FeedItem { /** * set the unique id of an item * - * @param $uniqueId String: unique id for the item + * @param string $uniqueId unique id for the item * @param $rssIsPermalink Boolean: set to true if the guid (unique id) is a permalink (RSS feeds only) */ public function setUniqueId( $uniqueId, $rssIsPermalink = false ) { @@ -170,7 +170,7 @@ class FeedItem { /** * Quickie hack... strip out wikilinks to more legible form from the comment. * - * @param $text String: wikitext + * @param string $text wikitext * @return String */ public static function stripComment( $text ) { @@ -243,9 +243,9 @@ abstract class ChannelFeed extends FeedItem { */ function contentType() { global $wgRequest; - $ctype = $wgRequest->getVal('ctype','application/xml'); - $allowedctypes = array('application/xml','text/xml','application/rss+xml','application/atom+xml'); - return (in_array($ctype, $allowedctypes) ? $ctype : 'application/xml'); + $ctype = $wgRequest->getVal( 'ctype', 'application/xml' ); + $allowedctypes = array( 'application/xml', 'text/xml', 'application/rss+xml', 'application/atom+xml' ); + return (in_array( $ctype, $allowedctypes ) ? $ctype : 'application/xml'); } /** @@ -282,7 +282,7 @@ class RSSFeed extends ChannelFeed { } /** - * Ouput an RSS 2.0 header + * Output an RSS 2.0 header */ function outHeader() { global $wgVersion; @@ -318,7 +318,7 @@ class RSSFeed extends ChannelFeed { } /** - * Ouput an RSS 2.0 footer + * Output an RSS 2.0 footer */ function outFooter() { ?> @@ -362,7 +362,7 @@ class AtomFeed extends ChannelFeed { } /** - * Atom 1.0 requires a unique, opaque IRI as a unique indentifier + * Atom 1.0 requires a unique, opaque IRI as a unique identifier * for every feed we create. For now just use the URL, but who * can tell if that's right? If we put options on the feed, do we * have to change the id? Maybe? Maybe not. @@ -409,7 +409,7 @@ class AtomFeed extends ChannelFeed { } /** - * Outputs the footer for Atom 1.0 feed (basicly '\'). + * Outputs the footer for Atom 1.0 feed (basically '\'). */ function outFooter() {?> getVal( 'action' ) === 'purge'; - if ( $purge && $wgUser->isAllowed('purge') ) { + if ( $purge && $wgUser->isAllowed( 'purge' ) ) { $messageMemc->delete( $timekey ); $messageMemc->delete( $key ); } @@ -48,7 +48,7 @@ class FeedUtils { /** * Check whether feeds can be used and that $type is a valid feed type * - * @param $type String: feed type, as requested by the user + * @param string $type feed type, as requested by the user * @return Boolean */ public static function checkFeedOutput( $type ) { @@ -85,9 +85,9 @@ class FeedUtils { $row->rc_last_oldid, $row->rc_this_oldid, $timestamp, ($row->rc_deleted & Revision::DELETED_COMMENT) - ? wfMessage('rev-deleted-comment')->escaped() + ? wfMessage( 'rev-deleted-comment' )->escaped() : $row->rc_comment, - $actiontext + $actiontext ); } @@ -98,15 +98,15 @@ class FeedUtils { * @param $oldid Integer: old revision's id * @param $newid Integer: new revision's id * @param $timestamp Integer: new revision's timestamp - * @param $comment String: new revision's comment - * @param $actiontext String: text of the action; in case of log event + * @param string $comment new revision's comment + * @param string $actiontext text of the action; in case of log event * @return String */ - public static function formatDiffRow( $title, $oldid, $newid, $timestamp, $comment, $actiontext='' ) { + public static function formatDiffRow( $title, $oldid, $newid, $timestamp, $comment, $actiontext = '' ) { global $wgFeedDiffCutoff, $wgLang; wfProfileIn( __METHOD__ ); - # log enties + // log entries $completeText = '

    ' . implode( ' ', array_filter( array( @@ -115,7 +115,7 @@ class FeedUtils { // NOTE: Check permissions for anonymous users, not current user. // No "privileged" version should end up in the cache. - // Most feed readers will not log in anway. + // Most feed readers will not log in anyway. $anon = new User(); $accErrors = $title->getUserPermissionsErrors( 'read', $anon, true ); @@ -138,13 +138,23 @@ class FeedUtils { $diffText = ''; // Don't bother generating the diff if we won't be able to show it if ( $wgFeedDiffCutoff > 0 ) { - $de = new DifferenceEngine( $title, $oldid, $newid ); - $diffText = $de->getDiff( - wfMessage( 'previousrevision' )->text(), // hack - wfMessage( 'revisionasof', - $wgLang->timeanddate( $timestamp ), - $wgLang->date( $timestamp ), - $wgLang->time( $timestamp ) )->text() ); + $rev = Revision::newFromId( $oldid ); + + if ( !$rev ) { + $diffText = false; + } else { + $context = clone RequestContext::getMain(); + $context->setTitle( $title ); + + $contentHandler = $rev->getContentHandler(); + $de = $contentHandler->createDifferenceEngine( $context, $oldid, $newid ); + $diffText = $de->getDiff( + wfMessage( 'previousrevision' )->text(), // hack + wfMessage( 'revisionasof', + $wgLang->timeanddate( $timestamp ), + $wgLang->date( $timestamp ), + $wgLang->time( $timestamp ) )->text() ); + } } if ( $wgFeedDiffCutoff <= 0 || ( strlen( $diffText ) > $wgFeedDiffCutoff ) ) { @@ -162,16 +172,36 @@ class FeedUtils { } else { $rev = Revision::newFromId( $newid ); if( $wgFeedDiffCutoff <= 0 || is_null( $rev ) ) { - $newtext = ''; + $newContent = ContentHandler::getForTitle( $title )->makeEmptyContent(); + } else { + $newContent = $rev->getContent(); + } + + if ( $newContent instanceof TextContent ) { + // only textual content has a "source view". + $text = $newContent->getNativeData(); + + if ( $wgFeedDiffCutoff <= 0 || strlen( $text ) > $wgFeedDiffCutoff ) { + $html = null; + } else { + $html = nl2br( htmlspecialchars( $text ) ); + } } else { - $newtext = $rev->getText(); + //XXX: we could get an HTML representation of the content via getParserOutput, but that may + // contain JS magic and generally may not be suitable for inclusion in a feed. + // Perhaps Content should have a getDescriptiveHtml method and/or a getSourceText method. + //Compare also ApiFeedContributions::feedItemDesc + $html = null; } - if ( $wgFeedDiffCutoff <= 0 || strlen( $newtext ) > $wgFeedDiffCutoff ) { + + if ( $html === null ) { + // Omit large new page diffs, bug 29110 + // Also use diff link for non-textual content $diffText = self::getDiffLink( $title, $newid ); } else { $diffText = '

    ' . wfMessage( 'newpage' )->text() . '

    ' . - '
    ' . nl2br( htmlspecialchars( $newtext ) ) . '
    '; + '
    ' . $html . '
    '; } } $completeText .= $diffText; @@ -192,7 +222,7 @@ class FeedUtils { protected static function getDiffLink( Title $title, $newid, $oldid = null ) { $queryParameters = ($oldid == null) ? "diff={$newid}" - : "diff={$newid}&oldid={$oldid}" ; + : "diff={$newid}&oldid={$oldid}"; $diffUrl = $title->getFullUrl( $queryParameters ); $diffLink = Html::element( 'a', array( 'href' => $diffUrl ), @@ -206,18 +236,18 @@ class FeedUtils { * Might be 'cleaner' to use DOM or XSLT or something, * but *gack* it's a pain in the ass. * - * @param $text String: diff's HTML output + * @param string $text diff's HTML output * @return String: modified HTML */ public static function applyDiffStyle( $text ) { $styles = array( 'diff' => 'background-color: white; color:black;', - 'diff-otitle' => 'background-color: white; color:black;', - 'diff-ntitle' => 'background-color: white; color:black;', - 'diff-addedline' => 'background: #cfc; color:black; font-size: smaller;', - 'diff-deletedline' => 'background: #ffa; color:black; font-size: smaller;', - 'diff-context' => 'background: #eee; color:black; font-size: smaller;', - 'diffchange' => 'color: red; font-weight: bold; text-decoration: none;', + 'diff-otitle' => 'background-color: white; color:black; text-align: center;', + 'diff-ntitle' => 'background-color: white; color:black; text-align: center;', + 'diff-addedline' => 'color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;', + 'diff-deletedline' => 'color:black; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;', + 'diff-context' => 'background-color: #f9f9f9; color: #333333; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #e6e6e6; vertical-align: top; white-space: pre-wrap;', + 'diffchange' => 'font-weight: bold; text-decoration: none;', ); foreach( $styles as $class => $style ) { diff --git a/includes/FileDeleteForm.php b/includes/FileDeleteForm.php index e75ad729..28403cca 100644 --- a/includes/FileDeleteForm.php +++ b/includes/FileDeleteForm.php @@ -80,13 +80,13 @@ class FileDeleteForm { $this->oldimage = $wgRequest->getText( 'oldimage', false ); $token = $wgRequest->getText( 'wpEditToken' ); # Flag to hide all contents of the archived revisions - $suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed('suppressrevision'); + $suppress = $wgRequest->getVal( 'wpSuppress' ) && $wgUser->isAllowed( 'suppressrevision' ); if( $this->oldimage ) { $this->oldfile = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $this->title, $this->oldimage ); } - if( !self::haveDeletableFile($this->file, $this->oldfile, $this->oldimage) ) { + if( !self::haveDeletableFile( $this->file, $this->oldfile, $this->oldimage ) ) { $wgOut->addHTML( $this->prepareMessage( 'filedelete-nofile' ) ); $wgOut->addReturnTo( $this->title ); return; @@ -140,10 +140,11 @@ class FileDeleteForm { * * @param $title Title object * @param File $file: file object - * @param $oldimage String: archive name - * @param $reason String: reason of the deletion + * @param string $oldimage archive name + * @param string $reason reason of the deletion * @param $suppress Boolean: whether to mark all deleted versions as restricted * @param $user User object performing the request + * @throws MWException * @return bool|Status */ public static function doDelete( &$title, &$file, &$oldimage, $reason, $suppress, User $user = null ) { @@ -308,7 +309,7 @@ class FileDeleteForm { * showing an appropriate message depending upon whether * it's a current file or an old version * - * @param $message String: message base + * @param string $message message base * @return String */ private function prepareMessage( $message ) { @@ -343,7 +344,7 @@ class FileDeleteForm { * * @return bool */ - public static function isValidOldSpec($oldimage) { + public static function isValidOldSpec( $oldimage ) { return strlen( $oldimage ) >= 16 && strpos( $oldimage, '/' ) === false && strpos( $oldimage, '\\' ) === false; @@ -359,7 +360,7 @@ class FileDeleteForm { * @param $oldimage File * @return bool */ - public static function haveDeletableFile(&$file, &$oldfile, $oldimage) { + public static function haveDeletableFile( &$file, &$oldfile, $oldimage ) { return $oldimage ? $oldfile && $oldfile->exists() && $oldfile->isLocal() : $file && $file->exists() && $file->isLocal(); @@ -374,8 +375,9 @@ class FileDeleteForm { $q = array(); $q['action'] = 'delete'; - if( $this->oldimage ) + if( $this->oldimage ) { $q['oldimage'] = $this->oldimage; + } return $this->title->getLocalUrl( $q ); } diff --git a/includes/ForkController.php b/includes/ForkController.php index 448bc03b..89ad9553 100644 --- a/includes/ForkController.php +++ b/includes/ForkController.php @@ -53,7 +53,7 @@ class ForkController { const RESTART_ON_ERROR = 1; public function __construct( $numProcs, $flags = 0 ) { - if ( php_sapi_name() != 'cli' ) { + if ( PHP_SAPI != 'cli' ) { throw new MWException( "ForkController cannot be used from the web." ); } $this->procsToStart = $numProcs; @@ -140,7 +140,7 @@ class ForkController { // Don't share DB, storage, or memcached connections wfGetLBFactory()->destroyInstance(); FileBackendGroup::destroySingleton(); - LockManagerGroup::destroySingleton(); + LockManagerGroup::destroySingletons(); ObjectCache::clear(); $wgMemc = null; } diff --git a/includes/FormOptions.php b/includes/FormOptions.php index 33bbd86a..8477ed98 100644 --- a/includes/FormOptions.php +++ b/includes/FormOptions.php @@ -2,7 +2,7 @@ /** * Helper class to keep track of options when mixing links and form elements. * - * Copyright © 2008, Niklas Laxstiröm + * Copyright © 2008, Niklas Laxström * Copyright © 2011, Antoine Musso * * This program is free software; you can redistribute it and/or modify @@ -22,7 +22,7 @@ * * @file * @author Niklas Laxström - * @author Antoine Musso + * @author Antoine Musso */ /** @@ -83,6 +83,7 @@ class FormOptions implements ArrayAccess { * which will be assumed as INT if the data is an integer. * * @param $data Mixed: value to guess type for + * @throws MWException * @exception MWException Unsupported datatype * @return int Type constant */ @@ -103,8 +104,9 @@ class FormOptions implements ArrayAccess { /** * Verify the given option name exist. * - * @param $name String: option name + * @param string $name option name * @param $strict Boolean: throw an exception when the option does not exist (default false) + * @throws MWException * @return Boolean: true if option exist, false otherwise */ public function validateName( $name, $strict = false ) { @@ -121,7 +123,7 @@ class FormOptions implements ArrayAccess { /** * Use to set the value of an option. * - * @param $name String: option name + * @param string $name option name * @param $value Mixed: value for the option * @param $force Boolean: whether to set the value when it is equivalent to the default value for this option (default false). * @return null @@ -141,7 +143,7 @@ class FormOptions implements ArrayAccess { * Get the value for the given option name. * Internally use getValueReal() * - * @param $name String: option name + * @param string $name option name * @return Mixed */ public function getValue( $name ) { @@ -152,7 +154,7 @@ class FormOptions implements ArrayAccess { /** * @todo Document - * @param $option Array: array structure describing the option + * @param array $option array structure describing the option * @return Mixed. Value or the default value if it is null */ protected function getValueReal( $option ) { @@ -166,7 +168,7 @@ class FormOptions implements ArrayAccess { /** * Delete the option value. * This will make future calls to getValue() return the default value. - * @param $name String: option name + * @param string $name option name * @return null */ public function reset( $name ) { @@ -176,8 +178,9 @@ class FormOptions implements ArrayAccess { /** * @todo Document - * @param $name String: option name - * @return null + * @param string $name Option name + * @throws MWException If option does not exist. + * @return mixed Value or the default value if it is null. */ public function consumeValue( $name ) { $this->validateName( $name, true ); @@ -188,7 +191,7 @@ class FormOptions implements ArrayAccess { /** * @todo Document - * @param $names Array: array of option names + * @param array $names array of option names * @return null */ public function consumeValues( /*Array*/ $names ) { @@ -205,11 +208,12 @@ class FormOptions implements ArrayAccess { /** * Validate and set an option integer value - * The value will be altered to fit in the range. + * The value will be altered to fit in the range. * - * @param $name String: option name - * @param $min Int: minimum value - * @param $max Int: maximum value + * @param string $name option name + * @param int $min minimum value + * @param int $max maximum value + * @throws MWException * @exception MWException Option is not of type int * @return null */ diff --git a/includes/GitInfo.php b/includes/GitInfo.php index c3c30733..6f7f8020 100644 --- a/includes/GitInfo.php +++ b/includes/GitInfo.php @@ -41,10 +41,18 @@ class GitInfo { private static $viewers = false; /** - * @param $dir string The root directory of the repo where the .git dir can be found + * @param string $dir The root directory of the repo where the .git dir can be found */ public function __construct( $dir ) { - $this->basedir = "{$dir}/.git/"; + $this->basedir = "{$dir}/.git"; + if ( is_readable( $this->basedir ) && !is_dir( $this->basedir ) ) { + $GITfile = file_get_contents( $this->basedir ); + if ( strlen( $GITfile ) > 8 && substr( $GITfile, 0, 8 ) === 'gitdir: ' ) { + $path = rtrim( substr( $GITfile, 8 ), "\r\n" ); + $isAbsolute = $path[0] === '/' || substr( $path, 1, 1 ) === ':'; + $this->basedir = $isAbsolute ? $path : "{$dir}/{$path}"; + } + } } /** @@ -62,7 +70,7 @@ class GitInfo { /** * Check if a string looks like a hex encoded SHA1 hash * - * @param $str string The string to check + * @param string $str The string to check * @return bool Whether or not the string looks like a SHA1 */ public static function isSHA1( $str ) { @@ -102,7 +110,7 @@ class GitInfo { } // If not a SHA1 it may be a ref: - $REFfile = "{$this->basedir}{$HEAD}"; + $REFfile = "{$this->basedir}/{$HEAD}"; if ( !is_readable( $REFfile ) ) { return false; } diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 50758c89..016736f4 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -50,7 +50,7 @@ if ( !function_exists( 'mb_substr' ) ) { * @codeCoverageIgnore * @return string */ - function mb_substr( $str, $start, $count='end' ) { + function mb_substr( $str, $start, $count = 'end' ) { return Fallback::mb_substr( $str, $start, $count ); } @@ -94,7 +94,6 @@ if( !function_exists( 'mb_strrpos' ) ) { } } - // Support for Wietse Venema's taint feature if ( !function_exists( 'istainted' ) ) { /** @@ -168,7 +167,8 @@ function wfArrayLookup( $a, $b ) { * @param $key String|Int * @param $value Mixed * @param $default Mixed - * @param $changed Array to alter + * @param array $changed to alter + * @throws MWException */ function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) { if ( is_null( $changed ) ) { @@ -232,8 +232,8 @@ function wfMergeErrorArrays( /*...*/ ) { /** * Insert array into another array after the specified *KEY* * - * @param $array Array: The array. - * @param $insert Array: The array to insert. + * @param array $array The array. + * @param array $insert The array to insert. * @param $after Mixed: The key to insert after * @return Array */ @@ -311,12 +311,12 @@ function wfRandom() { } /** - * Get a random string containing a number of pesudo-random hex + * Get a random string containing a number of pseudo-random hex * characters. * @note This is not secure, if you are trying to generate some sort * of token please use MWCryptRand instead. * - * @param $length int The length of the string to generate + * @param int $length The length of the string to generate * @return String * @since 1.20 */ @@ -349,7 +349,7 @@ function wfRandomString( $length = 32 ) { * * @param $s String: * @return string -*/ + */ function wfUrlencode( $s ) { static $needle; if ( is_null( $s ) ) { @@ -379,8 +379,8 @@ function wfUrlencode( $s ) { * "days=7&limit=100". Options in the first array override options in the second. * Options set to null or false will not be output. * - * @param $array1 Array ( String|Array ) - * @param $array2 Array ( String|Array ) + * @param array $array1 ( String|Array ) + * @param array $array2 ( String|Array ) * @param $prefix String * @return String */ @@ -391,7 +391,7 @@ function wfArrayToCgi( $array1, $array2 = null, $prefix = '' ) { $cgi = ''; foreach ( $array1 as $key => $value ) { - if ( !is_null($value) && $value !== false ) { + if ( !is_null( $value ) && $value !== false ) { if ( $cgi != '' ) { $cgi .= '&'; } @@ -422,11 +422,11 @@ function wfArrayToCgi( $array1, $array2 = null, $prefix = '' ) { /** * This is the logical opposite of wfArrayToCgi(): it accepts a query string as - * its argument and returns the same string in array form. This allows compa- - * tibility with legacy functions that accept raw query strings instead of nice + * its argument and returns the same string in array form. This allows compatibility + * with legacy functions that accept raw query strings instead of nice * arrays. Of course, keys and values are urldecode()d. * - * @param $query String: query string + * @param string $query query string * @return array Array version of input */ function wfCgiToArray( $query ) { @@ -506,7 +506,7 @@ function wfAppendQuery( $url, $query ) { * @todo this won't work with current-path-relative URLs * like "subdir/foo.html", etc. * - * @param $url String: either fully-qualified or a local path + query + * @param string $url either fully-qualified or a local path + query * @param $defaultProto Mixed: one of the PROTO_* constants. Determines the * protocol to use if $url or $wgServer is * protocol-relative @@ -576,7 +576,7 @@ function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) { * @todo Need to integrate this into wfExpandUrl (bug 32168) * * @since 1.19 - * @param $urlParts Array URL parts, as output from wfParseUrl + * @param array $urlParts URL parts, as output from wfParseUrl * @return string URL assembled from its component parts */ function wfAssembleUrl( $urlParts ) { @@ -628,7 +628,7 @@ function wfAssembleUrl( $urlParts ) { * * @todo Need to integrate this into wfExpandUrl (bug 32168) * - * @param $urlPath String URL path, potentially containing dot-segments + * @param string $urlPath URL path, potentially containing dot-segments * @return string URL path with all dot-segments removed */ function wfRemoveDotSegments( $urlPath ) { @@ -705,7 +705,7 @@ function wfRemoveDotSegments( $urlPath ) { /** * Returns a regular expression of url protocols * - * @param $includeProtocolRelative bool If false, remove '//' from the returned protocol list. + * @param bool $includeProtocolRelative If false, remove '//' from the returned protocol list. * DO NOT USE this directly, use wfUrlProtocolsWithoutProtRel() instead * @return String */ @@ -765,7 +765,7 @@ function wfUrlProtocolsWithoutProtRel() { * 2) Handles protocols that don't use :// (e.g., mailto: and news: , as well as protocol-relative URLs) correctly * 3) Adds a "delimiter" element to the array, either '://', ':' or '//' (see (2)) * - * @param $url String: a URL to parse + * @param string $url a URL to parse * @return Array: bits of the URL in an associative array, per PHP docs */ function wfParseUrl( $url ) { @@ -808,9 +808,14 @@ function wfParseUrl( $url ) { if ( !isset( $bits['host'] ) ) { $bits['host'] = ''; - /* parse_url loses the third / for file:///c:/ urls (but not on variants) */ - if ( substr( $bits['path'], 0, 1 ) !== '/' ) { - $bits['path'] = '/' . $bits['path']; + // bug 45069 + if ( isset( $bits['path'] ) ) { + /* parse_url loses the third / for file:///c:/ urls (but not on variants) */ + if ( substr( $bits['path'], 0, 1 ) !== '/' ) { + $bits['path'] = '/' . $bits['path']; + } + } else { + $bits['path'] = ''; } } @@ -845,8 +850,6 @@ function wfExpandIRI_callback( $matches ) { return urldecode( $matches[1] ); } - - /** * Make URL indexes, appropriate for the el_index field of externallinks. * @@ -903,8 +906,8 @@ function wfMakeUrlIndexes( $url ) { /** * Check whether a given URL has a domain that occurs in a given set of domains - * @param $url string URL - * @param $domains array Array of domains (strings) + * @param string $url URL + * @param array $domains Array of domains (strings) * @return bool True if the host part of $url ends in one of the strings in $domains */ function wfMatchesDomainList( $url, $domains ) { @@ -932,7 +935,7 @@ function wfMatchesDomainList( $url, $domains ) { * $wgDebugComments - if on, some debug items may appear in comments in the HTML output. * * @param $text String - * @param $logonly Bool: set true to avoid appearing in HTML when $wgDebugComments is set + * @param bool $logonly set true to avoid appearing in HTML when $wgDebugComments is set */ function wfDebug( $text, $logonly = false ) { global $wgDebugLogFile, $wgProfileOnly, $wgDebugRawPage, $wgDebugLogPrefix; @@ -1004,7 +1007,7 @@ function wfDebugTimer() { /** * Send a line giving PHP memory usage. * - * @param $exact Bool: print exact values instead of kilobytes (default: false) + * @param bool $exact print exact values instead of kilobytes (default: false) */ function wfDebugMem( $exact = false ) { $mem = memory_get_usage(); @@ -1022,7 +1025,7 @@ function wfDebugMem( $exact = false ) { * * @param $logGroup String * @param $text String - * @param $public Bool: whether to log the event in the public log if no private + * @param bool $public whether to log the event in the public log if no private * log file is specified, (default true) */ function wfDebugLog( $logGroup, $text, $public = true ) { @@ -1036,14 +1039,14 @@ function wfDebugLog( $logGroup, $text, $public = true ) { wfErrorLog( "$time $host $wiki: $text", $wgDebugLogGroups[$logGroup] ); } } elseif ( $public === true ) { - wfDebug( $text, true ); + wfDebug( "[$logGroup] $text", true ); } } /** * Log for database errors * - * @param $text String: database error message. + * @param string $text database error message. */ function wfLogDBError( $text ) { global $wgDBerrorLog, $wgDBerrorLogTZ; @@ -1076,9 +1079,9 @@ function wfLogDBError( $text ) { * Throws a warning that $function is deprecated * * @param $function String - * @param $version String|bool: Version of MediaWiki that the function was deprecated in (Added in 1.19). - * @param $component String|bool: Added in 1.19. - * @param $callerOffset integer: How far up the callstack is the original + * @param string|bool $version Version of MediaWiki that the function was deprecated in (Added in 1.19). + * @param string|bool $component Added in 1.19. + * @param $callerOffset integer: How far up the call stack is the original * caller. 2 = function that called the function that called * wfDeprecated (Added in 1.20) * @@ -1092,7 +1095,7 @@ function wfDeprecated( $function, $version = false, $component = false, $callerO * Send a warning either to the debug log or in a PHP error depending on * $wgDevelopmentWarnings * - * @param $msg String: message to send + * @param string $msg message to send * @param $callerOffset Integer: number of items to go back in the backtrace to * find the correct caller (1 = function calling wfWarn, ...) * @param $level Integer: PHP error level; only used when $wgDevelopmentWarnings @@ -1109,7 +1112,8 @@ function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) { * send lines to the specified port, prefixed by the specified prefix and a space. * * @param $text String - * @param $file String filename + * @param string $file filename + * @throws MWException */ function wfErrorLog( $text, $file ) { if ( substr( $file, 0, 4 ) == 'udp:' ) { @@ -1212,9 +1216,18 @@ function wfLogProfilingData() { if ( $wgUser->isItemLoaded( 'id' ) && $wgUser->isAnon() ) { $forward .= ' anon'; } + + // Command line script uses a FauxRequest object which does not have + // any knowledge about an URL and throw an exception instead. + try { + $requestUrl = $wgRequest->getRequestURL(); + } catch ( MWException $e ) { + $requestUrl = 'n/a'; + } + $log = sprintf( "%s\t%04.3f\t%s\n", gmdate( 'YmdHis' ), $elapsed, - urldecode( $wgRequest->getRequestURL() . $forward ) ); + urldecode( $requestUrl . $forward ) ); wfErrorLog( $log . $profiler->getOutput(), $wgDebugLogFile ); } @@ -1224,17 +1237,21 @@ function wfLogProfilingData() { * * @param $key String * @param $count Int + * @return void */ function wfIncrStats( $key, $count = 1 ) { global $wgStatsMethod; $count = intval( $count ); + if ( $count == 0 ) { + return; + } if( $wgStatsMethod == 'udp' ) { - global $wgUDPProfilerHost, $wgUDPProfilerPort, $wgDBname, $wgAggregateStatsID; + global $wgUDPProfilerHost, $wgUDPProfilerPort, $wgAggregateStatsID; static $socket; - $id = $wgAggregateStatsID !== false ? $wgAggregateStatsID : $wgDBname; + $id = $wgAggregateStatsID !== false ? $wgAggregateStatsID : wfWikiID(); if ( !$socket ) { $socket = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP ); @@ -1366,9 +1383,13 @@ function wfUILang() { } /** - * This is the new function for getting translated interface messages. - * See the Message class for documentation how to use them. - * The intention is that this function replaces all old wfMsg* functions. + * This is the function for getting translated interface messages. + * + * @see Message class for documentation how to use them. + * @see https://www.mediawiki.org/wiki/Manual:Messages_API + * + * This function replaces all old wfMsg* functions. + * * @param $key \string Message key. * Varargs: normal message parameters. * @return Message @@ -1404,7 +1425,7 @@ function wfMessageFallback( /*...*/ ) { * * @deprecated since 1.18 * - * @param $key String: lookup key for the message, usually + * @param string $key lookup key for the message, usually * defined in languages/Language.php * * Parameters to the message, which can be used to insert variable text into @@ -1416,6 +1437,8 @@ function wfMessageFallback( /*...*/ ) { * @return String */ function wfMsg( $key ) { + wfDeprecated( __METHOD__, '1.21' ); + $args = func_get_args(); array_shift( $args ); return wfMsgReal( $key, $args ); @@ -1430,6 +1453,8 @@ function wfMsg( $key ) { * @return String */ function wfMsgNoTrans( $key ) { + wfDeprecated( __METHOD__, '1.21' ); + $args = func_get_args(); array_shift( $args ); return wfMsgReal( $key, $args, true, false, false ); @@ -1456,11 +1481,13 @@ function wfMsgNoTrans( $key ) { * * @deprecated since 1.18 * - * @param $key String: lookup key for the message, usually + * @param string $key lookup key for the message, usually * defined in languages/Language.php * @return String */ function wfMsgForContent( $key ) { + wfDeprecated( __METHOD__, '1.21' ); + global $wgForceUIMsgAsContentMsg; $args = func_get_args(); array_shift( $args ); @@ -1482,6 +1509,8 @@ function wfMsgForContent( $key ) { * @return String */ function wfMsgForContentNoTrans( $key ) { + wfDeprecated( __METHOD__, '1.21' ); + global $wgForceUIMsgAsContentMsg; $args = func_get_args(); array_shift( $args ); @@ -1499,7 +1528,7 @@ function wfMsgForContentNoTrans( $key ) { * * @deprecated since 1.18 * - * @param $key String: key to get. + * @param string $key key to get. * @param $args * @param $useDB Boolean * @param $forContent Mixed: Language code, or false for user lang, true for content lang. @@ -1507,6 +1536,8 @@ function wfMsgForContentNoTrans( $key ) { * @return String: the requested message. */ function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform = true ) { + wfDeprecated( __METHOD__, '1.21' ); + wfProfileIn( __METHOD__ ); $message = wfMsgGetKey( $key, $useDB, $forContent, $transform ); $message = wfMsgReplaceArgs( $message, $args ); @@ -1521,12 +1552,14 @@ function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform * * @param $key String * @param $useDB Bool - * @param $langCode String: Code of the language to get the message for, or + * @param string $langCode Code of the language to get the message for, or * behaves as a content language switch if it is a boolean. * @param $transform Boolean: whether to parse magic words, etc. * @return string */ function wfMsgGetKey( $key, $useDB = true, $langCode = false, $transform = true ) { + wfDeprecated( __METHOD__, '1.21' ); + wfRunHooks( 'NormalizeMessageKey', array( &$key, &$useDB, &$langCode, &$transform ) ); $cache = MessageCache::singleton(); @@ -1581,6 +1614,8 @@ function wfMsgReplaceArgs( $message, $args ) { * @return string */ function wfMsgHtml( $key ) { + wfDeprecated( __METHOD__, '1.21' ); + $args = func_get_args(); array_shift( $args ); return wfMsgReplaceArgs( htmlspecialchars( wfMsgGetKey( $key ) ), $args ); @@ -1600,6 +1635,8 @@ function wfMsgHtml( $key ) { * @return string */ function wfMsgWikiHtml( $key ) { + wfDeprecated( __METHOD__, '1.21' ); + $args = func_get_args(); array_shift( $args ); return wfMsgReplaceArgs( @@ -1613,8 +1650,8 @@ function wfMsgWikiHtml( $key ) { * * @deprecated since 1.18 * - * @param $key String: key of the message - * @param $options Array: processing rules. Can take the following options: + * @param string $key key of the message + * @param array $options processing rules. Can take the following options: * parse: parses wikitext to HTML * parseinline: parses wikitext to HTML and removes the surrounding * p's added by parser or tidy @@ -1625,12 +1662,14 @@ function wfMsgWikiHtml( $key ) { * content: fetch message for content language instead of interface * Also can accept a single associative argument, of the form 'language' => 'xx': * language: Language object or language code to fetch message for - * (overriden by content). + * (overridden by content). * Behavior for conflicting options (e.g., parse+parseinline) is undefined. * * @return String */ function wfMsgExt( $key, $options ) { + wfDeprecated( __METHOD__, '1.21' ); + $args = func_get_args(); array_shift( $args ); array_shift( $args ); @@ -1703,7 +1742,7 @@ function wfMsgExt( $key, $options ) { /** * Since wfMsg() and co suck, they don't return false if the message key they * looked up didn't exist but a XHTML string, this function checks for the - * nonexistance of messages by checking the MessageCache::get() result directly. + * nonexistence of messages by checking the MessageCache::get() result directly. * * @deprecated since 1.18. Use Message::isDisabled(). * @@ -1711,6 +1750,8 @@ function wfMsgExt( $key, $options ) { * @return Boolean True if the message *doesn't* exist. */ function wfEmptyMsg( $key ) { + wfDeprecated( __METHOD__, '1.21' ); + return MessageCache::singleton()->get( $key, /*useDB*/true, /*content*/false ) === false; } @@ -1718,7 +1759,8 @@ function wfEmptyMsg( $key ) { * Throw a debugging exception. This function previously once exited the process, * but now throws an exception instead, with similar results. * - * @param $msg String: message shown when dying. + * @param string $msg message shown when dying. + * @throws MWException */ function wfDebugDieBacktrace( $msg = '' ) { throw new MWException( $msg ); @@ -1783,13 +1825,13 @@ function wfReportTime() { * * With Zend Optimizer 3.2.0 loaded, this causes segfaults under somewhat * murky circumstances, which may be triggered in part by stub objects - * or other fancy talkin'. + * or other fancy talking'. * * Will return an empty array if Zend Optimizer is detected or if * debug_backtrace is disabled, otherwise the output from * debug_backtrace() (trimmed). * - * @param $limit int This parameter can be used to limit the number of stack frames returned + * @param int $limit This parameter can be used to limit the number of stack frames returned * * @return array of backtrace information */ @@ -1895,7 +1937,7 @@ function wfGetCaller( $level = 2 ) { * Return a string consisting of callers in the stack. Useful sometimes * for profiling specific points. * - * @param $limit int The maximum depth of the stack frame to return, or false for + * @param int $limit The maximum depth of the stack frame to return, or false for * the entire stack. * @return String */ @@ -1920,10 +1962,8 @@ function wfFormatStackFrame( $frame ) { $frame['function']; } - /* Some generic result counters, pulled out of SearchEngine */ - /** * @todo document * @@ -1941,8 +1981,8 @@ function wfShowingResults( $offset, $limit ) { * @param $offset String * @param $limit Integer * @param $link String - * @param $query String: optional URL query parameter string - * @param $atend Bool: optional param for specified if this is the last page + * @param string $query optional URL query parameter string + * @param bool $atend optional param for specified if this is the last page * @return String * @deprecated in 1.19; use Language::viewPrevNext() instead */ @@ -1968,8 +2008,8 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { /** * Make a list item, used by various special pages * - * @param $page String Page link - * @param $details String Text between brackets + * @param string $page Page link + * @param string $details Text between brackets * @param $oppositedm Boolean Add the direction mark opposite to your * language, to display text properly * @return String @@ -2018,8 +2058,8 @@ function wfClientAcceptsGzip( $force = false ) { * Obtain the offset and limit values from the request string; * used in special pages * - * @param $deflimit Int default limit if none supplied - * @param $optionname String Name of a user preference to check against + * @param int $deflimit default limit if none supplied + * @param string $optionname Name of a user preference to check against * @return array * */ @@ -2034,7 +2074,7 @@ function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) { * is achieved by substituting certain characters with HTML entities. * As required by the callers, "" is not used. * - * @param $text String: text to be escaped + * @param string $text text to be escaped * @return String */ function wfEscapeWikiText( $text ) { @@ -2050,7 +2090,7 @@ function wfEscapeWikiText( $text ) { } /** - * Get the current unix timetstamp with microseconds. Useful for profiling + * Get the current unix timestamp with microseconds. Useful for profiling * @return Float */ function wfTime() { @@ -2207,7 +2247,7 @@ function wfClearOutputBuffers() { * factors * * @param $accept String - * @param $def String default + * @param string $def default * @return Array */ function wfAcceptToPrefs( $accept, $def = '*/*' ) { @@ -2267,8 +2307,8 @@ function mimeTypeMatch( $type, $avail ) { * array of type to preference (preference is a float between 0.0 and 1.0). * Wildcards in the types are acceptable. * - * @param $cprefs Array: client's acceptable type list - * @param $sprefs Array: server's offered types + * @param array $cprefs client's acceptable type list + * @param array $sprefs server's offered types * @return string * * @todo FIXME: Doesn't handle params like 'text/plain; charset=UTF-8' @@ -2389,11 +2429,6 @@ define( 'TS_ORACLE', 6 ); */ define( 'TS_POSTGRES', 7 ); -/** - * DB2 format time - */ -define( 'TS_DB2', 8 ); - /** * ISO 8601 basic format with no timezone: 19860209T200000Z. This is used by ResourceLoader */ @@ -2405,7 +2440,7 @@ define( 'TS_ISO_8601_BASIC', 9 ); * @param $outputtype Mixed: A timestamp in one of the supported formats, the * function will autodetect which format is supplied and act * accordingly. - * @param $ts Mixed: the timestamp to convert or 0 for the current timestamp + * @param $ts Mixed: optional timestamp to convert, default 0 for the current time * @return Mixed: String / false The same date in the format specified in $outputtype or false */ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) { @@ -2413,7 +2448,7 @@ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) { $timestamp = new MWTimestamp( $ts ); return $timestamp->getTimestamp( $outputtype ); } catch( TimestampException $e ) { - wfDebug("wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n"); + wfDebug( "wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n" ); return false; } } @@ -2509,9 +2544,10 @@ function wfTempDir() { /** * Make directory, and make all parent directories if they don't exist * - * @param $dir String: full path to directory to create + * @param string $dir full path to directory to create * @param $mode Integer: chmod value to use, default is $wgDirectoryMode - * @param $caller String: optional caller param for debugging. + * @param string $caller optional caller param for debugging. + * @throws MWException * @return bool */ function wfMkdirParents( $dir, $mode = null, $caller = null ) { @@ -2585,12 +2621,14 @@ function wfPercent( $nr, $acc = 2, $round = true ) { /** * Find out whether or not a mixed variable exists in a string * + * @deprecated Just use str(i)pos * @param $needle String * @param $str String * @param $insensitive Boolean * @return Boolean */ function in_string( $needle, $str, $insensitive = false ) { + wfDeprecated( __METHOD__, '1.21' ); $func = 'strpos'; if( $insensitive ) $func = 'stripos'; @@ -2633,9 +2671,9 @@ function wfIniGetBool( $setting ) { * Wrapper function for PHP's dl(). This doesn't work in most situations from * PHP 5.3 onward, and is usually disabled in shared environments anyway. * - * @param $extension String A PHP extension. The file suffix (.so or .dll) + * @param string $extension A PHP extension. The file suffix (.so or .dll) * should be omitted - * @param $fileName String Name of the library, if not $extension.suffix + * @param string $fileName Name of the library, if not $extension.suffix * @return Bool - Whether or not the extension is loaded */ function wfDl( $extension, $fileName = null ) { @@ -2644,8 +2682,7 @@ function wfDl( $extension, $fileName = null ) { } $canDl = false; - $sapi = php_sapi_name(); - if( $sapi == 'cli' || $sapi == 'cgi' || $sapi == 'embed' ) { + if( PHP_SAPI == 'cli' || PHP_SAPI == 'cgi' || PHP_SAPI == 'embed' ) { $canDl = ( function_exists( 'dl' ) && is_callable( 'dl' ) && wfIniGetBool( 'enable_dl' ) && !wfIniGetBool( 'safe_mode' ) ); } @@ -2673,7 +2710,7 @@ function wfDl( $extension, $fileName = null ) { * @param varargs * @return String */ -function wfEscapeShellArg( ) { +function wfEscapeShellArg() { wfInitShellLocale(); $args = func_get_args(); @@ -2729,17 +2766,18 @@ function wfEscapeShellArg( ) { /** * Execute a shell command, with time and memory limits mirrored from the PHP * configuration if supported. - * @param $cmd String Command line, properly escaped for shell. + * @param string $cmd Command line, properly escaped for shell. * @param &$retval null|Mixed optional, will receive the program's exit code. * (non-zero is usually failure) - * @param $environ Array optional environment variables which should be + * @param array $environ optional environment variables which should be * added to the executed command environment. - * @param $limits Array optional array with limits(filesize, memory, time) + * @param array $limits optional array with limits(filesize, memory, time, walltime) * this overwrites the global wgShellMax* limits. * @return string collected stdout as a string (trailing newlines stripped) */ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array() ) { - global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime; + global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime, + $wgMaxShellWallClockTime, $wgShellCgroup; static $disabled; if ( is_null( $disabled ) ) { @@ -2786,15 +2824,27 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array $cmd = $envcmd . $cmd; if ( php_uname( 's' ) == 'Linux' ) { - $time = intval ( isset($limits['time']) ? $limits['time'] : $wgMaxShellTime ); - $mem = intval ( isset($limits['memory']) ? $limits['memory'] : $wgMaxShellMemory ); - $filesize = intval ( isset($limits['filesize']) ? $limits['filesize'] : $wgMaxShellFileSize ); - - if ( $time > 0 && $mem > 0 ) { - $script = "$IP/bin/ulimit4.sh"; - if ( is_executable( $script ) ) { - $cmd = '/bin/bash ' . escapeshellarg( $script ) . " $time $mem $filesize " . escapeshellarg( $cmd ); - } + $time = intval ( isset( $limits['time'] ) ? $limits['time'] : $wgMaxShellTime ); + if ( isset( $limits['walltime'] ) ) { + $wallTime = intval( $limits['walltime'] ); + } elseif ( isset( $limits['time'] ) ) { + $wallTime = $time; + } else { + $wallTime = intval( $wgMaxShellWallClockTime ); + } + $mem = intval ( isset( $limits['memory'] ) ? $limits['memory'] : $wgMaxShellMemory ); + $filesize = intval ( isset( $limits['filesize'] ) ? $limits['filesize'] : $wgMaxShellFileSize ); + + if ( $time > 0 || $mem > 0 || $filesize > 0 || $wallTime > 0 ) { + $cmd = '/bin/bash ' . escapeshellarg( "$IP/includes/limit.sh" ) . ' ' . + escapeshellarg( $cmd ) . ' ' . + escapeshellarg( + "MW_CPU_LIMIT=$time; " . + 'MW_CGROUP=' . escapeshellarg( $wgShellCgroup ) . '; ' . + "MW_MEM_LIMIT=$mem; " . + "MW_FILE_SIZE_LIMIT=$filesize; " . + "MW_WALL_CLOCK_LIMIT=$wallTime" + ); } } wfDebug( "wfShellExec: $cmd\n" ); @@ -2840,9 +2890,9 @@ function wfShellMaintenanceCmd( $script, array $parameters = array(), array $opt * Generate a shell-escaped command line string to run a MediaWiki cli script. * Note that $parameters should be a flat array and an option with an argument * should consist of two consecutive items in the array (do not use "--option value"). - * @param $script string MediaWiki cli script path - * @param $parameters Array Arguments and options to the script - * @param $options Array Associative array of options: + * @param string $script MediaWiki cli script path + * @param array $parameters Arguments and options to the script + * @param array $options Associative array of options: * 'php': The path to the php executable * 'wrapper': Path to a PHP wrapper to handle the maintenance script * @return Array @@ -2891,15 +2941,19 @@ function wfMerge( $old, $mine, $yours, &$result ) { $mytextFile = fopen( $mytextName = tempnam( $td, 'merge-mine-' ), 'w' ); $yourtextFile = fopen( $yourtextName = tempnam( $td, 'merge-your-' ), 'w' ); - fwrite( $oldtextFile, $old ); + # NOTE: diff3 issues a warning to stderr if any of the files does not end with + # a newline character. To avoid this, we normalize the trailing whitespace before + # creating the diff. + + fwrite( $oldtextFile, rtrim( $old ) . "\n" ); fclose( $oldtextFile ); - fwrite( $mytextFile, $mine ); + fwrite( $mytextFile, rtrim( $mine ) . "\n" ); fclose( $mytextFile ); - fwrite( $yourtextFile, $yours ); + fwrite( $yourtextFile, rtrim( $yours ) . "\n" ); fclose( $yourtextFile ); # Check for a conflict - $cmd = $wgDiff3 . ' -a --overlap-only ' . + $cmd = wfEscapeShellArg( $wgDiff3 ) . ' -a --overlap-only ' . wfEscapeShellArg( $mytextName ) . ' ' . wfEscapeShellArg( $oldtextName ) . ' ' . wfEscapeShellArg( $yourtextName ); @@ -2913,7 +2967,7 @@ function wfMerge( $old, $mine, $yours, &$result ) { pclose( $handle ); # Merge differences - $cmd = $wgDiff3 . ' -a -e --merge ' . + $cmd = wfEscapeShellArg( $wgDiff3 ) . ' -a -e --merge ' . wfEscapeShellArg( $mytextName, $oldtextName, $yourtextName ); $handle = popen( $cmd, 'r' ); $result = ''; @@ -2940,9 +2994,9 @@ function wfMerge( $old, $mine, $yours, &$result ) { * Returns unified plain-text diff of two texts. * Useful for machine processing of diffs. * - * @param $before String: the text before the changes. - * @param $after String: the text after the changes. - * @param $params String: command-line options for the diff command. + * @param string $before the text before the changes. + * @param string $after the text after the changes. + * @param string $params command-line options for the diff command. * @return String: unified diff of $before and $after */ function wfDiff( $before, $after, $params = '-u' ) { @@ -3022,6 +3076,7 @@ function wfDiff( $before, $after, $params = '-u' ) { * * @param $req_ver Mixed: the version to check, can be a string, an integer, or * a float + * @throws MWException */ function wfUsePHP( $req_ver ) { $php_ver = PHP_VERSION; @@ -3043,6 +3098,7 @@ function wfUsePHP( $req_ver ) { * * @param $req_ver Mixed: the version to check, can be a string, an integer, or * a float + * @throws MWException */ function wfUseMW( $req_ver ) { global $wgVersion; @@ -3061,7 +3117,7 @@ function wfUseMW( $req_ver ) { * We'll consider it so always, as we don't want '\s' in our Unix paths either. * * @param $path String - * @param $suffix String: to remove if present + * @param string $suffix to remove if present * @return String */ function wfBaseName( $path, $suffix = '' ) { @@ -3081,8 +3137,8 @@ function wfBaseName( $path, $suffix = '' ) { * May explode on non-matching case-insensitive paths, * funky symlinks, etc. * - * @param $path String: absolute destination path including target filename - * @param $from String: Absolute source path, directory only + * @param string $path absolute destination path including target filename + * @param string $from Absolute source path, directory only * @return String */ function wfRelativePath( $path, $from ) { @@ -3140,91 +3196,105 @@ function wfDoUpdates( $commit = '' ) { * Supports base 2 through 36; digit values 10-36 are represented * as lowercase letters a-z. Input is case-insensitive. * - * @param $input String: of digits - * @param $sourceBase Integer: 2-36 - * @param $destBase Integer: 2-36 - * @param $pad Integer: 1 or greater - * @param $lowercase Boolean - * @return String or false on invalid input - */ -function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, $lowercase = true ) { - $input = strval( $input ); - if( $sourceBase < 2 || + * @param string $input Input number + * @param int $sourceBase Base of the input number + * @param int $destBase Desired base of the output + * @param int $pad Minimum number of digits in the output (pad with zeroes) + * @param bool $lowercase Whether to output in lowercase or uppercase + * @param string $engine Either "gmp", "bcmath", or "php" + * @return string|bool The output number as a string, or false on error + */ +function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, $lowercase = true, $engine = 'auto' ) { + $input = (string)$input; + if( + $sourceBase < 2 || $sourceBase > 36 || $destBase < 2 || $destBase > 36 || - $pad < 1 || - $sourceBase != intval( $sourceBase ) || - $destBase != intval( $destBase ) || - $pad != intval( $pad ) || - !is_string( $input ) || - $input == '' ) { + $sourceBase != (int) $sourceBase || + $destBase != (int) $destBase || + $pad != (int) $pad || + !preg_match( "/^[" . substr( '0123456789abcdefghijklmnopqrstuvwxyz', 0, $sourceBase ) . "]+$/i", $input ) + ) { return false; } - $digitChars = ( $lowercase ) ? '0123456789abcdefghijklmnopqrstuvwxyz' : '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $inDigits = array(); - $outChars = ''; - // Decode and validate input string - $input = strtolower( $input ); - for( $i = 0; $i < strlen( $input ); $i++ ) { - $n = strpos( $digitChars, $input[$i] ); - if( $n === false || $n > $sourceBase ) { - return false; + static $baseChars = array ( + 10 => 'a', 11 => 'b', 12 => 'c', 13 => 'd', 14 => 'e', 15 => 'f', + 16 => 'g', 17 => 'h', 18 => 'i', 19 => 'j', 20 => 'k', 21 => 'l', + 22 => 'm', 23 => 'n', 24 => 'o', 25 => 'p', 26 => 'q', 27 => 'r', + 28 => 's', 29 => 't', 30 => 'u', 31 => 'v', 32 => 'w', 33 => 'x', + 34 => 'y', 35 => 'z', + + '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, + '6' => 6, '7' => 7, '8' => 8, '9' => 9, 'a' => 10, 'b' => 11, + 'c' => 12, 'd' => 13, 'e' => 14, 'f' => 15, 'g' => 16, 'h' => 17, + 'i' => 18, 'j' => 19, 'k' => 20, 'l' => 21, 'm' => 22, 'n' => 23, + 'o' => 24, 'p' => 25, 'q' => 26, 'r' => 27, 's' => 28, 't' => 29, + 'u' => 30, 'v' => 31, 'w' => 32, 'x' => 33, 'y' => 34, 'z' => 35 + ); + + if( extension_loaded( 'gmp' ) && ( $engine == 'auto' || $engine == 'gmp' ) ) { + $result = gmp_strval( gmp_init( $input, $sourceBase ), $destBase ); + } elseif( extension_loaded( 'bcmath' ) && ( $engine == 'auto' || $engine == 'bcmath' ) ) { + $decimal = '0'; + foreach( str_split( strtolower( $input ) ) as $char ) { + $decimal = bcmul( $decimal, $sourceBase ); + $decimal = bcadd( $decimal, $baseChars[$char] ); } - $inDigits[] = $n; - } - // Iterate over the input, modulo-ing out an output digit - // at a time until input is gone. - while( count( $inDigits ) ) { - $work = 0; - $workDigits = array(); + for( $result = ''; bccomp( $decimal, 0 ); $decimal = bcdiv( $decimal, $destBase, 0 ) ) { + $result .= $baseChars[bcmod( $decimal, $destBase )]; + } - // Long division... - foreach( $inDigits as $digit ) { - $work *= $sourceBase; - $work += $digit; + $result = strrev( $result ); + } else { + $inDigits = array(); + foreach( str_split( strtolower( $input ) ) as $char ) { + $inDigits[] = $baseChars[$char]; + } - if( $work < $destBase ) { - // Gonna need to pull another digit. - if( count( $workDigits ) ) { - // Avoid zero-padding; this lets us find - // the end of the input very easily when - // length drops to zero. - $workDigits[] = 0; - } - } else { - // Finally! Actual division! - $workDigits[] = intval( $work / $destBase ); + // Iterate over the input, modulo-ing out an output digit + // at a time until input is gone. + $result = ''; + while( $inDigits ) { + $work = 0; + $workDigits = array(); + + // Long division... + foreach( $inDigits as $digit ) { + $work *= $sourceBase; + $work += $digit; - // Isn't it annoying that most programming languages - // don't have a single divide-and-remainder operator, - // even though the CPU implements it that way? - $work = $work % $destBase; + if( $workDigits || $work >= $destBase ) { + $workDigits[] = (int) ( $work / $destBase ); + } + $work %= $destBase; } - } - // All that division leaves us with a remainder, - // which is conveniently our next output digit. - $outChars .= $digitChars[$work]; + // All that division leaves us with a remainder, + // which is conveniently our next output digit. + $result .= $baseChars[$work]; + + // And we continue! + $inDigits = $workDigits; + } - // And we continue! - $inDigits = $workDigits; + $result = strrev( $result ); } - while( strlen( $outChars ) < $pad ) { - $outChars .= '0'; + if( !$lowercase ) { + $result = strtoupper( $result ); } - return strrev( $outChars ); + return str_pad( $result, $pad, '0', STR_PAD_LEFT ); } /** * Create an object with a given name and an array of construct parameters * * @param $name String - * @param $p Array: parameters + * @param array $p parameters * @return object * @deprecated since 1.18, warnings in 1.18, removal in 1.20 */ @@ -3251,7 +3321,7 @@ function wfHttpOnlySafe() { } /** - * Check if there is sufficent entropy in php's built-in session generation + * Check if there is sufficient entropy in php's built-in session generation * @return bool true = there is sufficient entropy */ function wfCheckEntropy() { @@ -3414,7 +3484,7 @@ function wfSplitWikiID( $wiki ) { * belongs to. May contain a single string if the query is only * in one group. * - * @param $wiki String: the wiki ID, or false for the current wiki + * @param string $wiki the wiki ID, or false for the current wiki * * Note: multiple calls to wfGetDB(DB_SLAVE) during the course of one request * will always return the same object, unless the underlying connection or load @@ -3432,7 +3502,7 @@ function &wfGetDB( $db, $groups = array(), $wiki = false ) { /** * Get a load balancer object. * - * @param $wiki String: wiki ID, or false for the current wiki + * @param string $wiki wiki ID, or false for the current wiki * @return LoadBalancer */ function wfGetLB( $wiki = false ) { @@ -3452,8 +3522,8 @@ function &wfGetLBFactory() { * Find a file. * Shortcut for RepoGroup::singleton()->findFile() * - * @param $title String or Title object - * @param $options array Associative array of options: + * @param string $title or Title object + * @param array $options Associative array of options: * time: requested time for an archived image, or false for the * current version. An image object will be returned which was * created at the specified time. @@ -3511,7 +3581,7 @@ function wfQueriesMustScale() { * extensions; this is a wrapper around $wgScriptExtension etc. * except for 'index' and 'load' which use $wgScript/$wgLoadScript * - * @param $script String: script filename, sans extension + * @param string $script script filename, sans extension * @return String */ function wfScript( $script = 'index' ) { @@ -3559,16 +3629,6 @@ function wfBoolToStr( $value ) { return $value ? 'true' : 'false'; } -/** - * Load an extension messages file - * - * @deprecated since 1.16, warnings in 1.18, remove in 1.20 - * @codeCoverageIgnore - */ -function wfLoadExtensionMessages() { - wfDeprecated( __FUNCTION__, '1.16' ); -} - /** * Get a platform-independent path to the null file, e.g. /dev/null * @@ -3642,8 +3702,8 @@ function wfCountDown( $n ) { * characters before hashing. * @return string * @codeCoverageIgnore - * @deprecated since 1.20; Please use MWCryptRand for security purposes and wfRandomString for pesudo-random strings - * @warning This method is NOT secure. Additionally it has many callers that use it for pesudo-random purposes. + * @deprecated since 1.20; Please use MWCryptRand for security purposes and wfRandomString for pseudo-random strings + * @warning This method is NOT secure. Additionally it has many callers that use it for pseudo-random purposes. */ function wfGenerateToken( $salt = '' ) { wfDeprecated( __METHOD__, '1.20' ); @@ -3732,7 +3792,7 @@ function wfShorthandToInteger( $string = '' ) { * Get the normalised IETF language tag * See unit test for examples. * - * @param $code String: The language code. + * @param string $code The language code. * @return String: The language code which complying with BCP 47 standards. */ function wfBCP47( $code ) { @@ -3815,8 +3875,8 @@ function wfGetLangConverterCacheStorage() { /** * Call hook functions defined in $wgHooks * - * @param $event String: event name - * @param $args Array: parameters passed to hook functions + * @param string $event event name + * @param array $args parameters passed to hook functions * @return Boolean True if no handler aborted the hook */ function wfRunHooks( $event, $args = array() ) { @@ -3826,9 +3886,9 @@ function wfRunHooks( $event, $args = array() ) { /** * Wrapper around php's unpack. * - * @param $format String: The format string (See php's docs) + * @param string $format The format string (See php's docs) * @param $data: A binary string of binary data - * @param $length integer or false: The minimun length of $data. This is to + * @param $length integer or false: The minimum length of $data. This is to * prevent reading beyond the end of $data. false to disable the check. * * Also be careful when using this function to read unsigned 32 bit integer @@ -3868,9 +3928,9 @@ function wfUnpack( $format, $data, $length=false ) { * * Any subsequent links on the same line are considered to be exceptions, * i.e. articles where the image may occur inline. * - * @param $name string the image name to check + * @param string $name the image name to check * @param $contextTitle Title|bool the page on which the image occurs, if known - * @param $blacklist string wikitext of a file blacklist + * @param string $blacklist wikitext of a file blacklist * @return bool */ function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) { diff --git a/includes/HTMLForm.php b/includes/HTMLForm.php index 6f89d5b8..68639739 100644 --- a/includes/HTMLForm.php +++ b/includes/HTMLForm.php @@ -112,6 +112,7 @@ class HTMLForm extends ContextSource { 'submit' => 'HTMLSubmitField', 'hidden' => 'HTMLHiddenField', 'edittools' => 'HTMLEditTools', + 'checkmatrix' => 'HTMLCheckMatrix', // HTMLTextField will output the correct type="" attribute automagically. // There are about four zillion other HTML5 input types, like url, but @@ -189,10 +190,10 @@ class HTMLForm extends ContextSource { /** * Build a new HTMLForm from an array of field attributes - * @param $descriptor Array of Field constructs, as described above + * @param array $descriptor of Field constructs, as described above * @param $context IContextSource available since 1.18, will become compulsory in 1.18. * Obviates the need to call $form->setTitle() - * @param $messagePrefix String a prefix to go in front of default messages + * @param string $messagePrefix a prefix to go in front of default messages */ public function __construct( $descriptor, /*IContextSource*/ $context = null, $messagePrefix = '' ) { if ( $context instanceof IContextSource ) { @@ -247,8 +248,9 @@ class HTMLForm extends ContextSource { /** * Set format in which to display the form - * @param $format String the name of the format to use, must be one of + * @param string $format the name of the format to use, must be one of * $this->availableDisplayFormats + * @throws MWException * @since 1.20 * @return HTMLForm $this for chaining calls (since 1.20) */ @@ -279,7 +281,8 @@ class HTMLForm extends ContextSource { /** * Initialise a new Object for the field * @param $fieldname string - * @param $descriptor string input Descriptor, as described above + * @param string $descriptor input Descriptor, as described above + * @throws MWException * @return HTMLFormField subclass */ static function loadInputFromParameters( $fieldname, $descriptor ) { @@ -313,6 +316,7 @@ class HTMLForm extends ContextSource { * @attention When doing method chaining, that should be the very last * method call before displayForm(). * + * @throws MWException * @return HTMLForm $this for chaining calls (since 1.20) */ function prepareForm() { @@ -374,11 +378,12 @@ class HTMLForm extends ContextSource { } /** - * Validate all the fields, and call the submision callback + * Validate all the fields, and call the submission callback * function if everything is kosher. + * @throws MWException * @return Mixed Bool true == Successful submission, Bool false - * == No submission attempted, anything else == Error to - * display. + * == No submission attempted, anything else == Error to + * display. */ function trySubmit() { # Check for validation @@ -412,7 +417,7 @@ class HTMLForm extends ContextSource { /** * Set a callback to a function to do something with the form * once it's been successfully validated. - * @param $cb String function name. The function will be passed + * @param string $cb function name. The function will be passed * the output from HTMLForm::filterDataForSubmit, and must * return Bool true on success, Bool false if no submission * was attempted, or String HTML output to display on error. @@ -436,7 +441,7 @@ class HTMLForm extends ContextSource { /** * Set the introductory message, overwriting any existing message. - * @param $msg String complete text of message to display + * @param string $msg complete text of message to display * @return HTMLForm $this for chaining calls (since 1.20) */ function setIntro( $msg ) { @@ -447,7 +452,7 @@ class HTMLForm extends ContextSource { /** * Set the introductory message, overwriting any existing message. * @since 1.19 - * @param $msg String complete text of message to display + * @param string $msg complete text of message to display * @return HTMLForm $this for chaining calls (since 1.20) */ function setPreText( $msg ) { @@ -457,7 +462,7 @@ class HTMLForm extends ContextSource { /** * Add introductory text. - * @param $msg String complete text of message to display + * @param string $msg complete text of message to display * @return HTMLForm $this for chaining calls (since 1.20) */ function addPreText( $msg ) { @@ -467,8 +472,8 @@ class HTMLForm extends ContextSource { /** * Add header text, inside the form. - * @param $msg String complete text of message to display - * @param $section string The section to add the header to + * @param string $msg complete text of message to display + * @param string $section The section to add the header to * @return HTMLForm $this for chaining calls (since 1.20) */ function addHeaderText( $msg, $section = null ) { @@ -486,7 +491,7 @@ class HTMLForm extends ContextSource { /** * Set header text, inside the form. * @since 1.19 - * @param $msg String complete text of message to display + * @param string $msg complete text of message to display * @param $section The section to add the header to * @return HTMLForm $this for chaining calls (since 1.20) */ @@ -501,8 +506,8 @@ class HTMLForm extends ContextSource { /** * Add footer text, inside the form. - * @param $msg String complete text of message to display - * @param $section string The section to add the footer text to + * @param string $msg complete text of message to display + * @param string $section The section to add the footer text to * @return HTMLForm $this for chaining calls (since 1.20) */ function addFooterText( $msg, $section = null ) { @@ -520,8 +525,8 @@ class HTMLForm extends ContextSource { /** * Set footer text, inside the form. * @since 1.19 - * @param $msg String complete text of message to display - * @param $section string The section to add the footer text to + * @param string $msg complete text of message to display + * @param string $section The section to add the footer text to * @return HTMLForm $this for chaining calls (since 1.20) */ function setFooterText( $msg, $section = null ) { @@ -535,7 +540,7 @@ class HTMLForm extends ContextSource { /** * Add text to the end of the display. - * @param $msg String complete text of message to display + * @param string $msg complete text of message to display * @return HTMLForm $this for chaining calls (since 1.20) */ function addPostText( $msg ) { @@ -545,7 +550,7 @@ class HTMLForm extends ContextSource { /** * Set text at the end of the display. - * @param $msg String complete text of message to display + * @param string $msg complete text of message to display * @return HTMLForm $this for chaining calls (since 1.20) */ function setPostText( $msg ) { @@ -555,8 +560,8 @@ class HTMLForm extends ContextSource { /** * Add a hidden field to the output - * @param $name String field name. This will be used exactly as entered - * @param $value String field value + * @param string $name field name. This will be used exactly as entered + * @param string $value field value * @param $attribs Array * @return HTMLForm $this for chaining calls (since 1.20) */ @@ -568,9 +573,9 @@ class HTMLForm extends ContextSource { /** * Add a button to the form - * @param $name String field name. - * @param $value String field value - * @param $id String DOM id for the button (default: null) + * @param string $name field name. + * @param string $value field value + * @param string $id DOM id for the button (default: null) * @param $attribs Array * @return HTMLForm $this for chaining calls (since 1.20) */ @@ -620,7 +625,7 @@ class HTMLForm extends ContextSource { /** * Wrap the form innards in an actual "
    " element - * @param $html String HTML contents to wrap. + * @param string $html HTML contents to wrap. * @return String wrapped HTML. */ function wrapForm( $html ) { @@ -635,9 +640,9 @@ class HTMLForm extends ContextSource { : 'application/x-www-form-urlencoded'; # Attributes $attribs = array( - 'action' => $this->mAction === false ? $this->getTitle()->getFullURL() : $this->mAction, - 'method' => $this->mMethod, - 'class' => 'visualClear', + 'action' => $this->mAction === false ? $this->getTitle()->getFullURL() : $this->mAction, + 'method' => $this->mMethod, + 'class' => 'visualClear', 'enctype' => $encType, ); if ( !empty( $this->mId ) ) { @@ -708,8 +713,8 @@ class HTMLForm extends ContextSource { foreach ( $this->mButtons as $button ) { $attrs = array( - 'type' => 'submit', - 'name' => $button['name'], + 'type' => 'submit', + 'name' => $button['name'], 'value' => $button['value'] ); @@ -760,7 +765,7 @@ class HTMLForm extends ContextSource { /** * Format a stack of error messages into a single HTML string - * @param $errors Array of message keys/values + * @param array $errors of message keys/values * @return String HTML, a "'; } - wfProfileOut( __METHOD__ ); + wfProfileOut( __METHOD__ ); return $outText; } /** * Returns HTML for the "hidden categories on this page" list. * - * @param $hiddencats Array of hidden categories from Article::getHiddenCategories + * @param array $hiddencats of hidden categories from Article::getHiddenCategories * or similar * @return String: HTML output */ @@ -1888,7 +2032,7 @@ class Linker { } $outText .= ''; } - wfProfileOut( __METHOD__ ); + wfProfileOut( __METHOD__ ); return $outText; } @@ -1896,7 +2040,7 @@ class Linker { * Format a size in bytes for output, using an appropriate * unit (B, KB, MB or GB) according to the magnitude in question * - * @param $size int Size to format + * @param int $size Size to format * @return String */ public static function formatSize( $size ) { @@ -1910,7 +2054,7 @@ class Linker { * isn't always, because sometimes the accesskey needs to go on a different * element than the id, for reverse-compatibility, etc.) * - * @param $name String: id of the element, minus prefixes. + * @param string $name id of the element, minus prefixes. * @param $options Mixed: null or the string 'withaccess' to add an access- * key hint * @return String: contents of the title attribute (which you must HTML- @@ -1928,7 +2072,7 @@ class Linker { # Compatibility: formerly some tooltips had [alt-.] hardcoded $tooltip = preg_replace( "/ ?\[alt-.\]$/", '', $tooltip ); # Message equal to '-' means suppress it. - if ( $tooltip == '-' ) { + if ( $tooltip == '-' ) { $tooltip = false; } } @@ -1956,7 +2100,7 @@ class Linker { * the id but isn't always, because sometimes the accesskey needs to go on * a different element than the id, for reverse-compatibility, etc.) * - * @param $name String: id of the element, minus prefixes. + * @param string $name id of the element, minus prefixes. * @return String: contents of the accesskey attribute (which you must HTML- * escape), or false for no accesskey attribute */ @@ -2010,17 +2154,17 @@ class Linker { // RevDelete links using revision ID are stable across // page deletion and undeletion; use when possible. $query = array( - 'type' => 'revision', + 'type' => 'revision', 'target' => $title->getPrefixedDBkey(), - 'ids' => $rev->getId() + 'ids' => $rev->getId() ); } else { // Older deleted entries didn't save a revision ID. // We have to refer to these by timestamp, ick! $query = array( - 'type' => 'archive', + 'type' => 'archive', 'target' => $title->getPrefixedDBkey(), - 'ids' => $rev->getTimestamp() + 'ids' => $rev->getTimestamp() ); } return Linker::revDeleteLink( $query, @@ -2031,7 +2175,7 @@ class Linker { /** * Creates a (show/hide) link for deleting revisions/log entries * - * @param $query Array: query parameters to be passed to link() + * @param array $query query parameters to be passed to link() * @param $restricted Boolean: set to true to use a "" instead of a "" * @param $delete Boolean: set to true to use (show/hide) rather than (show) * @@ -2070,17 +2214,17 @@ class Linker { * This function is a shortcut to makeBrokenLinkObj(Title::newFromText($title),...). Do not call * it if you already have a title object handy. See makeBrokenLinkObj for further documentation. * - * @param $title String: The text of the title - * @param $text String: Link text - * @param $query String: Optional query part - * @param $trail String: Optional trail. Alphabetic characters at the start of this string will + * @param string $title The text of the title + * @param string $text Link text + * @param string $query Optional query part + * @param string $trail Optional trail. Alphabetic characters at the start of this string will * be included in the link text. Other characters will be appended after * the end of the link. * @return string */ static function makeBrokenLink( $title, $text = '', $query = '', $trail = '' ) { wfDeprecated( __METHOD__, '1.16' ); - + $nt = Title::newFromText( $title ); if ( $nt instanceof Title ) { return self::makeBrokenLinkObj( $nt, $text, $query, $trail ); @@ -2091,7 +2235,7 @@ class Linker { } /** - * @deprecated since 1.16 Use link() + * @deprecated since 1.16 Use link(); warnings since 1.21 * * Make a link for a title which may or may not be in the database. If you need to * call this lots of times, pre-fill the link cache with a LinkBatch, otherwise each @@ -2100,16 +2244,16 @@ class Linker { * @param $nt Title: the title object to make the link from, e.g. from * Title::newFromText. * @param $text String: link text - * @param $query String: optional query part - * @param $trail String: optional trail. Alphabetic characters at the start of this string will + * @param string $query optional query part + * @param string $trail optional trail. Alphabetic characters at the start of this string will * be included in the link text. Other characters will be appended after * the end of the link. - * @param $prefix String: optional prefix. As trail, only before instead of after. + * @param string $prefix optional prefix. As trail, only before instead of after. * @return string */ static function makeLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) { - # wfDeprecated( __METHOD__, '1.16' ); // See r105985 and it's revert. Somewhere still used. - + wfDeprecated( __METHOD__, '1.21' ); + wfProfileIn( __METHOD__ ); $query = wfCgiToArray( $query ); list( $inside, $trail ) = self::splitTrail( $trail ); @@ -2124,7 +2268,7 @@ class Linker { } /** - * @deprecated since 1.16 Use link() + * @deprecated since 1.16 Use link(); warnings since 1.21 * * Make a link for a title which definitely exists. This is faster than makeLinkObj because * it doesn't have to do a database query. It's also valid for interwiki titles and special @@ -2134,16 +2278,16 @@ class Linker { * @param $text String: text to replace the title * @param $query String: link target * @param $trail String: text after link - * @param $prefix String: text before link text - * @param $aprops String: extra attributes to the a-element + * @param string $prefix text before link text + * @param string $aprops extra attributes to the a-element * @param $style String: style to apply - if empty, use getInternalLinkAttributesObj instead * @return string the a-element */ static function makeKnownLinkObj( - $title, $text = '', $query = '', $trail = '', $prefix = '' , $aprops = '', $style = '' + $title, $text = '', $query = '', $trail = '', $prefix = '', $aprops = '', $style = '' ) { - # wfDeprecated( __METHOD__, '1.16' ); // See r105985 and it's revert. Somewhere still used. - + wfDeprecated( __METHOD__, '1.21' ); + wfProfileIn( __METHOD__ ); if ( $text == '' ) { @@ -2170,16 +2314,16 @@ class Linker { * * @param $title Title object of the target page * @param $text String: Link text - * @param $query String: Optional query part - * @param $trail String: Optional trail. Alphabetic characters at the start of this string will + * @param string $query Optional query part + * @param string $trail Optional trail. Alphabetic characters at the start of this string will * be included in the link text. Other characters will be appended after * the end of the link. - * @param $prefix String: Optional prefix + * @param string $prefix Optional prefix * @return string */ static function makeBrokenLinkObj( $title, $text = '', $query = '', $trail = '', $prefix = '' ) { wfDeprecated( __METHOD__, '1.16' ); - + wfProfileIn( __METHOD__ ); list( $inside, $trail ) = self::splitTrail( $trail ); @@ -2206,12 +2350,12 @@ class Linker { * @param $trail String: optional trail. Alphabetic characters at the start of this string will * be included in the link text. Other characters will be appended after * the end of the link. - * @param $prefix String: Optional prefix + * @param string $prefix Optional prefix * @return string */ static function makeColouredLinkObj( $nt, $colour, $text = '', $query = '', $trail = '', $prefix = '' ) { wfDeprecated( __METHOD__, '1.16' ); - + if ( $colour != '' ) { $style = self::getInternalLinkAttributesObj( $nt, $text, $colour ); } else { @@ -2268,8 +2412,8 @@ class DummyLinker { * Use PHP's magic __call handler to transform instance calls to a dummy instance * into static calls to the new Linker for backwards compatibility. * - * @param $fname String Name of called method - * @param $args Array Arguments to the method + * @param string $fname Name of called method + * @param array $args Arguments to the method * @return mixed */ public function __call( $fname, $args ) { diff --git a/includes/LinksUpdate.php b/includes/LinksUpdate.php index 87db4d60..d99ae22d 100644 --- a/includes/LinksUpdate.php +++ b/includes/LinksUpdate.php @@ -49,6 +49,7 @@ class LinksUpdate extends SqlDataUpdate { * @param $title Title of the page we're updating * @param $parserOutput ParserOutput: output from a full parse of this page * @param $recursive Boolean: queue jobs for recursive updates? + * @throws MWException */ function __construct( $title, $parserOutput, $recursive = true ) { parent::__construct( false ); // no implicit transaction @@ -71,6 +72,7 @@ class LinksUpdate extends SqlDataUpdate { } $this->mParserOutput = $parserOutput; + $this->mLinks = $parserOutput->getLinks(); $this->mImages = $parserOutput->getImages(); $this->mTemplates = $parserOutput->getTemplates(); @@ -211,14 +213,14 @@ class LinksUpdate extends SqlDataUpdate { $existing = $this->getExistingImages(); $imageUpdates = array_diff_key( $existing, $this->mImages ) + array_diff_key( $this->mImages, $existing ); - $this->dumbTableUpdate( 'pagelinks', $this->getLinkInsertions(), 'pl_from' ); - $this->dumbTableUpdate( 'imagelinks', $this->getImageInsertions(), 'il_from' ); + $this->dumbTableUpdate( 'pagelinks', $this->getLinkInsertions(), 'pl_from' ); + $this->dumbTableUpdate( 'imagelinks', $this->getImageInsertions(), 'il_from' ); $this->dumbTableUpdate( 'categorylinks', $this->getCategoryInsertions(), 'cl_from' ); $this->dumbTableUpdate( 'templatelinks', $this->getTemplateInsertions(), 'tl_from' ); $this->dumbTableUpdate( 'externallinks', $this->getExternalInsertions(), 'el_from' ); - $this->dumbTableUpdate( 'langlinks', $this->getInterlangInsertions(),'ll_from' ); - $this->dumbTableUpdate( 'iwlinks', $this->getInterwikiInsertions(),'iwl_from' ); - $this->dumbTableUpdate( 'page_props', $this->getPropertyInsertions(), 'pp_page' ); + $this->dumbTableUpdate( 'langlinks', $this->getInterlangInsertions(), 'll_from' ); + $this->dumbTableUpdate( 'iwlinks', $this->getInterwikiInsertions(), 'iwl_from' ); + $this->dumbTableUpdate( 'page_props', $this->getPropertyInsertions(), 'pp_page' ); # Update the cache of all the category pages and image description # pages which were changed, and fix the category table count @@ -236,26 +238,20 @@ class LinksUpdate extends SqlDataUpdate { } function queueRecursiveJobs() { - global $wgUpdateRowsPerJob; wfProfileIn( __METHOD__ ); - $cache = $this->mTitle->getBacklinkCache(); - $batches = $cache->partition( 'templatelinks', $wgUpdateRowsPerJob ); - if ( !$batches ) { - wfProfileOut( __METHOD__ ); - return; - } - $jobs = array(); - foreach ( $batches as $batch ) { - list( $start, $end ) = $batch; - $params = array( - 'table' => 'templatelinks', - 'start' => $start, - 'end' => $end, + if ( $this->mTitle->getBacklinkCache()->hasLinks( 'templatelinks' ) ) { + $job = new RefreshLinksJob2( + $this->mTitle, + array( + 'table' => 'templatelinks', + ) + Job::newRootJobParams( // "overall" refresh links job info + "refreshlinks:templatelinks:{$this->mTitle->getPrefixedText()}" + ) ); - $jobs[] = new RefreshLinksJob2( $this->mTitle, $params ); + JobQueueGroup::singleton()->push( $job ); + JobQueueGroup::singleton()->deduplicateRootJob( $job ); } - Job::batchInsert( $jobs ); wfProfileOut( __METHOD__ ); } @@ -269,8 +265,8 @@ class LinksUpdate extends SqlDataUpdate { /** * Update all the appropriate counts in the category table. - * @param $added array associative array of category name => sort key - * @param $deleted array associative array of category name => sort key + * @param array $added associative array of category name => sort key + * @param array $deleted associative array of category name => sort key */ function updateCategoryCounts( $added, $deleted ) { $a = WikiPage::factory( $this->mTitle ); @@ -295,7 +291,7 @@ class LinksUpdate extends SqlDataUpdate { $this->mDb->delete( $table, array( $fromField => $this->mId ), __METHOD__ ); if ( count( $insertions ) ) { # The link array was constructed without FOR UPDATE, so there may - # be collisions. This may cause minor link table inconsistencies, + # be collisions. This may cause minor link table inconsistencies, # which is better than crippling the site with lock contention. $this->mDb->insert( $table, $insertions, __METHOD__, array( 'IGNORE' ) ); } @@ -346,6 +342,7 @@ class LinksUpdate extends SqlDataUpdate { } if ( count( $insertions ) ) { $this->mDb->insert( $table, $insertions, __METHOD__, 'IGNORE' ); + wfRunHooks( 'LinksUpdateAfterInsert', array( $this, $table, $insertions ) ); } } @@ -363,9 +360,9 @@ class LinksUpdate extends SqlDataUpdate { : $dbkeys; foreach ( $diffs as $dbk => $id ) { $arr[] = array( - 'pl_from' => $this->mId, + 'pl_from' => $this->mId, 'pl_namespace' => $ns, - 'pl_title' => $dbk + 'pl_title' => $dbk ); } } @@ -383,9 +380,9 @@ class LinksUpdate extends SqlDataUpdate { $diffs = isset( $existing[$ns] ) ? array_diff_key( $dbkeys, $existing[$ns] ) : $dbkeys; foreach ( $diffs as $dbk => $id ) { $arr[] = array( - 'tl_from' => $this->mId, + 'tl_from' => $this->mId, 'tl_namespace' => $ns, - 'tl_title' => $dbk + 'tl_title' => $dbk ); } } @@ -404,7 +401,7 @@ class LinksUpdate extends SqlDataUpdate { foreach( $diffs as $iname => $dummy ) { $arr[] = array( 'il_from' => $this->mId, - 'il_to' => $iname + 'il_to' => $iname ); } return $arr; @@ -421,9 +418,9 @@ class LinksUpdate extends SqlDataUpdate { foreach( $diffs as $url => $dummy ) { foreach( wfMakeUrlIndexes( $url ) as $index ) { $arr[] = array( - 'el_from' => $this->mId, - 'el_to' => $url, - 'el_index' => $index, + 'el_from' => $this->mId, + 'el_to' => $url, + 'el_index' => $index, ); } } @@ -433,7 +430,7 @@ class LinksUpdate extends SqlDataUpdate { /** * Get an array of category insertions * - * @param $existing array mapping existing category names to sort keys. If both + * @param array $existing mapping existing category names to sort keys. If both * match a link in $this, the link will be omitted from the output * * @return array @@ -462,8 +459,8 @@ class LinksUpdate extends SqlDataUpdate { $this->mTitle->getCategorySortkey( $prefix ) ); $arr[] = array( - 'cl_from' => $this->mId, - 'cl_to' => $name, + 'cl_from' => $this->mId, + 'cl_to' => $name, 'cl_sortkey' => $sortkey, 'cl_timestamp' => $this->mDb->timestamp(), 'cl_sortkey_prefix' => $prefix, @@ -477,7 +474,7 @@ class LinksUpdate extends SqlDataUpdate { /** * Get an array of interlanguage link insertions * - * @param $existing Array mapping existing language codes to titles + * @param array $existing mapping existing language codes to titles * * @return array */ @@ -486,8 +483,8 @@ class LinksUpdate extends SqlDataUpdate { $arr = array(); foreach( $diffs as $lang => $title ) { $arr[] = array( - 'll_from' => $this->mId, - 'll_lang' => $lang, + 'll_from' => $this->mId, + 'll_lang' => $lang, 'll_title' => $title ); } @@ -504,9 +501,9 @@ class LinksUpdate extends SqlDataUpdate { $arr = array(); foreach ( $diffs as $name => $value ) { $arr[] = array( - 'pp_page' => $this->mId, - 'pp_propname' => $name, - 'pp_value' => $value, + 'pp_page' => $this->mId, + 'pp_propname' => $name, + 'pp_value' => $value, ); } return $arr; @@ -524,9 +521,9 @@ class LinksUpdate extends SqlDataUpdate { $diffs = isset( $existing[$prefix] ) ? array_diff_key( $dbkeys, $existing[$prefix] ) : $dbkeys; foreach ( $diffs as $dbk => $id ) { $arr[] = array( - 'iwl_from' => $this->mId, + 'iwl_from' => $this->mId, 'iwl_prefix' => $prefix, - 'iwl_title' => $dbk + 'iwl_title' => $dbk ); } } @@ -823,11 +820,16 @@ class LinksDeletionUpdate extends SqlDataUpdate { * Constructor * * @param $page WikiPage Page we are updating + * @throws MWException */ function __construct( WikiPage $page ) { parent::__construct( false ); // no implicit transaction $this->mPage = $page; + + if ( !$page->exists() ) { + throw new MWException( "Page ID not known, perhaps the page doesn't exist?" ); + } } /** @@ -879,4 +881,16 @@ class LinksDeletionUpdate extends SqlDataUpdate { __METHOD__ ); } } + + /** + * Update all the appropriate counts in the category table. + * @param array $added associative array of category name => sort key + * @param array $deleted associative array of category name => sort key + */ + function updateCategoryCounts( $added, $deleted ) { + $a = WikiPage::factory( $this->mTitle ); + $a->updateCategoryCounts( + array_keys( $added ), array_keys( $deleted ) + ); + } } diff --git a/includes/LocalisationCache.php b/includes/LocalisationCache.php deleted file mode 100644 index d8e5d3a3..00000000 --- a/includes/LocalisationCache.php +++ /dev/null @@ -1,1281 +0,0 @@ - - * zh-hans -> en ). Some common errors are corrected, for example namespace - * names with spaces instead of underscores, but heavyweight processing, such - * as grammatical transformation, is done by the caller. - */ -class LocalisationCache { - /** Configuration associative array */ - var $conf; - - /** - * True if recaching should only be done on an explicit call to recache(). - * Setting this reduces the overhead of cache freshness checking, which - * requires doing a stat() for every extension i18n file. - */ - var $manualRecache = false; - - /** - * True to treat all files as expired until they are regenerated by this object. - */ - var $forceRecache = false; - - /** - * The cache data. 3-d array, where the first key is the language code, - * the second key is the item key e.g. 'messages', and the third key is - * an item specific subkey index. Some items are not arrays and so for those - * items, there are no subkeys. - */ - var $data = array(); - - /** - * The persistent store object. An instance of LCStore. - * - * @var LCStore - */ - var $store; - - /** - * A 2-d associative array, code/key, where presence indicates that the item - * is loaded. Value arbitrary. - * - * For split items, if set, this indicates that all of the subitems have been - * loaded. - */ - var $loadedItems = array(); - - /** - * A 3-d associative array, code/key/subkey, where presence indicates that - * the subitem is loaded. Only used for the split items, i.e. messages. - */ - var $loadedSubitems = array(); - - /** - * An array where presence of a key indicates that that language has been - * initialised. Initialisation includes checking for cache expiry and doing - * any necessary updates. - */ - var $initialisedLangs = array(); - - /** - * An array mapping non-existent pseudo-languages to fallback languages. This - * is filled by initShallowFallback() when data is requested from a language - * that lacks a Messages*.php file. - */ - var $shallowFallbacks = array(); - - /** - * An array where the keys are codes that have been recached by this instance. - */ - var $recachedLangs = array(); - - /** - * All item keys - */ - static public $allKeys = array( - 'fallback', 'namespaceNames', 'bookstoreList', - 'magicWords', 'messages', 'rtl', 'capitalizeAllNouns', 'digitTransformTable', - 'separatorTransformTable', 'fallback8bitEncoding', 'linkPrefixExtension', - 'linkTrail', 'namespaceAliases', - 'dateFormats', 'datePreferences', 'datePreferenceMigrationMap', - 'defaultDateFormat', 'extraUserToggles', 'specialPageAliases', - 'imageFiles', 'preloadedMessages', 'namespaceGenderAliases', - 'digitGroupingPattern', 'pluralRules', 'compiledPluralRules', - ); - - /** - * Keys for items which consist of associative arrays, which may be merged - * by a fallback sequence. - */ - static public $mergeableMapKeys = array( 'messages', 'namespaceNames', - 'dateFormats', 'imageFiles', 'preloadedMessages' - ); - - /** - * Keys for items which are a numbered array. - */ - static public $mergeableListKeys = array( 'extraUserToggles' ); - - /** - * Keys for items which contain an array of arrays of equivalent aliases - * for each subitem. The aliases may be merged by a fallback sequence. - */ - static public $mergeableAliasListKeys = array( 'specialPageAliases' ); - - /** - * Keys for items which contain an associative array, and may be merged if - * the primary value contains the special array key "inherit". That array - * key is removed after the first merge. - */ - static public $optionalMergeKeys = array( 'bookstoreList' ); - - /** - * Keys for items that are formatted like $magicWords - */ - static public $magicWordKeys = array( 'magicWords' ); - - /** - * Keys for items where the subitems are stored in the backend separately. - */ - static public $splitKeys = array( 'messages' ); - - /** - * Keys which are loaded automatically by initLanguage() - */ - static public $preloadedKeys = array( 'dateFormats', 'namespaceNames' ); - - /** - * Associative array of cached plural rules. The key is the language code, - * the value is an array of plural rules for that language. - */ - var $pluralRules = null; - - var $mergeableKeys = null; - - /** - * Constructor. - * For constructor parameters, see the documentation in DefaultSettings.php - * for $wgLocalisationCacheConf. - * - * @param $conf Array - */ - function __construct( $conf ) { - global $wgCacheDirectory; - - $this->conf = $conf; - $storeConf = array(); - if ( !empty( $conf['storeClass'] ) ) { - $storeClass = $conf['storeClass']; - } else { - switch ( $conf['store'] ) { - case 'files': - case 'file': - $storeClass = 'LCStore_CDB'; - break; - case 'db': - $storeClass = 'LCStore_DB'; - break; - case 'accel': - $storeClass = 'LCStore_Accel'; - break; - case 'detect': - $storeClass = $wgCacheDirectory ? 'LCStore_CDB' : 'LCStore_DB'; - break; - default: - throw new MWException( - 'Please set $wgLocalisationCacheConf[\'store\'] to something sensible.' ); - } - } - - wfDebug( get_class( $this ) . ": using store $storeClass\n" ); - if ( !empty( $conf['storeDirectory'] ) ) { - $storeConf['directory'] = $conf['storeDirectory']; - } - - $this->store = new $storeClass( $storeConf ); - foreach ( array( 'manualRecache', 'forceRecache' ) as $var ) { - if ( isset( $conf[$var] ) ) { - $this->$var = $conf[$var]; - } - } - } - - /** - * Returns true if the given key is mergeable, that is, if it is an associative - * array which can be merged through a fallback sequence. - * @param $key - * @return bool - */ - public function isMergeableKey( $key ) { - if ( $this->mergeableKeys === null ) { - $this->mergeableKeys = array_flip( array_merge( - self::$mergeableMapKeys, - self::$mergeableListKeys, - self::$mergeableAliasListKeys, - self::$optionalMergeKeys, - self::$magicWordKeys - ) ); - } - return isset( $this->mergeableKeys[$key] ); - } - - /** - * Get a cache item. - * - * Warning: this may be slow for split items (messages), since it will - * need to fetch all of the subitems from the cache individually. - * @param $code - * @param $key - * @return mixed - */ - public function getItem( $code, $key ) { - if ( !isset( $this->loadedItems[$code][$key] ) ) { - wfProfileIn( __METHOD__ . '-load' ); - $this->loadItem( $code, $key ); - wfProfileOut( __METHOD__ . '-load' ); - } - - if ( $key === 'fallback' && isset( $this->shallowFallbacks[$code] ) ) { - return $this->shallowFallbacks[$code]; - } - - return $this->data[$code][$key]; - } - - /** - * Get a subitem, for instance a single message for a given language. - * @param $code - * @param $key - * @param $subkey - * @return null - */ - public function getSubitem( $code, $key, $subkey ) { - if ( !isset( $this->loadedSubitems[$code][$key][$subkey] ) && - !isset( $this->loadedItems[$code][$key] ) ) { - wfProfileIn( __METHOD__ . '-load' ); - $this->loadSubitem( $code, $key, $subkey ); - wfProfileOut( __METHOD__ . '-load' ); - } - - if ( isset( $this->data[$code][$key][$subkey] ) ) { - return $this->data[$code][$key][$subkey]; - } else { - return null; - } - } - - /** - * Get the list of subitem keys for a given item. - * - * This is faster than array_keys($lc->getItem(...)) for the items listed in - * self::$splitKeys. - * - * Will return null if the item is not found, or false if the item is not an - * array. - * @param $code - * @param $key - * @return bool|null|string - */ - public function getSubitemList( $code, $key ) { - if ( in_array( $key, self::$splitKeys ) ) { - return $this->getSubitem( $code, 'list', $key ); - } else { - $item = $this->getItem( $code, $key ); - if ( is_array( $item ) ) { - return array_keys( $item ); - } else { - return false; - } - } - } - - /** - * Load an item into the cache. - * @param $code - * @param $key - */ - protected function loadItem( $code, $key ) { - if ( !isset( $this->initialisedLangs[$code] ) ) { - $this->initLanguage( $code ); - } - - // Check to see if initLanguage() loaded it for us - if ( isset( $this->loadedItems[$code][$key] ) ) { - return; - } - - if ( isset( $this->shallowFallbacks[$code] ) ) { - $this->loadItem( $this->shallowFallbacks[$code], $key ); - return; - } - - if ( in_array( $key, self::$splitKeys ) ) { - $subkeyList = $this->getSubitem( $code, 'list', $key ); - foreach ( $subkeyList as $subkey ) { - if ( isset( $this->data[$code][$key][$subkey] ) ) { - continue; - } - $this->data[$code][$key][$subkey] = $this->getSubitem( $code, $key, $subkey ); - } - } else { - $this->data[$code][$key] = $this->store->get( $code, $key ); - } - - $this->loadedItems[$code][$key] = true; - } - - /** - * Load a subitem into the cache - * @param $code - * @param $key - * @param $subkey - * @return - */ - protected function loadSubitem( $code, $key, $subkey ) { - if ( !in_array( $key, self::$splitKeys ) ) { - $this->loadItem( $code, $key ); - return; - } - - if ( !isset( $this->initialisedLangs[$code] ) ) { - $this->initLanguage( $code ); - } - - // Check to see if initLanguage() loaded it for us - if ( isset( $this->loadedItems[$code][$key] ) || - isset( $this->loadedSubitems[$code][$key][$subkey] ) ) { - return; - } - - if ( isset( $this->shallowFallbacks[$code] ) ) { - $this->loadSubitem( $this->shallowFallbacks[$code], $key, $subkey ); - return; - } - - $value = $this->store->get( $code, "$key:$subkey" ); - $this->data[$code][$key][$subkey] = $value; - $this->loadedSubitems[$code][$key][$subkey] = true; - } - - /** - * Returns true if the cache identified by $code is missing or expired. - * @return bool - */ - public function isExpired( $code ) { - if ( $this->forceRecache && !isset( $this->recachedLangs[$code] ) ) { - wfDebug( __METHOD__ . "($code): forced reload\n" ); - return true; - } - - $deps = $this->store->get( $code, 'deps' ); - $keys = $this->store->get( $code, 'list', 'messages' ); - $preload = $this->store->get( $code, 'preload' ); - // Different keys may expire separately, at least in LCStore_Accel - if ( $deps === null || $keys === null || $preload === null ) { - wfDebug( __METHOD__ . "($code): cache missing, need to make one\n" ); - return true; - } - - foreach ( $deps as $dep ) { - // Because we're unserializing stuff from cache, we - // could receive objects of classes that don't exist - // anymore (e.g. uninstalled extensions) - // When this happens, always expire the cache - if ( !$dep instanceof CacheDependency || $dep->isExpired() ) { - wfDebug( __METHOD__ . "($code): cache for $code expired due to " . - get_class( $dep ) . "\n" ); - return true; - } - } - - return false; - } - - /** - * Initialise a language in this object. Rebuild the cache if necessary. - * @param $code - */ - protected function initLanguage( $code ) { - if ( isset( $this->initialisedLangs[$code] ) ) { - return; - } - - $this->initialisedLangs[$code] = true; - - # If the code is of the wrong form for a Messages*.php file, do a shallow fallback - if ( !Language::isValidBuiltInCode( $code ) ) { - $this->initShallowFallback( $code, 'en' ); - return; - } - - # Recache the data if necessary - if ( !$this->manualRecache && $this->isExpired( $code ) ) { - if ( file_exists( Language::getMessagesFileName( $code ) ) ) { - $this->recache( $code ); - } elseif ( $code === 'en' ) { - throw new MWException( 'MessagesEn.php is missing.' ); - } else { - $this->initShallowFallback( $code, 'en' ); - } - return; - } - - # Preload some stuff - $preload = $this->getItem( $code, 'preload' ); - if ( $preload === null ) { - if ( $this->manualRecache ) { - // No Messages*.php file. Do shallow fallback to en. - if ( $code === 'en' ) { - throw new MWException( 'No localisation cache found for English. ' . - 'Please run maintenance/rebuildLocalisationCache.php.' ); - } - $this->initShallowFallback( $code, 'en' ); - return; - } else { - throw new MWException( 'Invalid or missing localisation cache.' ); - } - } - $this->data[$code] = $preload; - foreach ( $preload as $key => $item ) { - if ( in_array( $key, self::$splitKeys ) ) { - foreach ( $item as $subkey => $subitem ) { - $this->loadedSubitems[$code][$key][$subkey] = true; - } - } else { - $this->loadedItems[$code][$key] = true; - } - } - } - - /** - * Create a fallback from one language to another, without creating a - * complete persistent cache. - * @param $primaryCode - * @param $fallbackCode - */ - public function initShallowFallback( $primaryCode, $fallbackCode ) { - $this->data[$primaryCode] =& $this->data[$fallbackCode]; - $this->loadedItems[$primaryCode] =& $this->loadedItems[$fallbackCode]; - $this->loadedSubitems[$primaryCode] =& $this->loadedSubitems[$fallbackCode]; - $this->shallowFallbacks[$primaryCode] = $fallbackCode; - } - - /** - * Read a PHP file containing localisation data. - * @param $_fileName - * @param $_fileType - * @return array - */ - protected function readPHPFile( $_fileName, $_fileType ) { - // Disable APC caching - $_apcEnabled = ini_set( 'apc.cache_by_default', '0' ); - include( $_fileName ); - ini_set( 'apc.cache_by_default', $_apcEnabled ); - - if ( $_fileType == 'core' || $_fileType == 'extension' ) { - $data = compact( self::$allKeys ); - } elseif ( $_fileType == 'aliases' ) { - $data = compact( 'aliases' ); - } else { - throw new MWException( __METHOD__ . ": Invalid file type: $_fileType" ); - } - return $data; - } - - /** - * Get the compiled plural rules for a given language from the XML files. - * @since 1.20 - */ - public function getCompiledPluralRules( $code ) { - $rules = $this->getPluralRules( $code ); - if ( $rules === null ) { - return null; - } - try { - $compiledRules = CLDRPluralRuleEvaluator::compile( $rules ); - } catch( CLDRPluralRuleError $e ) { - wfDebugLog( 'l10n', $e->getMessage() . "\n" ); - return array(); - } - return $compiledRules; - } - - /** - * Get the plural rules for a given language from the XML files. - * Cached. - * @since 1.20 - */ - public function getPluralRules( $code ) { - if ( $this->pluralRules === null ) { - $cldrPlural = __DIR__ . "/../languages/data/plurals.xml"; - $mwPlural = __DIR__ . "/../languages/data/plurals-mediawiki.xml"; - // Load CLDR plural rules - $this->loadPluralFile( $cldrPlural ); - if ( file_exists( $mwPlural ) ) { - // Override or extend - $this->loadPluralFile( $mwPlural ); - } - } - if ( !isset( $this->pluralRules[$code] ) ) { - return null; - } else { - return $this->pluralRules[$code]; - } - } - - - /** - * Load a plural XML file with the given filename, compile the relevant - * rules, and save the compiled rules in a process-local cache. - */ - protected function loadPluralFile( $fileName ) { - $doc = new DOMDocument; - $doc->load( $fileName ); - $rulesets = $doc->getElementsByTagName( "pluralRules" ); - foreach ( $rulesets as $ruleset ) { - $codes = $ruleset->getAttribute( 'locales' ); - $rules = array(); - $ruleElements = $ruleset->getElementsByTagName( "pluralRule" ); - foreach ( $ruleElements as $elt ) { - $rules[] = $elt->nodeValue; - } - foreach ( explode( ' ', $codes ) as $code ) { - $this->pluralRules[$code] = $rules; - } - } - } - - /** - * Read the data from the source files for a given language, and register - * the relevant dependencies in the $deps array. If the localisation - * exists, the data array is returned, otherwise false is returned. - */ - protected function readSourceFilesAndRegisterDeps( $code, &$deps ) { - $fileName = Language::getMessagesFileName( $code ); - if ( !file_exists( $fileName ) ) { - return false; - } - - $deps[] = new FileDependency( $fileName ); - $data = $this->readPHPFile( $fileName, 'core' ); - - # Load CLDR plural rules for JavaScript - $data['pluralRules'] = $this->getPluralRules( $code ); - # And for PHP - $data['compiledPluralRules'] = $this->getCompiledPluralRules( $code ); - - $deps['plurals'] = new FileDependency( __DIR__ . "/../languages/data/plurals.xml" ); - $deps['plurals-mw'] = new FileDependency( __DIR__ . "/../languages/data/plurals-mediawiki.xml" ); - return $data; - } - - /** - * Merge two localisation values, a primary and a fallback, overwriting the - * primary value in place. - * @param $key - * @param $value - * @param $fallbackValue - */ - protected function mergeItem( $key, &$value, $fallbackValue ) { - if ( !is_null( $value ) ) { - if ( !is_null( $fallbackValue ) ) { - if ( in_array( $key, self::$mergeableMapKeys ) ) { - $value = $value + $fallbackValue; - } elseif ( in_array( $key, self::$mergeableListKeys ) ) { - $value = array_unique( array_merge( $fallbackValue, $value ) ); - } elseif ( in_array( $key, self::$mergeableAliasListKeys ) ) { - $value = array_merge_recursive( $value, $fallbackValue ); - } elseif ( in_array( $key, self::$optionalMergeKeys ) ) { - if ( !empty( $value['inherit'] ) ) { - $value = array_merge( $fallbackValue, $value ); - } - - if ( isset( $value['inherit'] ) ) { - unset( $value['inherit'] ); - } - } elseif ( in_array( $key, self::$magicWordKeys ) ) { - $this->mergeMagicWords( $value, $fallbackValue ); - } - } - } else { - $value = $fallbackValue; - } - } - - /** - * @param $value - * @param $fallbackValue - */ - protected function mergeMagicWords( &$value, $fallbackValue ) { - foreach ( $fallbackValue as $magicName => $fallbackInfo ) { - if ( !isset( $value[$magicName] ) ) { - $value[$magicName] = $fallbackInfo; - } else { - $oldSynonyms = array_slice( $fallbackInfo, 1 ); - $newSynonyms = array_slice( $value[$magicName], 1 ); - $synonyms = array_values( array_unique( array_merge( - $newSynonyms, $oldSynonyms ) ) ); - $value[$magicName] = array_merge( array( $fallbackInfo[0] ), $synonyms ); - } - } - } - - /** - * Given an array mapping language code to localisation value, such as is - * found in extension *.i18n.php files, iterate through a fallback sequence - * to merge the given data with an existing primary value. - * - * Returns true if any data from the extension array was used, false - * otherwise. - * @param $codeSequence - * @param $key - * @param $value - * @param $fallbackValue - * @return bool - */ - protected function mergeExtensionItem( $codeSequence, $key, &$value, $fallbackValue ) { - $used = false; - foreach ( $codeSequence as $code ) { - if ( isset( $fallbackValue[$code] ) ) { - $this->mergeItem( $key, $value, $fallbackValue[$code] ); - $used = true; - } - } - - return $used; - } - - /** - * Load localisation data for a given language for both core and extensions - * and save it to the persistent cache store and the process cache - * @param $code - */ - public function recache( $code ) { - global $wgExtensionMessagesFiles; - wfProfileIn( __METHOD__ ); - - if ( !$code ) { - throw new MWException( "Invalid language code requested" ); - } - $this->recachedLangs[$code] = true; - - # Initial values - $initialData = array_combine( - self::$allKeys, - array_fill( 0, count( self::$allKeys ), null ) ); - $coreData = $initialData; - $deps = array(); - - # Load the primary localisation from the source file - $data = $this->readSourceFilesAndRegisterDeps( $code, $deps ); - if ( $data === false ) { - wfDebug( __METHOD__ . ": no localisation file for $code, using fallback to en\n" ); - $coreData['fallback'] = 'en'; - } else { - wfDebug( __METHOD__ . ": got localisation for $code from source\n" ); - - # Merge primary localisation - foreach ( $data as $key => $value ) { - $this->mergeItem( $key, $coreData[$key], $value ); - } - - } - - # Fill in the fallback if it's not there already - if ( is_null( $coreData['fallback'] ) ) { - $coreData['fallback'] = $code === 'en' ? false : 'en'; - } - if ( $coreData['fallback'] === false ) { - $coreData['fallbackSequence'] = array(); - } else { - $coreData['fallbackSequence'] = array_map( 'trim', explode( ',', $coreData['fallback'] ) ); - $len = count( $coreData['fallbackSequence'] ); - - # Ensure that the sequence ends at en - if ( $coreData['fallbackSequence'][$len - 1] !== 'en' ) { - $coreData['fallbackSequence'][] = 'en'; - } - - # Load the fallback localisation item by item and merge it - foreach ( $coreData['fallbackSequence'] as $fbCode ) { - # Load the secondary localisation from the source file to - # avoid infinite cycles on cyclic fallbacks - $fbData = $this->readSourceFilesAndRegisterDeps( $fbCode, $deps ); - if ( $fbData === false ) { - continue; - } - - foreach ( self::$allKeys as $key ) { - if ( !isset( $fbData[$key] ) ) { - continue; - } - - if ( is_null( $coreData[$key] ) || $this->isMergeableKey( $key ) ) { - $this->mergeItem( $key, $coreData[$key], $fbData[$key] ); - } - } - } - } - - $codeSequence = array_merge( array( $code ), $coreData['fallbackSequence'] ); - - # Load the extension localisations - # This is done after the core because we know the fallback sequence now. - # But it has a higher precedence for merging so that we can support things - # like site-specific message overrides. - $allData = $initialData; - foreach ( $wgExtensionMessagesFiles as $fileName ) { - $data = $this->readPHPFile( $fileName, 'extension' ); - $used = false; - - foreach ( $data as $key => $item ) { - if ( $this->mergeExtensionItem( $codeSequence, $key, $allData[$key], $item ) ) { - $used = true; - } - } - - if ( $used ) { - $deps[] = new FileDependency( $fileName ); - } - } - - # Merge core data into extension data - foreach ( $coreData as $key => $item ) { - $this->mergeItem( $key, $allData[$key], $item ); - } - - # Add cache dependencies for any referenced globals - $deps['wgExtensionMessagesFiles'] = new GlobalDependency( 'wgExtensionMessagesFiles' ); - $deps['version'] = new ConstantDependency( 'MW_LC_VERSION' ); - - # Add dependencies to the cache entry - $allData['deps'] = $deps; - - # Replace spaces with underscores in namespace names - $allData['namespaceNames'] = str_replace( ' ', '_', $allData['namespaceNames'] ); - - # And do the same for special page aliases. $page is an array. - foreach ( $allData['specialPageAliases'] as &$page ) { - $page = str_replace( ' ', '_', $page ); - } - # Decouple the reference to prevent accidental damage - unset( $page ); - - # If there were no plural rules, return an empty array - if ( $allData['pluralRules'] === null ) { - $allData['pluralRules'] = array(); - } - if ( $allData['compiledPluralRules'] === null ) { - $allData['compiledPluralRules'] = array(); - } - - # Set the list keys - $allData['list'] = array(); - foreach ( self::$splitKeys as $key ) { - $allData['list'][$key] = array_keys( $allData[$key] ); - } - # Run hooks - wfRunHooks( 'LocalisationCacheRecache', array( $this, $code, &$allData ) ); - - if ( is_null( $allData['namespaceNames'] ) ) { - throw new MWException( __METHOD__ . ': Localisation data failed sanity check! ' . - 'Check that your languages/messages/MessagesEn.php file is intact.' ); - } - - # Set the preload key - $allData['preload'] = $this->buildPreload( $allData ); - - # Save to the process cache and register the items loaded - $this->data[$code] = $allData; - foreach ( $allData as $key => $item ) { - $this->loadedItems[$code][$key] = true; - } - - # Save to the persistent cache - $this->store->startWrite( $code ); - foreach ( $allData as $key => $value ) { - if ( in_array( $key, self::$splitKeys ) ) { - foreach ( $value as $subkey => $subvalue ) { - $this->store->set( "$key:$subkey", $subvalue ); - } - } else { - $this->store->set( $key, $value ); - } - } - $this->store->finishWrite(); - - # Clear out the MessageBlobStore - # HACK: If using a null (i.e. disabled) storage backend, we - # can't write to the MessageBlobStore either - if ( !$this->store instanceof LCStore_Null ) { - MessageBlobStore::clear(); - } - - wfProfileOut( __METHOD__ ); - } - - /** - * Build the preload item from the given pre-cache data. - * - * The preload item will be loaded automatically, improving performance - * for the commonly-requested items it contains. - * @param $data - * @return array - */ - protected function buildPreload( $data ) { - $preload = array( 'messages' => array() ); - foreach ( self::$preloadedKeys as $key ) { - $preload[$key] = $data[$key]; - } - - foreach ( $data['preloadedMessages'] as $subkey ) { - if ( isset( $data['messages'][$subkey] ) ) { - $subitem = $data['messages'][$subkey]; - } else { - $subitem = null; - } - $preload['messages'][$subkey] = $subitem; - } - - return $preload; - } - - /** - * Unload the data for a given language from the object cache. - * Reduces memory usage. - * @param $code - */ - public function unload( $code ) { - unset( $this->data[$code] ); - unset( $this->loadedItems[$code] ); - unset( $this->loadedSubitems[$code] ); - unset( $this->initialisedLangs[$code] ); - - foreach ( $this->shallowFallbacks as $shallowCode => $fbCode ) { - if ( $fbCode === $code ) { - $this->unload( $shallowCode ); - } - } - } - - /** - * Unload all data - */ - public function unloadAll() { - foreach ( $this->initialisedLangs as $lang => $unused ) { - $this->unload( $lang ); - } - } - - /** - * Disable the storage backend - */ - public function disableBackend() { - $this->store = new LCStore_Null; - $this->manualRecache = false; - } -} - -/** - * Interface for the persistence layer of LocalisationCache. - * - * The persistence layer is two-level hierarchical cache. The first level - * is the language, the second level is the item or subitem. - * - * Since the data for a whole language is rebuilt in one operation, it needs - * to have a fast and atomic method for deleting or replacing all of the - * current data for a given language. The interface reflects this bulk update - * operation. Callers writing to the cache must first call startWrite(), then - * will call set() a couple of thousand times, then will call finishWrite() - * to commit the operation. When finishWrite() is called, the cache is - * expected to delete all data previously stored for that language. - * - * The values stored are PHP variables suitable for serialize(). Implementations - * of LCStore are responsible for serializing and unserializing. - */ -interface LCStore { - /** - * Get a value. - * @param $code string Language code - * @param $key string Cache key - */ - function get( $code, $key ); - - /** - * Start a write transaction. - * @param $code Language code - */ - function startWrite( $code ); - - /** - * Finish a write transaction. - */ - function finishWrite(); - - /** - * Set a key to a given value. startWrite() must be called before this - * is called, and finishWrite() must be called afterwards. - * @param $key - * @param $value - */ - function set( $key, $value ); -} - -/** - * LCStore implementation which uses PHP accelerator to store data. - * This will work if one of XCache, WinCache or APC cacher is configured. - * (See ObjectCache.php) - */ -class LCStore_Accel implements LCStore { - var $currentLang; - var $keys; - - public function __construct() { - $this->cache = wfGetCache( CACHE_ACCEL ); - } - - public function get( $code, $key ) { - $k = wfMemcKey( 'l10n', $code, 'k', $key ); - $r = $this->cache->get( $k ); - return $r === false ? null : $r; - } - - public function startWrite( $code ) { - $k = wfMemcKey( 'l10n', $code, 'l' ); - $keys = $this->cache->get( $k ); - if ( $keys ) { - foreach ( $keys as $k ) { - $this->cache->delete( $k ); - } - } - $this->currentLang = $code; - $this->keys = array(); - } - - public function finishWrite() { - if ( $this->currentLang ) { - $k = wfMemcKey( 'l10n', $this->currentLang, 'l' ); - $this->cache->set( $k, array_keys( $this->keys ) ); - } - $this->currentLang = null; - $this->keys = array(); - } - - public function set( $key, $value ) { - if ( $this->currentLang ) { - $k = wfMemcKey( 'l10n', $this->currentLang, 'k', $key ); - $this->keys[$k] = true; - $this->cache->set( $k, $value ); - } - } -} - -/** - * LCStore implementation which uses the standard DB functions to store data. - * This will work on any MediaWiki installation. - */ -class LCStore_DB implements LCStore { - var $currentLang; - var $writesDone = false; - - /** - * @var DatabaseBase - */ - var $dbw; - var $batch; - var $readOnly = false; - - public function get( $code, $key ) { - if ( $this->writesDone ) { - $db = wfGetDB( DB_MASTER ); - } else { - $db = wfGetDB( DB_SLAVE ); - } - $row = $db->selectRow( 'l10n_cache', array( 'lc_value' ), - array( 'lc_lang' => $code, 'lc_key' => $key ), __METHOD__ ); - if ( $row ) { - return unserialize( $row->lc_value ); - } else { - return null; - } - } - - public function startWrite( $code ) { - if ( $this->readOnly ) { - return; - } - - if ( !$code ) { - throw new MWException( __METHOD__ . ": Invalid language \"$code\"" ); - } - - $this->dbw = wfGetDB( DB_MASTER ); - try { - $this->dbw->begin( __METHOD__ ); - $this->dbw->delete( 'l10n_cache', array( 'lc_lang' => $code ), __METHOD__ ); - } catch ( DBQueryError $e ) { - if ( $this->dbw->wasReadOnlyError() ) { - $this->readOnly = true; - $this->dbw->rollback( __METHOD__ ); - $this->dbw->ignoreErrors( false ); - return; - } else { - throw $e; - } - } - - $this->currentLang = $code; - $this->batch = array(); - } - - public function finishWrite() { - if ( $this->readOnly ) { - return; - } - - if ( $this->batch ) { - $this->dbw->insert( 'l10n_cache', $this->batch, __METHOD__ ); - } - - $this->dbw->commit( __METHOD__ ); - $this->currentLang = null; - $this->dbw = null; - $this->batch = array(); - $this->writesDone = true; - } - - public function set( $key, $value ) { - if ( $this->readOnly ) { - return; - } - - if ( is_null( $this->currentLang ) ) { - throw new MWException( __CLASS__ . ': must call startWrite() before calling set()' ); - } - - $this->batch[] = array( - 'lc_lang' => $this->currentLang, - 'lc_key' => $key, - 'lc_value' => serialize( $value ) ); - - if ( count( $this->batch ) >= 100 ) { - $this->dbw->insert( 'l10n_cache', $this->batch, __METHOD__ ); - $this->batch = array(); - } - } -} - -/** - * LCStore implementation which stores data as a collection of CDB files in the - * directory given by $wgCacheDirectory. If $wgCacheDirectory is not set, this - * will throw an exception. - * - * Profiling indicates that on Linux, this implementation outperforms MySQL if - * the directory is on a local filesystem and there is ample kernel cache - * space. The performance advantage is greater when the DBA extension is - * available than it is with the PHP port. - * - * See Cdb.php and http://cr.yp.to/cdb.html - */ -class LCStore_CDB implements LCStore { - var $readers, $writer, $currentLang, $directory; - - function __construct( $conf = array() ) { - global $wgCacheDirectory; - - if ( isset( $conf['directory'] ) ) { - $this->directory = $conf['directory']; - } else { - $this->directory = $wgCacheDirectory; - } - } - - public function get( $code, $key ) { - if ( !isset( $this->readers[$code] ) ) { - $fileName = $this->getFileName( $code ); - - if ( !file_exists( $fileName ) ) { - $this->readers[$code] = false; - } else { - $this->readers[$code] = CdbReader::open( $fileName ); - } - } - - if ( !$this->readers[$code] ) { - return null; - } else { - $value = $this->readers[$code]->get( $key ); - - if ( $value === false ) { - return null; - } - return unserialize( $value ); - } - } - - public function startWrite( $code ) { - if ( !file_exists( $this->directory ) ) { - if ( !wfMkdirParents( $this->directory, null, __METHOD__ ) ) { - throw new MWException( "Unable to create the localisation store " . - "directory \"{$this->directory}\"" ); - } - } - - // Close reader to stop permission errors on write - if ( !empty( $this->readers[$code] ) ) { - $this->readers[$code]->close(); - } - - $this->writer = CdbWriter::open( $this->getFileName( $code ) ); - $this->currentLang = $code; - } - - public function finishWrite() { - // Close the writer - $this->writer->close(); - $this->writer = null; - unset( $this->readers[$this->currentLang] ); - $this->currentLang = null; - } - - public function set( $key, $value ) { - if ( is_null( $this->writer ) ) { - throw new MWException( __CLASS__ . ': must call startWrite() before calling set()' ); - } - $this->writer->set( $key, serialize( $value ) ); - } - - protected function getFileName( $code ) { - if ( !$code || strpos( $code, '/' ) !== false ) { - throw new MWException( __METHOD__ . ": Invalid language \"$code\"" ); - } - return "{$this->directory}/l10n_cache-$code.cdb"; - } -} - -/** - * Null store backend, used to avoid DB errors during install - */ -class LCStore_Null implements LCStore { - public function get( $code, $key ) { - return null; - } - - public function startWrite( $code ) {} - public function finishWrite() {} - public function set( $key, $value ) {} -} - -/** - * A localisation cache optimised for loading large amounts of data for many - * languages. Used by rebuildLocalisationCache.php. - */ -class LocalisationCache_BulkLoad extends LocalisationCache { - /** - * A cache of the contents of data files. - * Core files are serialized to avoid using ~1GB of RAM during a recache. - */ - var $fileCache = array(); - - /** - * Most recently used languages. Uses the linked-list aspect of PHP hashtables - * to keep the most recently used language codes at the end of the array, and - * the language codes that are ready to be deleted at the beginning. - */ - var $mruLangs = array(); - - /** - * Maximum number of languages that may be loaded into $this->data - */ - var $maxLoadedLangs = 10; - - /** - * @param $fileName - * @param $fileType - * @return array|mixed - */ - protected function readPHPFile( $fileName, $fileType ) { - $serialize = $fileType === 'core'; - if ( !isset( $this->fileCache[$fileName][$fileType] ) ) { - $data = parent::readPHPFile( $fileName, $fileType ); - - if ( $serialize ) { - $encData = serialize( $data ); - } else { - $encData = $data; - } - - $this->fileCache[$fileName][$fileType] = $encData; - - return $data; - } elseif ( $serialize ) { - return unserialize( $this->fileCache[$fileName][$fileType] ); - } else { - return $this->fileCache[$fileName][$fileType]; - } - } - - /** - * @param $code - * @param $key - * @return mixed - */ - public function getItem( $code, $key ) { - unset( $this->mruLangs[$code] ); - $this->mruLangs[$code] = true; - return parent::getItem( $code, $key ); - } - - /** - * @param $code - * @param $key - * @param $subkey - * @return - */ - public function getSubitem( $code, $key, $subkey ) { - unset( $this->mruLangs[$code] ); - $this->mruLangs[$code] = true; - return parent::getSubitem( $code, $key, $subkey ); - } - - /** - * @param $code - */ - public function recache( $code ) { - parent::recache( $code ); - unset( $this->mruLangs[$code] ); - $this->mruLangs[$code] = true; - $this->trimCache(); - } - - /** - * @param $code - */ - public function unload( $code ) { - unset( $this->mruLangs[$code] ); - parent::unload( $code ); - } - - /** - * Unload cached languages until there are less than $this->maxLoadedLangs - */ - protected function trimCache() { - while ( count( $this->data ) > $this->maxLoadedLangs && count( $this->mruLangs ) ) { - reset( $this->mruLangs ); - $code = key( $this->mruLangs ); - wfDebug( __METHOD__ . ": unloading $code\n" ); - $this->unload( $code ); - } - } - -} diff --git a/includes/MagicWord.php b/includes/MagicWord.php index 42791f57..7b669249 100644 --- a/includes/MagicWord.php +++ b/includes/MagicWord.php @@ -217,7 +217,7 @@ class MagicWord { /**#@-*/ - function __construct($id = 0, $syn = array(), $cs = false) { + function __construct( $id = 0, $syn = array(), $cs = false ) { $this->mId = $id; $this->mSynonyms = (array)$syn; $this->mCaseSensitive = $cs; @@ -282,6 +282,7 @@ class MagicWord { */ static function getDoubleUnderscoreArray() { if ( is_null( self::$mDoubleUnderscoreArray ) ) { + wfRunHooks( 'GetDoubleUnderscoreIDs', array( &self::$mDoubleUnderscoreIDs ) ); self::$mDoubleUnderscoreArray = new MagicWordArray( self::$mDoubleUnderscoreIDs ); } return self::$mDoubleUnderscoreArray; @@ -366,7 +367,7 @@ class MagicWord { * @return string */ function getRegex() { - if ($this->mRegex == '' ) { + if ( $this->mRegex == '' ) { $this->initRegex(); } return $this->mRegex; @@ -392,7 +393,7 @@ class MagicWord { * @return string */ function getRegexStart() { - if ($this->mRegex == '' ) { + if ( $this->mRegex == '' ) { $this->initRegex(); } return $this->mRegexStart; @@ -404,7 +405,7 @@ class MagicWord { * @return string */ function getBaseRegex() { - if ($this->mRegex == '') { + if ( $this->mRegex == '' ) { $this->initRegex(); } return $this->mBaseRegex; @@ -453,9 +454,9 @@ class MagicWord { # blank elements and re-sort the indices. # See also bug 6526 - $matches = array_values(array_filter($matches)); + $matches = array_values( array_filter( $matches ) ); - if ( count($matches) == 1 ) { + if ( count( $matches ) == 1 ) { return $matches[0]; } else { return $matches[1]; @@ -463,7 +464,6 @@ class MagicWord { } } - /** * Returns true if the text matches the word, and alters the * input string, removing all instances of the word @@ -534,7 +534,7 @@ class MagicWord { * * @return string */ - function getVariableRegex() { + function getVariableRegex() { if ( $this->mVariableRegex == '' ) { $this->initRegex(); } @@ -577,7 +577,7 @@ class MagicWord { * * @return bool */ - function getWasModified(){ + function getWasModified() { return $this->mModified; } @@ -594,10 +594,10 @@ class MagicWord { * * @return bool */ - function replaceMultiple( $magicarr, $subject, &$result ){ + function replaceMultiple( $magicarr, $subject, &$result ) { $search = array(); $replace = array(); - foreach( $magicarr as $id => $replacement ){ + foreach( $magicarr as $id => $replacement ) { $mw = MagicWord::get( $id ); $search[] = $mw->getRegex(); $replace[] = $replacement; @@ -617,7 +617,7 @@ class MagicWord { function addToArray( &$array, $value ) { global $wgContLang; foreach ( $this->mSynonyms as $syn ) { - $array[$wgContLang->lc($syn)] = $value; + $array[$wgContLang->lc( $syn )] = $value; } } @@ -811,7 +811,7 @@ class MagicWordArray { return array( $magicName, $paramValue ); } // This shouldn't happen either - throw new MWException( __METHOD__.': parameter not found' ); + throw new MWException( __METHOD__ . ': parameter not found' ); } /** diff --git a/includes/MappedIterator.php b/includes/MappedIterator.php new file mode 100644 index 00000000..b4376f44 --- /dev/null +++ b/includes/MappedIterator.php @@ -0,0 +1,96 @@ +baseIterator = new ArrayIterator( $iter ); + } elseif ( $iter instanceof Iterator ) { + $this->baseIterator = $iter; + } else { + throw new MWException( "Invalid base iterator provided." ); + } + $this->vCallback = $vCallback; + } + + /** + * @return void + */ + public function rewind() { + $this->baseIterator->rewind(); + } + + /** + * @return Mixed|null Returns null if out of range + */ + public function current() { + if ( !$this->baseIterator->valid() ) { + return null; // out of range + } + return call_user_func_array( $this->vCallback, array( $this->baseIterator->current() ) ); + } + + /** + * @return Mixed|null Returns null if out of range + */ + public function key() { + if ( !$this->baseIterator->valid() ) { + return null; // out of range + } + return $this->baseIterator->key(); + } + + /** + * @return void + */ + public function next() { + $this->baseIterator->next(); + } + + /** + * @return bool + */ + public function valid() { + return $this->baseIterator->valid(); + } +} diff --git a/includes/Message.php b/includes/Message.php index b0147b9a..5719f830 100644 --- a/includes/Message.php +++ b/includes/Message.php @@ -22,11 +22,11 @@ */ /** - * The Message class provides methods which fullfil two basic services: + * The Message class provides methods which fulfil two basic services: * - fetching interface messages * - processing messages into a variety of formats * - * First implemented with MediaWiki 1.17, the Message class is intented to + * First implemented with MediaWiki 1.17, the Message class is intended to * replace the old wfMsg* functions that over time grew unusable. * @see https://www.mediawiki.org/wiki/Manual:Messages_API for equivalences * between old and new functions. @@ -202,6 +202,11 @@ class Message { */ protected $title = null; + /** + * Content object representing the message + */ + protected $content = null; + /** * @var string */ @@ -211,7 +216,7 @@ class Message { * Constructor. * @since 1.17 * @param $key: message key, or array of message keys to try and use the first non-empty message for - * @param $params Array message parameters + * @param array $params message parameters * @return Message: $this */ public function __construct( $key, $params = array() ) { @@ -221,12 +226,45 @@ class Message { $this->language = $wgLang; } + /** + * Returns the message key + * + * @since 1.21 + * + * @return string + */ + public function getKey() { + return $this->key; + } + + /** + * Returns the message parameters + * + * @since 1.21 + * + * @return string[] + */ + public function getParams() { + return $this->parameters; + } + + /** + * Returns the message format + * + * @since 1.21 + * + * @return string + */ + public function getFormat() { + return $this->format; + } + /** * Factory function that is just wrapper for the real constructor. It is - * intented to be used instead of the real constructor, because it allows + * intended to be used instead of the real constructor, because it allows * chaining method calls, while new objects don't. * @since 1.17 - * @param $key String: message key + * @param string $key message key * @param Varargs: parameters as Strings * @return Message: $this */ @@ -247,9 +285,9 @@ class Message { public static function newFallbackSequence( /*...*/ ) { $keys = func_get_args(); if ( func_num_args() == 1 ) { - if ( is_array($keys[0]) ) { + if ( is_array( $keys[0] ) ) { // Allow an array to be passed as the first argument instead - $keys = array_values($keys[0]); + $keys = array_values( $keys[0] ); } else { // Optimize a single string to not need special fallback handling $keys = $keys[0]; @@ -332,6 +370,7 @@ class Message { * turned off. * @since 1.17 * @param $lang Mixed: language code or Language object. + * @throws MWException * @return Message: $this */ public function inLanguage( $lang ) { @@ -404,6 +443,18 @@ class Message { return $this; } + /** + * Returns the message as a Content object. + * @return Content + */ + public function content() { + if ( !$this->content ) { + $this->content = new MessageContent( $this ); + } + + return $this->content; + } + /** * Returns the message parsed from wikitext to HTML. * @since 1.17 @@ -413,13 +464,22 @@ class Message { $string = $this->fetchMessage(); if ( $string === false ) { - $key = htmlspecialchars( is_array( $this->key ) ? $this->key[0] : $this->key ); + $key = htmlspecialchars( is_array( $this->key ) ? $this->key[0] : $this->key ); if ( $this->format === 'plain' ) { return '<' . $key . '>'; } return '<' . $key . '>'; } + # Replace $* with a list of parameters for &uselang=qqx. + if ( strpos( $string, '$*' ) !== false ) { + $paramlist = ''; + if ( $this->parameters !== array() ) { + $paramlist = ': $' . implode( ', $', range( 1, count( $this->parameters ) ) ); + } + $string = str_replace( '$*', $paramlist, $string ); + } + # Replace parameters before text parsing $string = $this->replaceParameters( $string, 'before' ); @@ -430,11 +490,11 @@ class Message { if( preg_match( '/^

    (.*)\n?<\/p>\n?$/sU', $string, $m ) ) { $string = $m[1]; } - } elseif( $this->format === 'block-parse' ){ + } elseif( $this->format === 'block-parse' ) { $string = $this->parseText( $string ); - } elseif( $this->format === 'text' ){ + } elseif( $this->format === 'text' ) { $string = $this->transformText( $string ); - } elseif( $this->format === 'escaped' ){ + } elseif( $this->format === 'escaped' ) { $string = $this->transformText( $string ); $string = htmlspecialchars( $string, ENT_QUOTES, 'UTF-8', false ); } @@ -447,13 +507,30 @@ class Message { /** * Magic method implementation of the above (for PHP >= 5.2.0), so we can do, eg: - * $foo = Message::get($key); + * $foo = Message::get( $key ); * $string = "$foo"; * @since 1.18 * @return String */ public function __toString() { - return $this->toString(); + // PHP doesn't allow __toString to throw exceptions and will + // trigger a fatal error if it does. So, catch any exceptions. + + try { + return $this->toString(); + } catch ( Exception $ex ) { + try { + trigger_error( "Exception caught in " . __METHOD__ . " (message " . $this->key . "): " + . $ex, E_USER_WARNING ); + } catch ( Exception $ex ) { + // Doh! Cause a fatal error after all? + } + + if ( $this->format === 'plain' ) { + return '<' . $this->key . '>'; + } + return '<' . $this->key . '>'; + } } /** @@ -477,7 +554,7 @@ class Message { } /** - * Returns the message text as-is, only parameters are subsituted. + * Returns the message text as-is, only parameters are substituted. * @since 1.17 * @return String: Unescaped untransformed message text. */ @@ -530,7 +607,7 @@ class Message { /** * Check whether a message does not exist, is an empty string, or is "-" * @since 1.18 - * @return Bool: true if is is and false if not + * @return Bool: true if it is and false if not */ public function isDisabled() { $message = $this->fetchMessage(); @@ -556,10 +633,10 @@ class Message { } /** - * Substitutes any paramaters into the message text. + * Substitutes any parameters into the message text. * @since 1.17 - * @param $message String: the message text - * @param $type String: either before or after + * @param string $message the message text + * @param string $type either before or after * @return String */ protected function replaceParameters( $message, $type = 'before' ) { @@ -577,7 +654,7 @@ class Message { /** * Extracts the parameter type and preprocessed the value if needed. * @since 1.18 - * @param $param String|Array: Parameter as defined in this class. + * @param string|array $param Parameter as defined in this class. * @return Tuple(type, value) */ protected function extractParam( $param ) { @@ -601,17 +678,18 @@ class Message { /** * Wrapper for what ever method we use to parse wikitext. * @since 1.17 - * @param $string String: Wikitext message contents + * @param string $string Wikitext message contents * @return string Wikitext parsed into HTML */ protected function parseText( $string ) { - return MessageCache::singleton()->parse( $string, $this->title, /*linestart*/true, $this->interface, $this->language )->getText(); + $out = MessageCache::singleton()->parse( $string, $this->title, /*linestart*/true, $this->interface, $this->language ); + return is_object( $out ) ? $out->getText() : $out; } /** * Wrapper for what ever method we use to {{-transform wikitext. * @since 1.17 - * @param $string String: Wikitext message contents + * @param string $string Wikitext message contents * @return string Wikitext with {{-constructs replaced with their values. */ protected function transformText( $string ) { @@ -621,6 +699,7 @@ class Message { /** * Wrapper for what ever method we use to get message contents * @since 1.17 + * @throws MWException * @return string */ protected function fetchMessage() { @@ -645,3 +724,45 @@ class Message { } } + +/** + * Variant of the Message class. + * + * Rather than treating the message key as a lookup + * value (which is passed to the MessageCache and + * translated as necessary), a RawMessage key is + * treated as the actual message. + * + * All other functionality (parsing, escaping, etc.) + * is preserved. + * + * @since 1.21 + */ +class RawMessage extends Message { + /** + * Call the parent constructor, then store the key as + * the message. + * + * @param string $key Message to use + * @param array $params Parameters for the message + * @see Message::__construct + */ + public function __construct( $key, $params = array() ) { + parent::__construct( $key, $params ); + // The key is the message. + $this->message = $key; + } + + /** + * Fetch the message (in this case, the key). + * + * @return string + */ + public function fetchMessage() { + // Just in case the message is unset somewhere. + if( !isset( $this->message ) ) { + $this->message = $this->key; + } + return $this->message; + } +} diff --git a/includes/MessageBlobStore.php b/includes/MessageBlobStore.php index c96ea56e..8a8142b7 100644 --- a/includes/MessageBlobStore.php +++ b/includes/MessageBlobStore.php @@ -29,7 +29,7 @@ * A message blob is a JSON object containing the interface messages for a * certain resource in a certain language. These message blobs are cached * in the msg_resource table and automatically invalidated when one of their - * consistuent messages or the resource itself is changed. + * constituent messages or the resource itself is changed. */ class MessageBlobStore { @@ -37,8 +37,8 @@ class MessageBlobStore { * Get the message blobs for a set of modules * * @param $resourceLoader ResourceLoader object - * @param $modules array Array of module objects keyed by module name - * @param $lang string Language code + * @param array $modules Array of module objects keyed by module name + * @param string $lang Language code * @return array An array mapping module names to message blobs */ public static function get( ResourceLoader $resourceLoader, $modules, $lang ) { @@ -68,9 +68,9 @@ class MessageBlobStore { * present, it is not regenerated; instead, the preexisting blob * is fetched and returned. * - * @param $name String: module name + * @param string $name module name * @param $module ResourceLoaderModule object - * @param $lang String: language code + * @param string $lang language code * @return mixed Message blob or false if the module has no messages */ public static function insertMessageBlob( $name, ResourceLoaderModule $module, $lang ) { @@ -125,9 +125,9 @@ class MessageBlobStore { /** * Update the message blob for a given module in a given language * - * @param $name String: module name + * @param string $name module name * @param $module ResourceLoaderModule object - * @param $lang String: language code + * @param string $lang language code * @return String Regenerated message blob, or null if there was no blob for the given module/language pair */ public static function updateModule( $name, ResourceLoaderModule $module, $lang ) { @@ -195,7 +195,7 @@ class MessageBlobStore { /** * Update a single message in all message blobs it occurs in. * - * @param $key String: message key + * @param string $key message key */ public static function updateMessage( $key ) { try { @@ -255,8 +255,8 @@ class MessageBlobStore { /** * Create an update queue for updateMessage() * - * @param $key String: message key - * @param $prevUpdates Array: updates queue to refresh or null to build a fresh update queue + * @param string $key message key + * @param array $prevUpdates updates queue to refresh or null to build a fresh update queue * @return Array: updates queue */ private static function getUpdatesForMessage( $key, $prevUpdates = null ) { @@ -306,9 +306,9 @@ class MessageBlobStore { /** * Reencode a message blob with the updated value for a message * - * @param $blob String: message blob (JSON object) - * @param $key String: message key - * @param $lang String: language code + * @param string $blob message blob (JSON object) + * @param string $key message key + * @param string $lang language code * @return Message blob with $key replaced with its new value */ private static function reencodeBlob( $blob, $key, $lang ) { @@ -323,8 +323,9 @@ class MessageBlobStore { * Modules whose blobs are not in the database are silently dropped. * * @param $resourceLoader ResourceLoader object - * @param $modules Array of module names - * @param $lang String: language code + * @param array $modules of module names + * @param string $lang language code + * @throws MWException * @return array Array mapping module names to blobs */ private static function getFromDB( ResourceLoader $resourceLoader, $modules, $lang ) { @@ -360,7 +361,7 @@ class MessageBlobStore { * Generate the message blob for a given module in a given language. * * @param $module ResourceLoaderModule object - * @param $lang String: language code + * @param string $lang language code * @return String: JSON object */ private static function generateMessageBlob( ResourceLoaderModule $module, $lang ) { diff --git a/includes/Metadata.php b/includes/Metadata.php index 0ca15393..0b8014a7 100644 --- a/includes/Metadata.php +++ b/includes/Metadata.php @@ -34,7 +34,7 @@ abstract class RdfMetaData { $this->mArticle = $article; } - public abstract function show(); + abstract public function show(); protected function setup() { global $wgOut, $wgRequest; @@ -42,7 +42,7 @@ abstract class RdfMetaData { $httpaccept = isset( $_SERVER['HTTP_ACCEPT'] ) ? $_SERVER['HTTP_ACCEPT'] : null; $rdftype = wfNegotiateType( wfAcceptToPrefs( $httpaccept ), wfAcceptToPrefs( self::RDF_TYPE_PREFS ) ); - if( !$rdftype ){ + if( !$rdftype ) { throw new HttpError( 406, wfMessage( 'notacceptable' ) ); } @@ -70,7 +70,7 @@ abstract class RdfMetaData { $lastEditor = User::newFromId( $this->mArticle->getUser() ); $this->person( 'creator', $lastEditor ); - foreach( $this->mArticle->getContributors() as $user ){ + foreach( $this->mArticle->getContributors() as $user ) { $this->person( 'contributor', $user ); } @@ -82,10 +82,10 @@ abstract class RdfMetaData { print "\t\t{$value}\n"; } - protected function date($timestamp) { - return substr($timestamp, 0, 4) . '-' - . substr($timestamp, 4, 2) . '-' - . substr($timestamp, 6, 2); + protected function date( $timestamp ) { + return substr( $timestamp, 0, 4 ) . '-' + . substr( $timestamp, 4, 2 ) . '-' + . substr( $timestamp, 6, 2 ); } protected function pageOrString( $name, $page, $str ) { @@ -95,7 +95,7 @@ abstract class RdfMetaData { $nt = Title::newFromText( $page ); } - if( !$nt || $nt->getArticleID() == 0 ){ + if( !$nt || $nt->getArticleID() == 0 ) { $this->element( $name, $str ); } else { $this->page( $name, $nt ); @@ -110,13 +110,13 @@ abstract class RdfMetaData { $this->url( $name, $title->getFullUrl() ); } - protected function url($name, $url) { + protected function url( $name, $url ) { $url = htmlspecialchars( $url ); print "\t\t\n"; } protected function person( $name, User $user ) { - if( $user->isAnon() ){ + if( $user->isAnon() ) { $this->element( $name, wfMessage( 'anonymous' )->numParams( 1 )->text() ); } else { $real = $user->getRealName(); @@ -141,19 +141,19 @@ abstract class RdfMetaData { global $wgRightsPage, $wgRightsUrl, $wgRightsText; if( $wgRightsPage && ( $nt = Title::newFromText( $wgRightsPage ) ) - && ($nt->getArticleID() != 0)) { - $this->page('rights', $nt); - } elseif( $wgRightsUrl ){ - $this->url('rights', $wgRightsUrl); - } elseif( $wgRightsText ){ + && ( $nt->getArticleID() != 0 ) ) { + $this->page( 'rights', $nt ); + } elseif( $wgRightsUrl ) { + $this->url( 'rights', $wgRightsUrl ); + } elseif( $wgRightsText ) { $this->element( 'rights', $wgRightsText ); } } - protected function getTerms( $url ){ + protected function getTerms( $url ) { global $wgLicenseTerms; - if( $wgLicenseTerms ){ + if( $wgLicenseTerms ) { return $wgLicenseTerms; } else { $known = $this->getKnownLicenses(); @@ -166,23 +166,23 @@ abstract class RdfMetaData { } protected function getKnownLicenses() { - $ccLicenses = array('by', 'by-nd', 'by-nd-nc', 'by-nc', - 'by-nc-sa', 'by-sa'); - $ccVersions = array('1.0', '2.0'); + $ccLicenses = array( 'by', 'by-nd', 'by-nd-nc', 'by-nc', + 'by-nc-sa', 'by-sa' ); + $ccVersions = array( '1.0', '2.0' ); $knownLicenses = array(); - foreach ($ccVersions as $version) { - foreach ($ccLicenses as $license) { - if( $version == '2.0' && substr( $license, 0, 2) != 'by' ) { + foreach ( $ccVersions as $version ) { + foreach ( $ccLicenses as $license ) { + if( $version == '2.0' && substr( $license, 0, 2 ) != 'by' ) { # 2.0 dropped the non-attribs licenses continue; } $lurl = "http://creativecommons.org/licenses/{$license}/{$version}/"; - $knownLicenses[$lurl] = explode('-', $license); + $knownLicenses[$lurl] = explode( '-', $license ); $knownLicenses[$lurl][] = 're'; $knownLicenses[$lurl][] = 'di'; $knownLicenses[$lurl][] = 'no'; - if (!in_array('nd', $knownLicenses[$lurl])) { + if ( !in_array( 'nd', $knownLicenses[$lurl] ) ) { $knownLicenses[$lurl][] = 'de'; } } @@ -191,13 +191,12 @@ abstract class RdfMetaData { /* Handle the GPL and LGPL, too. */ $knownLicenses['http://creativecommons.org/licenses/GPL/2.0/'] = - array('de', 're', 'di', 'no', 'sa', 'sc'); + array( 'de', 're', 'di', 'no', 'sa', 'sc' ); $knownLicenses['http://creativecommons.org/licenses/LGPL/2.1/'] = - array('de', 're', 'di', 'no', 'sa', 'sc'); + array( 'de', 're', 'di', 'no', 'sa', 'sc' ); $knownLicenses['http://www.gnu.org/copyleft/fdl.html'] = - array('de', 're', 'di', 'no', 'sa', 'sc'); + array( 'de', 're', 'di', 'no', 'sa', 'sc' ); return $knownLicenses; } } - diff --git a/includes/MimeMagic.php b/includes/MimeMagic.php index 1873e7bf..edabd54c 100644 --- a/includes/MimeMagic.php +++ b/includes/MimeMagic.php @@ -25,21 +25,21 @@ * This is used as a fallback to mime.types files. * An extensive list of well known mime types is provided by * the file mime.types in the includes directory. - * + * * This list concatenated with mime.types is used to create a mime <-> ext * map. Each line contains a mime type followed by a space separated list of - * extensions. If multiple extensions for a single mime type exist or if + * extensions. If multiple extensions for a single mime type exist or if * multiple mime types exist for a single extension then in most cases * MediaWiki assumes that the first extension following the mime type is the * canonical extension, and the first time a mime type appears for a certain * extension is considered the canonical mime type. - * + * * (Note that appending $wgMimeTypeFile to the end of MM_WELL_KNOWN_MIME_TYPES - * sucks because you can't redefine canonical types. This could be fixed by + * sucks because you can't redefine canonical types. This could be fixed by * appending MM_WELL_KNOWN_MIME_TYPES behind $wgMimeTypeFile, but who knows * what will break? In practice this probably isn't a problem anyway -- Bryan) */ -define('MM_WELL_KNOWN_MIME_TYPES',<<mMimeToExt = array(); $this->mToMime = array(); - $lines = explode( "\n",$types ); + $lines = explode( "\n", $types ); foreach ( $lines as $s ) { $s = trim( $s ); if ( empty( $s ) ) { @@ -231,7 +231,7 @@ class MimeMagic { } $mime = substr( $s, 0, $i ); - $ext = trim( substr($s, $i+1 ) ); + $ext = trim( substr( $s, $i+1 ) ); if ( empty( $ext ) ) { continue; @@ -272,17 +272,17 @@ class MimeMagic { if ( $wgMimeInfoFile ) { if ( is_file( $wgMimeInfoFile ) and is_readable( $wgMimeInfoFile ) ) { - wfDebug( __METHOD__.": loading mime info from $wgMimeInfoFile\n" ); + wfDebug( __METHOD__ . ": loading mime info from $wgMimeInfoFile\n" ); $info .= "\n"; $info .= file_get_contents( $wgMimeInfoFile ); } else { - wfDebug(__METHOD__.": can't load mime info from $wgMimeInfoFile\n"); + wfDebug( __METHOD__ . ": can't load mime info from $wgMimeInfoFile\n" ); } } else { - wfDebug(__METHOD__.": no mime info file defined, using build-ins only.\n"); + wfDebug( __METHOD__ . ": no mime info file defined, using build-ins only.\n" ); } - $info = str_replace( array( "\r\n", "\n\r", "\n\n", "\r\r", "\r" ), "\n", $info); + $info = str_replace( array( "\r\n", "\n\r", "\n\n", "\r\r", "\r" ), "\n", $info ); $info = str_replace( "\t", " ", $info ); $this->mMimeTypeAliases = array(); @@ -330,9 +330,9 @@ class MimeMagic { $this->mMediaTypes[$mtype][] = $mime; } - if ( sizeof( $m ) > 1 ) { + if ( count( $m ) > 1 ) { $main = $m[0]; - for ( $i=1; $imMimeTypeAliases[$mime] = $main; } @@ -346,17 +346,17 @@ class MimeMagic { * @return MimeMagic */ public static function &singleton() { - if ( !isset( self::$instance ) ) { + if ( self::$instance === null ) { self::$instance = new MimeMagic; } return self::$instance; } - /** - * Returns a list of file extensions for a given mime type as a space + /** + * Returns a list of file extensions for a given mime type as a space * separated string or null if the mime type was unrecognized. Resolves * mime type aliases. - * + * * @param $mime string * @return string|null */ @@ -379,10 +379,10 @@ class MimeMagic { return null; } - /** - * Returns a list of mime types for a given file extension as a space + /** + * Returns a list of mime types for a given file extension as a space * separated string or null if the extension was unrecognized. - * + * * @param $ext string * @return string|null */ @@ -393,10 +393,10 @@ class MimeMagic { return $r; } - /** + /** * Returns a single mime type for a given file extension or null if unknown. * This is always the first type from the list returned by getTypesForExtension($ext). - * + * * @param $ext string * @return string|null */ @@ -413,12 +413,11 @@ class MimeMagic { return $m; } - - /** - * Tests if the extension matches the given mime type. Returns true if a - * match was found, null if the mime type is unknown, and false if the + /** + * Tests if the extension matches the given mime type. Returns true if a + * match was found, null if the mime type is unknown, and false if the * mime type is known but no matches where found. - * + * * @param $extension string * @param $mime string * @return bool|null @@ -427,21 +426,21 @@ class MimeMagic { $ext = $this->getExtensionsForType( $mime ); if ( !$ext ) { - return null; // Unknown mime type + return null; // Unknown mime type } $ext = explode( ' ', $ext ); $extension = strtolower( $extension ); - return in_array( $extension, $ext ); + return in_array( $extension, $ext ); } - /** - * Returns true if the mime type is known to represent an image format + /** + * Returns true if the mime type is known to represent an image format * supported by the PHP GD library. * * @param $mime string - * + * * @return bool */ public function isPHPImageType( $mime ) { @@ -489,32 +488,32 @@ class MimeMagic { return in_array( strtolower( $extension ), $types ); } - /** + /** * Improves a mime type using the file extension. Some file formats are very generic, - * so their mime type is not very meaningful. A more useful mime type can be derived - * by looking at the file extension. Typically, this method would be called on the + * so their mime type is not very meaningful. A more useful mime type can be derived + * by looking at the file extension. Typically, this method would be called on the * result of guessMimeType(). - * + * * Currently, this method does the following: * * If $mime is "unknown/unknown" and isRecognizableExtension( $ext ) returns false, - * return the result of guessTypesForExtension($ext). + * return the result of guessTypesForExtension($ext). * * If $mime is "application/x-opc+zip" and isMatchingExtension( $ext, $mime ) - * gives true, return the result of guessTypesForExtension($ext). + * gives true, return the result of guessTypesForExtension($ext). * - * @param $mime String: the mime type, typically guessed from a file's content. - * @param $ext String: the file extension, as taken from the file name + * @param string $mime the mime type, typically guessed from a file's content. + * @param string $ext the file extension, as taken from the file name * * @return string the mime type */ public function improveTypeFromExtension( $mime, $ext ) { if ( $mime === 'unknown/unknown' ) { if ( $this->isRecognizableExtension( $ext ) ) { - wfDebug( __METHOD__. ': refusing to guess mime type for .' . + wfDebug( __METHOD__ . ': refusing to guess mime type for .' . "$ext file, we should have recognized it\n" ); } else { - // Not something we can detect, so simply + // Not something we can detect, so simply // trust the file extension $mime = $this->guessTypesForExtension( $ext ); } @@ -525,7 +524,7 @@ class MimeMagic { // find the proper mime type for that file extension $mime = $this->guessTypesForExtension( $ext ); } else { - wfDebug( __METHOD__. ": refusing to guess better type for $mime file, " . + wfDebug( __METHOD__ . ": refusing to guess better type for $mime file, " . ".$ext is not a known OPC extension.\n" ); $mime = 'application/zip'; } @@ -535,34 +534,34 @@ class MimeMagic { $mime = $this->mMimeTypeAliases[$mime]; } - wfDebug(__METHOD__.": improved mime type for .$ext: $mime\n"); + wfDebug( __METHOD__ . ": improved mime type for .$ext: $mime\n" ); return $mime; } - /** - * Mime type detection. This uses detectMimeType to detect the mime type - * of the file, but applies additional checks to determine some well known - * file formats that may be missed or misinterpreter by the default mime - * detection (namely XML based formats like XHTML or SVG, as well as ZIP + /** + * Mime type detection. This uses detectMimeType to detect the mime type + * of the file, but applies additional checks to determine some well known + * file formats that may be missed or misinterpreted by the default mime + * detection (namely XML based formats like XHTML or SVG, as well as ZIP * based formats like OPC/ODF files). * - * @param $file String: the file to check + * @param string $file the file to check * @param $ext Mixed: the file extension, or true (default) to extract it from the filename. - * Set it to false to ignore the extension. DEPRECATED! Set to false, use + * Set it to false to ignore the extension. DEPRECATED! Set to false, use * improveTypeFromExtension($mime, $ext) later to improve mime type. * * @return string the mime type of $file */ public function guessMimeType( $file, $ext = true ) { if ( $ext ) { // TODO: make $ext default to false. Or better, remove it. - wfDebug( __METHOD__.": WARNING: use of the \$ext parameter is deprecated. " . + wfDebug( __METHOD__ . ": WARNING: use of the \$ext parameter is deprecated. " . "Use improveTypeFromExtension(\$mime, \$ext) instead.\n" ); } $mime = $this->doGuessMimeType( $file, $ext ); if( !$mime ) { - wfDebug( __METHOD__.": internal type detection failed for $file (.$ext)...\n" ); + wfDebug( __METHOD__ . ": internal type detection failed for $file (.$ext)...\n" ); $mime = $this->detectMimeType( $file, $ext ); } @@ -570,7 +569,7 @@ class MimeMagic { $mime = $this->mMimeTypeAliases[$mime]; } - wfDebug(__METHOD__.": guessed mime type of $file: $mime\n"); + wfDebug( __METHOD__ . ": guessed mime type of $file: $mime\n" ); return $mime; } @@ -587,7 +586,7 @@ class MimeMagic { // @todo FIXME: Shouldn't this be rb? $f = fopen( $file, 'rt' ); wfRestoreWarnings(); - + if( !$f ) { return 'unknown/unknown'; } @@ -629,7 +628,7 @@ class MimeMagic { $doctype = strpos( $head, "\x42\x82" ); if ( $doctype ) { // Next byte is datasize, then data (sizes larger than 1 byte are very stupid muxers) - $data = substr($head, $doctype+3, 8); + $data = substr( $head, $doctype+3, 8 ); if ( strncmp( $data, "matroska", 8 ) == 0 ) { wfDebug( __METHOD__ . ": recognized file as video/x-matroska\n" ); return "video/x-matroska"; @@ -661,12 +660,11 @@ class MimeMagic { * strings like " 'n*', 'UTF-16LE' => 'v*' ); $chars = unpack( $pack[$script_type], substr( $head, 2 ) ); @@ -720,14 +718,14 @@ class MimeMagic { if ( preg_match( '%/?([^\s]+/)(\w+)%', $head, $match ) ) { $mime = "application/x-{$match[2]}"; - wfDebug( __METHOD__.": shell script recognized as $mime\n" ); + wfDebug( __METHOD__ . ": shell script recognized as $mime\n" ); return $mime; } } // Check for ZIP variants (before getimagesize) if ( strpos( $tail, "PK\x05\x06" ) !== false ) { - wfDebug( __METHOD__.": ZIP header present in $file\n" ); + wfDebug( __METHOD__ . ": ZIP header present in $file\n" ); return $this->detectZipType( $head, $tail, $ext ); } @@ -737,36 +735,36 @@ class MimeMagic { if( $gis && isset( $gis['mime'] ) ) { $mime = $gis['mime']; - wfDebug( __METHOD__.": getimagesize detected $file as $mime\n" ); + wfDebug( __METHOD__ . ": getimagesize detected $file as $mime\n" ); return $mime; } // Also test DjVu $deja = new DjVuImage( $file ); if( $deja->isValid() ) { - wfDebug( __METHOD__.": detected $file as image/vnd.djvu\n" ); + wfDebug( __METHOD__ . ": detected $file as image/vnd.djvu\n" ); return 'image/vnd.djvu'; } return false; } - + /** * Detect application-specific file type of a given ZIP file from its * header data. Currently works for OpenDocument and OpenXML types... * If can't tell, returns 'application/zip'. * - * @param $header String: some reasonably-sized chunk of file header + * @param string $header some reasonably-sized chunk of file header * @param $tail String: the tail of the file * @param $ext Mixed: the file extension, or true to extract it from the filename. - * Set it to false (default) to ignore the extension. DEPRECATED! Set to false, + * Set it to false (default) to ignore the extension. DEPRECATED! Set to false, * use improveTypeFromExtension($mime, $ext) later to improve mime type. * * @return string */ function detectZipType( $header, $tail = null, $ext = false ) { if( $ext ) { # TODO: remove $ext param - wfDebug( __METHOD__.": WARNING: use of the \$ext parameter is deprecated. " . + wfDebug( __METHOD__ . ": WARNING: use of the \$ext parameter is deprecated. " . "Use improveTypeFromExtension(\$mime, \$ext) instead.\n" ); } @@ -797,30 +795,31 @@ class MimeMagic { if ( preg_match( $opendocRegex, substr( $header, 30 ), $matches ) ) { $mime = $matches[1]; - wfDebug( __METHOD__.": detected $mime from ZIP archive\n" ); + wfDebug( __METHOD__ . ": detected $mime from ZIP archive\n" ); } elseif ( preg_match( $openxmlRegex, substr( $header, 30 ) ) ) { $mime = "application/x-opc+zip"; - # TODO: remove the block below, as soon as improveTypeFromExtension is used everywhere - if ( $ext !== true && $ext !== false ) { + # TODO: remove the block below, as soon as improveTypeFromExtension is used everywhere + if ( $ext !== true && $ext !== false ) { /** This is the mode used by getPropsFromPath - * These mime's are stored in the database, where we don't really want - * x-opc+zip, because we use it only for internal purposes - */ + * These mime's are stored in the database, where we don't really want + * x-opc+zip, because we use it only for internal purposes + */ if ( $this->isMatchingExtension( $ext, $mime) ) { /* A known file extension for an OPC file, - * find the proper mime type for that file extension */ + * find the proper mime type for that file extension + */ $mime = $this->guessTypesForExtension( $ext ); } else { $mime = "application/zip"; } } - wfDebug( __METHOD__.": detected an Open Packaging Conventions archive: $mime\n" ); - } elseif ( substr( $header, 0, 8 ) == "\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1" && + wfDebug( __METHOD__ . ": detected an Open Packaging Conventions archive: $mime\n" ); + } elseif ( substr( $header, 0, 8 ) == "\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1" && ($headerpos = strpos( $tail, "PK\x03\x04" ) ) !== false && preg_match( $openxmlRegex, substr( $tail, $headerpos + 30 ) ) ) { if ( substr( $header, 512, 4) == "\xEC\xA5\xC1\x00" ) { $mime = "application/msword"; - } + } switch( substr( $header, 512, 6) ) { case "\xEC\xA5\xC1\x00\x0E\x00": case "\xEC\xA5\xC1\x00\x1C\x00": @@ -843,27 +842,27 @@ class MimeMagic { break; } - wfDebug( __METHOD__.": detected a MS Office document with OPC trailer\n"); + wfDebug( __METHOD__ . ": detected a MS Office document with OPC trailer\n" ); } else { - wfDebug( __METHOD__.": unable to identify type of ZIP archive\n" ); + wfDebug( __METHOD__ . ": unable to identify type of ZIP archive\n" ); } return $mime; } - /** - * Internal mime type detection. Detection is done using an external - * program, if $wgMimeDetectorCommand is set. Otherwise, the fileinfo - * extension and mime_content_type are tried (in this order), if they - * are available. If the dections fails and $ext is not false, the mime + /** + * Internal mime type detection. Detection is done using an external + * program, if $wgMimeDetectorCommand is set. Otherwise, the fileinfo + * extension and mime_content_type are tried (in this order), if they + * are available. If the detections fails and $ext is not false, the mime * type is guessed from the file extension, using guessTypesForExtension. - * - * If the mime type is still unknown, getimagesize is used to detect the - * mime type if the file is an image. If no mime type can be determined, + * + * If the mime type is still unknown, getimagesize is used to detect the + * mime type if the file is an image. If no mime type can be determined, * this function returns 'unknown/unknown'. * - * @param $file String: the file to check + * @param string $file the file to check * @param $ext Mixed: the file extension, or true (default) to extract it from the filename. - * Set it to false to ignore the extension. DEPRECATED! Set to false, use + * Set it to false to ignore the extension. DEPRECATED! Set to false, use * improveTypeFromExtension($mime, $ext) later to improve mime type. * * @return string the mime type of $file @@ -872,7 +871,7 @@ class MimeMagic { global $wgMimeDetectorCommand; if ( $ext ) { # TODO: make $ext default to false. Or better, remove it. - wfDebug( __METHOD__.": WARNING: use of the \$ext parameter is deprecated. Use improveTypeFromExtension(\$mime, \$ext) instead.\n" ); + wfDebug( __METHOD__ . ": WARNING: use of the \$ext parameter is deprecated. Use improveTypeFromExtension(\$mime, \$ext) instead.\n" ); } $m = null; @@ -898,22 +897,22 @@ class MimeMagic { $m = finfo_file( $mime_magic_resource, $file ); finfo_close( $mime_magic_resource ); } else { - wfDebug( __METHOD__.": finfo_open failed on ".FILEINFO_MIME."!\n" ); + wfDebug( __METHOD__ . ": finfo_open failed on ".FILEINFO_MIME."!\n" ); } } elseif ( function_exists( "mime_content_type" ) ) { # NOTE: this function is available since PHP 4.3.0, but only if # PHP was compiled with --with-mime-magic or, before 4.3.2, with --enable-mime-magic. # - # On Windows, you must set mime_magic.magicfile in php.ini to point to the mime.magic file bundeled with PHP; + # On Windows, you must set mime_magic.magicfile in php.ini to point to the mime.magic file bundled with PHP; # sometimes, this may even be needed under linus/unix. # # Also note that this has been DEPRECATED in favor of the fileinfo extension by PECL, see above. # see http://www.php.net/manual/en/ref.mime-magic.php for details. - $m = mime_content_type($file); + $m = mime_content_type( $file ); } else { - wfDebug( __METHOD__.": no magic mime detector found!\n" ); + wfDebug( __METHOD__ . ": no magic mime detector found!\n" ); } if ( $m ) { @@ -925,7 +924,7 @@ class MimeMagic { if ( strpos( $m, 'unknown' ) !== false ) { $m = null; } else { - wfDebug( __METHOD__.": magic mime type of $file: $m\n" ); + wfDebug( __METHOD__ . ": magic mime type of $file: $m\n" ); return $m; } } @@ -937,11 +936,11 @@ class MimeMagic { } if ( $ext ) { if( $this->isRecognizableExtension( $ext ) ) { - wfDebug( __METHOD__. ": refusing to guess mime type for .$ext file, we should have recognized it\n" ); + wfDebug( __METHOD__ . ": refusing to guess mime type for .$ext file, we should have recognized it\n" ); } else { $m = $this->guessTypesForExtension( $ext ); if ( $m ) { - wfDebug( __METHOD__.": extension mime type of $file: $m\n" ); + wfDebug( __METHOD__ . ": extension mime type of $file: $m\n" ); return $m; } } @@ -962,9 +961,9 @@ class MimeMagic { * @todo analyse file if need be * @todo look at multiple extension, separately and together. * - * @param $path String: full path to the image file, in case we have to look at the contents + * @param string $path full path to the image file, in case we have to look at the contents * (if null, only the mime type is used to determine the media type code). - * @param $mime String: mime type. If null it will be guessed using guessMimeType. + * @param string $mime mime type. If null it will be guessed using guessMimeType. * * @return (int?string?) a value to be used with the MEDIATYPE_xxx constants. */ @@ -1037,17 +1036,17 @@ class MimeMagic { return $type; } - /** + /** * Returns a media code matching the given mime type or file extension. * File extensions are represented by a string starting with a dot (.) to * distinguish them from mime types. * - * This funktion relies on the mapping defined by $this->mMediaTypes + * This function relies on the mapping defined by $this->mMediaTypes * @access private * @return int|string */ function findMediaType( $extMime ) { - if ( strpos( $extMime, '.' ) === 0 ) { + if ( strpos( $extMime, '.' ) === 0 ) { // If it's an extension, look up the mime types $m = $this->getTypesForExtension( substr( $extMime, 1 ) ); if ( !$m ) { @@ -1066,7 +1065,7 @@ class MimeMagic { foreach ( $m as $mime ) { foreach ( $this->mMediaTypes as $type => $codes ) { - if ( in_array($mime, $codes, true ) ) { + if ( in_array( $mime, $codes, true ) ) { return $type; } } @@ -1076,12 +1075,12 @@ class MimeMagic { } /** - * Get the MIME types that various versions of Internet Explorer would + * Get the MIME types that various versions of Internet Explorer would * detect from a chunk of the content. * - * @param $fileName String: the file name (unused at present) - * @param $chunk String: the first 256 bytes of the file - * @param $proposed String: the MIME type proposed by the server + * @param string $fileName the file name (unused at present) + * @param string $chunk the first 256 bytes of the file + * @param string $proposed the MIME type proposed by the server * @return Array */ public function getIEMimeTypes( $fileName, $chunk, $proposed ) { diff --git a/includes/Namespace.php b/includes/Namespace.php index 2e2b8d61..fccfbedb 100644 --- a/includes/Namespace.php +++ b/includes/Namespace.php @@ -48,6 +48,7 @@ class MWNamespace { * @param $index * @param $method * + * @throws MWException * @return bool */ private static function isMethodValidFor( $index, $method ) { @@ -60,13 +61,13 @@ class MWNamespace { /** * Can pages in the given namespace be moved? * - * @param $index Int: namespace index + * @param int $index namespace index * @return bool */ public static function isMovable( $index ) { global $wgAllowImageMoving; - $result = !( $index < NS_MAIN || ( $index == NS_FILE && !$wgAllowImageMoving ) || $index == NS_CATEGORY ); + $result = !( $index < NS_MAIN || ( $index == NS_FILE && !$wgAllowImageMoving ) || $index == NS_CATEGORY ); /** * @since 1.20 @@ -79,7 +80,7 @@ class MWNamespace { /** * Is the given namespace is a subject (non-talk) namespace? * - * @param $index Int: namespace index + * @param int $index namespace index * @return bool * @since 1.19 */ @@ -100,7 +101,7 @@ class MWNamespace { /** * Is the given namespace a talk namespace? * - * @param $index Int: namespace index + * @param int $index namespace index * @return bool */ public static function isTalk( $index ) { @@ -111,7 +112,7 @@ class MWNamespace { /** * Get the talk namespace index for a given namespace * - * @param $index Int: namespace index + * @param int $index namespace index * @return int */ public static function getTalk( $index ) { @@ -125,7 +126,7 @@ class MWNamespace { * Get the subject namespace index for a given namespace * Special namespaces (NS_MEDIA, NS_SPECIAL) are always the subject. * - * @param $index Int: Namespace index + * @param int $index Namespace index * @return int */ public static function getSubject( $index ) { @@ -144,7 +145,7 @@ class MWNamespace { * For talk namespaces, returns the subject (non-talk) namespace * For subject (non-talk) namespaces, returns the talk namespace * - * @param $index Int: namespace index + * @param int $index namespace index * @return int or null if no associated namespace could be found */ public static function getAssociated( $index ) { @@ -180,8 +181,8 @@ class MWNamespace { * of this function rather than directly doing comparison will make * sure that code will not potentially break. * - * @param $ns1 int The first namespace index - * @param $ns2 int The second namespae index + * @param int $ns1 The first namespace index + * @param int $ns2 The second namespace index * * @return bool * @since 1.19 @@ -195,8 +196,8 @@ class MWNamespace { * eg: NS_USER and NS_USER wil return true, as well * NS_USER and NS_USER_TALK will return true. * - * @param $ns1 int The first namespace index - * @param $ns2 int The second namespae index + * @param int $ns1 The first namespace index + * @param int $ns2 The second namespace index * * @return bool * @since 1.19 @@ -209,12 +210,14 @@ class MWNamespace { * Returns array of all defined namespaces with their canonical * (English) names. * + * @param bool $rebuild rebuild namespace list (default = false). Used for testing. + * * @return array * @since 1.17 */ - public static function getCanonicalNamespaces() { + public static function getCanonicalNamespaces( $rebuild = false ) { static $namespaces = null; - if ( $namespaces === null ) { + if ( $namespaces === null || $rebuild ) { global $wgExtraNamespaces, $wgCanonicalNamespaceNames; $namespaces = array( NS_MAIN => '' ) + $wgCanonicalNamespaceNames; if ( is_array( $wgExtraNamespaces ) ) { @@ -228,7 +231,7 @@ class MWNamespace { /** * Returns the canonical (English) name for a given index * - * @param $index Int: namespace index + * @param int $index namespace index * @return string or false if no canonical definition. */ public static function getCanonicalName( $index ) { @@ -244,7 +247,7 @@ class MWNamespace { * Returns the index for a given canonical name, or NULL * The input *must* be converted to lower case first * - * @param $name String: namespace name + * @param string $name namespace name * @return int */ public static function getCanonicalIndex( $name ) { @@ -284,18 +287,18 @@ class MWNamespace { /** * Can this namespace ever have a talk namespace? * - * @param $index Int: namespace index + * @param int $index namespace index * @return bool */ - public static function canTalk( $index ) { + public static function canTalk( $index ) { return $index >= NS_MAIN; - } + } /** * Does this namespace contain content, for the purposes of calculating * statistics, etc? * - * @param $index Int: index to check + * @param int $index index to check * @return bool */ public static function isContent( $index ) { @@ -316,7 +319,7 @@ class MWNamespace { /** * Does the namespace allow subpages? * - * @param $index int Index to check + * @param int $index Index to check * @return bool */ public static function hasSubpages( $index ) { @@ -369,7 +372,7 @@ class MWNamespace { /** * Is the namespace first-letter capitalized? * - * @param $index int Index to check + * @param int $index Index to check * @return bool */ public static function isCapitalized( $index ) { @@ -397,7 +400,7 @@ class MWNamespace { * genders. Not all languages make a distinction here. * * @since 1.18 - * @param $index int Index to check + * @param int $index Index to check * @return bool */ public static function hasGenderDistinction( $index ) { @@ -408,7 +411,7 @@ class MWNamespace { * It is not possible to use pages from this namespace as template? * * @since 1.20 - * @param $index int Index to check + * @param int $index Index to check * @return bool */ public static function isNonincludable( $index ) { @@ -416,4 +419,18 @@ class MWNamespace { return $wgNonincludableNamespaces && in_array( $index, $wgNonincludableNamespaces ); } + /** + * Get the default content model for a namespace + * This does not mean that all pages in that namespace have the model + * + * @since 1.21 + * @param int $index Index to check + * @return null|string default model name for the given namespace, if set + */ + public static function getNamespaceContentModel( $index ) { + global $wgNamespaceContentModels; + return isset( $wgNamespaceContentModels[$index] ) + ? $wgNamespaceContentModels[$index] + : null; + } } diff --git a/includes/OutputHandler.php b/includes/OutputHandler.php index 46a43f63..6b40c307 100644 --- a/includes/OutputHandler.php +++ b/includes/OutputHandler.php @@ -22,9 +22,9 @@ /** * Standard output handler for use with ob_start - * + * * @param $s string - * + * * @return string */ function wfOutputHandler( $s ) { @@ -85,14 +85,14 @@ function wfRequestExtension() { /** * Handler that compresses data with gzip if allowed by the Accept header. * Unlike ob_gzhandler, it works for HEAD requests too. - * + * * @param $s string * * @return string */ function wfGzipHandler( $s ) { if( !function_exists( 'gzencode' ) ) { - wfDebug( __FUNCTION__ . "() skipping compression (gzencode unavaible)\n" ); + wfDebug( __FUNCTION__ . "() skipping compression (gzencode unavailable)\n" ); return $s; } if( headers_sent() ) { @@ -156,7 +156,7 @@ function wfMangleFlashPolicy( $s ) { * @param $length int */ function wfDoContentLength( $length ) { - if ( !headers_sent() && $_SERVER['SERVER_PROTOCOL'] == 'HTTP/1.0' ) { + if ( !headers_sent() && isset( $_SERVER['SERVER_PROTOCOL'] ) && $_SERVER['SERVER_PROTOCOL'] == 'HTTP/1.0' ) { header( "Content-Length: $length" ); } } diff --git a/includes/OutputPage.php b/includes/OutputPage.php index b4a81bb1..1e0c396a 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -43,6 +43,7 @@ class OutputPage extends ContextSource { var $mKeywords = array(); var $mLinktags = array(); + var $mCanonicalUrl = false; /// Additional stylesheets. Looks like this is for extensions. Might be replaced by resource loader. var $mExtStyles = array(); @@ -122,7 +123,7 @@ class OutputPage extends ContextSource { var $mScripts = ''; /** - * Inline CSS styles. Use addInlineStyle() sparsingly + * Inline CSS styles. Use addInlineStyle() sparingly */ var $mInlineStyles = ''; @@ -247,6 +248,11 @@ class OutputPage extends ContextSource { */ private $mRedirectedFrom = null; + /** + * Additional key => value data + */ + private $mProperties = array(); + /** * Constructor for OutputPage. This should not be called directly. * Instead a new RequestContext should be created and it will implicitly create @@ -255,7 +261,7 @@ class OutputPage extends ContextSource { function __construct( IContextSource $context = null ) { if ( $context === null ) { # Extensions should use `new RequestContext` instead of `new OutputPage` now. - wfDeprecated( __METHOD__ ); + wfDeprecated( __METHOD__, '1.18' ); } else { $this->setContext( $context ); } @@ -264,8 +270,8 @@ class OutputPage extends ContextSource { /** * Redirect to $url rather than displaying the normal page * - * @param $url String: URL - * @param $responsecode String: HTTP status code + * @param string $url URL + * @param string $responsecode HTTP status code */ public function redirect( $url, $responsecode = '302' ) { # Strip newlines as a paranoia check for header injection in PHP<5.1.2 @@ -295,8 +301,8 @@ class OutputPage extends ContextSource { * Add a new "" tag * To add an http-equiv meta tag, precede the name with "http:" * - * @param $name String tag name - * @param $val String tag value + * @param string $name tag name + * @param string $val tag value */ function addMeta( $name, $val ) { array_push( $this->mMetatags, array( $name, $val ) ); @@ -305,7 +311,7 @@ class OutputPage extends ContextSource { /** * Add a keyword or a list of keywords in the page header * - * @param $text String or array of strings + * @param string $text or array of strings */ function addKeyword( $text ) { if( is_array( $text ) ) { @@ -316,9 +322,11 @@ class OutputPage extends ContextSource { } /** - * Add a new \ tag to the page header + * Add a new \ tag to the page header. + * + * Note: use setCanonicalUrl() for rel=canonical. * - * @param $linkarr Array: associative array of attributes. + * @param array $linkarr associative array of attributes. */ function addLink( $linkarr ) { array_push( $this->mLinktags, $linkarr ); @@ -327,7 +335,7 @@ class OutputPage extends ContextSource { /** * Add a new \ with "rel" attribute set to "meta" * - * @param $linkarr Array: associative array mapping attribute names to their + * @param array $linkarr associative array mapping attribute names to their * values, both keys and values will be escaped, and the * "rel" attribute will be automatically added */ @@ -336,6 +344,14 @@ class OutputPage extends ContextSource { $this->addLink( $linkarr ); } + /** + * Set the URL to be used for the . This should be used + * in preference to addLink(), to avoid duplicate link tags. + */ + function setCanonicalUrl( $url ) { + $this->mCanonicalUrl = $url; + } + /** * Get the value of the "rel" attribute for metadata links * @@ -355,7 +371,7 @@ class OutputPage extends ContextSource { /** * Add raw HTML to the list of scripts (including \ tag, etc.) * - * @param $script String: raw HTML + * @param string $script raw HTML */ function addScript( $script ) { $this->mScripts .= $script . "\n"; @@ -364,7 +380,7 @@ class OutputPage extends ContextSource { /** * Register and add a stylesheet from an extension directory. * - * @param $url String path to sheet. Provide either a full url (beginning + * @param string $url path to sheet. Provide either a full url (beginning * with 'http', etc) or a relative path from the document root * (beginning with '/'). Otherwise it behaves identically to * addStyle() and draws from the /skins folder. @@ -385,9 +401,9 @@ class OutputPage extends ContextSource { /** * Add a JavaScript file out of skins/common, or a given relative path. * - * @param $file String: filename in skins/common or complete on-server path + * @param string $file filename in skins/common or complete on-server path * (/foo/bar.js) - * @param $version String: style version of the file. Defaults to $wgStyleVersion + * @param string $version style version of the file. Defaults to $wgStyleVersion */ public function addScriptFile( $file, $version = null ) { global $wgStylePath, $wgStyleVersion; @@ -405,7 +421,7 @@ class OutputPage extends ContextSource { /** * Add a self-contained script tag with the given contents * - * @param $script String: JavaScript text, no "