From 63601400e476c6cf43d985f3e7b9864681695ed4 Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Fri, 18 Jan 2013 16:46:04 +0100 Subject: Update to MediaWiki 1.20.2 this update includes: * adjusted Arch Linux skin * updated FluxBBAuthPlugin * patch for https://bugzilla.wikimedia.org/show_bug.cgi?id=44024 --- includes/Action.php | 64 +- includes/AjaxDispatcher.php | 56 +- includes/AjaxResponse.php | 109 +- includes/Article.php | 593 +++---- includes/AuthPlugin.php | 22 + includes/AutoLoader.php | 223 ++- includes/Autopromote.php | 25 +- includes/BacklinkCache.php | 62 +- includes/Block.php | 100 +- includes/CacheHelper.php | 392 +++++ includes/Category.php | 48 +- includes/CategoryPage.php | 21 +- includes/CategoryViewer.php | 52 +- includes/Categoryfinder.php | 21 + includes/Cdb.php | 17 +- includes/Cdb_PHP.php | 17 +- includes/ChangeTags.php | 30 +- includes/ChangesFeed.php | 28 +- includes/ChangesList.php | 276 +-- includes/Collation.php | 22 +- includes/ConfEditor.php | 32 + includes/Cookie.php | 26 +- includes/CryptRand.php | 33 +- includes/DataUpdate.php | 124 ++ includes/DefaultSettings.php | 1432 ++++++++++------ includes/DeferredUpdates.php | 36 +- includes/Defines.php | 27 +- includes/DeprecatedGlobal.php | 55 + includes/EditPage.php | 690 ++++---- includes/Exception.php | 229 ++- includes/Export.php | 517 +++++- includes/ExternalEdit.php | 14 +- includes/ExternalStore.php | 25 +- includes/ExternalStoreDB.php | 23 +- includes/ExternalStoreHttp.php | 20 + includes/ExternalUser.php | 4 +- includes/FakeTitle.php | 23 +- includes/Fallback.php | 4 +- includes/Feed.php | 34 +- includes/FeedUtils.php | 41 +- includes/FileDeleteForm.php | 108 +- includes/ForkController.php | 25 +- includes/FormOptions.php | 35 +- includes/GitInfo.php | 214 +++ includes/GlobalFunctions.php | 746 +++++---- includes/HTMLForm.php | 716 ++++++-- includes/HistoryBlob.php | 64 +- includes/Hooks.php | 2 +- includes/Html.php | 133 +- includes/HttpFunctions.old.php | 21 + includes/HttpFunctions.php | 72 +- includes/IP.php | 48 +- includes/ImageFunctions.php | 87 - includes/ImageGallery.php | 55 +- includes/ImagePage.php | 468 +++--- includes/ImageQueryPage.php | 21 + includes/Import.php | 57 +- includes/Init.php | 21 + includes/Licenses.php | 45 +- includes/LinkFilter.php | 23 +- includes/Linker.php | 415 +++-- includes/LinksUpdate.php | 153 +- includes/LocalisationCache.php | 192 ++- includes/MWFunction.php | 4 +- includes/MagicWord.php | 48 +- includes/Message.php | 95 +- includes/MessageBlobStore.php | 7 +- includes/Metadata.php | 32 +- includes/MimeMagic.php | 22 +- includes/Namespace.php | 73 +- includes/OutputHandler.php | 25 +- includes/OutputPage.php | 403 +++-- includes/PHPVersionError.php | 25 +- includes/PageQueryPage.php | 29 +- includes/Pager.php | 264 ++- includes/PathRouter.php | 62 +- includes/PoolCounter.php | 22 + includes/Preferences.php | 102 +- includes/PrefixSearch.php | 24 +- includes/ProtectionForm.php | 85 +- includes/ProxyTools.php | 30 +- includes/QueryPage.php | 68 +- includes/RecentChange.php | 168 +- includes/Revision.php | 255 ++- includes/RevisionList.php | 48 +- includes/Sanitizer.php | 111 +- includes/ScopedPHPTimeout.php | 84 + includes/SeleniumWebSettings.php | 19 + includes/Setup.php | 64 +- includes/SiteConfiguration.php | 149 +- includes/SiteStats.php | 226 ++- includes/Skin.php | 233 ++- includes/SkinLegacy.php | 186 ++- includes/SkinTemplate.php | 332 ++-- includes/SpecialPage.php | 217 ++- includes/SpecialPageFactory.php | 36 +- includes/SqlDataUpdate.php | 150 ++ includes/SquidPurgeClient.php | 42 +- includes/Status.php | 76 +- includes/StreamFile.php | 51 +- includes/StringUtils.php | 94 +- includes/StubObject.php | 22 + includes/Timestamp.php | 229 +++ includes/Title.php | 306 ++-- includes/TitleArray.php | 19 + includes/User.php | 326 ++-- includes/UserArray.php | 20 + includes/UserMailer.php | 58 +- includes/UserRightsProxy.php | 21 + includes/ViewCountUpdate.php | 9 +- includes/WatchedItem.php | 105 +- includes/WebRequest.php | 114 +- includes/WebStart.php | 2 +- includes/Wiki.php | 199 +-- includes/WikiCategoryPage.php | 21 + includes/WikiError.php | 12 +- includes/WikiFilePage.php | 29 +- includes/WikiMap.php | 40 +- includes/WikiPage.php | 629 ++++--- includes/Xml.php | 87 +- includes/XmlTypeCheck.php | 20 + includes/ZhClient.php | 20 + includes/ZhConversion.php | 76 +- includes/ZipDirectoryReader.php | 46 +- includes/actions/CachedAction.php | 182 ++ includes/actions/CreditsAction.php | 2 +- includes/actions/HistoryAction.php | 92 +- includes/actions/InfoAction.php | 629 ++++++- includes/actions/PurgeAction.php | 6 +- includes/actions/RawAction.php | 26 +- includes/actions/RevertAction.php | 36 +- includes/actions/RevisiondeleteAction.php | 2 +- includes/actions/RollbackAction.php | 6 +- includes/actions/ViewAction.php | 3 - includes/actions/WatchAction.php | 12 +- includes/api/ApiBase.php | 224 ++- includes/api/ApiBlock.php | 61 +- includes/api/ApiComparePages.php | 51 +- includes/api/ApiDelete.php | 75 +- includes/api/ApiDisabled.php | 2 +- includes/api/ApiEditPage.php | 148 +- includes/api/ApiEmailUser.php | 24 +- includes/api/ApiExpandTemplates.php | 10 +- includes/api/ApiFeedContributions.php | 10 +- includes/api/ApiFeedWatchlist.php | 9 +- includes/api/ApiFileRevert.php | 38 +- includes/api/ApiFormatBase.php | 17 +- includes/api/ApiFormatDbg.php | 2 +- includes/api/ApiFormatJson.php | 2 +- includes/api/ApiFormatPhp.php | 2 +- includes/api/ApiFormatRaw.php | 2 +- includes/api/ApiFormatTxt.php | 2 +- includes/api/ApiFormatWddx.php | 2 +- includes/api/ApiFormatXml.php | 41 +- includes/api/ApiFormatYaml.php | 2 +- includes/api/ApiHelp.php | 2 +- includes/api/ApiImport.php | 42 +- includes/api/ApiLogin.php | 64 +- includes/api/ApiLogout.php | 6 +- includes/api/ApiMain.php | 161 +- includes/api/ApiMove.php | 51 +- includes/api/ApiOpenSearch.php | 4 +- includes/api/ApiOptions.php | 183 ++ includes/api/ApiPageSet.php | 30 +- includes/api/ApiParamInfo.php | 58 +- includes/api/ApiParse.php | 115 +- includes/api/ApiPatrol.php | 15 +- includes/api/ApiProtect.php | 62 +- includes/api/ApiPurge.php | 38 +- includes/api/ApiQuery.php | 96 +- includes/api/ApiQueryAllCategories.php | 57 +- includes/api/ApiQueryAllImages.php | 409 +++++ includes/api/ApiQueryAllLinks.php | 68 +- includes/api/ApiQueryAllMessages.php | 298 ++++ includes/api/ApiQueryAllPages.php | 366 ++++ includes/api/ApiQueryAllUsers.php | 103 +- includes/api/ApiQueryAllimages.php | 267 --- includes/api/ApiQueryAllmessages.php | 277 --- includes/api/ApiQueryAllpages.php | 333 ---- includes/api/ApiQueryBacklinks.php | 100 +- includes/api/ApiQueryBase.php | 13 +- includes/api/ApiQueryBlocks.php | 87 +- includes/api/ApiQueryCategories.php | 45 +- includes/api/ApiQueryCategoryInfo.php | 35 +- includes/api/ApiQueryCategoryMembers.php | 69 +- includes/api/ApiQueryDeletedrevs.php | 40 +- includes/api/ApiQueryDisabled.php | 2 +- includes/api/ApiQueryDuplicateFiles.php | 143 +- includes/api/ApiQueryExtLinksUsage.php | 17 +- includes/api/ApiQueryExternalLinks.php | 10 +- includes/api/ApiQueryFilearchive.php | 110 +- includes/api/ApiQueryIWBacklinks.php | 59 +- includes/api/ApiQueryIWLinks.php | 43 +- includes/api/ApiQueryImageInfo.php | 145 +- includes/api/ApiQueryImages.php | 38 +- includes/api/ApiQueryInfo.php | 259 ++- includes/api/ApiQueryLangBacklinks.php | 59 +- includes/api/ApiQueryLangLinks.php | 38 +- includes/api/ApiQueryLinks.php | 43 +- includes/api/ApiQueryLogEvents.php | 66 +- includes/api/ApiQueryProtectedTitles.php | 38 +- includes/api/ApiQueryQueryPage.php | 37 +- includes/api/ApiQueryRandom.php | 10 + includes/api/ApiQueryRecentChanges.php | 147 +- includes/api/ApiQueryRevisions.php | 98 +- includes/api/ApiQuerySearch.php | 59 +- includes/api/ApiQuerySiteinfo.php | 66 +- includes/api/ApiQueryStashImageInfo.php | 6 +- includes/api/ApiQueryTags.php | 21 +- includes/api/ApiQueryUserContributions.php | 100 +- includes/api/ApiQueryUserInfo.php | 74 +- includes/api/ApiQueryUsers.php | 96 +- includes/api/ApiQueryWatchlist.php | 85 +- includes/api/ApiQueryWatchlistRaw.php | 50 +- includes/api/ApiResult.php | 4 +- includes/api/ApiRollback.php | 26 +- includes/api/ApiSetNotificationTimestamp.php | 285 ++++ includes/api/ApiTokens.php | 158 ++ includes/api/ApiUnblock.php | 48 +- includes/api/ApiUndelete.php | 22 +- includes/api/ApiUpload.php | 130 +- includes/api/ApiUserrights.php | 8 +- includes/api/ApiWatch.php | 18 +- includes/cache/CacheDependency.php | 23 +- includes/cache/FileCacheBase.php | 35 +- includes/cache/GenderCache.php | 93 +- includes/cache/HTMLCacheUpdate.php | 21 + includes/cache/HTMLFileCache.php | 33 +- includes/cache/LinkBatch.php | 28 +- includes/cache/LinkCache.php | 22 + includes/cache/MemcachedSessions.php | 98 -- includes/cache/MessageCache.php | 40 +- includes/cache/ObjectFileCache.php | 24 +- includes/cache/ProcessCacheLRU.php | 120 ++ includes/cache/ResourceFileCache.php | 24 +- includes/cache/SquidUpdate.php | 64 +- includes/cache/UserCache.php | 134 ++ includes/dao/IDBAccessObject.php | 55 + includes/db/CloneDatabase.php | 1 + includes/db/Database.php | 701 ++++---- includes/db/DatabaseError.php | 27 +- includes/db/DatabaseIbm_db2.php | 165 +- includes/db/DatabaseMssql.php | 53 +- includes/db/DatabaseMysql.php | 159 +- includes/db/DatabaseOracle.php | 48 +- includes/db/DatabasePostgres.php | 622 +++++-- includes/db/DatabaseSqlite.php | 80 +- includes/db/DatabaseUtility.php | 28 +- includes/db/IORMRow.php | 275 +++ includes/db/IORMTable.php | 448 +++++ includes/db/LBFactory.php | 29 +- includes/db/LBFactory_Multi.php | 17 +- includes/db/LBFactory_Single.php | 21 + includes/db/LoadBalancer.php | 25 +- includes/db/LoadMonitor.php | 17 +- includes/db/ORMIterator.php | 31 + includes/db/ORMResult.php | 123 ++ includes/db/ORMRow.php | 663 ++++++++ includes/db/ORMTable.php | 675 ++++++++ includes/debug/Debug.php | 371 +++- includes/diff/DairikiDiff.php | 20 +- includes/diff/DifferenceEngine.php | 88 +- includes/filebackend/FSFile.php | 252 +++ includes/filebackend/FSFileBackend.php | 986 +++++++++++ includes/filebackend/FileBackend.php | 1173 +++++++++++++ includes/filebackend/FileBackendGroup.php | 187 +++ includes/filebackend/FileBackendMultiWrite.php | 689 ++++++++ includes/filebackend/FileBackendStore.php | 1766 ++++++++++++++++++++ includes/filebackend/FileOp.php | 764 +++++++++ includes/filebackend/FileOpBatch.php | 240 +++ includes/filebackend/SwiftFileBackend.php | 1544 +++++++++++++++++ includes/filebackend/TempFSFile.php | 121 ++ includes/filebackend/filejournal/DBFileJournal.php | 152 ++ includes/filebackend/filejournal/FileJournal.php | 196 +++ includes/filebackend/lockmanager/DBLockManager.php | 374 +++++ includes/filebackend/lockmanager/FSLockManager.php | 255 +++ includes/filebackend/lockmanager/LSLockManager.php | 218 +++ includes/filebackend/lockmanager/LockManager.php | 425 +++++ .../filebackend/lockmanager/LockManagerGroup.php | 143 ++ .../filebackend/lockmanager/MemcLockManager.php | 319 ++++ includes/filerepo/FSRepo.php | 20 + includes/filerepo/FileRepo.php | 659 +++++--- includes/filerepo/FileRepoStatus.php | 17 +- includes/filerepo/ForeignAPIRepo.php | 138 +- includes/filerepo/ForeignDBRepo.php | 40 +- includes/filerepo/ForeignDBViaLBRepo.php | 37 +- includes/filerepo/LocalRepo.php | 66 +- includes/filerepo/NullRepo.php | 54 +- includes/filerepo/RepoGroup.php | 120 +- includes/filerepo/backend/FSFile.php | 233 --- includes/filerepo/backend/FSFileBackend.php | 600 ------- includes/filerepo/backend/FileBackend.php | 1739 ------------------- includes/filerepo/backend/FileBackendGroup.php | 156 -- .../filerepo/backend/FileBackendMultiWrite.php | 420 ----- includes/filerepo/backend/FileOp.php | 697 -------- includes/filerepo/backend/SwiftFileBackend.php | 877 ---------- includes/filerepo/backend/TempFSFile.php | 92 - .../filerepo/backend/lockmanager/DBLockManager.php | 469 ------ .../filerepo/backend/lockmanager/FSLockManager.php | 202 --- .../filerepo/backend/lockmanager/LSLockManager.php | 295 ---- .../filerepo/backend/lockmanager/LockManager.php | 182 -- .../backend/lockmanager/LockManagerGroup.php | 89 - includes/filerepo/file/ArchivedFile.php | 21 +- includes/filerepo/file/File.php | 219 ++- includes/filerepo/file/ForeignAPIFile.php | 90 +- includes/filerepo/file/ForeignDBFile.php | 46 +- includes/filerepo/file/LocalFile.php | 491 ++++-- includes/filerepo/file/OldLocalFile.php | 103 +- includes/filerepo/file/UnregisteredLocalFile.php | 57 +- includes/installer/CliInstaller.php | 21 +- includes/installer/DatabaseInstaller.php | 38 +- includes/installer/DatabaseUpdater.php | 127 +- includes/installer/Ibm_db2Installer.php | 17 +- includes/installer/Ibm_db2Updater.php | 25 +- includes/installer/InstallDocFormatter.php | 20 + includes/installer/Installer.i18n.php | 7 +- includes/installer/Installer.php | 98 +- includes/installer/LocalSettingsGenerator.php | 34 +- includes/installer/MysqlInstaller.php | 33 +- includes/installer/MysqlUpdater.php | 115 +- includes/installer/OracleInstaller.php | 20 +- includes/installer/OracleUpdater.php | 59 +- includes/installer/PostgresInstaller.php | 57 +- includes/installer/PostgresUpdater.php | 248 ++- includes/installer/SqliteInstaller.php | 17 +- includes/installer/SqliteUpdater.php | 33 +- includes/installer/WebInstaller.php | 57 +- includes/installer/WebInstallerOutput.php | 26 +- includes/installer/WebInstallerPage.php | 101 +- includes/interwiki/Interwiki.php | 41 +- includes/job/DoubleRedirectJob.php | 30 +- includes/job/EmaillingJob.php | 15 + includes/job/EnotifNotifyJob.php | 15 + includes/job/Job.php | 447 +++++ includes/job/JobQueue.php | 413 ----- includes/job/RefreshLinksJob.php | 162 +- includes/job/UploadFromUrlJob.php | 33 +- includes/json/FormatJson.php | 32 +- includes/json/Services_JSON.php | 1 + includes/libs/CSSJanus.php | 16 +- includes/libs/CSSMin.php | 26 +- includes/libs/GenericArrayObject.php | 244 +++ includes/libs/HttpStatus.php | 21 + includes/libs/IEContentAnalyzer.php | 6 + includes/libs/IEUrlExtension.php | 26 +- includes/libs/JavaScriptMinifier.php | 10 +- includes/libs/jsminplus.php | 2 +- includes/logging/LogEntry.php | 43 +- includes/logging/LogEventsList.php | 347 ++-- includes/logging/LogFormatter.php | 393 ++++- includes/logging/LogPage.php | 72 +- includes/logging/LogPager.php | 2 + includes/logging/PatrolLog.php | 53 +- includes/media/BMP.php | 17 +- includes/media/Bitmap.php | 64 +- includes/media/BitmapMetadataHandler.php | 41 +- includes/media/Bitmap_ClientOnly.php | 20 +- includes/media/DjVu.php | 50 +- includes/media/DjVuImage.php | 4 +- includes/media/Exif.php | 9 + includes/media/ExifBitmap.php | 20 +- includes/media/FormatMetadata.php | 125 +- includes/media/GIF.php | 30 +- includes/media/GIFMetadataExtractor.php | 17 +- includes/media/Generic.php | 751 --------- includes/media/IPTC.php | 31 +- includes/media/ImageHandler.php | 249 +++ includes/media/Jpeg.php | 17 + includes/media/JpegMetadataExtractor.php | 44 +- includes/media/MediaHandler.php | 560 +++++++ includes/media/MediaTransformOutput.php | 99 +- includes/media/PNG.php | 29 +- includes/media/PNGMetadataExtractor.php | 16 + includes/media/SVG.php | 79 +- includes/media/SVGMetadataExtractor.php | 33 +- includes/media/Tiff.php | 15 + includes/media/XCF.php | 17 +- includes/media/XMP.php | 62 +- includes/media/XMPInfo.php | 22 + includes/media/XMPValidate.php | 22 + includes/mobile/DeviceDetection.php | 459 +++++ includes/normal/RandomTest.php | 4 - includes/normal/UtfNormal.php | 3 +- includes/normal/UtfNormalDefines.php | 15 + includes/normal/UtfNormalTest.php | 1 + includes/normal/UtfNormalTest2.php | 18 +- includes/objectcache/APCBagOStuff.php | 60 +- includes/objectcache/BagOStuff.php | 119 +- includes/objectcache/DBABagOStuff.php | 127 +- includes/objectcache/EhcacheBagOStuff.php | 82 +- includes/objectcache/EmptyBagOStuff.php | 37 + includes/objectcache/HashBagOStuff.php | 44 + includes/objectcache/MemcachedBagOStuff.php | 180 ++ includes/objectcache/MemcachedClient.php | 281 ++-- includes/objectcache/MemcachedPeclBagOStuff.php | 237 +++ includes/objectcache/MemcachedPhpBagOStuff.php | 139 +- includes/objectcache/MultiWriteBagOStuff.php | 86 +- includes/objectcache/ObjectCache.php | 48 +- includes/objectcache/ObjectCacheSessionHandler.php | 145 ++ includes/objectcache/RedisBagOStuff.php | 413 +++++ includes/objectcache/SqlBagOStuff.php | 314 ++-- includes/objectcache/WinCacheBagOStuff.php | 24 + includes/objectcache/XCacheBagOStuff.php | 44 +- includes/parser/CacheTime.php | 132 ++ includes/parser/CoreLinkFunctions.php | 16 + includes/parser/CoreParserFunctions.php | 169 +- includes/parser/CoreTagHooks.php | 16 + includes/parser/DateFormatter.php | 91 +- includes/parser/LinkHolderArray.php | 32 +- includes/parser/Parser.php | 373 +++-- includes/parser/ParserCache.php | 33 +- includes/parser/ParserOptions.php | 55 +- includes/parser/ParserOutput.php | 175 +- includes/parser/Parser_DiffTest.php | 16 + includes/parser/Parser_LinkHooks.php | 20 +- includes/parser/Preprocessor.php | 28 +- includes/parser/Preprocessor_DOM.php | 80 +- includes/parser/Preprocessor_Hash.php | 78 +- includes/parser/Preprocessor_HipHop.hphp | 106 +- includes/parser/StripState.php | 55 +- includes/parser/Tidy.php | 22 +- includes/profiler/Profiler.php | 131 +- includes/profiler/ProfilerSimple.php | 78 +- includes/profiler/ProfilerSimpleText.php | 17 + includes/profiler/ProfilerSimpleTrace.php | 36 +- includes/profiler/ProfilerSimpleUDP.php | 21 + includes/profiler/ProfilerStub.php | 27 +- includes/resourceloader/ResourceLoader.php | 166 +- includes/resourceloader/ResourceLoaderContext.php | 13 +- .../resourceloader/ResourceLoaderFileModule.php | 15 +- .../ResourceLoaderFilePageModule.php | 21 + .../ResourceLoaderLanguageDataModule.php | 122 ++ includes/resourceloader/ResourceLoaderModule.php | 21 +- .../ResourceLoaderNoscriptModule.php | 2 + .../resourceloader/ResourceLoaderSiteModule.php | 2 + .../resourceloader/ResourceLoaderStartUpModule.php | 54 +- .../ResourceLoaderUserCSSPrefsModule.php | 14 +- .../ResourceLoaderUserGroupsModule.php | 39 +- .../resourceloader/ResourceLoaderUserModule.php | 57 +- .../ResourceLoaderUserOptionsModule.php | 11 +- .../ResourceLoaderUserTokensModule.php | 9 + .../resourceloader/ResourceLoaderWikiModule.php | 19 +- includes/revisiondelete/RevisionDelete.php | 152 +- .../revisiondelete/RevisionDeleteAbstracts.php | 38 +- includes/revisiondelete/RevisionDeleteUser.php | 22 +- includes/revisiondelete/RevisionDeleter.php | 84 +- includes/search/SearchEngine.php | 47 +- includes/search/SearchIBM_DB2.php | 5 +- includes/search/SearchMssql.php | 7 +- includes/search/SearchMySQL.php | 5 +- includes/search/SearchOracle.php | 4 +- includes/search/SearchPostgres.php | 1 + includes/search/SearchSqlite.php | 1 + includes/search/SearchUpdate.php | 15 + includes/specials/SpecialActiveusers.php | 54 +- includes/specials/SpecialAllmessages.php | 22 +- includes/specials/SpecialAllpages.php | 75 +- includes/specials/SpecialAncientpages.php | 6 +- includes/specials/SpecialBlock.php | 196 ++- includes/specials/SpecialBlockList.php | 2 +- includes/specials/SpecialBooksources.php | 13 +- includes/specials/SpecialBrokenRedirects.php | 8 +- includes/specials/SpecialCachedPage.php | 198 +++ includes/specials/SpecialCategories.php | 9 +- includes/specials/SpecialChangeEmail.php | 84 +- includes/specials/SpecialChangePassword.php | 31 +- includes/specials/SpecialConfirmemail.php | 4 +- includes/specials/SpecialContributions.php | 413 +++-- includes/specials/SpecialDeadendpages.php | 10 +- includes/specials/SpecialDeletedContributions.php | 55 +- includes/specials/SpecialDisambiguations.php | 75 +- includes/specials/SpecialDoubleRedirects.php | 16 +- includes/specials/SpecialEditWatchlist.php | 84 +- includes/specials/SpecialEmailuser.php | 74 +- includes/specials/SpecialExport.php | 49 +- includes/specials/SpecialFewestrevisions.php | 16 +- includes/specials/SpecialFileDuplicateSearch.php | 30 +- includes/specials/SpecialFilepath.php | 6 +- includes/specials/SpecialImport.php | 97 +- includes/specials/SpecialJavaScriptTest.php | 61 +- includes/specials/SpecialLinkSearch.php | 29 +- includes/specials/SpecialListfiles.php | 60 +- includes/specials/SpecialListgrouprights.php | 44 +- includes/specials/SpecialListredirects.php | 8 +- includes/specials/SpecialListusers.php | 126 +- includes/specials/SpecialLockdb.php | 10 +- includes/specials/SpecialLog.php | 35 +- includes/specials/SpecialLonelypages.php | 8 +- includes/specials/SpecialMIMEsearch.php | 31 +- includes/specials/SpecialMergeHistory.php | 66 +- includes/specials/SpecialMostcategories.php | 40 +- includes/specials/SpecialMostimages.php | 6 +- includes/specials/SpecialMostinterwikis.php | 112 ++ includes/specials/SpecialMostlinked.php | 17 +- includes/specials/SpecialMostlinkedcategories.php | 22 +- includes/specials/SpecialMostlinkedtemplates.php | 24 +- includes/specials/SpecialMovepage.php | 77 +- includes/specials/SpecialNewimages.php | 34 +- includes/specials/SpecialNewpages.php | 66 +- includes/specials/SpecialPasswordReset.php | 17 +- includes/specials/SpecialPopularpages.php | 14 +- includes/specials/SpecialPreferences.php | 7 +- includes/specials/SpecialPrefixindex.php | 78 +- includes/specials/SpecialProtectedpages.php | 89 +- includes/specials/SpecialProtectedtitles.php | 55 +- includes/specials/SpecialRandompage.php | 2 +- includes/specials/SpecialRecentchanges.php | 119 +- includes/specials/SpecialRecentchangeslinked.php | 23 +- includes/specials/SpecialRevisiondelete.php | 105 +- includes/specials/SpecialSearch.php | 159 +- includes/specials/SpecialShortpages.php | 55 +- includes/specials/SpecialStatistics.php | 46 +- includes/specials/SpecialTags.php | 27 +- includes/specials/SpecialUnblock.php | 15 +- includes/specials/SpecialUncategorizedimages.php | 6 +- includes/specials/SpecialUncategorizedpages.php | 6 +- includes/specials/SpecialUndelete.php | 111 +- includes/specials/SpecialUnusedcategories.php | 7 +- includes/specials/SpecialUnusedimages.php | 8 +- includes/specials/SpecialUnusedtemplates.php | 16 +- includes/specials/SpecialUnwatchedpages.php | 20 +- includes/specials/SpecialUpload.php | 141 +- includes/specials/SpecialUploadStash.php | 44 +- includes/specials/SpecialUserlogin.php | 158 +- includes/specials/SpecialUserlogout.php | 2 +- includes/specials/SpecialUserrights.php | 38 +- includes/specials/SpecialVersion.php | 256 ++- includes/specials/SpecialWantedcategories.php | 6 +- includes/specials/SpecialWantedfiles.php | 9 +- includes/specials/SpecialWantedpages.php | 8 +- includes/specials/SpecialWantedtemplates.php | 8 +- includes/specials/SpecialWatchlist.php | 81 +- includes/specials/SpecialWhatlinkshere.php | 44 +- includes/specials/SpecialWithoutinterwiki.php | 18 +- includes/templates/NoLocalSettings.php | 17 +- includes/templates/Usercreate.php | 22 +- includes/templates/Userlogin.php | 38 +- includes/tidy.conf | 1 + includes/upload/UploadBase.php | 148 +- includes/upload/UploadFromChunks.php | 122 +- includes/upload/UploadFromFile.php | 33 +- includes/upload/UploadFromStash.php | 28 +- includes/upload/UploadFromUrl.php | 86 +- includes/upload/UploadStash.php | 48 +- includes/zhtable/Makefile.py | 14 +- includes/zhtable/simp2trad.manual | 1 - includes/zhtable/toCN.manual | 1 - includes/zhtable/toHK.manual | 58 + includes/zhtable/toTW.manual | 1 + includes/zhtable/trad2simp.manual | 1 - includes/zhtable/tradphrases.manual | 3 + 551 files changed, 44613 insertions(+), 19014 deletions(-) create mode 100644 includes/CacheHelper.php create mode 100644 includes/DataUpdate.php create mode 100644 includes/DeprecatedGlobal.php create mode 100644 includes/GitInfo.php delete mode 100644 includes/ImageFunctions.php create mode 100644 includes/ScopedPHPTimeout.php create mode 100644 includes/SqlDataUpdate.php create mode 100644 includes/Timestamp.php create mode 100644 includes/actions/CachedAction.php create mode 100644 includes/api/ApiOptions.php create mode 100644 includes/api/ApiQueryAllImages.php create mode 100644 includes/api/ApiQueryAllMessages.php create mode 100644 includes/api/ApiQueryAllPages.php delete mode 100644 includes/api/ApiQueryAllimages.php delete mode 100644 includes/api/ApiQueryAllmessages.php delete mode 100644 includes/api/ApiQueryAllpages.php create mode 100644 includes/api/ApiSetNotificationTimestamp.php create mode 100644 includes/api/ApiTokens.php delete mode 100644 includes/cache/MemcachedSessions.php create mode 100644 includes/cache/ProcessCacheLRU.php create mode 100644 includes/cache/UserCache.php create mode 100644 includes/dao/IDBAccessObject.php create mode 100644 includes/db/IORMRow.php create mode 100644 includes/db/IORMTable.php create mode 100644 includes/db/ORMIterator.php create mode 100644 includes/db/ORMResult.php create mode 100644 includes/db/ORMRow.php create mode 100644 includes/db/ORMTable.php create mode 100644 includes/filebackend/FSFile.php create mode 100644 includes/filebackend/FSFileBackend.php create mode 100644 includes/filebackend/FileBackend.php create mode 100644 includes/filebackend/FileBackendGroup.php create mode 100644 includes/filebackend/FileBackendMultiWrite.php create mode 100644 includes/filebackend/FileBackendStore.php create mode 100644 includes/filebackend/FileOp.php create mode 100644 includes/filebackend/FileOpBatch.php create mode 100644 includes/filebackend/SwiftFileBackend.php create mode 100644 includes/filebackend/TempFSFile.php create mode 100644 includes/filebackend/filejournal/DBFileJournal.php create mode 100644 includes/filebackend/filejournal/FileJournal.php create mode 100644 includes/filebackend/lockmanager/DBLockManager.php create mode 100644 includes/filebackend/lockmanager/FSLockManager.php create mode 100644 includes/filebackend/lockmanager/LSLockManager.php create mode 100644 includes/filebackend/lockmanager/LockManager.php create mode 100644 includes/filebackend/lockmanager/LockManagerGroup.php create mode 100644 includes/filebackend/lockmanager/MemcLockManager.php delete mode 100644 includes/filerepo/backend/FSFile.php delete mode 100644 includes/filerepo/backend/FSFileBackend.php delete mode 100644 includes/filerepo/backend/FileBackend.php delete mode 100644 includes/filerepo/backend/FileBackendGroup.php delete mode 100644 includes/filerepo/backend/FileBackendMultiWrite.php delete mode 100644 includes/filerepo/backend/FileOp.php delete mode 100644 includes/filerepo/backend/SwiftFileBackend.php delete mode 100644 includes/filerepo/backend/TempFSFile.php delete mode 100644 includes/filerepo/backend/lockmanager/DBLockManager.php delete mode 100644 includes/filerepo/backend/lockmanager/FSLockManager.php delete mode 100644 includes/filerepo/backend/lockmanager/LSLockManager.php delete mode 100644 includes/filerepo/backend/lockmanager/LockManager.php delete mode 100644 includes/filerepo/backend/lockmanager/LockManagerGroup.php create mode 100644 includes/job/Job.php delete mode 100644 includes/job/JobQueue.php create mode 100644 includes/libs/GenericArrayObject.php delete mode 100644 includes/media/Generic.php create mode 100644 includes/media/ImageHandler.php create mode 100644 includes/media/MediaHandler.php create mode 100644 includes/mobile/DeviceDetection.php create mode 100644 includes/objectcache/MemcachedBagOStuff.php create mode 100644 includes/objectcache/MemcachedPeclBagOStuff.php create mode 100644 includes/objectcache/ObjectCacheSessionHandler.php create mode 100644 includes/objectcache/RedisBagOStuff.php create mode 100644 includes/parser/CacheTime.php create mode 100644 includes/resourceloader/ResourceLoaderLanguageDataModule.php create mode 100644 includes/specials/SpecialCachedPage.php create mode 100644 includes/specials/SpecialMostinterwikis.php (limited to 'includes') diff --git a/includes/Action.php b/includes/Action.php index 37c48488..51922251 100644 --- a/includes/Action.php +++ b/includes/Action.php @@ -1,15 +1,6 @@ canUseWikiPage() ) { return 'view'; } - + $action = Action::factory( $actionName, $context->getWikiPage() ); if ( $action instanceof Action ) { return $action->getName(); @@ -266,6 +273,7 @@ abstract class Action { * * @param $user User: the user to check, or null to use the context user * @throws ErrorPageError + * @return bool True on success */ protected function checkCanExecute( User $user ) { $right = $this->getRestriction(); @@ -277,7 +285,7 @@ abstract class Action { } if ( $this->requiresUnblock() && $user->isBlocked() ) { - $block = $user->mBlock; + $block = $user->getBlock(); throw new UserBlockedError( $block ); } @@ -287,6 +295,7 @@ abstract class Action { if ( $this->requiresWrite() && wfReadOnly() ) { throw new ReadOnlyError(); } + return true; } /** @@ -332,7 +341,7 @@ abstract class Action { * @return String */ protected function getDescription() { - return wfMsgHtml( strtolower( $this->getName() ) ); + return $this->msg( strtolower( $this->getName() ) )->escaped(); } /** @@ -350,6 +359,9 @@ abstract class Action { public abstract function execute(); } +/** + * An action which shows a form and does something based on the input from the form + */ abstract class FormAction extends Action { /** @@ -385,7 +397,7 @@ abstract class FormAction extends Action { // Give hooks a chance to alter the form, adding extra fields or text etc wfRunHooks( 'ActionModifyFormFields', array( $this->getName(), &$this->fields, $this->page ) ); - $form = new HTMLForm( $this->fields, $this->getContext() ); + $form = new HTMLForm( $this->fields, $this->getContext(), $this->getName() ); $form->setSubmitCallback( array( $this, 'onSubmit' ) ); // Retain query parameters (uselang etc) @@ -444,8 +456,8 @@ abstract class FormAction extends Action { /** * @see Action::execute() * @throws ErrorPageError - * @param array|null $data - * @param bool $captureErrors + * @param $data array|null + * @param $captureErrors bool * @return bool */ public function execute( array $data = null, $captureErrors = true ) { @@ -486,9 +498,7 @@ abstract class FormAction extends Action { } /** - * 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). + * An action which just does something, without showing a form first. */ abstract class FormlessAction extends Action { @@ -501,15 +511,23 @@ abstract class FormlessAction extends Action { /** * We don't want an HTMLForm + * @return bool */ protected function getFormFields() { return false; } + /** + * @param $data Array + * @return bool + */ public function onSubmit( $data ) { return false; } + /** + * @return bool + */ public function onSuccess() { return false; } diff --git a/includes/AjaxDispatcher.php b/includes/AjaxDispatcher.php index 5bc9f067..b00cf309 100644 --- a/includes/AjaxDispatcher.php +++ b/includes/AjaxDispatcher.php @@ -1,10 +1,28 @@ mode ) ) { return; @@ -84,7 +113,7 @@ class AjaxDispatcher { 'Bad Request', "unknown function " . (string) $this->func_name ); - } elseif ( !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) + } elseif ( !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) && !$wgUser->isAllowed( 'read' ) ) { wfHttpError( @@ -94,14 +123,8 @@ class AjaxDispatcher { } else { wfDebug( __METHOD__ . ' dispatching ' . $this->func_name . "\n" ); - if ( strpos( $this->func_name, '::' ) !== false ) { - $func = explode( '::', $this->func_name, 2 ); - } else { - $func = $this->func_name; - } - try { - $result = call_user_func_array( $func, $this->args ); + $result = call_user_func_array( $this->func_name, $this->args ); if ( $result === false || $result === null ) { wfDebug( __METHOD__ . ' ERROR while dispatching ' @@ -134,7 +157,6 @@ class AjaxDispatcher { } } - $wgOut = null; wfProfileOut( __METHOD__ ); } } diff --git a/includes/AjaxResponse.php b/includes/AjaxResponse.php index e60ca23c..6bf94ccb 100644 --- a/includes/AjaxResponse.php +++ b/includes/AjaxResponse.php @@ -1,6 +1,21 @@ disable() */ + /** + * Disables output. Can be set by calling $AjaxResponse->disable() + * @var bool $mDisabled + */ private $mDisabled; - /** Date for the HTTP header Last-modified */ + /** + * Date for the HTTP header Last-modified + * @var string|false $mLastModified + */ private $mLastModified; - /** HTTP response code */ + /** + * HTTP response code + * @var string $mResponseCode + */ private $mResponseCode; - /** HTTP Vary header */ + /** + * HTTP Vary header + * @var string $mVary + */ private $mVary; - /** Content of our HTTP response */ + /** + * Content of our HTTP response + * @var string $mText + */ private $mText; + /** + * @param $text string|null + */ function __construct( $text = null ) { $this->mCacheDuration = null; $this->mVary = null; @@ -49,41 +89,67 @@ class AjaxResponse { } } + /** + * Set the number of seconds to get the response cached by a proxy + * @param $duration int + */ function setCacheDuration( $duration ) { $this->mCacheDuration = $duration; } + /** + * Set the HTTP Vary header + * @param $vary string + */ function setVary( $vary ) { $this->mVary = $vary; } + /** + * Set the HTTP response code + * @param $code string + */ function setResponseCode( $code ) { $this->mResponseCode = $code; } + /** + * Set the HTTP header Content-Type + * @param $type string + */ function setContentType( $type ) { $this->mContentType = $type; } + /** + * Disable output. + */ function disable() { $this->mDisabled = true; } - /** Add content to the response */ + /** + * Add content to the response + * @param $text string + */ function addText( $text ) { if ( ! $this->mDisabled && $text ) { $this->mText .= $text; } } - /** Output text */ + /** + * Output text + */ function printText() { if ( ! $this->mDisabled ) { print $this->mText; } } - /** Construct the header and output it */ + /** + * Construct the header and output it + */ function sendHeaders() { global $wgUseSquid, $wgUseESI; @@ -139,8 +205,10 @@ class AjaxResponse { /** * checkLastModified tells the client to use the client-cached response if * possible. If sucessful, the AjaxResponse is disabled so that - * any future call to AjaxResponse::printText() have no effect. The method - * returns true iff the response code was set to 304 Not Modified. + * any future call to AjaxResponse::printText() have no effect. + * + * @param $timestamp string + * @return bool Returns true if the response code was set to 304 Not Modified. */ function checkLastModified ( $timestamp ) { global $wgCachePages, $wgCacheEpoch, $wgUser; @@ -148,21 +216,21 @@ class AjaxResponse { if ( !$timestamp || $timestamp == '19700101000000' ) { wfDebug( "$fname: CACHE DISABLED, NO TIMESTAMP\n" ); - return; + return false; } if ( !$wgCachePages ) { wfDebug( "$fname: CACHE DISABLED\n", false ); - return; + return false; } if ( $wgUser->getOption( 'nocache' ) ) { wfDebug( "$fname: USER DISABLED CACHE\n", false ); - return; + return false; } $timestamp = wfTimestamp( TS_MW, $timestamp ); - $lastmod = wfTimestamp( TS_RFC2822, max( $timestamp, $wgUser->mTouched, $wgCacheEpoch ) ); + $lastmod = wfTimestamp( TS_RFC2822, max( $timestamp, $wgUser->getTouched(), $wgCacheEpoch ) ); if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { # IE sends sizes after the date like this: @@ -191,11 +259,12 @@ class AjaxResponse { wfDebug( "$fname: client did not send If-Modified-Since header\n", false ); $this->mLastModified = $lastmod; } + return false; } /** - * @param $mckey - * @param $touched + * @param $mckey string + * @param $touched int * @return bool */ function loadFromMemcached( $mckey, $touched ) { @@ -222,7 +291,7 @@ class AjaxResponse { } /** - * @param $mckey + * @param $mckey string * @param $expiry int * @return bool */ diff --git a/includes/Article.php b/includes/Article.php index b07f309c..9ab4b6ba 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -1,6 +1,22 @@ mPage->getID() === 0 ) { @@ -204,7 +245,8 @@ class Article extends Page { $text = ''; } } else { - $text = wfMsgExt( $wgUser->isLoggedIn() ? 'noarticletext' : 'noarticletextanon', 'parsemag' ); + $message = $this->getContext()->getUser()->isLoggedIn() ? 'noarticletext' : 'noarticletextanon'; + $text = wfMessage( $message )->text(); } wfProfileOut( __METHOD__ ); @@ -235,11 +277,10 @@ class Article extends Page { * @return int The old id for the request */ public function getOldIDFromRequest() { - global $wgRequest; - $this->mRedirectUrl = false; - $oldid = $wgRequest->getIntOrNull( 'oldid' ); + $request = $this->getContext()->getRequest(); + $oldid = $request->getIntOrNull( 'oldid' ); if ( $oldid === null ) { return 0; @@ -248,17 +289,21 @@ class Article extends Page { if ( $oldid !== 0 ) { # Load the given revision and check whether the page is another one. # In that case, update this instance to reflect the change. - $this->mRevision = Revision::newFromId( $oldid ); - if ( $this->mRevision !== null ) { - // Revision title doesn't match the page title given? - if ( $this->mPage->getID() != $this->mRevision->getPage() ) { - $function = array( get_class( $this->mPage ), 'newFromID' ); - $this->mPage = call_user_func( $function, $this->mRevision->getPage() ); + if ( $oldid === $this->mPage->getLatest() ) { + $this->mRevision = $this->mPage->getRevision(); + } else { + $this->mRevision = Revision::newFromId( $oldid ); + if ( $this->mRevision !== null ) { + // Revision title doesn't match the page title given? + if ( $this->mPage->getID() != $this->mRevision->getPage() ) { + $function = array( get_class( $this->mPage ), 'newFromID' ); + $this->mPage = call_user_func( $function, $this->mRevision->getPage() ); + } } } } - if ( $wgRequest->getVal( 'direction' ) == 'next' ) { + if ( $request->getVal( 'direction' ) == 'next' ) { $nextid = $this->getTitle()->getNextRevisionID( $oldid ); if ( $nextid ) { $oldid = $nextid; @@ -266,7 +311,7 @@ class Article extends Page { } else { $this->mRedirectUrl = $this->getTitle()->getFullURL( 'redirect=no' ); } - } elseif ( $wgRequest->getVal( 'direction' ) == 'prev' ) { + } elseif ( $request->getVal( 'direction' ) == 'prev' ) { $previd = $this->getTitle()->getPreviousRevisionID( $oldid ); if ( $previd ) { $oldid = $previd; @@ -306,9 +351,7 @@ class Article extends Page { # Pre-fill content with error message so that if something # fails we'll have something telling us what we intended. - $t = $this->getTitle()->getPrefixedText(); - $d = $oldid ? wfMsgExt( 'missingarticle-rev', array( 'escape' ), $oldid ) : ''; - $this->mContent = wfMsgNoTrans( 'missing-article', $t, $d ) ; + $this->mContent = wfMessage( 'missing-revision', $oldid )->plain(); if ( $oldid ) { # $this->mRevision might already be fetched by getOldIDFromRequest() @@ -400,8 +443,7 @@ class Article extends Page { * page of the given title. */ public function view() { - global $wgUser, $wgOut, $wgRequest, $wgParser; - global $wgUseFileCache, $wgUseETag, $wgDebugToolbar; + global $wgParser, $wgUseFileCache, $wgUseETag, $wgDebugToolbar; wfProfileIn( __METHOD__ ); @@ -411,17 +453,19 @@ class Article extends Page { # the first call of this method even if $oldid is used way below. $oldid = $this->getOldID(); + $user = $this->getContext()->getUser(); # Another whitelist check in case getOldID() is altering the title - $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $wgUser ); + $permErrors = $this->getTitle()->getUserPermissionsErrors( 'read', $user ); if ( count( $permErrors ) ) { wfDebug( __METHOD__ . ": denied on secondary read check\n" ); wfProfileOut( __METHOD__ ); throw new PermissionsError( 'read', $permErrors ); } + $outputPage = $this->getContext()->getOutput(); # getOldID() may as well want us to redirect somewhere else if ( $this->mRedirectUrl ) { - $wgOut->redirect( $this->mRedirectUrl ); + $outputPage->redirect( $this->mRedirectUrl ); wfDebug( __METHOD__ . ": redirecting due to oldid\n" ); wfProfileOut( __METHOD__ ); @@ -429,7 +473,7 @@ class Article extends Page { } # If we got diff in the query, we want to see a diff page instead of the article. - if ( $wgRequest->getCheck( 'diff' ) ) { + if ( $this->getContext()->getRequest()->getCheck( 'diff' ) ) { wfDebug( __METHOD__ . ": showing diff page\n" ); $this->showDiffPage(); wfProfileOut( __METHOD__ ); @@ -438,31 +482,31 @@ class Article extends Page { } # Set page title (may be overridden by DISPLAYTITLE) - $wgOut->setPageTitle( $this->getTitle()->getPrefixedText() ); + $outputPage->setPageTitle( $this->getTitle()->getPrefixedText() ); - $wgOut->setArticleFlag( true ); + $outputPage->setArticleFlag( true ); # Allow frames by default - $wgOut->allowClickjacking(); + $outputPage->allowClickjacking(); $parserCache = ParserCache::singleton(); $parserOptions = $this->getParserOptions(); # Render printable version, use printable version cache - if ( $wgOut->isPrintable() ) { + if ( $outputPage->isPrintable() ) { $parserOptions->setIsPrintable( true ); $parserOptions->setEditSection( false ); - } elseif ( !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit' ) ) { + } elseif ( !$this->isCurrent() || !$this->getTitle()->quickUserCan( 'edit', $user ) ) { $parserOptions->setEditSection( false ); } # Try client and file cache if ( !$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched() ) { if ( $wgUseETag ) { - $wgOut->setETag( $parserCache->getETag( $this, $parserOptions ) ); + $outputPage->setETag( $parserCache->getETag( $this, $parserOptions ) ); } # Is it client cached? - if ( $wgOut->checkLastModified( $this->mPage->getTouched() ) ) { + if ( $outputPage->checkLastModified( $this->mPage->getTouched() ) ) { wfDebug( __METHOD__ . ": done 304\n" ); wfProfileOut( __METHOD__ ); @@ -471,8 +515,8 @@ class Article extends Page { } elseif ( $wgUseFileCache && $this->tryFileCache() ) { wfDebug( __METHOD__ . ": done file cache\n" ); # tell wgOut that output is taken care of - $wgOut->disable(); - $this->mPage->doViewUpdates( $wgUser ); + $outputPage->disable(); + $this->mPage->doViewUpdates( $user ); wfProfileOut( __METHOD__ ); return; @@ -482,7 +526,7 @@ class Article extends Page { # Should the parser cache be used? $useParserCache = $this->mPage->isParserCacheUsed( $parserOptions, $oldid ); wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" ); - if ( $wgUser->getStubThreshold() ) { + if ( $user->getStubThreshold() ) { wfIncrStats( 'pcache_miss_stub' ); } @@ -520,14 +564,14 @@ class Article extends Page { } else { wfDebug( __METHOD__ . ": showing parser cache contents\n" ); } - $wgOut->addParserOutput( $this->mParserOutput ); + $outputPage->addParserOutput( $this->mParserOutput ); # Ensure that UI elements requiring revision ID have # the correct version information. - $wgOut->setRevisionId( $this->mPage->getLatest() ); + $outputPage->setRevisionId( $this->mPage->getLatest() ); # Preload timestamp to avoid a DB hit $cachedTimestamp = $this->mParserOutput->getTimestamp(); if ( $cachedTimestamp !== null ) { - $wgOut->setRevisionTimestamp( $cachedTimestamp ); + $outputPage->setRevisionTimestamp( $cachedTimestamp ); $this->mPage->setTimestamp( $cachedTimestamp ); } $outputDone = true; @@ -551,16 +595,16 @@ class Article extends Page { # Ensure that UI elements requiring revision ID have # the correct version information. - $wgOut->setRevisionId( $this->getRevIdFetched() ); + $outputPage->setRevisionId( $this->getRevIdFetched() ); # Preload timestamp to avoid a DB hit - $wgOut->setRevisionTimestamp( $this->getTimestamp() ); + $outputPage->setRevisionTimestamp( $this->getTimestamp() ); # Pages containing custom CSS or JavaScript get special treatment if ( $this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage() ) { wfDebug( __METHOD__ . ": showing CSS/JS source\n" ); $this->showCssOrJsPage(); $outputDone = true; - } elseif( !wfRunHooks( 'ArticleViewCustom', array( $this->mContent, $this->getTitle(), $wgOut ) ) ) { + } elseif( !wfRunHooks( 'ArticleViewCustom', array( $this->mContent, $this->getTitle(), $outputPage ) ) ) { # Allow extensions do their own custom view for certain pages $outputDone = true; } else { @@ -569,10 +613,10 @@ class Article extends Page { if ( $rt ) { wfDebug( __METHOD__ . ": showing redirect=no page\n" ); # Viewing a redirect page (e.g. with parameter redirect=no) - $wgOut->addHTML( $this->viewRedirect( $rt ) ); + $outputPage->addHTML( $this->viewRedirect( $rt ) ); # Parse just to get categories, displaytitle, etc. $this->mParserOutput = $wgParser->parse( $text, $this->getTitle(), $parserOptions ); - $wgOut->addParserOutputNoText( $this->mParserOutput ); + $outputPage->addParserOutputNoText( $this->mParserOutput ); $outputDone = true; } } @@ -587,12 +631,12 @@ class Article extends Page { if ( !$poolArticleView->execute() ) { $error = $poolArticleView->getError(); if ( $error ) { - $wgOut->clearHTML(); // for release() errors - $wgOut->enableClientCache( false ); - $wgOut->setRobotPolicy( 'noindex,nofollow' ); + $outputPage->clearHTML(); // for release() errors + $outputPage->enableClientCache( false ); + $outputPage->setRobotPolicy( 'noindex,nofollow' ); $errortext = $error->getWikiText( false, 'view-pool-error' ); - $wgOut->addWikiText( '
' . $errortext . '
' ); + $outputPage->addWikiText( '
' . $errortext . '
' ); } # Connection or timeout error wfProfileOut( __METHOD__ ); @@ -600,12 +644,12 @@ class Article extends Page { } $this->mParserOutput = $poolArticleView->getParserOutput(); - $wgOut->addParserOutput( $this->mParserOutput ); + $outputPage->addParserOutput( $this->mParserOutput ); # Don't cache a dirty ParserOutput object if ( $poolArticleView->getIsDirty() ) { - $wgOut->setSquidMaxage( 0 ); - $wgOut->addHTML( "\n" ); + $outputPage->setSquidMaxage( 0 ); + $outputPage->addHTML( "\n" ); } $outputDone = true; @@ -634,17 +678,17 @@ class Article extends Page { if ( $this->getTitle()->isMainPage() ) { $msg = wfMessage( 'pagetitle-view-mainpage' )->inContentLanguage(); if ( !$msg->isDisabled() ) { - $wgOut->setHTMLTitle( $msg->title( $this->getTitle() )->text() ); + $outputPage->setHTMLTitle( $msg->title( $this->getTitle() )->text() ); } } # Check for any __NOINDEX__ tags on the page using $pOutput $policy = $this->getRobotPolicy( 'view', $pOutput ); - $wgOut->setIndexPolicy( $policy['index'] ); - $wgOut->setFollowPolicy( $policy['follow'] ); + $outputPage->setIndexPolicy( $policy['index'] ); + $outputPage->setFollowPolicy( $policy['follow'] ); $this->showViewFooter(); - $this->mPage->doViewUpdates( $wgUser ); + $this->mPage->doViewUpdates( $user ); wfProfileOut( __METHOD__ ); } @@ -654,11 +698,10 @@ class Article extends Page { * @param $pOutput ParserOutput */ public function adjustDisplayTitle( ParserOutput $pOutput ) { - global $wgOut; # Adjust the title if it was set by displaytitle, -{T|}- or language conversion $titleText = $pOutput->getTitleText(); if ( strval( $titleText ) !== '' ) { - $wgOut->setPageTitle( $titleText ); + $this->getContext()->getOutput()->setPageTitle( $titleText ); } } @@ -667,13 +710,13 @@ class Article extends Page { * Article::view() only, other callers should use the DifferenceEngine class. */ public function showDiffPage() { - global $wgRequest, $wgUser; - - $diff = $wgRequest->getVal( 'diff' ); - $rcid = $wgRequest->getVal( 'rcid' ); - $diffOnly = $wgRequest->getBool( 'diffonly', $wgUser->getOption( 'diffonly' ) ); - $purge = $wgRequest->getVal( 'action' ) == 'purge'; - $unhide = $wgRequest->getInt( 'unhide' ) == 1; + $request = $this->getContext()->getRequest(); + $user = $this->getContext()->getUser(); + $diff = $request->getVal( 'diff' ); + $rcid = $request->getVal( 'rcid' ); + $diffOnly = $request->getBool( 'diffonly', $user->getOption( 'diffonly' ) ); + $purge = $request->getVal( 'action' ) == 'purge'; + $unhide = $request->getInt( 'unhide' ) == 1; $oldid = $this->getOldID(); $de = new DifferenceEngine( $this->getContext(), $oldid, $diff, $rcid, $purge, $unhide ); @@ -683,7 +726,7 @@ class Article extends Page { if ( $diff == 0 || $diff == $this->mPage->getLatest() ) { # Run view updates for current revision only - $this->mPage->doViewUpdates( $wgUser ); + $this->mPage->doViewUpdates( $user ); } } @@ -695,22 +738,21 @@ class Article extends Page { * page views. */ protected function showCssOrJsPage() { - global $wgOut; - $dir = $this->getContext()->getLanguage()->getDir(); $lang = $this->getContext()->getLanguage()->getCode(); - $wgOut->wrapWikiMsg( "
\n$1\n
", + $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(), $wgOut ) ) ) { + 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 );
-			$wgOut->addHTML( "
\n" );
-			$wgOut->addHTML( htmlspecialchars( $this->mContent ) );
-			$wgOut->addHTML( "\n
\n" ); + $outputPage->addHTML( "
\n" );
+			$outputPage->addHTML( htmlspecialchars( $this->mContent ) );
+			$outputPage->addHTML( "\n
\n" ); } } @@ -722,8 +764,7 @@ class Article extends Page { * TODO: actions other than 'view' */ public function getRobotPolicy( $action, $pOutput ) { - global $wgOut, $wgArticleRobotPolicies, $wgNamespaceRobotPolicies; - global $wgDefaultRobotPolicy, $wgRequest; + global $wgArticleRobotPolicies, $wgNamespaceRobotPolicies, $wgDefaultRobotPolicy; $ns = $this->getTitle()->getNamespace(); @@ -745,13 +786,13 @@ class Article extends Page { 'index' => 'noindex', 'follow' => 'nofollow' ); - } elseif ( $wgOut->isPrintable() ) { + } elseif ( $this->getContext()->getOutput()->isPrintable() ) { # Discourage indexing of printable versions, but encourage following return array( 'index' => 'noindex', 'follow' => 'follow' ); - } elseif ( $wgRequest->getInt( 'curid' ) ) { + } elseif ( $this->getContext()->getRequest()->getInt( 'curid' ) ) { # For ?curid=x urls, disallow indexing return array( 'index' => 'noindex', @@ -794,7 +835,7 @@ class Article extends Page { * merging of several policies using array_merge(). * @param $policy Mixed, returns empty array on null/false/'', transparent * to already-converted arrays, converts String. - * @return Array: 'index' => , 'follow' => + * @return Array: 'index' => \, 'follow' => \ */ public static function formatRobotPolicy( $policy ) { if ( is_array( $policy ) ) { @@ -820,15 +861,16 @@ class Article extends Page { /** * If this request is a redirect view, send "redirected from" subtitle to - * $wgOut. Returns true if the header was needed, false if this is not a - * redirect view. Handles both local and remote redirects. + * the output. Returns true if the header was needed, false if this is not + * a redirect view. Handles both local and remote redirects. * * @return boolean */ public function showRedirectedFromHeader() { - global $wgOut, $wgRequest, $wgRedirectSources; + global $wgRedirectSources; + $outputPage = $this->getContext()->getOutput(); - $rdfrom = $wgRequest->getVal( 'rdfrom' ); + $rdfrom = $this->getContext()->getRequest()->getVal( 'rdfrom' ); if ( isset( $this->mRedirectedFrom ) ) { // This is an internally redirected page view. @@ -841,21 +883,21 @@ class Article extends Page { array( 'redirect' => 'no' ) ); - $wgOut->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) ); + $outputPage->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) ); // Set the fragment if one was specified in the redirect if ( strval( $this->getTitle()->getFragment() ) != '' ) { $fragment = Xml::escapeJsString( $this->getTitle()->getFragmentForURL() ); - $wgOut->addInlineScript( "redirectToFragment(\"$fragment\");" ); + $outputPage->addInlineScript( "redirectToFragment(\"$fragment\");" ); } // Add a tag - $wgOut->addLink( array( 'rel' => 'canonical', + $outputPage->addLink( array( 'rel' => 'canonical', 'href' => $this->getTitle()->getLocalURL() ) ); - // Tell $wgOut the user arrived at this article through a redirect - $wgOut->setRedirectedFrom( $this->mRedirectedFrom ); + // Tell the output object that the user arrived at this article through a redirect + $outputPage->setRedirectedFrom( $this->mRedirectedFrom ); return true; } @@ -864,7 +906,7 @@ class Article extends Page { // If it was reported from a trusted site, supply a backlink. if ( $wgRedirectSources && preg_match( $wgRedirectSources, $rdfrom ) ) { $redir = Linker::makeExternalLink( $rdfrom, $rdfrom ); - $wgOut->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) ); + $outputPage->addSubtitle( wfMessage( 'redirectedfrom' )->rawParams( $redir ) ); return true; } @@ -878,11 +920,9 @@ class Article extends Page { * [[MediaWiki:Talkpagetext]]. For Article::view(). */ public function showNamespaceHeader() { - global $wgOut; - if ( $this->getTitle()->isTalkPage() ) { if ( !wfMessage( 'talkpageheader' )->isDisabled() ) { - $wgOut->wrapWikiMsg( "
\n$1\n
", array( 'talkpageheader' ) ); + $this->getContext()->getOutput()->wrapWikiMsg( "
\n$1\n
", array( 'talkpageheader' ) ); } } } @@ -891,11 +931,9 @@ class Article extends Page { * Show the footer section of an ordinary page view */ public function showViewFooter() { - global $wgOut; - # check if we're displaying a [[User talk:x.x.x.x]] anonymous talk page if ( $this->getTitle()->getNamespace() == NS_USER_TALK && IP::isValid( $this->getTitle()->getText() ) ) { - $wgOut->addWikiMsg( 'anontalkpagetext' ); + $this->getContext()->getOutput()->addWikiMsg( 'anontalkpagetext' ); } # If we have been passed an &rcid= parameter, we want to give the user a @@ -912,33 +950,32 @@ class Article extends Page { * desired, does nothing. */ public function showPatrolFooter() { - global $wgOut, $wgRequest, $wgUser; - - $rcid = $wgRequest->getVal( 'rcid' ); + $request = $this->getContext()->getRequest(); + $outputPage = $this->getContext()->getOutput(); + $user = $this->getContext()->getUser(); + $rcid = $request->getVal( 'rcid' ); - if ( !$rcid || !$this->getTitle()->quickUserCan( 'patrol' ) ) { + if ( !$rcid || !$this->getTitle()->quickUserCan( 'patrol', $user ) ) { return; } - $token = $wgUser->getEditToken( $rcid ); - $wgOut->preventClickjacking(); + $token = $user->getEditToken( $rcid ); + $outputPage->preventClickjacking(); - $wgOut->addHTML( + $link = Linker::linkKnown( + $this->getTitle(), + wfMessage( 'markaspatrolledtext' )->escaped(), + array(), + array( + 'action' => 'markpatrolled', + 'rcid' => $rcid, + 'token' => $token, + ) + ); + + $outputPage->addHTML( "' ); } @@ -948,7 +985,8 @@ class Article extends Page { * namespace, show the default message text. To be called from Article::view(). */ public function showMissingArticle() { - global $wgOut, $wgRequest, $wgUser, $wgSend404Code; + global $wgSend404Code; + $outputPage = $this->getContext()->getOutput(); # 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 ) { @@ -958,13 +996,13 @@ class Article extends Page { $ip = User::isIP( $rootPart ); if ( !($user && $user->isLoggedIn()) && !$ip ) { # User does not exist - $wgOut->wrapWikiMsg( "
\n\$1\n
", + $outputPage->wrapWikiMsg( "
\n\$1\n
", array( 'userpage-userdoesnotexist-view', wfEscapeWikiText( $rootPart ) ) ); } elseif ( $user->isBlocked() ) { # Show log extract if the user is currently blocked LogEventsList::showLogExtract( - $wgOut, + $outputPage, 'block', - $user->getUserPage()->getPrefixedText(), + $user->getUserPage(), '', array( 'lim' => 1, @@ -981,7 +1019,7 @@ class Article extends Page { wfRunHooks( 'ShowMissingArticle', array( $this ) ); # Show delete and move logs - LogEventsList::showLogExtract( $wgOut, array( 'delete', 'move' ), $this->getTitle()->getPrefixedText(), '', + LogEventsList::showLogExtract( $outputPage, array( 'delete', 'move' ), $this->getTitle(), '', array( 'lim' => 10, 'conds' => array( "log_action != 'revision'" ), 'showIfEmpty' => false, @@ -991,7 +1029,7 @@ class Article extends Page { if ( !$this->mPage->hasViewableContent() && $wgSend404Code ) { // If there's no backing content, send a 404 Not Found // for better machine handling of broken links. - $wgRequest->response()->header( "HTTP/1.1 404 Not Found" ); + $this->getContext()->getRequest()->response()->header( "HTTP/1.1 404 Not Found" ); } $hookResult = wfRunHooks( 'BeforeDisplayNoArticleText', array( $this ) ); @@ -1003,56 +1041,50 @@ class Article extends Page { # Show error message $oldid = $this->getOldID(); if ( $oldid ) { - $text = wfMsgNoTrans( 'missing-article', - $this->getTitle()->getPrefixedText(), - wfMsgNoTrans( 'missingarticle-rev', $oldid ) ); + $text = wfMessage( 'missing-revision', $oldid )->plain(); } elseif ( $this->getTitle()->getNamespace() === NS_MEDIAWIKI ) { // Use the default message text $text = $this->getTitle()->getDefaultMessageText(); + } elseif ( $this->getTitle()->quickUserCan( 'create', $this->getContext()->getUser() ) + && $this->getTitle()->quickUserCan( 'edit', $this->getContext()->getUser() ) + ) { + $text = wfMessage( 'noarticletext' )->plain(); } else { - $createErrors = $this->getTitle()->getUserPermissionsErrors( 'create', $wgUser ); - $editErrors = $this->getTitle()->getUserPermissionsErrors( 'edit', $wgUser ); - $errors = array_merge( $createErrors, $editErrors ); - - if ( !count( $errors ) ) { - $text = wfMsgNoTrans( 'noarticletext' ); - } else { - $text = wfMsgNoTrans( 'noarticletext-nopermission' ); - } + $text = wfMessage( 'noarticletext-nopermission' )->plain(); } $text = "
\n$text\n
"; - $wgOut->addWikiText( $text ); + $outputPage->addWikiText( $text ); } /** * If the revision requested for view is deleted, check permissions. - * Send either an error message or a warning header to $wgOut. + * Send either an error message or a warning header to the output. * * @return boolean true if the view is allowed, false if not. */ public function showDeletedRevisionHeader() { - global $wgOut, $wgRequest; - if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) { // Not deleted return true; } + $outputPage = $this->getContext()->getOutput(); + $user = $this->getContext()->getUser(); // If the user is not allowed to see it... - if ( !$this->mRevision->userCan( Revision::DELETED_TEXT ) ) { - $wgOut->wrapWikiMsg( "\n", + if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) { + $outputPage->wrapWikiMsg( "\n", 'rev-deleted-text-permission' ); return false; // If the user needs to confirm that they want to see it... - } elseif ( $wgRequest->getInt( 'unhide' ) != 1 ) { + } elseif ( $this->getContext()->getRequest()->getInt( 'unhide' ) != 1 ) { # Give explanation and add a link to view the revision... $oldid = intval( $this->getOldID() ); $link = $this->getTitle()->getFullUrl( "oldid={$oldid}&unhide=1" ); $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ? 'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide'; - $wgOut->wrapWikiMsg( "\n", + $outputPage->wrapWikiMsg( "\n", array( $msg, $link ) ); return false; @@ -1060,7 +1092,7 @@ class Article extends Page { } else { $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ? 'rev-suppressed-text-view' : 'rev-deleted-text-view'; - $wgOut->wrapWikiMsg( "\n", $msg ); + $outputPage->wrapWikiMsg( "\n", $msg ); return true; } @@ -1072,30 +1104,36 @@ class Article extends Page { * Revision as of \; view current revision * \<- Previous version | Next Version -\> * - * @param $oldid String: revision ID of this article revision + * @param $oldid int: revision ID of this article revision */ public function setOldSubtitle( $oldid = 0 ) { - global $wgLang, $wgOut, $wgUser, $wgRequest; - if ( !wfRunHooks( 'DisplayOldSubtitle', array( &$this, &$oldid ) ) ) { return; } - $unhide = $wgRequest->getInt( 'unhide' ) == 1; + $unhide = $this->getContext()->getRequest()->getInt( 'unhide' ) == 1; # Cascade unhide param in links for easy deletion browsing $extraParams = array(); - if ( $wgRequest->getVal( 'unhide' ) ) { + if ( $unhide ) { $extraParams['unhide'] = 1; } - $revision = Revision::newFromId( $oldid ); + if ( $this->mRevision && $this->mRevision->getId() === $oldid ) { + $revision = $this->mRevision; + } else { + $revision = Revision::newFromId( $oldid ); + } + $timestamp = $revision->getTimestamp(); $current = ( $oldid == $this->mPage->getLatest() ); - $td = $wgLang->timeanddate( $timestamp, true ); - $tddate = $wgLang->date( $timestamp, true ); - $tdtime = $wgLang->time( $timestamp, true ); + $language = $this->getContext()->getLanguage(); + $user = $this->getContext()->getUser(); + + $td = $language->userTimeAndDate( $timestamp, $user ); + $tddate = $language->userDate( $timestamp, $user ); + $tdtime = $language->userTime( $timestamp, $user ); # Show user links if allowed to see them. If hidden, then show them only if requested... $userlinks = Linker::revUserTools( $revision, !$unhide ); @@ -1104,89 +1142,85 @@ class Article extends Page { ? 'revision-info-current' : 'revision-info'; - $wgOut->addSubtitle( "
" . wfMessage( $infomsg, + $outputPage = $this->getContext()->getOutput(); + $outputPage->addSubtitle( "
" . wfMessage( $infomsg, $td )->rawParams( $userlinks )->params( $revision->getID(), $tddate, $tdtime, $revision->getUser() )->parse() . "
" ); $lnk = $current - ? wfMsgHtml( 'currentrevisionlink' ) - : Linker::link( + ? wfMessage( 'currentrevisionlink' )->escaped() + : Linker::linkKnown( $this->getTitle(), - wfMsgHtml( 'currentrevisionlink' ), + wfMessage( 'currentrevisionlink' )->escaped(), array(), - $extraParams, - array( 'known', 'noclasses' ) + $extraParams ); $curdiff = $current - ? wfMsgHtml( 'diff' ) - : Linker::link( + ? wfMessage( 'diff' )->escaped() + : Linker::linkKnown( $this->getTitle(), - wfMsgHtml( 'diff' ), + wfMessage( 'diff' )->escaped(), array(), array( 'diff' => 'cur', 'oldid' => $oldid - ) + $extraParams, - array( 'known', 'noclasses' ) + ) + $extraParams ); $prev = $this->getTitle()->getPreviousRevisionID( $oldid ) ; $prevlink = $prev - ? Linker::link( + ? Linker::linkKnown( $this->getTitle(), - wfMsgHtml( 'previousrevision' ), + wfMessage( 'previousrevision' )->escaped(), array(), array( 'direction' => 'prev', 'oldid' => $oldid - ) + $extraParams, - array( 'known', 'noclasses' ) + ) + $extraParams ) - : wfMsgHtml( 'previousrevision' ); + : wfMessage( 'previousrevision' )->escaped(); $prevdiff = $prev - ? Linker::link( + ? Linker::linkKnown( $this->getTitle(), - wfMsgHtml( 'diff' ), + wfMessage( 'diff' )->escaped(), array(), array( 'diff' => 'prev', 'oldid' => $oldid - ) + $extraParams, - array( 'known', 'noclasses' ) + ) + $extraParams ) - : wfMsgHtml( 'diff' ); + : wfMessage( 'diff' )->escaped(); $nextlink = $current - ? wfMsgHtml( 'nextrevision' ) - : Linker::link( + ? wfMessage( 'nextrevision' )->escaped() + : Linker::linkKnown( $this->getTitle(), - wfMsgHtml( 'nextrevision' ), + wfMessage( 'nextrevision' )->escaped(), array(), array( 'direction' => 'next', 'oldid' => $oldid - ) + $extraParams, - array( 'known', 'noclasses' ) + ) + $extraParams ); $nextdiff = $current - ? wfMsgHtml( 'diff' ) - : Linker::link( + ? wfMessage( 'diff' )->escaped() + : Linker::linkKnown( $this->getTitle(), - wfMsgHtml( 'diff' ), + wfMessage( 'diff' )->escaped(), array(), array( 'diff' => 'next', 'oldid' => $oldid - ) + $extraParams, - array( 'known', 'noclasses' ) + ) + $extraParams ); - $cdel = Linker::getRevDeleteLink( $wgUser, $revision, $this->getTitle() ); + $cdel = Linker::getRevDeleteLink( $user, $revision, $this->getTitle() ); if ( $cdel !== '' ) { $cdel .= ' '; } - $wgOut->addSubtitle( "
" . $cdel . - wfMsgExt( 'revision-nav', array( 'escapenoentities', 'parsemag', 'replaceafter' ), - $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff ) . "
" ); + $outputPage->addSubtitle( "
" . $cdel . + wfMessage( 'revision-nav' )->rawParams( + $prevdiff, $prevlink, $lnk, $curdiff, $nextlink, $nextdiff + )->escaped() . "
" ); } /** @@ -1198,7 +1232,7 @@ class Article extends Page { * @return string containing HMTL with redirect link */ public function viewRedirect( $target, $appendSubtitle = true, $forceKnown = false ) { - global $wgOut, $wgStylePath; + global $wgStylePath; if ( !is_array( $target ) ) { $target = array( $target ); @@ -1208,7 +1242,8 @@ class Article extends Page { $imageDir = $lang->getDir(); if ( $appendSubtitle ) { - $wgOut->appendSubtitle( wfMsgHtml( 'redirectpagesub' ) ); + $out = $this->getContext()->getOutput(); + $out->addSubtitle( wfMessage( 'redirectpagesub' )->escaped() ); } // the loop prepends the arrow image before the link, so the first case needs to be outside @@ -1246,9 +1281,7 @@ class Article extends Page { * Handle action=render */ public function render() { - global $wgOut; - - $wgOut->setArticleBodyOnly( true ); + $this->getContext()->getOutput()->setArticleBodyOnly( true ); $this->view(); } @@ -1271,8 +1304,6 @@ class Article extends Page { * UI entry point for page deletion */ public function delete() { - global $wgOut, $wgRequest, $wgLang; - # This code desperately needs to be totally rewritten $title = $this->getTitle(); @@ -1290,48 +1321,54 @@ class Article extends Page { } # Better double-check that it hasn't been deleted yet! - $dbw = wfGetDB( DB_MASTER ); - $conds = $title->pageCond(); - $latest = $dbw->selectField( 'page', 'page_latest', $conds, __METHOD__ ); - if ( $latest === false ) { - $wgOut->setPageTitle( wfMessage( 'cannotdelete-title', $title->getPrefixedText() ) ); - $wgOut->wrapWikiMsg( "
\n$1\n
", + $this->mPage->loadPageData( 'fromdbmaster' ); + if ( !$this->mPage->exists() ) { + $deleteLogPage = new LogPage( 'delete' ); + $outputPage = $this->getContext()->getOutput(); + $outputPage->setPageTitle( wfMessage( 'cannotdelete-title', $title->getPrefixedText() ) ); + $outputPage->wrapWikiMsg( "
\n$1\n
", array( 'cannotdelete', wfEscapeWikiText( $title->getPrefixedText() ) ) ); - $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) ); + $outputPage->addHTML( + Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) + ); LogEventsList::showLogExtract( - $wgOut, + $outputPage, 'delete', - $title->getPrefixedText() + $title ); return; } - $deleteReasonList = $wgRequest->getText( 'wpDeleteReasonList', 'other' ); - $deleteReason = $wgRequest->getText( 'wpReason' ); + $request = $this->getContext()->getRequest(); + $deleteReasonList = $request->getText( 'wpDeleteReasonList', 'other' ); + $deleteReason = $request->getText( 'wpReason' ); if ( $deleteReasonList == 'other' ) { $reason = $deleteReason; } elseif ( $deleteReason != '' ) { // Entry from drop down menu + additional comment - $reason = $deleteReasonList . wfMsgForContent( 'colon-separator' ) . $deleteReason; + $colonseparator = wfMessage( 'colon-separator' )->inContentLanguage()->text(); + $reason = $deleteReasonList . $colonseparator . $deleteReason; } else { $reason = $deleteReasonList; } - if ( $wgRequest->wasPosted() && $user->matchEditToken( $wgRequest->getVal( 'wpEditToken' ), + if ( $request->wasPosted() && $user->matchEditToken( $request->getVal( 'wpEditToken' ), array( 'delete', $this->getTitle()->getPrefixedText() ) ) ) { # Flag to hide all contents of the archived revisions - $suppress = $wgRequest->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' ); + $suppress = $request->getVal( 'wpSuppress' ) && $user->isAllowed( 'suppressrevision' ); $this->doDelete( $reason, $suppress ); - if ( $wgRequest->getCheck( 'wpWatch' ) && $user->isLoggedIn() ) { - $this->doWatch(); - } elseif ( $title->userIsWatching() ) { - $this->doUnwatch(); + if ( $user->isLoggedIn() && $request->getCheck( 'wpWatch' ) != $user->isWatched( $title ) ) { + if ( $request->getCheck( 'wpWatch' ) ) { + WatchAction::doWatch( $title, $user ); + } else { + WatchAction::doUnwatch( $title, $user ); + } } return; @@ -1347,10 +1384,10 @@ class Article extends Page { if ( $hasHistory ) { $revisions = $this->mTitle->estimateRevisionCount(); // @todo FIXME: i18n issue/patchwork message - $wgOut->addHTML( '' . - wfMsgExt( 'historywarning', array( 'parseinline' ), $wgLang->formatNum( $revisions ) ) . - wfMsgHtml( 'word-separator' ) . Linker::link( $title, - wfMsgHtml( 'history' ), + $this->getContext()->getOutput()->addHTML( '' . + wfMessage( 'historywarning' )->numParams( $revisions )->parse() . + wfMessage( 'word-separator' )->plain() . Linker::linkKnown( $title, + wfMessage( 'history' )->escaped(), array( 'rel' => 'archives' ), array( 'action' => 'history' ) ) . '' @@ -1358,12 +1395,12 @@ class Article extends Page { if ( $this->mTitle->isBigDeletion() ) { global $wgDeleteRevisionsLimit; - $wgOut->wrapWikiMsg( "
\n$1\n
\n", - array( 'delete-warning-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ) ); + $this->getContext()->getOutput()->wrapWikiMsg( "
\n$1\n
\n", + array( 'delete-warning-toobig', $this->getContext()->getLanguage()->formatNum( $wgDeleteRevisionsLimit ) ) ); } } - return $this->confirmDelete( $reason ); + $this->confirmDelete( $reason ); } /** @@ -1372,16 +1409,15 @@ class Article extends Page { * @param $reason String: prefilled reason */ public function confirmDelete( $reason ) { - global $wgOut; - wfDebug( "Article::confirmDelete\n" ); - $wgOut->setPageTitle( wfMessage( 'delete-confirm', $this->getTitle()->getPrefixedText() ) ); - $wgOut->addBacklinkSubtitle( $this->getTitle() ); - $wgOut->setRobotPolicy( 'noindex,nofollow' ); - $wgOut->addWikiMsg( 'confirmdeletetext' ); + $outputPage = $this->getContext()->getOutput(); + $outputPage->setPageTitle( wfMessage( 'delete-confirm', $this->getTitle()->getPrefixedText() ) ); + $outputPage->addBacklinkSubtitle( $this->getTitle() ); + $outputPage->setRobotPolicy( 'noindex,nofollow' ); + $outputPage->addWikiMsg( 'confirmdeletetext' ); - wfRunHooks( 'ArticleConfirmDelete', array( $this, $wgOut, &$reason ) ); + wfRunHooks( 'ArticleConfirmDelete', array( $this, $outputPage, &$reason ) ); $user = $this->getContext()->getUser(); @@ -1389,33 +1425,33 @@ class Article extends Page { $suppress = " " . - Xml::checkLabel( wfMsg( 'revdelete-suppress' ), + Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(), 'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '4' ) ) . " "; } else { $suppress = ''; } - $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $this->getTitle()->userIsWatching(); + $checkWatch = $user->getBoolOption( 'watchdeletion' ) || $user->isWatched( $this->getTitle() ); $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getTitle()->getLocalURL( 'action=delete' ), 'id' => 'deleteconfirm' ) ) . Xml::openElement( 'fieldset', array( 'id' => 'mw-delete-table' ) ) . - Xml::tags( 'legend', null, wfMsgExt( 'delete-legend', array( 'parsemag', 'escapenoentities' ) ) ) . + Xml::tags( 'legend', null, wfMessage( 'delete-legend' )->escaped() ) . Xml::openElement( 'table', array( 'id' => 'mw-deleteconfirm-table' ) ) . " " . - Xml::label( wfMsg( 'deletecomment' ), 'wpDeleteReasonList' ) . + Xml::label( wfMessage( 'deletecomment' )->text(), 'wpDeleteReasonList' ) . " " . Xml::listDropDown( 'wpDeleteReasonList', - wfMsgForContent( 'deletereason-dropdown' ), - wfMsgForContent( 'deletereasonotherlist' ), '', 'wpReasonDropDown', 1 ) . + wfMessage( 'deletereason-dropdown' )->inContentLanguage()->text(), + wfMessage( 'deletereasonotherlist' )->inContentLanguage()->text(), '', 'wpReasonDropDown', 1 ) . " " . - Xml::label( wfMsg( 'deleteotherreason' ), 'wpReason' ) . + Xml::label( wfMessage( 'deleteotherreason' )->text(), 'wpReason' ) . " " . Html::input( 'wpReason', $reason, 'text', array( @@ -1434,7 +1470,7 @@ class Article extends Page { " . - Xml::checkLabel( wfMsg( 'watchthis' ), + Xml::checkLabel( wfMessage( 'watchthis' )->text(), 'wpWatch', 'wpWatch', $checkWatch, array( 'tabindex' => '3' ) ) . " "; @@ -1445,7 +1481,7 @@ class Article extends Page { " . - Xml::submitButton( wfMsg( 'deletepage' ), + Xml::submitButton( wfMessage( 'deletepage' )->text(), array( 'name' => 'wpConfirmB', 'id' => 'wpConfirmB', 'tabindex' => '5' ) ) . " " . @@ -1458,17 +1494,19 @@ class Article extends Page { $title = Title::makeTitle( NS_MEDIAWIKI, 'Deletereason-dropdown' ); $link = Linker::link( $title, - wfMsgHtml( 'delete-edit-reasonlist' ), + wfMessage( 'delete-edit-reasonlist' )->escaped(), array(), array( 'action' => 'edit' ) ); $form .= '

' . $link . '

'; } - $wgOut->addHTML( $form ); - $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) ); - LogEventsList::showLogExtract( $wgOut, 'delete', - $this->getTitle()->getPrefixedText() + $outputPage->addHTML( $form ); + + $deleteLogPage = new LogPage( 'delete' ); + $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) ); + LogEventsList::showLogExtract( $outputPage, 'delete', + $this->getTitle() ); } @@ -1478,34 +1516,35 @@ class Article extends Page { * @param $suppress bool */ public function doDelete( $reason, $suppress = false ) { - global $wgOut; - $error = ''; - if ( $this->mPage->doDeleteArticle( $reason, $suppress, 0, true, $error ) ) { + $outputPage = $this->getContext()->getOutput(); + $status = $this->mPage->doDeleteArticleReal( $reason, $suppress, 0, true, $error ); + if ( $status->isGood() ) { $deleted = $this->getTitle()->getPrefixedText(); - $wgOut->setPageTitle( wfMessage( 'actioncomplete' ) ); - $wgOut->setRobotPolicy( 'noindex,nofollow' ); + $outputPage->setPageTitle( wfMessage( 'actioncomplete' ) ); + $outputPage->setRobotPolicy( 'noindex,nofollow' ); - $loglink = '[[Special:Log/delete|' . wfMsgNoTrans( 'deletionlog' ) . ']]'; + $loglink = '[[Special:Log/delete|' . wfMessage( 'deletionlog' )->text() . ']]'; - $wgOut->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink ); - $wgOut->returnToMain( false ); + $outputPage->addWikiMsg( 'deletedtext', wfEscapeWikiText( $deleted ), $loglink ); + $outputPage->returnToMain( false ); } else { - $wgOut->setPageTitle( wfMessage( 'cannotdelete-title', $this->getTitle()->getPrefixedText() ) ); + $outputPage->setPageTitle( wfMessage( 'cannotdelete-title', $this->getTitle()->getPrefixedText() ) ); if ( $error == '' ) { - $wgOut->wrapWikiMsg( "
\n$1\n
", - array( 'cannotdelete', wfEscapeWikiText( $this->getTitle()->getPrefixedText() ) ) + $outputPage->addWikiText( + "
\n" . $status->getWikiText() . "\n
" ); - $wgOut->addHTML( Xml::element( 'h2', null, LogPage::logName( 'delete' ) ) ); + $deleteLogPage = new LogPage( 'delete' ); + $outputPage->addHTML( Xml::element( 'h2', null, $deleteLogPage->getName()->text() ) ); LogEventsList::showLogExtract( - $wgOut, + $outputPage, 'delete', - $this->getTitle()->getPrefixedText() + $this->getTitle() ); } else { - $wgOut->addHTML( $error ); + $outputPage->addHTML( $error ); } } } @@ -1578,22 +1617,22 @@ class Article extends Page { * @return ParserOutput or false if the given revsion ID is not found */ public function getParserOutput( $oldid = null, User $user = null ) { - global $wgUser; - - $user = is_null( $user ) ? $wgUser : $user; - $parserOptions = $this->mPage->makeParserOptions( $user ); + if ( $user === null ) { + $parserOptions = $this->getParserOptions(); + } else { + $parserOptions = $this->mPage->makeParserOptions( $user ); + } return $this->mPage->getParserOutput( $parserOptions, $oldid ); } /** * Get parser options suitable for rendering the primary article wikitext - * @return ParserOptions|false + * @return ParserOptions */ public function getParserOptions() { - global $wgUser; if ( !$this->mParserOptions ) { - $this->mParserOptions = $this->mPage->makeParserOptions( $wgUser ); + $this->mParserOptions = $this->mPage->makeParserOptions( $this->getContext() ); } // Clone to allow modifications of the return value without affecting cache return clone $this->mParserOptions; @@ -1645,6 +1684,7 @@ class Article extends Page { /** * Handle action=purge * @deprecated since 1.19 + * @return Action|bool|null false if the action is disabled, null if it is not recognised */ public function purge() { return Action::factory( 'purge', $this )->show(); @@ -1679,7 +1719,7 @@ class Article extends Page { } /** - * Add this page to $wgUser's watchlist + * Add this page to the current user's watchlist * * This is safe to be called multiple times * @@ -1687,9 +1727,8 @@ class Article extends Page { * @deprecated since 1.18 */ public function doWatch() { - global $wgUser; wfDeprecated( __METHOD__, '1.18' ); - return WatchAction::doWatch( $this->getTitle(), $wgUser ); + return WatchAction::doWatch( $this->getTitle(), $this->getContext()->getUser() ); } /** @@ -1708,24 +1747,21 @@ class Article extends Page { * @deprecated since 1.18 */ public function doUnwatch() { - global $wgUser; wfDeprecated( __METHOD__, '1.18' ); - return WatchAction::doUnwatch( $this->getTitle(), $wgUser ); + return WatchAction::doUnwatch( $this->getTitle(), $this->getContext()->getUser() ); } /** * Output a redirect back to the article. * This is typically used after an edit. * - * @deprecated in 1.18; call $wgOut->redirect() directly + * @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 */ public function doRedirect( $noRedir = false, $sectionAnchor = '', $extraQuery = '' ) { wfDeprecated( __METHOD__, '1.18' ); - global $wgOut; - if ( $noRedir ) { $query = 'redirect=no'; if ( $extraQuery ) @@ -1734,7 +1770,7 @@ class Article extends Page { $query = $extraQuery; } - $wgOut->redirect( $this->getTitle()->getFullURL( $query ) . $sectionAnchor ); + $this->getContext()->getOutput()->redirect( $this->getTitle()->getFullURL( $query ) . $sectionAnchor ); } /** @@ -1776,6 +1812,7 @@ class Article extends Page { * * @param $fname String Name of called method * @param $args Array Arguments to the method + * @return mixed */ public function __call( $fname, $args ) { if ( is_callable( array( $this->mPage, $fname ) ) ) { @@ -1832,8 +1869,7 @@ class Article extends Page { * @return array */ public function doRollback( $fromP, $summary, $token, $bot, &$resultDetails, User $user = null ) { - global $wgUser; - $user = is_null( $user ) ? $wgUser : $user; + $user = is_null( $user ) ? $this->getContext()->getUser() : $user; return $this->mPage->doRollback( $fromP, $summary, $token, $bot, $resultDetails, $user ); } @@ -1846,8 +1882,7 @@ class Article extends Page { * @return array */ public function commitRollback( $fromP, $summary, $bot, &$resultDetails, User $guser = null ) { - global $wgUser; - $guser = is_null( $guser ) ? $wgUser : $guser; + $guser = is_null( $guser ) ? $this->getContext()->getUser() : $guser; return $this->mPage->commitRollback( $fromP, $summary, $bot, $resultDetails, $guser ); } diff --git a/includes/AuthPlugin.php b/includes/AuthPlugin.php index e8bab859..2e42439c 100644 --- a/includes/AuthPlugin.php +++ b/includes/AuthPlugin.php @@ -34,6 +34,12 @@ * someone logs in who can be authenticated externally. */ class AuthPlugin { + + /** + * @var string + */ + protected $domain; + /** * Check whether there exists a user account with the given name. * The name will be normalized to MediaWiki's requirements, so @@ -83,6 +89,19 @@ class AuthPlugin { $this->domain = $domain; } + /** + * Get the user's domain + * + * @return string + */ + public function getDomain() { + if ( isset( $this->domain ) ) { + return $this->domain; + } else { + return 'invaliddomain'; + } + } + /** * Check to see if the specific domain is a valid domain. * @@ -103,6 +122,7 @@ class AuthPlugin { * forget the & on your function declaration. * * @param $user User object + * @return bool */ public function updateUser( &$user ) { # Override this and do something @@ -256,6 +276,8 @@ class AuthPlugin { /** * If you want to munge the case of an account name before the final * check, now is your chance. + * @param $username string + * @return string */ public function getCanonicalName( $username ) { return $username; diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 93fac45f..d3a2c548 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -2,6 +2,21 @@ /** * This defines autoloading handler for whole MediaWiki framework * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file */ @@ -27,6 +42,7 @@ $wgAutoloadLocalClasses = array( 'BadTitleError' => 'includes/Exception.php', 'BaseTemplate' => 'includes/SkinTemplate.php', 'Block' => 'includes/Block.php', + 'CacheHelper' => 'includes/CacheHelper.php', 'Category' => 'includes/Category.php', 'Categoryfinder' => 'includes/Categoryfinder.php', 'CategoryPage' => 'includes/CategoryPage.php', @@ -53,9 +69,11 @@ $wgAutoloadLocalClasses = array( 'CurlHttpRequest' => 'includes/HttpFunctions.php', 'DeferrableUpdate' => 'includes/DeferredUpdates.php', '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', 'Dump7ZipOutput' => 'includes/Export.php', @@ -92,6 +110,7 @@ $wgAutoloadLocalClasses = array( 'FormAction' => 'includes/Action.php', 'FormOptions' => 'includes/FormOptions.php', 'FormSpecialPage' => 'includes/SpecialPage.php', + 'GitInfo' => 'includes/GitInfo.php', 'HashtableReplacer' => 'includes/StringUtils.php', 'HistoryBlob' => 'includes/HistoryBlob.php', 'HistoryBlobCurStub' => 'includes/HistoryBlob.php', @@ -117,7 +136,10 @@ $wgAutoloadLocalClasses = array( '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', @@ -140,6 +162,7 @@ $wgAutoloadLocalClasses = array( 'Linker' => 'includes/Linker.php', 'LinkFilter' => 'includes/LinkFilter.php', 'LinksUpdate' => 'includes/LinksUpdate.php', + 'LinksDeletionUpdate' => 'includes/LinksUpdate.php', 'LocalisationCache' => 'includes/LocalisationCache.php', 'LocalisationCache_BulkLoad' => 'includes/LocalisationCache.php', 'MagicWord' => 'includes/MagicWord.php', @@ -153,6 +176,7 @@ $wgAutoloadLocalClasses = array( 'MWException' => 'includes/Exception.php', 'MWExceptionHandler' => 'includes/Exception.php', 'MWFunction' => 'includes/MWFunction.php', + 'MWHookException' => 'includes/Hooks.php', 'MWHttpRequest' => 'includes/HttpFunctions.php', 'MWInit' => 'includes/Init.php', 'MWNamespace' => 'includes/Namespace.php', @@ -185,12 +209,16 @@ $wgAutoloadLocalClasses = array( 'ReplacementArray' => 'includes/StringUtils.php', 'Replacer' => 'includes/StringUtils.php', 'ReverseChronologicalPager' => 'includes/Pager.php', + 'RevisionItem' => 'includes/RevisionList.php', 'RevisionItemBase' => 'includes/RevisionList.php', 'RevisionListBase' => 'includes/RevisionList.php', 'Revision' => 'includes/Revision.php', 'RevisionList' => 'includes/RevisionList.php', 'RSSFeed' => 'includes/Feed.php', 'Sanitizer' => 'includes/Sanitizer.php', + 'DataUpdate' => 'includes/DataUpdate.php', + 'SqlDataUpdate' => 'includes/SqlDataUpdate.php', + 'ScopedPHPTimeout' => 'includes/ScopedPHPTimeout.php', 'SiteConfiguration' => 'includes/SiteConfiguration.php', 'SiteStats' => 'includes/SiteStats.php', 'SiteStatsInit' => 'includes/SiteStats.php', @@ -217,16 +245,20 @@ $wgAutoloadLocalClasses = array( 'StubObject' => 'includes/StubObject.php', 'StubUserLang' => 'includes/StubObject.php', 'TablePager' => 'includes/Pager.php', + 'MWTimestamp' => 'includes/Timestamp.php', 'Title' => 'includes/Title.php', 'TitleArray' => 'includes/TitleArray.php', 'TitleArrayFromResult' => 'includes/TitleArray.php', 'ThrottledError' => 'includes/Exception.php', 'UnlistedSpecialPage' => 'includes/SpecialPage.php', + 'UploadSourceAdapter' => 'includes/Import.php', 'UppercaseCollation' => 'includes/Collation.php', 'User' => 'includes/User.php', 'UserArray' => 'includes/UserArray.php', 'UserArrayFromResult' => 'includes/UserArray.php', 'UserBlockedError' => 'includes/Exception.php', + 'UserNotLoggedIn' => 'includes/Exception.php', + 'UserCache' => 'includes/cache/UserCache.php', 'UserMailer' => 'includes/UserMailer.php', 'UserRightsProxy' => 'includes/UserRightsProxy.php', 'ViewCountUpdate' => 'includes/ViewCountUpdate.php', @@ -249,12 +281,15 @@ $wgAutoloadLocalClasses = array( 'Xml' => 'includes/Xml.php', 'XmlDumpWriter' => 'includes/Export.php', 'XmlJsCode' => 'includes/Xml.php', + 'XMLReader2' => 'includes/Import.php', 'XmlSelect' => 'includes/Xml.php', 'XmlTypeCheck' => 'includes/XmlTypeCheck.php', 'ZhClient' => 'includes/ZhClient.php', 'ZipDirectoryReader' => 'includes/ZipDirectoryReader.php', + 'ZipDirectoryReaderError' => 'includes/ZipDirectoryReader.php', # includes/actions + 'CachedAction' => 'includes/actions/CachedAction.php', 'CreditsAction' => 'includes/actions/CreditsAction.php', 'DeleteAction' => 'includes/actions/DeleteAction.php', 'EditAction' => 'includes/actions/EditAction.php', @@ -310,6 +345,7 @@ $wgAutoloadLocalClasses = array( 'ApiMain' => 'includes/api/ApiMain.php', 'ApiMove' => 'includes/api/ApiMove.php', 'ApiOpenSearch' => 'includes/api/ApiOpenSearch.php', + 'ApiOptions' => 'includes/api/ApiOptions.php', 'ApiPageSet' => 'includes/api/ApiPageSet.php', 'ApiParamInfo' => 'includes/api/ApiParamInfo.php', 'ApiParse' => 'includes/api/ApiParse.php', @@ -318,10 +354,10 @@ $wgAutoloadLocalClasses = array( 'ApiPurge' => 'includes/api/ApiPurge.php', 'ApiQuery' => 'includes/api/ApiQuery.php', 'ApiQueryAllCategories' => 'includes/api/ApiQueryAllCategories.php', - 'ApiQueryAllimages' => 'includes/api/ApiQueryAllimages.php', + 'ApiQueryAllImages' => 'includes/api/ApiQueryAllImages.php', 'ApiQueryAllLinks' => 'includes/api/ApiQueryAllLinks.php', - 'ApiQueryAllmessages' => 'includes/api/ApiQueryAllmessages.php', - 'ApiQueryAllpages' => 'includes/api/ApiQueryAllpages.php', + 'ApiQueryAllMessages' => 'includes/api/ApiQueryAllMessages.php', + 'ApiQueryAllPages' => 'includes/api/ApiQueryAllPages.php', 'ApiQueryAllUsers' => 'includes/api/ApiQueryAllUsers.php', 'ApiQueryBacklinks' => 'includes/api/ApiQueryBacklinks.php', 'ApiQueryBase' => 'includes/api/ApiQueryBase.php', @@ -363,11 +399,14 @@ $wgAutoloadLocalClasses = array( 'ApiResult' => 'includes/api/ApiResult.php', 'ApiRollback' => 'includes/api/ApiRollback.php', 'ApiRsd' => 'includes/api/ApiRsd.php', + 'ApiSetNotificationTimestamp' => 'includes/api/ApiSetNotificationTimestamp.php', + 'ApiTokens' => 'includes/api/ApiTokens.php', 'ApiUnblock' => 'includes/api/ApiUnblock.php', 'ApiUndelete' => 'includes/api/ApiUndelete.php', 'ApiUpload' => 'includes/api/ApiUpload.php', 'ApiUserrights' => 'includes/api/ApiUserrights.php', 'ApiWatch' => 'includes/api/ApiWatch.php', + 'UsageException' => 'includes/api/ApiMain.php', # includes/cache 'CacheDependency' => 'includes/cache/CacheDependency.php', @@ -384,19 +423,21 @@ $wgAutoloadLocalClasses = array( 'LinkCache' => 'includes/cache/LinkCache.php', 'MessageCache' => 'includes/cache/MessageCache.php', 'ObjectFileCache' => 'includes/cache/ObjectFileCache.php', + 'ProcessCacheLRU' => 'includes/cache/ProcessCacheLRU.php', 'ResourceFileCache' => 'includes/cache/ResourceFileCache.php', 'SquidUpdate' => 'includes/cache/SquidUpdate.php', 'TitleDependency' => 'includes/cache/CacheDependency.php', 'TitleListDependency' => 'includes/cache/CacheDependency.php', - 'UsageException' => 'includes/api/ApiMain.php', - # includes/context 'ContextSource' => 'includes/context/ContextSource.php', 'DerivativeContext' => 'includes/context/DerivativeContext.php', 'IContextSource' => 'includes/context/IContextSource.php', 'RequestContext' => 'includes/context/RequestContext.php', + # includes/dao + 'IDBAccessObject' => 'includes/dao/IDBAccessObject.php', + # includes/db 'Blob' => 'includes/db/DatabaseUtility.php', 'ChronologyProtector' => 'includes/db/LBFactory.php', @@ -411,9 +452,12 @@ $wgAutoloadLocalClasses = array( 'DatabaseSqlite' => 'includes/db/DatabaseSqlite.php', 'DatabaseSqliteStandalone' => 'includes/db/DatabaseSqlite.php', 'DatabaseType' => 'includes/db/Database.php', + 'DBAccessError' => 'includes/db/LBFactory.php', 'DBConnectionError' => 'includes/db/DatabaseError.php', 'DBError' => 'includes/db/DatabaseError.php', 'DBObject' => 'includes/db/DatabaseUtility.php', + 'IORMRow' => 'includes/db/IORMRow.php', + 'IORMTable' => 'includes/db/IORMTable.php', 'DBMasterPos' => 'includes/db/DatabaseUtility.php', 'DBQueryError' => 'includes/db/DatabaseError.php', 'DBUnexpectedError' => 'includes/db/DatabaseError.php', @@ -421,7 +465,10 @@ $wgAutoloadLocalClasses = array( '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', 'LBFactory_Simple' => 'includes/db/LBFactory.php', 'LBFactory_Single' => 'includes/db/LBFactory_Single.php', @@ -431,12 +478,20 @@ $wgAutoloadLocalClasses = array( 'LoadMonitor' => 'includes/db/LoadMonitor.php', 'LoadMonitor_MySQL' => 'includes/db/LoadMonitor.php', 'LoadMonitor_Null' => 'includes/db/LoadMonitor.php', + 'MssqlField' => 'includes/db/DatabaseMssql.php', + 'MssqlResult' => 'includes/db/DatabaseMssql.php', 'MySQLField' => 'includes/db/DatabaseMysql.php', 'MySQLMasterPos' => 'includes/db/DatabaseMysql.php', 'ORAField' => 'includes/db/DatabaseOracle.php', 'ORAResult' => 'includes/db/DatabaseOracle.php', + 'ORMIterator' => 'includes/db/ORMIterator.php', + 'ORMResult' => 'includes/db/ORMResult.php', + 'ORMRow' => 'includes/db/ORMRow.php', + 'ORMTable' => 'includes/db/ORMTable.php', 'PostgresField' => 'includes/db/DatabasePostgres.php', + 'PostgresTransactionState' => 'includes/db/DatabasePostgres.php', 'ResultWrapper' => 'includes/db/DatabaseUtility.php', + 'SavepointPostgres' => 'includes/db/DatabasePostgres.php', 'SQLiteField' => 'includes/db/DatabaseSqlite.php', # includes/debug @@ -466,6 +521,50 @@ $wgAutoloadLocalClasses = array( 'ExternalUser_MediaWiki' => 'includes/extauth/MediaWiki.php', 'ExternalUser_vB' => 'includes/extauth/vB.php', + # includes/filebackend + 'FileBackendGroup' => 'includes/filebackend/FileBackendGroup.php', + 'FileBackend' => 'includes/filebackend/FileBackend.php', + 'FileBackendStore' => 'includes/filebackend/FileBackendStore.php', + 'FileBackendStoreShardListIterator' => 'includes/filebackend/FileBackendStore.php', + 'FileBackendStoreShardDirIterator' => 'includes/filebackend/FileBackendStore.php', + 'FileBackendStoreShardFileIterator' => 'includes/filebackend/FileBackendStore.php', + 'FileBackendMultiWrite' => 'includes/filebackend/FileBackendMultiWrite.php', + 'FileBackendStoreOpHandle' => 'includes/filebackend/FileBackendStore.php', + 'FSFile' => 'includes/filebackend/FSFile.php', + 'FSFileBackend' => 'includes/filebackend/FSFileBackend.php', + 'FSFileBackendList' => 'includes/filebackend/FSFileBackend.php', + 'FSFileBackendDirList' => 'includes/filebackend/FSFileBackend.php', + 'FSFileBackendFileList' => 'includes/filebackend/FSFileBackend.php', + 'FSFileOpHandle' => 'includes/filebackend/FSFileBackend.php', + 'SwiftFileBackend' => 'includes/filebackend/SwiftFileBackend.php', + 'SwiftFileBackendList' => 'includes/filebackend/SwiftFileBackend.php', + 'SwiftFileBackendDirList' => 'includes/filebackend/SwiftFileBackend.php', + 'SwiftFileBackendFileList' => 'includes/filebackend/SwiftFileBackend.php', + 'SwiftFileOpHandle' => 'includes/filebackend/SwiftFileBackend.php', + 'TempFSFile' => 'includes/filebackend/TempFSFile.php', + 'FileJournal' => 'includes/filebackend/filejournal/FileJournal.php', + 'DBFileJournal' => 'includes/filebackend/filejournal/DBFileJournal.php', + 'NullFileJournal' => 'includes/filebackend/filejournal/FileJournal.php', + 'LockManagerGroup' => 'includes/filebackend/lockmanager/LockManagerGroup.php', + 'LockManager' => 'includes/filebackend/lockmanager/LockManager.php', + 'ScopedLock' => 'includes/filebackend/lockmanager/LockManager.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', + 'NullLockManager' => 'includes/filebackend/lockmanager/LockManager.php', + 'FileOp' => 'includes/filebackend/FileOp.php', + 'FileOpBatch' => 'includes/filebackend/FileOpBatch.php', + 'StoreFileOp' => 'includes/filebackend/FileOp.php', + 'CopyFileOp' => 'includes/filebackend/FileOp.php', + 'MoveFileOp' => 'includes/filebackend/FileOp.php', + 'DeleteFileOp' => 'includes/filebackend/FileOp.php', + 'ConcatenateFileOp' => 'includes/filebackend/FileOp.php', + 'CreateFileOp' => 'includes/filebackend/FileOp.php', + 'NullFileOp' => 'includes/filebackend/FileOp.php', + # includes/filerepo 'FileRepo' => 'includes/filerepo/FileRepo.php', 'FileRepoStatus' => 'includes/filerepo/FileRepoStatus.php', @@ -476,6 +575,7 @@ $wgAutoloadLocalClasses = array( 'LocalRepo' => 'includes/filerepo/LocalRepo.php', 'NullRepo' => 'includes/filerepo/NullRepo.php', 'RepoGroup' => 'includes/filerepo/RepoGroup.php', + 'TempFileRepo' => 'includes/filerepo/FileRepo.php', # includes/filerepo/file 'ArchivedFile' => 'includes/filerepo/file/ArchivedFile.php', @@ -488,36 +588,6 @@ $wgAutoloadLocalClasses = array( 'LocalFileRestoreBatch' => 'includes/filerepo/file/LocalFile.php', 'OldLocalFile' => 'includes/filerepo/file/OldLocalFile.php', 'UnregisteredLocalFile' => 'includes/filerepo/file/UnregisteredLocalFile.php', - 'FSFile' => 'includes/filerepo/backend/FSFile.php', - 'TempFSFile' => 'includes/filerepo/backend/TempFSFile.php', - - # includes/filerepo/backend - 'FileBackendGroup' => 'includes/filerepo/backend/FileBackendGroup.php', - 'FileBackend' => 'includes/filerepo/backend/FileBackend.php', - 'FileBackendStore' => 'includes/filerepo/backend/FileBackend.php', - 'FileBackendMultiWrite' => 'includes/filerepo/backend/FileBackendMultiWrite.php', - 'FileBackendStoreShardListIterator' => 'includes/filerepo/backend/FileBackend.php', - 'FSFileBackend' => 'includes/filerepo/backend/FSFileBackend.php', - 'FSFileBackendFileList' => 'includes/filerepo/backend/FSFileBackend.php', - 'SwiftFileBackend' => 'includes/filerepo/backend/SwiftFileBackend.php', - 'SwiftFileBackendFileList' => 'includes/filerepo/backend/SwiftFileBackend.php', - 'LockManagerGroup' => 'includes/filerepo/backend/lockmanager/LockManagerGroup.php', - 'LockManager' => 'includes/filerepo/backend/lockmanager/LockManager.php', - 'ScopedLock' => 'includes/filerepo/backend/lockmanager/LockManager.php', - 'FSLockManager' => 'includes/filerepo/backend/lockmanager/FSLockManager.php', - 'DBLockManager' => 'includes/filerepo/backend/lockmanager/DBLockManager.php', - 'LSLockManager' => 'includes/filerepo/backend/lockmanager/LSLockManager.php', - 'MySqlLockManager'=> 'includes/filerepo/backend/lockmanager/DBLockManager.php', - 'NullLockManager' => 'includes/filerepo/backend/lockmanager/LockManager.php', - 'FileOp' => 'includes/filerepo/backend/FileOp.php', - 'FileOpScopedPHPTimeout' => 'includes/filerepo/backend/FileOp.php', - 'StoreFileOp' => 'includes/filerepo/backend/FileOp.php', - 'CopyFileOp' => 'includes/filerepo/backend/FileOp.php', - 'MoveFileOp' => 'includes/filerepo/backend/FileOp.php', - 'DeleteFileOp' => 'includes/filerepo/backend/FileOp.php', - 'ConcatenateFileOp' => 'includes/filerepo/backend/FileOp.php', - 'CreateFileOp' => 'includes/filerepo/backend/FileOp.php', - 'NullFileOp' => 'includes/filerepo/backend/FileOp.php', # includes/installer 'CliInstaller' => 'includes/installer/CliInstaller.php', @@ -563,7 +633,7 @@ $wgAutoloadLocalClasses = array( 'DoubleRedirectJob' => 'includes/job/DoubleRedirectJob.php', 'EmaillingJob' => 'includes/job/EmaillingJob.php', 'EnotifNotifyJob' => 'includes/job/EnotifNotifyJob.php', - 'Job' => 'includes/job/JobQueue.php', + 'Job' => 'includes/job/Job.php', 'RefreshLinksJob' => 'includes/job/RefreshLinksJob.php', 'RefreshLinksJob2' => 'includes/job/RefreshLinksJob.php', 'UploadFromUrlJob' => 'includes/job/UploadFromUrlJob.php', @@ -575,13 +645,19 @@ $wgAutoloadLocalClasses = array( # includes/libs 'CSSJanus' => 'includes/libs/CSSJanus.php', + 'CSSJanus_Tokenizer' => 'includes/libs/CSSJanus.php', 'CSSMin' => 'includes/libs/CSSMin.php', + 'GenericArrayObject' => 'includes/libs/GenericArrayObject.php', 'HttpStatus' => 'includes/libs/HttpStatus.php', 'IEContentAnalyzer' => 'includes/libs/IEContentAnalyzer.php', 'IEUrlExtension' => 'includes/libs/IEUrlExtension.php', 'JavaScriptMinifier' => 'includes/libs/JavaScriptMinifier.php', + 'JSCompilerContext' => 'includes/libs/jsminplus.php', 'JSMinPlus' => 'includes/libs/jsminplus.php', + 'JSNode' => 'includes/libs/jsminplus.php', 'JSParser' => 'includes/libs/jsminplus.php', + 'JSToken' => 'includes/libs/jsminplus.php', + 'JSTokenizer' => 'includes/libs/jsminplus.php', # includes/logging 'DatabaseLogEntry' => 'includes/logging/LogEntry.php', @@ -613,17 +689,18 @@ $wgAutoloadLocalClasses = array( 'FormatMetadata' => 'includes/media/FormatMetadata.php', 'GIFHandler' => 'includes/media/GIF.php', 'GIFMetadataExtractor' => 'includes/media/GIFMetadataExtractor.php', - 'ImageHandler' => 'includes/media/Generic.php', + 'ImageHandler' => 'includes/media/ImageHandler.php', 'IPTC' => 'includes/media/IPTC.php', 'JpegHandler' => 'includes/media/Jpeg.php', 'JpegMetadataExtractor' => 'includes/media/JpegMetadataExtractor.php', - 'MediaHandler' => 'includes/media/Generic.php', + 'MediaHandler' => 'includes/media/MediaHandler.php', 'MediaTransformError' => 'includes/media/MediaTransformOutput.php', 'MediaTransformOutput' => 'includes/media/MediaTransformOutput.php', 'PNGHandler' => 'includes/media/PNG.php', 'PNGMetadataExtractor' => 'includes/media/PNGMetadataExtractor.php', 'SvgHandler' => 'includes/media/SVG.php', 'SVGMetadataExtractor' => 'includes/media/SVGMetadataExtractor.php', + 'SVGReader' => 'includes/media/SVGMetadataExtractor.php', 'ThumbnailImage' => 'includes/media/MediaTransformOutput.php', 'TiffHandler' => 'includes/media/Tiff.php', 'TransformParameterError' => 'includes/media/MediaTransformOutput.php', @@ -645,16 +722,20 @@ $wgAutoloadLocalClasses = array( 'HashBagOStuff' => 'includes/objectcache/HashBagOStuff.php', 'MediaWikiBagOStuff' => 'includes/objectcache/SqlBagOStuff.php', 'MemCachedClientforWiki' => 'includes/objectcache/MemcachedClient.php', + 'MemcachedBagOStuff' => 'includes/objectcache/MemcachedBagOStuff.php', + 'MemcachedPeclBagOStuff' => 'includes/objectcache/MemcachedPeclBagOStuff.php', 'MemcachedPhpBagOStuff' => 'includes/objectcache/MemcachedPhpBagOStuff.php', 'MultiWriteBagOStuff' => 'includes/objectcache/MultiWriteBagOStuff.php', 'MWMemcached' => 'includes/objectcache/MemcachedClient.php', 'ObjectCache' => 'includes/objectcache/ObjectCache.php', + 'ObjectCacheSessionHandler' => 'includes/objectcache/ObjectCacheSessionHandler.php', + 'RedisBagOStuff' => 'includes/objectcache/RedisBagOStuff.php', 'SqlBagOStuff' => 'includes/objectcache/SqlBagOStuff.php', 'WinCacheBagOStuff' => 'includes/objectcache/WinCacheBagOStuff.php', 'XCacheBagOStuff' => 'includes/objectcache/XCacheBagOStuff.php', # includes/parser - 'CacheTime' => 'includes/parser/ParserOutput.php', + 'CacheTime' => 'includes/parser/CacheTime.php', 'CoreLinkFunctions' => 'includes/parser/CoreLinkFunctions.php', 'CoreParserFunctions' => 'includes/parser/CoreParserFunctions.php', 'CoreTagHooks' => 'includes/parser/CoreTagHooks.php', @@ -662,6 +743,7 @@ $wgAutoloadLocalClasses = array( 'LinkHolderArray' => 'includes/parser/LinkHolderArray.php', 'LinkMarkerReplacer' => 'includes/parser/Parser_LinkHooks.php', 'MWTidy' => 'includes/parser/Tidy.php', + 'MWTidyWrapper' => 'includes/parser/Tidy.php', 'PPCustomFrame_DOM' => 'includes/parser/Preprocessor_DOM.php', 'PPCustomFrame_Hash' => 'includes/parser/Preprocessor_Hash.php', 'PPCustomFrame_HipHop' => 'includes/parser/Preprocessor_HipHop.hphp', @@ -727,11 +809,13 @@ $wgAutoloadLocalClasses = array( 'ResourceLoaderUserModule' => 'includes/resourceloader/ResourceLoaderUserModule.php', 'ResourceLoaderUserOptionsModule' => 'includes/resourceloader/ResourceLoaderUserOptionsModule.php', 'ResourceLoaderUserTokensModule' => 'includes/resourceloader/ResourceLoaderUserTokensModule.php', + 'ResourceLoaderLanguageDataModule' => 'includes/resourceloader/ResourceLoaderLanguageDataModule.php', 'ResourceLoaderWikiModule' => 'includes/resourceloader/ResourceLoaderWikiModule.php', # includes/revisiondelete 'RevDel_ArchivedFileItem' => 'includes/revisiondelete/RevisionDelete.php', 'RevDel_ArchivedFileList' => 'includes/revisiondelete/RevisionDelete.php', + 'RevDel_ArchivedRevisionItem' => 'includes/revisiondelete/RevisionDelete.php', 'RevDel_ArchiveItem' => 'includes/revisiondelete/RevisionDelete.php', 'RevDel_ArchiveList' => 'includes/revisiondelete/RevisionDelete.php', 'RevDel_FileItem' => 'includes/revisiondelete/RevisionDelete.php', @@ -747,6 +831,7 @@ $wgAutoloadLocalClasses = array( 'RevisionDeleteUser' => 'includes/revisiondelete/RevisionDeleteUser.php', # includes/search + 'MssqlSearchResultSet' => 'includes/search/SearchMssql.php', 'MySQLSearchResultSet' => 'includes/search/SearchMySQL.php', 'PostgresSearchResult' => 'includes/search/SearchPostgres.php', 'PostgresSearchResultSet' => 'includes/search/SearchPostgres.php', @@ -756,6 +841,7 @@ $wgAutoloadLocalClasses = array( 'SearchIBM_DB2' => 'includes/search/SearchIBM_DB2.php', 'SearchMssql' => 'includes/search/SearchMssql.php', 'SearchMySQL' => 'includes/search/SearchMySQL.php', + 'SearchNearMatchResultSet' => 'includes/search/SearchEngine.php', 'SearchOracle' => 'includes/search/SearchOracle.php', 'SearchPostgres' => 'includes/search/SearchPostgres.php', 'SearchResult' => 'includes/search/SearchEngine.php', @@ -773,6 +859,7 @@ $wgAutoloadLocalClasses = array( 'AncientPagesPage' => 'includes/specials/SpecialAncientpages.php', 'BlockListPager' => 'includes/specials/SpecialBlockList.php', 'BrokenRedirectsPage' => 'includes/specials/SpecialBrokenRedirects.php', + 'CategoryPager' => 'includes/specials/SpecialCategories.php', 'ContribsPager' => 'includes/specials/SpecialContributions.php', 'DBLockForm' => 'includes/specials/SpecialLockdb.php', 'DBUnlockForm' => 'includes/specials/SpecialUnlockdb.php', @@ -781,11 +868,14 @@ $wgAutoloadLocalClasses = array( 'DeletedContributionsPage' => 'includes/specials/SpecialDeletedContributions.php', 'DisambiguationsPage' => 'includes/specials/SpecialDisambiguations.php', 'DoubleRedirectsPage' => 'includes/specials/SpecialDoubleRedirects.php', + 'EditWatchlistCheckboxSeriesField' => 'includes/specials/SpecialEditWatchlist.php', + 'EditWatchlistNormalHTMLForm' => 'includes/specials/SpecialEditWatchlist.php', 'EmailConfirmation' => 'includes/specials/SpecialConfirmemail.php', 'EmailInvalidation' => 'includes/specials/SpecialConfirmemail.php', 'FewestrevisionsPage' => 'includes/specials/SpecialFewestrevisions.php', 'FileDuplicateSearchPage' => 'includes/specials/SpecialFileDuplicateSearch.php', 'HTMLBlockedUsersItemSelect' => 'includes/specials/SpecialBlockList.php', + 'ImageListPager' => 'includes/specials/SpecialListfiles.php', 'ImportReporter' => 'includes/specials/SpecialImport.php', 'IPBlockForm' => 'includes/specials/SpecialBlock.php', 'LinkSearchPage' => 'includes/specials/SpecialLinkSearch.php', @@ -793,17 +883,22 @@ $wgAutoloadLocalClasses = array( 'LoginForm' => 'includes/specials/SpecialUserlogin.php', 'LonelyPagesPage' => 'includes/specials/SpecialLonelypages.php', 'LongPagesPage' => 'includes/specials/SpecialLongpages.php', + 'MergeHistoryPager' => 'includes/specials/SpecialMergeHistory.php', 'MIMEsearchPage' => 'includes/specials/SpecialMIMEsearch.php', 'MostcategoriesPage' => 'includes/specials/SpecialMostcategories.php', 'MostimagesPage' => 'includes/specials/SpecialMostimages.php', + 'MostinterwikisPage' => 'includes/specials/SpecialMostinterwikis.php', 'MostlinkedCategoriesPage' => 'includes/specials/SpecialMostlinkedcategories.php', 'MostlinkedPage' => 'includes/specials/SpecialMostlinked.php', 'MostlinkedTemplatesPage' => 'includes/specials/SpecialMostlinkedtemplates.php', 'MostrevisionsPage' => 'includes/specials/SpecialMostrevisions.php', 'MovePageForm' => 'includes/specials/SpecialMovepage.php', + 'NewFilesPager' => 'includes/specials/SpecialNewimages.php', 'NewPagesPager' => 'includes/specials/SpecialNewpages.php', 'PageArchive' => 'includes/specials/SpecialUndelete.php', 'PopularPagesPage' => 'includes/specials/SpecialPopularpages.php', + 'ProtectedPagesPager' => 'includes/specials/SpecialProtectedpages.php', + 'ProtectedTitlesPager' => 'includes/specials/SpecialProtectedtitles.php', 'RandomPage' => 'includes/specials/SpecialRandompage.php', 'ShortPagesPage' => 'includes/specials/SpecialShortpages.php', 'SpecialActiveUsers' => 'includes/specials/SpecialActiveusers.php', @@ -814,6 +909,7 @@ $wgAutoloadLocalClasses = array( 'SpecialBlockList' => 'includes/specials/SpecialBlockList.php', 'SpecialBlockme' => 'includes/specials/SpecialBlockme.php', 'SpecialBookSources' => 'includes/specials/SpecialBooksources.php', + 'SpecialCachedPage' => 'includes/specials/SpecialCachedPage.php', 'SpecialCategories' => 'includes/specials/SpecialCategories.php', 'SpecialChangeEmail' => 'includes/specials/SpecialChangeEmail.php', 'SpecialChangePassword' => 'includes/specials/SpecialChangePassword.php', @@ -853,10 +949,11 @@ $wgAutoloadLocalClasses = array( 'SpecialUnlockdb' => 'includes/specials/SpecialUnlockdb.php', 'SpecialUpload' => 'includes/specials/SpecialUpload.php', 'SpecialUploadStash' => 'includes/specials/SpecialUploadStash.php', + 'SpecialUploadStashTooLargeException' => 'includes/specials/SpecialUploadStash.php', 'SpecialUserlogout' => 'includes/specials/SpecialUserlogout.php', 'SpecialVersion' => 'includes/specials/SpecialVersion.php', 'SpecialWatchlist' => 'includes/specials/SpecialWatchlist.php', - 'SpecialWhatlinkshere' => 'includes/specials/SpecialWhatlinkshere.php', + 'SpecialWhatLinksHere' => 'includes/specials/SpecialWhatlinkshere.php', 'UncategorizedCategoriesPage' => 'includes/specials/SpecialUncategorizedcategories.php', 'UncategorizedImagesPage' => 'includes/specials/SpecialUncategorizedimages.php', 'UncategorizedPagesPage' => 'includes/specials/SpecialUncategorizedpages.php', @@ -865,6 +962,8 @@ $wgAutoloadLocalClasses = array( 'UnusedimagesPage' => 'includes/specials/SpecialUnusedimages.php', 'UnusedtemplatesPage' => 'includes/specials/SpecialUnusedtemplates.php', 'UnwatchedpagesPage' => 'includes/specials/SpecialUnwatchedpages.php', + 'UploadChunkFileException' => 'includes/upload/UploadFromChunks.php', + 'UploadChunkZeroLengthFileException' => 'includes/upload/UploadFromChunks.php', 'UploadForm' => 'includes/specials/SpecialUpload.php', 'UploadSourceField' => 'includes/specials/SpecialUpload.php', 'UserrightsPage' => 'includes/specials/SpecialUserrights.php', @@ -899,9 +998,12 @@ $wgAutoloadLocalClasses = array( 'UploadStashNoSuchKeyException' => 'includes/upload/UploadStash.php', # languages + 'ConverterRule' => 'languages/LanguageConverter.php', 'FakeConverter' => 'languages/Language.php', 'Language' => 'languages/Language.php', 'LanguageConverter' => 'languages/LanguageConverter.php', + 'CLDRPluralRuleEvaluator' => 'languages/utils/CLDRPluralRuleEvaluator.php', + 'CLDRPluralRuleError' => 'languages/utils/CLDRPluralRuleEvaluator.php', # maintenance 'ConvertLinks' => 'maintenance/convertLinks.php', @@ -928,6 +1030,7 @@ $wgAutoloadLocalClasses = array( # maintenance/language 'csvStatsOutput' => 'maintenance/language/StatOutputs.php', + 'extensionLanguages' => 'maintenance/language/languages.inc', 'languages' => 'maintenance/language/languages.inc', 'MessageWriter' => 'maintenance/language/writeMessagesArray.inc', 'statsOutput' => 'maintenance/language/StatOutputs.php', @@ -938,16 +1041,26 @@ $wgAutoloadLocalClasses = array( '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', - 'ParserTestStaticParserHook' => 'tests/parser/parserTestsStaticParserHook.php', # tests/selenium 'Selenium' => 'tests/selenium/Selenium.php', @@ -958,6 +1071,23 @@ $wgAutoloadLocalClasses = array( 'SeleniumTestListener' => 'tests/selenium/SeleniumTestListener.php', 'SeleniumTestSuite' => 'tests/selenium/SeleniumTestSuite.php', 'SeleniumConfig' => 'tests/selenium/SeleniumConfig.php', + + # skins + 'CologneBlueTemplate' => 'skins/CologneBlue.php', + 'ModernTemplate' => 'skins/Modern.php', + 'MonoBookTemplate' => 'skins/MonoBook.php', + 'NostalgiaTemplate' => 'skins/Nostalgia.php', + 'SkinChick' => 'skins/Chick.php', + 'SkinCologneBlue' => 'skins/CologneBlue.php', + 'SkinModern' => 'skins/Modern.php', + 'SkinMonoBook' => 'skins/MonoBook.php', + 'SkinMySkin' => 'skins/MySkin.php', + 'SkinNostalgia' => 'skins/Nostalgia.php', + 'SkinSimple' => 'skins/Simple.php', + 'SkinStandard' => 'skins/Standard.php', + 'SkinVector' => 'skins/Vector.php', + 'StandardTemplate' => 'skins/Standard.php', + 'VectorTemplate' => 'skins/Vector.php', ); class AutoLoader { @@ -972,6 +1102,14 @@ class AutoLoader { static function autoload( $className ) { global $wgAutoloadClasses, $wgAutoloadLocalClasses; + // Workaround for PHP bug (5.3.2. is broken, it's fixed in 5.3.6). + // Strip leading backslashes from class names. When namespaces are used, leading backslashes are used to indicate + // the top-level namespace, e.g. \foo\Bar. When used like this in the code, the leading backslash isn't passed to + // the auto-loader ($className would be 'foo\Bar'). However, if a class is accessed using a string instead of a + // class literal (e.g. $class = '\foo\Bar'; new $class()), then some versions of PHP do not strip the leading + // backlash in this case, causing autoloading to fail. + $className = ltrim( $className, '\\' ); + if ( isset( $wgAutoloadLocalClasses[$className] ) ) { $filename = $wgAutoloadLocalClasses[$className]; } elseif ( isset( $wgAutoloadClasses[$className] ) ) { @@ -1014,6 +1152,7 @@ class AutoLoader { * Sanitizer that have define()s outside of their class definition. Of course * this wouldn't be necessary if everything in MediaWiki was class-based. Sigh. * + * @param $class string * @return Boolean Return the results of class_exists() so we know if we were successful */ static function loadClass( $class ) { diff --git a/includes/Autopromote.php b/includes/Autopromote.php index a2336030..9c77855d 100644 --- a/includes/Autopromote.php +++ b/includes/Autopromote.php @@ -1,9 +1,30 @@ 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 @@ -103,7 +141,7 @@ class BacklinkCache { /** * Get the slave connection to the database * When non existing, will initialize the connection. - * @return Database object + * @return DatabaseBase object */ protected function getDB() { if ( !isset( $this->db ) ) { @@ -179,6 +217,7 @@ class BacklinkCache { /** * Get the field name prefix for a given table * @param $table String + * @return null|string */ protected function getPrefix( $table ) { static $prefixes = array( @@ -206,6 +245,7 @@ class BacklinkCache { * 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 ); @@ -285,7 +325,7 @@ class BacklinkCache { */ public function partition( $table, $batchSize ) { - // 1) try partition cache ... + // 1) try partition cache ... if ( isset( $this->partitionCache[$table][$batchSize] ) ) { wfDebug( __METHOD__ . ": got from partition cache\n" ); @@ -340,7 +380,7 @@ class BacklinkCache { * Partition a DB result with backlinks in it into batches * @param $res ResultWrapper database result * @param $batchSize integer - * @return array @see + * @return array @see */ protected function partitionResult( $res, $batchSize ) { $batches = array(); diff --git a/includes/Block.php b/includes/Block.php index d80edb5e..732699dc 100644 --- a/includes/Block.php +++ b/includes/Block.php @@ -28,11 +28,15 @@ class Block { $mBlockEmail, $mDisableUsertalk, - $mCreateAccount; + $mCreateAccount, + $mParentBlockId; /// @var User|String protected $target; + // @var Integer Hack for foreign blocking (CentralAuth) + protected $forcedTargetID; + /// @var Block::TYPE_ constant. Can only be USER, IP or RANGE internally protected $type; @@ -72,7 +76,7 @@ class Block { $this->setTarget( $address ); if ( $this->target instanceof User && $user ) { - $this->target->setId( $user ); // needed for foreign users + $this->forcedTargetID = $user; // needed for foreign users } if ( $by ) { // local user $this->setBlocker( User::newFromID( $by ) ); @@ -122,17 +126,42 @@ class Block { $dbr = wfGetDB( DB_SLAVE ); $res = $dbr->selectRow( 'ipblocks', - '*', + self::selectFields(), array( 'ipb_id' => $id ), __METHOD__ ); if ( $res ) { - return Block::newFromRow( $res ); + return self::newFromRow( $res ); } else { return null; } } + /** + * Return the list of ipblocks fields that should be selected to create + * a new block. + * @return array + */ + public static function selectFields() { + return array( + 'ipb_id', + 'ipb_address', + 'ipb_by', + 'ipb_by_text', + 'ipb_reason', + 'ipb_timestamp', + 'ipb_auto', + 'ipb_anon_only', + 'ipb_create_account', + 'ipb_enable_autoblock', + 'ipb_expiry', + 'ipb_deleted', + 'ipb_block_email', + 'ipb_allow_usertalk', + 'ipb_parent_block_id', + ); + } + /** * 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 * @@ -243,7 +272,7 @@ class Block { } } - $res = $db->select( 'ipblocks', '*', $conds, __METHOD__ ); + $res = $db->select( 'ipblocks', self::selectFields(), $conds, __METHOD__ ); # This result could contain a block on the user, a block on the IP, and a russian-doll # set of rangeblocks. We want to choose the most specific one, so keep a leader board. @@ -256,7 +285,7 @@ class Block { $bestBlockPreventsEdit = null; foreach( $res as $row ){ - $block = Block::newFromRow( $row ); + $block = self::newFromRow( $row ); # Don't use expired blocks if( $block->deleteIfExpired() ){ @@ -365,6 +394,7 @@ class Block { $this->mAuto = $row->ipb_auto; $this->mHideName = $row->ipb_deleted; $this->mId = $row->ipb_id; + $this->mParentBlockId = $row->ipb_parent_block_id; // I wish I didn't have to do this $db = wfGetDB( DB_SLAVE ); @@ -408,6 +438,7 @@ class Block { } $dbw = wfGetDB( DB_MASTER ); + $dbw->delete( 'ipblocks', array( 'ipb_parent_block_id' => $this->getId() ), __METHOD__ ); $dbw->delete( 'ipblocks', array( 'ipb_id' => $this->getId() ), __METHOD__ ); return $dbw->affectedRows() > 0; @@ -483,9 +514,15 @@ class Block { } $expiry = $db->encodeExpiry( $this->mExpiry ); + if ( $this->forcedTargetID ) { + $uid = $this->forcedTargetID; + } else { + $uid = $this->target instanceof User ? $this->target->getID() : 0; + } + $a = array( 'ipb_address' => (string)$this->target, - 'ipb_user' => $this->target instanceof User ? $this->target->getID() : 0, + 'ipb_user' => $uid, 'ipb_by' => $this->getBy(), 'ipb_by_text' => $this->getByName(), 'ipb_reason' => $this->mReason, @@ -499,7 +536,8 @@ class Block { 'ipb_range_end' => $this->getRangeEnd(), 'ipb_deleted' => intval( $this->mHideName ), // typecast required for SQLite 'ipb_block_email' => $this->prevents( 'sendemail' ), - 'ipb_allow_usertalk' => !$this->prevents( 'editownusertalk' ) + 'ipb_allow_usertalk' => !$this->prevents( 'editownusertalk' ), + 'ipb_parent_block_id' => $this->mParentBlockId ); return $a; @@ -575,7 +613,7 @@ class Block { $key = wfMemcKey( 'ipb', 'autoblock', 'whitelist' ); $lines = $wgMemc->get( $key ); if ( !$lines ) { - $lines = explode( "\n", wfMsgForContentNoTrans( 'autoblock_whitelist' ) ); + $lines = explode( "\n", wfMessage( 'autoblock_whitelist' )->inContentLanguage()->plain() ); $wgMemc->set( $key, $lines, 3600 * 24 ); } @@ -649,7 +687,7 @@ class Block { wfDebug( "Autoblocking {$this->getTarget()}@" . $autoblockIP . "\n" ); $autoblock->setTarget( $autoblockIP ); $autoblock->setBlocker( $this->getBlocker() ); - $autoblock->mReason = wfMsgForContent( 'autoblocker', $this->getTarget(), $this->mReason ); + $autoblock->mReason = wfMessage( 'autoblocker', $this->getTarget(), $this->mReason )->inContentLanguage()->text(); $timestamp = wfTimestampNow(); $autoblock->mTimestamp = $timestamp; $autoblock->mAuto = 1; @@ -657,6 +695,7 @@ class Block { # Continue suppressing the name if needed $autoblock->mHideName = $this->mHideName; $autoblock->prevents( 'editownusertalk', $this->prevents( 'editownusertalk' ) ); + $autoblock->mParentBlockId = $this->mId; if ( $this->mExpiry == 'infinity' ) { # Original block was indefinite, start an autoblock now @@ -896,7 +935,7 @@ class Block { * Encode expiry for DB * * @param $expiry String: timestamp for expiry, or - * @param $db Database object + * @param $db DatabaseBase object * @return String * @deprecated since 1.18; use $dbw->encodeExpiry() instead */ @@ -963,41 +1002,6 @@ class Block { return wfGetDB( DB_SLAVE )->getInfinity(); } - /** - * Convert a DB-encoded expiry into a real string that humans can read. - * - * @param $encoded_expiry String: Database encoded expiry time - * @return Html-escaped String - * @deprecated since 1.18; use $wgLang->formatExpiry() instead - */ - public static function formatExpiry( $encoded_expiry ) { - wfDeprecated( __METHOD__, '1.18' ); - - global $wgContLang; - static $msg = null; - - if ( is_null( $msg ) ) { - $msg = array(); - $keys = array( 'infiniteblock', 'expiringblock' ); - - foreach ( $keys as $key ) { - $msg[$key] = wfMsgHtml( $key ); - } - } - - $expiry = $wgContLang->formatExpiry( $encoded_expiry, TS_MW ); - if ( $expiry == wfGetDB( DB_SLAVE )->getInfinity() ) { - $expirystr = $msg['infiniteblock']; - } else { - global $wgLang; - $expiredatestr = htmlspecialchars( $wgLang->date( $expiry, true ) ); - $expiretimestr = htmlspecialchars( $wgLang->time( $expiry, true ) ); - $expirystr = wfMsgReplaceArgs( $msg['expiringblock'], array( $expiredatestr, $expiretimestr ) ); - } - - return $expirystr; - } - /** * 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. @@ -1066,8 +1070,6 @@ class Block { * @return array( User|String, Block::TYPE_ constant ) */ public static function parseTarget( $target ) { - $target = trim( $target ); - # We may have been through this before if( $target instanceof User ){ if( IP::isValid( $target->getName() ) ){ @@ -1079,6 +1081,8 @@ class Block { return array( null, null ); } + $target = trim( $target ); + if ( IP::isValid( $target ) ) { # We can still create a User if it's an IP address, but we need to turn # off validation checking (which would exclude IP addresses) diff --git a/includes/CacheHelper.php b/includes/CacheHelper.php new file mode 100644 index 00000000..ac46fc42 --- /dev/null +++ b/includes/CacheHelper.php @@ -0,0 +1,392 @@ + + */ + +/** + * Interface for all classes implementing CacheHelper functionality. + * + * @since 1.20 + */ +interface ICacheHelper { + + /** + * Sets if the cache should be enabled or not. + * + * @since 1.20 + * @param boolean $cacheEnabled + */ + function setCacheEnabled( $cacheEnabled ); + + /** + * Initializes the caching. + * Should be called before the first time anything is added via addCachedHTML. + * + * @since 1.20 + * + * @param integer|null $cacheExpiry Sets the cache expiry, either ttl in seconds or unix timestamp. + * @param boolean|null $cacheEnabled Sets if the cache should be enabled or not. + */ + function startCache( $cacheExpiry = null, $cacheEnabled = null ); + + /** + * Get a cached value if available or compute it if not and then cache it if possible. + * The provided $computeFunction is only called when the computation needs to happen + * and should return a result value. $args are arguments that will be passed to the + * compute function when called. + * + * @since 1.20 + * + * @param {function} $computeFunction + * @param array|mixed $args + * @param string|null $key + * + * @return mixed + */ + function getCachedValue( $computeFunction, $args = array(), $key = null ); + + /** + * Saves the HTML to the cache in case it got recomputed. + * Should be called after the last time anything is added via addCachedHTML. + * + * @since 1.20 + */ + function saveCache(); + + /** + * Sets the time to live for the cache, in seconds or a unix timestamp + * indicating the point of expiry... + * + * @since 1.20 + * + * @param integer $cacheExpiry + */ + function setExpiry( $cacheExpiry ); + +} + +/** + * Helper class for caching various elements in a single cache entry. + * + * To get a cached value or compute it, use getCachedValue like this: + * $this->getCachedValue( $callback ); + * + * To add HTML that should be cached, use addCachedHTML like this: + * $this->addCachedHTML( $callback ); + * + * The callback function is only called when needed, so do all your expensive + * computations here. This function should returns the HTML to be cached. + * It should not add anything to the PageOutput object! + * + * Before the first addCachedHTML call, you should call $this->startCache(); + * After adding the last HTML that should be cached, call $this->saveCache(); + * + * @since 1.20 + */ +class CacheHelper implements ICacheHelper { + + /** + * The time to live for the cache, in seconds or a unix timestamp indicating the point of expiry. + * + * @since 1.20 + * @var integer + */ + protected $cacheExpiry = 3600; + + /** + * List of HTML chunks to be cached (if !hasCached) or that where cached (of hasCached). + * If not cached already, then the newly computed chunks are added here, + * if it as cached already, chunks are removed from this list as they are needed. + * + * @since 1.20 + * @var array + */ + protected $cachedChunks; + + /** + * Indicates if the to be cached content was already cached. + * Null if this information is not available yet. + * + * @since 1.20 + * @var boolean|null + */ + protected $hasCached = null; + + /** + * If the cache is enabled or not. + * + * @since 1.20 + * @var boolean + */ + protected $cacheEnabled = true; + + /** + * Function that gets called when initialization is done. + * + * @since 1.20 + * @var callable + */ + protected $onInitHandler = false; + + /** + * Elements to build a cache key with. + * + * @since 1.20 + * @var array + */ + protected $cacheKey = array(); + + /** + * Sets if the cache should be enabled or not. + * + * @since 1.20 + * @param boolean $cacheEnabled + */ + public function setCacheEnabled( $cacheEnabled ) { + $this->cacheEnabled = $cacheEnabled; + } + + /** + * Initializes the caching. + * Should be called before the first time anything is added via addCachedHTML. + * + * @since 1.20 + * + * @param integer|null $cacheExpiry Sets the cache expiry, either ttl in seconds or unix timestamp. + * @param boolean|null $cacheEnabled Sets if the cache should be enabled or not. + */ + public function startCache( $cacheExpiry = null, $cacheEnabled = null ) { + if ( is_null( $this->hasCached ) ) { + if ( !is_null( $cacheExpiry ) ) { + $this->cacheExpiry = $cacheExpiry; + } + + if ( !is_null( $cacheEnabled ) ) { + $this->setCacheEnabled( $cacheEnabled ); + } + + $this->initCaching(); + } + } + + /** + * Returns a message that notifies the user he/she is looking at + * a cached version of the page, including a refresh link. + * + * @since 1.20 + * + * @param IContextSource $context + * @param boolean $includePurgeLink + * + * @return string + */ + public function getCachedNotice( IContextSource $context, $includePurgeLink = true ) { + if ( $this->cacheExpiry < 86400 * 3650 ) { + $message = $context->msg( + 'cachedspecial-viewing-cached-ttl', + $context->getLanguage()->formatDuration( $this->cacheExpiry ) + )->escaped(); + } + else { + $message = $context->msg( + 'cachedspecial-viewing-cached-ts' + )->escaped(); + } + + if ( $includePurgeLink ) { + $refreshArgs = $context->getRequest()->getQueryValues(); + unset( $refreshArgs['title'] ); + $refreshArgs['action'] = 'purge'; + + $subPage = $context->getTitle()->getFullText(); + $subPage = explode( '/', $subPage, 2 ); + $subPage = count( $subPage ) > 1 ? $subPage[1] : false; + + $message .= ' ' . Linker::link( + $context->getTitle( $subPage ), + $context->msg( 'cachedspecial-refresh-now' )->escaped(), + array(), + $refreshArgs + ); + } + + return $message; + } + + /** + * Initializes the caching if not already done so. + * Should be called before any of the caching functionality is used. + * + * @since 1.20 + */ + protected function initCaching() { + if ( $this->cacheEnabled && is_null( $this->hasCached ) ) { + $cachedChunks = wfGetCache( CACHE_ANYTHING )->get( $this->getCacheKeyString() ); + + $this->hasCached = is_array( $cachedChunks ); + $this->cachedChunks = $this->hasCached ? $cachedChunks : array(); + + if ( $this->onInitHandler !== false ) { + call_user_func( $this->onInitHandler, $this->hasCached ); + } + } + } + + /** + * Get a cached value if available or compute it if not and then cache it if possible. + * The provided $computeFunction is only called when the computation needs to happen + * and should return a result value. $args are arguments that will be passed to the + * compute function when called. + * + * @since 1.20 + * + * @param {function} $computeFunction + * @param array|mixed $args + * @param string|null $key + * + * @return mixed + */ + public function getCachedValue( $computeFunction, $args = array(), $key = null ) { + $this->initCaching(); + + if ( $this->cacheEnabled && $this->hasCached ) { + $value = null; + + if ( is_null( $key ) ) { + $itemKey = array_keys( array_slice( $this->cachedChunks, 0, 1 ) ); + $itemKey = array_shift( $itemKey ); + + if ( !is_integer( $itemKey ) ) { + wfWarn( "Attempted to get item with non-numeric key while the next item in the queue has a key ($itemKey) in " . __METHOD__ ); + } + elseif ( is_null( $itemKey ) ) { + wfWarn( "Attempted to get an item while the queue is empty in " . __METHOD__ ); + } + else { + $value = array_shift( $this->cachedChunks ); + } + } + else { + if ( array_key_exists( $key, $this->cachedChunks ) ) { + $value = $this->cachedChunks[$key]; + unset( $this->cachedChunks[$key] ); + } + else { + wfWarn( "There is no item with key '$key' in this->cachedChunks in " . __METHOD__ ); + } + } + } + else { + if ( !is_array( $args ) ) { + $args = array( $args ); + } + + $value = call_user_func_array( $computeFunction, $args ); + + if ( $this->cacheEnabled ) { + if ( is_null( $key ) ) { + $this->cachedChunks[] = $value; + } + else { + $this->cachedChunks[$key] = $value; + } + } + } + + return $value; + } + + /** + * Saves the HTML to the cache in case it got recomputed. + * Should be called after the last time anything is added via addCachedHTML. + * + * @since 1.20 + */ + public function saveCache() { + if ( $this->cacheEnabled && $this->hasCached === false && !empty( $this->cachedChunks ) ) { + wfGetCache( CACHE_ANYTHING )->set( $this->getCacheKeyString(), $this->cachedChunks, $this->cacheExpiry ); + } + } + + /** + * Sets the time to live for the cache, in seconds or a unix timestamp + * indicating the point of expiry... + * + * @since 1.20 + * + * @param integer $cacheExpiry + */ + public function setExpiry( $cacheExpiry ) { + $this->cacheExpiry = $cacheExpiry; + } + + /** + * Returns the cache key to use to cache this page's HTML output. + * Is constructed from the special page name and language code. + * + * @since 1.20 + * + * @return string + * @throws MWException + */ + protected function getCacheKeyString() { + if ( $this->cacheKey === array() ) { + throw new MWException( 'No cache key set, so cannot obtain or save the CacheHelper values.' ); + } + + return call_user_func_array( 'wfMemcKey', $this->cacheKey ); + } + + /** + * Sets the cache key that should be used. + * + * @since 1.20 + * + * @param array $cacheKey + */ + public function setCacheKey( array $cacheKey ) { + $this->cacheKey = $cacheKey; + } + + /** + * Rebuild the content, even if it's already cached. + * This effectively has the same effect as purging the cache, + * since it will be overridden with the new value on the next request. + * + * @since 1.20 + */ + public function rebuildOnDemand() { + $this->hasCached = false; + } + + /** + * Sets a function that gets called when initialization of the cache is done. + * + * @since 1.20 + * + * @param $handlerFunction + */ + public function setOnInitializedHandler( $handlerFunction ) { + $this->onInitHandler = $handlerFunction; + } + +} diff --git a/includes/Category.php b/includes/Category.php index 9d9b5a67..b7b12e8a 100644 --- a/includes/Category.php +++ b/includes/Category.php @@ -1,14 +1,33 @@ getX( 'mFiles' ); } /** - * @return Title|false Title for this category, or false on failure. + * @return Title|bool Title for this category, or false on failure. */ public function getTitle() { if ( $this->mTitle ) return $this->mTitle; @@ -231,7 +250,10 @@ class Category { ); } - /** Generic accessor */ + /** + * Generic accessor + * @return bool + */ private function getX( $key ) { if ( !$this->initialize() ) { return false; @@ -257,7 +279,7 @@ class Category { } $dbw = wfGetDB( DB_MASTER ); - $dbw->begin(); + $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 @@ -275,13 +297,13 @@ class Category { 'IGNORE' ); - $cond1 = $dbw->conditional( 'page_namespace=' . NS_CATEGORY, 1, 'NULL' ); - $cond2 = $dbw->conditional( 'page_namespace=' . NS_FILE, 1, 'NULL' ); + $cond1 = $dbw->conditional( array( 'page_namespace' => NS_CATEGORY ), 1, 'NULL' ); + $cond2 = $dbw->conditional( array( 'page_namespace' => NS_FILE ), 1, 'NULL' ); $result = $dbw->selectRow( array( 'categorylinks', 'page' ), - array( 'COUNT(*) AS pages', - "COUNT($cond1) AS subcats", - "COUNT($cond2) AS files" + array( 'pages' => 'COUNT(*)', + 'subcats' => "COUNT($cond1)", + 'files' => "COUNT($cond2)" ), array( 'cl_to' => $this->mName, 'page_id = cl_from' ), __METHOD__, @@ -297,7 +319,7 @@ class Category { array( 'cat_title' => $this->mName ), __METHOD__ ); - $dbw->commit(); + $dbw->commit( __METHOD__ ); # Now we should update our local counts. $this->mPages = $result->pages; diff --git a/includes/CategoryPage.php b/includes/CategoryPage.php index eab7a356..32e270e8 100644 --- a/includes/CategoryPage.php +++ b/includes/CategoryPage.php @@ -1,14 +1,26 @@ msg( 'category-empty' )->parseAsBlock(); } $lang = $this->getLanguage(); @@ -172,7 +189,8 @@ class CategoryViewer extends ContextSource { * * @param Title $title * @param string $sortkey The human-readable sortkey (before transforming to icu or whatever). - */ + * @return string + */ function getSubcategorySortChar( $title, $sortkey ) { global $wgContLang; @@ -351,7 +369,7 @@ class CategoryViewer extends ContextSource { if ( $rescnt > 0 ) { # Showing subcategories $r .= "
\n"; - $r .= '

' . wfMsg( 'subcategories' ) . "

\n"; + $r .= '

' . $this->msg( 'subcategories' )->text() . "

\n"; $r .= $countmsg; $r .= $this->getSectionPagingLinks( 'subcat' ); $r .= $this->formatList( $this->children, $this->children_start_char ); @@ -365,7 +383,7 @@ class CategoryViewer extends ContextSource { * @return string */ function getPagesSection() { - $ti = htmlspecialchars( $this->title->getText() ); + $ti = wfEscapeWikiText( $this->title->getText() ); # Don't show articles section if there are none. $r = ''; @@ -380,7 +398,7 @@ class CategoryViewer extends ContextSource { if ( $rescnt > 0 ) { $r = "
\n"; - $r .= '

' . wfMsg( 'category_header', $ti ) . "

\n"; + $r .= '

' . $this->msg( 'category_header', $ti )->text() . "

\n"; $r .= $countmsg; $r .= $this->getSectionPagingLinks( 'page' ); $r .= $this->formatList( $this->articles, $this->articles_start_char ); @@ -401,7 +419,7 @@ class CategoryViewer extends ContextSource { $countmsg = $this->getCountMessage( $rescnt, $dbcnt, 'file' ); $r .= "
\n"; - $r .= '

' . wfMsg( 'category-media-header', htmlspecialchars( $this->title->getText() ) ) . "

\n"; + $r .= '

' . $this->msg( 'category-media-header', wfEscapeWikiText( $this->title->getText() ) )->text() . "

\n"; $r .= $countmsg; $r .= $this->getSectionPagingLinks( 'file' ); if ( $this->showGallery ) { @@ -486,11 +504,11 @@ class CategoryViewer extends ContextSource { # Split into three columns $columns = array_chunk( $columns, ceil( count( $columns ) / 3 ), true /* preserve keys */ ); - $ret = ''; + $ret = '
'; $prevchar = null; foreach ( $columns as $column ) { - $ret .= '"; # Main line @@ -880,7 +933,7 @@ class EnhancedChangesList extends ChangesList { # Article link if( $namehidden ) { - $r .= ' ' . wfMsgHtml( 'rev-deleted-event' ) . ''; + $r .= ' ' . $this->msg( 'rev-deleted-event' )->escaped() . ''; } elseif( $allLogs ) { $r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched ); } else { @@ -894,22 +947,22 @@ class EnhancedChangesList extends ChangesList { $n = count($block); static $nchanges = array(); if ( !isset( $nchanges[$n] ) ) { - $nchanges[$n] = wfMsgExt( 'nchanges', array( 'parsemag', 'escape' ), $this->getLanguage()->formatNum( $n ) ); + $nchanges[$n] = $this->msg( 'nchanges' )->numParams( $n )->escaped(); } # Total change link $r .= ' '; + $logtext = ''; if( !$allLogs ) { - $r .= '('; if( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) { - $r .= $nchanges[$n]; + $logtext .= $nchanges[$n]; } elseif( $isnew ) { - $r .= $nchanges[$n]; + $logtext .= $nchanges[$n]; } else { $params = $queryParams; $params['diff'] = $currentRevision; $params['oldid'] = $oldid; - $r .= Linker::link( + $logtext .= Linker::link( $block[0]->getTitle(), $nchanges[$n], array(), @@ -923,20 +976,25 @@ class EnhancedChangesList extends ChangesList { if( $allLogs ) { // don't show history link for logs } elseif( $namehidden || !$block[0]->getTitle()->exists() ) { - $r .= $this->message['pipe-separator'] . $this->message['hist'] . ')'; + $logtext .= $this->message['pipe-separator'] . $this->message['hist']; } else { $params = $queryParams; $params['action'] = 'history'; - $r .= $this->message['pipe-separator'] . + $logtext .= $this->message['pipe-separator'] . Linker::linkKnown( $block[0]->getTitle(), $this->message['hist'], array(), $params - ) . ')'; + ); } - $r .= ' . . '; + + if( $logtext !== '' ) { + $r .= $this->msg( 'parentheses' )->rawParams( $logtext )->escaped(); + } + + $r .= ' . . '; # Character difference (does not apply if only log items) if( $wgRCShowChangedSize && !$allLogs ) { @@ -950,13 +1008,12 @@ class EnhancedChangesList extends ChangesList { $first--; } # Get net change - $chardiff = $rcObj->getCharacterDifference( $block[$first]->mAttribs['rc_old_len'], - $block[$last]->mAttribs['rc_new_len'] ); + $chardiff = $this->formatCharacterDifference( $block[$first], $block[$last] ); if( $chardiff == '' ) { $r .= ' '; } else { - $r .= ' ' . $chardiff. ' . . '; + $r .= ' ' . $chardiff. ' . . '; } } @@ -969,10 +1026,9 @@ class EnhancedChangesList extends ChangesList { $classes = array(); $type = $rcObj->mAttribs['rc_type']; - #$r .= '"; @@ -190,27 +224,32 @@ class FileDeleteForm { $suppress = ''; } - $checkWatch = $wgUser->getBoolOption( 'watchdeletion' ) || $this->title->userIsWatching(); + $checkWatch = $wgUser->getBoolOption( 'watchdeletion' ) || $wgUser->isWatched( $this->title ); $form = Xml::openElement( 'form', array( 'method' => 'post', 'action' => $this->getAction(), 'id' => 'mw-img-deleteconfirm' ) ) . Xml::openElement( 'fieldset' ) . - Xml::element( 'legend', null, wfMsg( 'filedelete-legend' ) ) . + Xml::element( 'legend', null, wfMessage( 'filedelete-legend' )->text() ) . Html::hidden( 'wpEditToken', $wgUser->getEditToken( $this->oldimage ) ) . $this->prepareMessage( 'filedelete-intro' ) . Xml::openElement( 'table', array( 'id' => 'mw-img-deleteconfirm-table' ) ) . ""; @@ -232,7 +271,7 @@ class FileDeleteForm { " . @@ -244,7 +283,7 @@ class FileDeleteForm { $title = Title::makeTitle( NS_MEDIAWIKI, 'Filedelete-reason-dropdown' ); $link = Linker::link( $title, - wfMsgHtml( 'filedelete-edit-reasonlist' ), + wfMessage( 'filedelete-edit-reasonlist' )->escaped(), array(), array( 'action' => 'edit' ) ); @@ -259,7 +298,8 @@ class FileDeleteForm { */ private function showLogEntries() { global $wgOut; - $wgOut->addHTML( '

' . htmlspecialchars( LogPage::logName( 'delete' ) ) . "

\n" ); + $deleteLogPage = new LogPage( 'delete' ); + $wgOut->addHTML( '

' . $deleteLogPage->getName()->escaped() . "

\n" ); LogEventsList::showLogExtract( $wgOut, 'delete', $this->title ); } @@ -274,19 +314,17 @@ class FileDeleteForm { private function prepareMessage( $message ) { global $wgLang; if( $this->oldimage ) { - return wfMsgExt( + return wfMessage( "{$message}-old", # To ensure grep will find them: 'filedelete-intro-old', 'filedelete-nofile-old', 'filedelete-success-old' - 'parse', wfEscapeWikiText( $this->title->getText() ), $wgLang->date( $this->getTimestamp(), true ), $wgLang->time( $this->getTimestamp(), true ), - wfExpandUrl( $this->file->getArchiveUrl( $this->oldimage ), PROTO_CURRENT ) ); + wfExpandUrl( $this->file->getArchiveUrl( $this->oldimage ), PROTO_CURRENT ) )->parseAsBlock(); } else { - return wfMsgExt( + return wfMessage( $message, - 'parse', wfEscapeWikiText( $this->title->getText() ) - ); + )->parseAsBlock(); } } diff --git a/includes/ForkController.php b/includes/ForkController.php index 9cacef54..448bc03b 100644 --- a/includes/ForkController.php +++ b/includes/ForkController.php @@ -1,4 +1,24 @@ destroyInstance(); + FileBackendGroup::destroySingleton(); + LockManagerGroup::destroySingleton(); ObjectCache::clear(); $wgMemc = null; } diff --git a/includes/FormOptions.php b/includes/FormOptions.php index ccc87d8a..33bbd86a 100644 --- a/includes/FormOptions.php +++ b/includes/FormOptions.php @@ -1,16 +1,35 @@ options[$name] ); } - /** Retrieve an option value */ + /** + * Retrieve an option value + * @return Mixed + */ public function offsetGet( $name ) { return $this->getValue( $name ); } diff --git a/includes/GitInfo.php b/includes/GitInfo.php new file mode 100644 index 00000000..c3c30733 --- /dev/null +++ b/includes/GitInfo.php @@ -0,0 +1,214 @@ +basedir = "{$dir}/.git/"; + } + + /** + * Return a singleton for the repo at $IP + * @return GitInfo + */ + public static function repo() { + global $IP; + if ( is_null( self::$repo ) ) { + self::$repo = new self( $IP ); + } + return self::$repo; + } + + /** + * Check if a string looks like a hex encoded SHA1 hash + * + * @param $str string The string to check + * @return bool Whether or not the string looks like a SHA1 + */ + public static function isSHA1( $str ) { + return !!preg_match( '/^[0-9A-F]{40}$/i', $str ); + } + + /** + * Return the HEAD of the repo (without any opening "ref: ") + * @return string The HEAD + */ + public function getHead() { + $HEADfile = "{$this->basedir}/HEAD"; + + if ( !is_readable( $HEADfile ) ) { + return false; + } + + $HEAD = file_get_contents( $HEADfile ); + + if ( preg_match( "/ref: (.*)/", $HEAD, $m ) ) { + return rtrim( $m[1] ); + } else { + return rtrim( $HEAD ); + } + } + + /** + * Return the SHA1 for the current HEAD of the repo + * @return string A SHA1 or false + */ + public function getHeadSHA1() { + $HEAD = $this->getHead(); + + // If detached HEAD may be a SHA1 + if ( self::isSHA1( $HEAD ) ) { + return $HEAD; + } + + // If not a SHA1 it may be a ref: + $REFfile = "{$this->basedir}{$HEAD}"; + if ( !is_readable( $REFfile ) ) { + return false; + } + + $sha1 = rtrim( file_get_contents( $REFfile ) ); + + return $sha1; + } + + /** + * Return the name of the current branch, or HEAD if not found + * @return string The branch name, HEAD, or false + */ + public function getCurrentBranch() { + $HEAD = $this->getHead(); + if ( $HEAD && preg_match( "#^refs/heads/(.*)$#", $HEAD, $m ) ) { + return $m[1]; + } else { + return $HEAD; + } + } + + /** + * Get an URL to a web viewer link to the HEAD revision. + * + * @return string|bool string if an URL is available or false otherwise. + */ + public function getHeadViewUrl() { + $config = "{$this->basedir}/config"; + if ( !is_readable( $config ) ) { + return false; + } + + $configArray = parse_ini_file( $config, true ); + $remote = false; + + // Use the "origin" remote repo if available or any other repo if not. + if ( isset( $configArray['remote origin'] ) ) { + $remote = $configArray['remote origin']; + } else { + foreach( $configArray as $sectionName => $sectionConf ) { + if ( substr( $sectionName, 0, 6 ) == 'remote' ) { + $remote = $sectionConf; + } + } + } + + if ( $remote === false || !isset( $remote['url'] ) ) { + return false; + } + + $url = $remote['url']; + if ( substr( $url, -4 ) !== '.git' ) { + $url .= '.git'; + } + foreach( self::getViewers() as $repo => $viewer ) { + $pattern = '#^' . $repo . '$#'; + if ( preg_match( $pattern, $url ) ) { + $viewerUrl = preg_replace( $pattern, $viewer, $url ); + $headSHA1 = $this->getHeadSHA1(); + $replacements = array( + '%h' => substr( $headSHA1, 0, 7 ), + '%H' => $headSHA1 + ); + return strtr( $viewerUrl, $replacements ); + } + } + return false; + } + + /** + * @see self::getHeadSHA1 + * @return string + */ + public static function headSHA1() { + return self::repo()->getHeadSHA1(); + } + + /** + * @see self::getCurrentBranch + * @return string + */ + public static function currentBranch() { + return self::repo()->getCurrentBranch(); + } + + /** + * @see self::getHeadViewUrl() + * @return bool|string + */ + public static function headViewUrl() { + return self::repo()->getHeadViewUrl(); + } + + /** + * Gets the list of repository viewers + * @return array + */ + protected static function getViewers() { + global $wgGitRepositoryViewers; + + if( self::$viewers === false ) { + self::$viewers = $wgGitRepositoryViewers; + wfRunHooks( 'GitViewers', array( &self::$viewers ) ); + } + + return self::$viewers; + } +} diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 65fc643e..8f701c6b 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -1,6 +1,22 @@ $v ) { $cgi .= $firstTime ? '' : '&'; if ( is_array( $v ) ) { - $cgi .= wfArrayToCGI( $v, null, $key . "[$k]" ); + $cgi .= wfArrayToCgi( $v, null, $key . "[$k]" ); } else { $cgi .= urlencode( $key . "[$k]" ) . '=' . urlencode( $v ); } @@ -366,7 +421,7 @@ function wfArrayToCGI( $array1, $array2 = null, $prefix = '' ) { } /** - * This is the logical opposite of wfArrayToCGI(): it accepts a query string as + * 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 * arrays. Of course, keys and values are urldecode()d. @@ -423,7 +478,7 @@ function wfCgiToArray( $query ) { */ function wfAppendQuery( $url, $query ) { if ( is_array( $query ) ) { - $query = wfArrayToCGI( $query ); + $query = wfArrayToCgi( $query ); } if( $query != '' ) { if( false === strpos( $url, '?' ) ) { @@ -731,6 +786,9 @@ function wfParseUrl( $url ) { return false; } + // parse_url() incorrectly handles schemes case-sensitively. Convert it to lowercase. + $bits['scheme'] = strtolower( $bits['scheme'] ); + // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) { $bits['delimiter'] = '://'; @@ -764,6 +822,31 @@ function wfParseUrl( $url ) { return $bits; } +/** + * Take a URL, make sure it's expanded to fully qualified, and replace any + * encoded non-ASCII Unicode characters with their UTF-8 original forms + * for more compact display and legibility for local audiences. + * + * @todo handle punycode domains too + * + * @param $url string + * @return string + */ +function wfExpandIRI( $url ) { + return preg_replace_callback( '/((?:%[89A-F][0-9A-F])+)/i', 'wfExpandIRI_callback', wfExpandUrl( $url ) ); +} + +/** + * Private callback for wfExpandIRI + * @param array $matches + * @return string + */ +function wfExpandIRI_callback( $matches ) { + return urldecode( $matches[1] ); +} + + + /** * Make URL indexes, appropriate for the el_index field of externallinks. * @@ -852,25 +935,21 @@ function wfMatchesDomainList( $url, $domains ) { * @param $logonly Bool: set true to avoid appearing in HTML when $wgDebugComments is set */ function wfDebug( $text, $logonly = false ) { - global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly, $wgDebugRawPage; - global $wgDebugLogPrefix, $wgShowDebug; - - static $cache = array(); // Cache of unoutputted messages - $text = wfDebugTimer() . $text; + global $wgDebugLogFile, $wgProfileOnly, $wgDebugRawPage, $wgDebugLogPrefix; if ( !$wgDebugRawPage && wfIsDebugRawPage() ) { return; } - if ( ( $wgDebugComments || $wgShowDebug ) && !$logonly ) { - $cache[] = $text; + $timer = wfDebugTimer(); + if ( $timer !== '' ) { + $text = preg_replace( '/[^\n]/', $timer . '\0', $text, 1 ); + } - if ( isset( $wgOut ) && is_object( $wgOut ) ) { - // add the message and any cached messages to the output - array_map( array( $wgOut, 'debug' ), $cache ); - $cache = array(); - } + if ( !$logonly ) { + MWDebug::debugMsg( $text ); } + if ( wfRunHooks( 'Debug', array( $text, null /* no log group */ ) ) ) { if ( $wgDebugLogFile != '' && !$wgProfileOnly ) { # Strip unprintables; they can switch terminal modes when binary data @@ -880,12 +959,11 @@ function wfDebug( $text, $logonly = false ) { wfErrorLog( $text, $wgDebugLogFile ); } } - - MWDebug::debugMsg( $text ); } /** * Returns true if debug logging should be suppressed if $wgDebugRawPage = false + * @return bool */ function wfIsDebugRawPage() { static $cache; @@ -968,11 +1046,28 @@ function wfDebugLog( $logGroup, $text, $public = true ) { * @param $text String: database error message. */ function wfLogDBError( $text ) { - global $wgDBerrorLog; + global $wgDBerrorLog, $wgDBerrorLogTZ; + static $logDBErrorTimeZoneObject = null; + if ( $wgDBerrorLog ) { $host = wfHostname(); $wiki = wfWikiID(); - $text = date( 'D M j G:i:s T Y' ) . "\t$host\t$wiki\t$text"; + + if ( $wgDBerrorLogTZ && !$logDBErrorTimeZoneObject ) { + $logDBErrorTimeZoneObject = new DateTimeZone( $wgDBerrorLogTZ ); + } + + // Workaround for https://bugs.php.net/bug.php?id=52063 + // Can be removed when min PHP > 5.3.2 + if ( $logDBErrorTimeZoneObject === null ) { + $d = date_create( "now" ); + } else { + $d = date_create( "now", $logDBErrorTimeZoneObject ); + } + + $date = $d->format( 'D M j G:i:s T Y' ); + + $text = "$date\t$host\t$wiki\t$text"; wfErrorLog( $text, $wgDBerrorLog ); } } @@ -981,41 +1076,16 @@ function wfLogDBError( $text ) { * Throws a warning that $function is deprecated * * @param $function String - * @param $version String|false: Added in 1.19. - * @param $component String|false: Added in 1.19. - * + * @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 + * caller. 2 = function that called the function that called + * wfDeprecated (Added in 1.20) + * * @return null */ -function wfDeprecated( $function, $version = false, $component = false ) { - static $functionsWarned = array(); - - MWDebug::deprecated( $function, $version, $component ); - - if ( !isset( $functionsWarned[$function] ) ) { - $functionsWarned[$function] = true; - - if ( $version ) { - global $wgDeprecationReleaseLimit; - - if ( $wgDeprecationReleaseLimit && $component === false ) { - # Strip -* off the end of $version so that branches can use the - # format #.##-branchname to avoid issues if the branch is merged into - # a version of MediaWiki later than what it was branched from - $comparableVersion = preg_replace( '/-.*$/', '', $version ); - - # If the comparableVersion is larger than our release limit then - # skip the warning message for the deprecation - if ( version_compare( $wgDeprecationReleaseLimit, $comparableVersion, '<' ) ) { - return; - } - } - - $component = $component === false ? 'MediaWiki' : $component; - wfWarn( "Use of $function was deprecated in $component $version.", 2 ); - } else { - wfWarn( "Use of $function is deprecated.", 2 ); - } - } +function wfDeprecated( $function, $version = false, $component = false, $callerOffset = 2 ) { + MWDebug::deprecated( $function, $version, $component, $callerOffset + 1 ); } /** @@ -1029,34 +1099,7 @@ function wfDeprecated( $function, $version = false, $component = false ) { * is true */ function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) { - global $wgDevelopmentWarnings; - - MWDebug::warning( $msg, $callerOffset + 2 ); - - $callers = wfDebugBacktrace(); - if ( isset( $callers[$callerOffset + 1] ) ) { - $callerfunc = $callers[$callerOffset + 1]; - $callerfile = $callers[$callerOffset]; - if ( isset( $callerfile['file'] ) && isset( $callerfile['line'] ) ) { - $file = $callerfile['file'] . ' at line ' . $callerfile['line']; - } else { - $file = '(internal function)'; - } - $func = ''; - if ( isset( $callerfunc['class'] ) ) { - $func .= $callerfunc['class'] . '::'; - } - if ( isset( $callerfunc['function'] ) ) { - $func .= $callerfunc['function']; - } - $msg .= " [Called from $func in $file]"; - } - - if ( $wgDevelopmentWarnings ) { - trigger_error( $msg, $level ); - } else { - wfDebug( "$msg\n" ); - } + MWDebug::warning( $msg, $callerOffset + 1, $level ); } /** @@ -1176,6 +1219,57 @@ function wfLogProfilingData() { wfErrorLog( $log . $profiler->getOutput(), $wgDebugLogFile ); } +/** + * Increment a statistics counter + * + * @param $key String + * @param $count Int + */ +function wfIncrStats( $key, $count = 1 ) { + global $wgStatsMethod; + + $count = intval( $count ); + + if( $wgStatsMethod == 'udp' ) { + global $wgUDPProfilerHost, $wgUDPProfilerPort, $wgDBname, $wgAggregateStatsID; + static $socket; + + $id = $wgAggregateStatsID !== false ? $wgAggregateStatsID : $wgDBname; + + if ( !$socket ) { + $socket = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP ); + $statline = "stats/{$id} - 1 1 1 1 1 -total\n"; + socket_sendto( + $socket, + $statline, + strlen( $statline ), + 0, + $wgUDPProfilerHost, + $wgUDPProfilerPort + ); + } + $statline = "stats/{$id} - {$count} 1 1 1 1 {$key}\n"; + wfSuppressWarnings(); + socket_sendto( + $socket, + $statline, + strlen( $statline ), + 0, + $wgUDPProfilerHost, + $wgUDPProfilerPort + ); + wfRestoreWarnings(); + } elseif( $wgStatsMethod == 'cache' ) { + global $wgMemc; + $key = wfMemcKey( 'stats', $key ); + if ( is_null( $wgMemc->incr( $key, $count ) ) ) { + $wgMemc->add( $key, $count ); + } + } else { + // Disabled + } +} + /** * Check if the wiki read-only lock file is present. This can be used to lock * off editing functions, but doesn't guarantee that the database will not be @@ -1247,7 +1341,7 @@ function wfGetLangObj( $langcode = false ) { return $wgLang; } - $validCodes = array_keys( Language::getLanguageNames() ); + $validCodes = array_keys( Language::fetchLanguageNames() ); if( in_array( $langcode, $validCodes ) ) { # $langcode corresponds to a valid language. return Language::factory( $langcode ); @@ -1260,7 +1354,7 @@ function wfGetLangObj( $langcode = false ) { /** * Old function when $wgBetterDirectionality existed - * Removed in core, kept in extensions for backwards compat. + * All usage removed, wfUILang can be removed in near future * * @deprecated since 1.18 * @return Language @@ -1308,6 +1402,8 @@ function wfMessageFallback( /*...*/ ) { * Use wfMsgForContent() instead if the message should NOT * change depending on the user preferences. * + * @deprecated since 1.18 + * * @param $key String: lookup key for the message, usually * defined in languages/Language.php * @@ -1328,6 +1424,8 @@ function wfMsg( $key ) { /** * Same as above except doesn't transform the message * + * @deprecated since 1.18 + * * @param $key String * @return String */ @@ -1356,6 +1454,8 @@ function wfMsgNoTrans( $key ) { * customize potentially hundreds of messages in * order to, e.g., fix a link in every possible language. * + * @deprecated since 1.18 + * * @param $key String: lookup key for the message, usually * defined in languages/Language.php * @return String @@ -1376,6 +1476,8 @@ function wfMsgForContent( $key ) { /** * Same as above except doesn't transform the message * + * @deprecated since 1.18 + * * @param $key String * @return String */ @@ -1395,6 +1497,8 @@ function wfMsgForContentNoTrans( $key ) { /** * Really get a message * + * @deprecated since 1.18 + * * @param $key String: key to get. * @param $args * @param $useDB Boolean @@ -1413,6 +1517,8 @@ function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform /** * Fetch a message string value, but don't replace any keys yet. * + * @deprecated since 1.18 + * * @param $key String * @param $useDB Bool * @param $langCode String: Code of the language to get the message for, or @@ -1468,6 +1574,8 @@ function wfMsgReplaceArgs( $message, $args ) { * to pre-escape them if you really do want plaintext, or just wrap * the whole thing in htmlspecialchars(). * + * @deprecated since 1.18 + * * @param $key String * @param string ... parameters * @return string @@ -1485,6 +1593,8 @@ function wfMsgHtml( $key ) { * to pre-escape them if you really do want plaintext, or just wrap * the whole thing in htmlspecialchars(). * + * @deprecated since 1.18 + * * @param $key String * @param string ... parameters * @return string @@ -1500,6 +1610,9 @@ function wfMsgWikiHtml( $key ) { /** * Returns message in the requested format + * + * @deprecated since 1.18 + * * @param $key String: key of the message * @param $options Array: processing rules. Can take the following options: * parse: parses wikitext to HTML @@ -1592,6 +1705,8 @@ function wfMsgExt( $key, $options ) { * looked up didn't exist but a XHTML string, this function checks for the * nonexistance of messages by checking the MessageCache::get() result directly. * + * @deprecated since 1.18. Use Message::isDisabled(). + * * @param $key String: the message key looked up * @return Boolean True if the message *doesn't* exist. */ @@ -1619,6 +1734,15 @@ function wfDebugDieBacktrace( $msg = '' ) { function wfHostname() { static $host; if ( is_null( $host ) ) { + + # Hostname overriding + global $wgOverrideHostname; + if( $wgOverrideHostname !== false ) { + # Set static and skip any detection + $host = $wgOverrideHostname; + return $host; + } + if ( function_exists( 'posix_uname' ) ) { // This function not present on Windows $uname = posix_uname(); @@ -1692,7 +1816,7 @@ function wfDebugBacktrace( $limit = 0 ) { } if ( $limit && version_compare( PHP_VERSION, '5.4.0', '>=' ) ) { - return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit ), 1 ); + return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit + 1 ), 1 ); } else { return array_slice( debug_backtrace(), 1 ); } @@ -1751,25 +1875,27 @@ function wfBacktrace() { /** * Get the name of the function which called this function + * wfGetCaller( 1 ) is the function with the wfGetCaller() call (ie. __FUNCTION__) + * wfGetCaller( 2 ) [default] is the caller of the function running wfGetCaller() + * wfGetCaller( 3 ) is the parent of that. * * @param $level Int - * @return Bool|string + * @return string */ function wfGetCaller( $level = 2 ) { - $backtrace = wfDebugBacktrace( $level ); + $backtrace = wfDebugBacktrace( $level + 1 ); if ( isset( $backtrace[$level] ) ) { return wfFormatStackFrame( $backtrace[$level] ); } else { - $caller = 'unknown'; + return 'unknown'; } - return $caller; } /** * Return a string consisting of callers in the stack. Useful sometimes * for profiling specific points. * - * @param $limit The maximum depth of the stack frame to return, or false for + * @param $limit int The maximum depth of the stack frame to return, or false for * the entire stack. * @return String */ @@ -1786,7 +1912,7 @@ function wfGetAllCallers( $limit = 3 ) { * Return a string representation of frame * * @param $frame Array - * @return Bool + * @return string */ function wfFormatStackFrame( $frame ) { return isset( $frame['class'] ) ? @@ -1806,13 +1932,7 @@ function wfFormatStackFrame( $frame ) { * @return String */ function wfShowingResults( $offset, $limit ) { - global $wgLang; - return wfMsgExt( - 'showingresults', - array( 'parseinline' ), - $wgLang->formatNum( $limit ), - $wgLang->formatNum( $offset + 1 ) - ); + return wfMessage( 'showingresults' )->numParams( $limit, $offset + 1 )->parse(); } /** @@ -1828,7 +1948,7 @@ function wfShowingResults( $offset, $limit ) { */ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { wfDeprecated( __METHOD__, '1.19' ); - + global $wgLang; $query = wfCgiToArray( $query ); @@ -1856,6 +1976,8 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { * @deprecated since 1.19; use Language::specialList() instead */ function wfSpecialList( $page, $details, $oppositedm = true ) { + wfDeprecated( __METHOD__, '1.19' ); + global $wgLang; return $wgLang->specialList( $page, $details, $oppositedm ); } @@ -1910,7 +2032,7 @@ function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) { * Escapes the given text so that it may be output using addWikiText() * without any linking, formatting, etc. making its way through. This * is achieved by substituting certain characters with HTML entities. - * As required by the callers, is not used. + * As required by the callers, "" is not used. * * @param $text String: text to be escaped * @return String @@ -1978,7 +2100,7 @@ function wfSetBit( &$dest, $bit, $state = true ) { * A wrapper around the PHP function var_export(). * Either print it or add it to the regular output ($wgOut). * - * @param $var A PHP variable to dump. + * @param $var mixed A PHP variable to dump. */ function wfVarDump( $var ) { global $wgOut; @@ -2057,13 +2179,7 @@ function wfResetOutputBuffers( $resetGzipEncoding = true ) { if( $status['name'] == 'ob_gzhandler' ) { // Reset the 'Content-Encoding' field set by this handler // so we can start fresh. - if ( function_exists( 'header_remove' ) ) { - // Available since PHP 5.3.0 - header_remove( 'Content-Encoding' ); - } else { - // We need to provide a valid content-coding. See bug 28069 - header( 'Content-Encoding: identity' ); - } + header_remove( 'Content-Encoding' ); break; } } @@ -2212,11 +2328,7 @@ function wfSuppressWarnings( $end = false ) { } } else { if ( !$suppressCount ) { - // E_DEPRECATED is undefined in PHP 5.2 - if( !defined( 'E_DEPRECATED' ) ) { - define( 'E_DEPRECATED', 8192 ); - } - $originalLevel = error_reporting( E_ALL & ~( E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE | E_DEPRECATED ) ); + $originalLevel = error_reporting( E_ALL & ~( E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE | E_DEPRECATED | E_USER_DEPRECATED ) ); } ++$suppressCount; } @@ -2297,118 +2409,13 @@ define( 'TS_ISO_8601_BASIC', 9 ); * @return Mixed: String / false The same date in the format specified in $outputtype or false */ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) { - $uts = 0; - $da = array(); - $strtime = ''; - - if ( !$ts ) { // We want to catch 0, '', null... but not date strings starting with a letter. - $uts = time(); - $strtime = "@$uts"; - } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/D', $ts, $da ) ) { - # TS_DB - } elseif ( preg_match( '/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/D', $ts, $da ) ) { - # TS_EXIF - } elseif ( preg_match( '/^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/D', $ts, $da ) ) { - # TS_MW - } elseif ( preg_match( '/^-?\d{1,13}$/D', $ts ) ) { - # TS_UNIX - $uts = $ts; - $strtime = "@$ts"; // http://php.net/manual/en/datetime.formats.compound.php - } elseif ( preg_match( '/^\d{2}-\d{2}-\d{4} \d{2}:\d{2}:\d{2}.\d{6}$/', $ts ) ) { - # TS_ORACLE // session altered to DD-MM-YYYY HH24:MI:SS.FF6 - $strtime = preg_replace( '/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3", - str_replace( '+00:00', 'UTC', $ts ) ); - } elseif ( preg_match( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.*\d*)?Z$/', $ts, $da ) ) { - # TS_ISO_8601 - } elseif ( preg_match( '/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(?:\.*\d*)?Z$/', $ts, $da ) ) { - #TS_ISO_8601_BASIC - } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d*[\+\- ](\d\d)$/', $ts, $da ) ) { - # TS_POSTGRES - } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d* GMT$/', $ts, $da ) ) { - # TS_POSTGRES - } elseif (preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.\d\d\d$/', $ts, $da ) ) { - # TS_DB2 - } elseif ( preg_match( '/^[ \t\r\n]*([A-Z][a-z]{2},[ \t\r\n]*)?' . # Day of week - '\d\d?[ \t\r\n]*[A-Z][a-z]{2}[ \t\r\n]*\d{2}(?:\d{2})?' . # dd Mon yyyy - '[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d/S', $ts ) ) { # hh:mm:ss - # TS_RFC2822, accepting a trailing comment. See http://www.squid-cache.org/mail-archive/squid-users/200307/0122.html / r77171 - # The regex is a superset of rfc2822 for readability - $strtime = strtok( $ts, ';' ); - } elseif ( preg_match( '/^[A-Z][a-z]{5,8}, \d\d-[A-Z][a-z]{2}-\d{2} \d\d:\d\d:\d\d/', $ts ) ) { - # TS_RFC850 - $strtime = $ts; - } elseif ( preg_match( '/^[A-Z][a-z]{2} [A-Z][a-z]{2} +\d{1,2} \d\d:\d\d:\d\d \d{4}/', $ts ) ) { - # asctime - $strtime = $ts; - } else { - # Bogus value... + try { + $timestamp = new MWTimestamp( $ts ); + return $timestamp->getTimestamp( $outputtype ); + } catch( TimestampException $e ) { wfDebug("wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n"); - return false; } - - static $formats = array( - TS_UNIX => 'U', - TS_MW => 'YmdHis', - TS_DB => 'Y-m-d H:i:s', - TS_ISO_8601 => 'Y-m-d\TH:i:s\Z', - TS_ISO_8601_BASIC => 'Ymd\THis\Z', - TS_EXIF => 'Y:m:d H:i:s', // This shouldn't ever be used, but is included for completeness - TS_RFC2822 => 'D, d M Y H:i:s', - TS_ORACLE => 'd-m-Y H:i:s.000000', // Was 'd-M-y h.i.s A' . ' +00:00' before r51500 - TS_POSTGRES => 'Y-m-d H:i:s', - TS_DB2 => 'Y-m-d H:i:s', - ); - - if ( !isset( $formats[$outputtype] ) ) { - throw new MWException( 'wfTimestamp() called with illegal output type.' ); - } - - if ( function_exists( "date_create" ) ) { - if ( count( $da ) ) { - $ds = sprintf("%04d-%02d-%02dT%02d:%02d:%02d.00+00:00", - (int)$da[1], (int)$da[2], (int)$da[3], - (int)$da[4], (int)$da[5], (int)$da[6]); - - $d = date_create( $ds, new DateTimeZone( 'GMT' ) ); - } elseif ( $strtime ) { - $d = date_create( $strtime, new DateTimeZone( 'GMT' ) ); - } else { - return false; - } - - if ( !$d ) { - wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n"); - return false; - } - - $output = $d->format( $formats[$outputtype] ); - } else { - if ( count( $da ) ) { - // Warning! gmmktime() acts oddly if the month or day is set to 0 - // We may want to handle that explicitly at some point - $uts = gmmktime( (int)$da[4], (int)$da[5], (int)$da[6], - (int)$da[2], (int)$da[3], (int)$da[1] ); - } elseif ( $strtime ) { - $uts = strtotime( $strtime ); - } - - if ( $uts === false ) { - wfDebug("wfTimestamp() can't parse the timestamp (non 32-bit time? Update php): $outputtype; $ts\n"); - return false; - } - - if ( TS_UNIX == $outputtype ) { - return $uts; - } - $output = gmdate( $formats[$outputtype], $uts ); - } - - if ( ( $outputtype == TS_RFC2822 ) || ( $outputtype == TS_POSTGRES ) ) { - $output .= ' GMT'; - } - - return $output; } /** @@ -2472,11 +2479,10 @@ function swap( &$x, &$y ) { } /** - * Tries to get the system directory for temporary files. The TMPDIR, TMP, and - * TEMP environment variables are then checked in sequence, and if none are set - * try sys_get_temp_dir() for PHP >= 5.2.1. All else fails, return /tmp for Unix - * or C:\Windows\Temp for Windows and hope for the best. - * It is common to call it with tempnam(). + * Tries to get the system directory for temporary files. First + * $wgTmpDirectory is checked, and then the TMPDIR, TMP, and TEMP + * environment variables are then checked in sequence, and if none are + * set try sys_get_temp_dir(). * * NOTE: When possible, use instead the tmpfile() function to create * temporary files to avoid race conditions on file creation, etc. @@ -2484,17 +2490,20 @@ function swap( &$x, &$y ) { * @return String */ function wfTempDir() { - foreach( array( 'TMPDIR', 'TMP', 'TEMP' ) as $var ) { - $tmp = getenv( $var ); + global $wgTmpDirectory; + + if ( $wgTmpDirectory !== false ) { + return $wgTmpDirectory; + } + + $tmpDir = array_map( "getenv", array( 'TMPDIR', 'TMP', 'TEMP' ) ); + + foreach( $tmpDir as $tmp ) { if( $tmp && file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) { return $tmp; } } - if( function_exists( 'sys_get_temp_dir' ) ) { - return sys_get_temp_dir(); - } - # Usual defaults - return wfIsWindows() ? 'C:\Windows\Temp' : '/tmp'; + return sys_get_temp_dir(); } /** @@ -2509,7 +2518,7 @@ function wfMkdirParents( $dir, $mode = null, $caller = null ) { global $wgDirectoryMode; if ( FileBackend::isStoragePath( $dir ) ) { // sanity - throw new MWException( __FUNCTION__ . " given storage path `$dir`."); + throw new MWException( __FUNCTION__ . " given storage path '$dir'." ); } if ( !is_null( $caller ) ) { @@ -2533,62 +2542,12 @@ function wfMkdirParents( $dir, $mode = null, $caller = null ) { if( !$ok ) { // PHP doesn't report the path in its warning message, so add our own to aid in diagnosis. - trigger_error( __FUNCTION__ . ": failed to mkdir \"$dir\" mode $mode", E_USER_WARNING ); + trigger_error( sprintf( "%s: failed to mkdir \"%s\" mode 0%o", __FUNCTION__, $dir, $mode ), + E_USER_WARNING ); } return $ok; } -/** - * Increment a statistics counter - * - * @param $key String - * @param $count Int - */ -function wfIncrStats( $key, $count = 1 ) { - global $wgStatsMethod; - - $count = intval( $count ); - - if( $wgStatsMethod == 'udp' ) { - global $wgUDPProfilerHost, $wgUDPProfilerPort, $wgDBname, $wgAggregateStatsID; - static $socket; - - $id = $wgAggregateStatsID !== false ? $wgAggregateStatsID : $wgDBname; - - if ( !$socket ) { - $socket = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP ); - $statline = "stats/{$id} - {$count} 1 1 1 1 -total\n"; - socket_sendto( - $socket, - $statline, - strlen( $statline ), - 0, - $wgUDPProfilerHost, - $wgUDPProfilerPort - ); - } - $statline = "stats/{$id} - {$count} 1 1 1 1 {$key}\n"; - wfSuppressWarnings(); - socket_sendto( - $socket, - $statline, - strlen( $statline ), - 0, - $wgUDPProfilerHost, - $wgUDPProfilerPort - ); - wfRestoreWarnings(); - } elseif( $wgStatsMethod == 'cache' ) { - global $wgMemc; - $key = wfMemcKey( 'stats', $key ); - if ( is_null( $wgMemc->incr( $key, $count ) ) ) { - $wgMemc->add( $key, $count ); - } - } else { - // Disabled - } -} - /** * Remove a directory and all its content. * Does not hide error. @@ -2686,9 +2645,7 @@ function wfDl( $extension, $fileName = null ) { $canDl = false; $sapi = php_sapi_name(); - if( version_compare( PHP_VERSION, '5.3.0', '<' ) || - $sapi == 'cli' || $sapi == 'cgi' || $sapi == 'embed' ) - { + if( $sapi == 'cli' || $sapi == 'cgi' || $sapi == 'embed' ) { $canDl = ( function_exists( 'dl' ) && is_callable( 'dl' ) && wfIniGetBool( 'enable_dl' ) && !wfIniGetBool( 'safe_mode' ) ); } @@ -2773,13 +2730,15 @@ 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 &$retval optional, will receive the program's exit code. + * @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 * added to the executed command environment. - * @return collected stdout as a string (trailing newlines stripped) + * @param $limits Array optional array with limits(filesize, memory, time) + * this overwrites the global wgShellMax* limits. + * @return string collected stdout as a string (trailing newlines stripped) */ -function wfShellExec( $cmd, &$retval = null, $environ = array() ) { +function wfShellExec( $cmd, &$retval = null, $environ = array(), $limits = array() ) { global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime; static $disabled; @@ -2826,19 +2785,10 @@ function wfShellExec( $cmd, &$retval = null, $environ = array() ) { } $cmd = $envcmd . $cmd; - if ( wfIsWindows() ) { - if ( version_compare( PHP_VERSION, '5.3.0', '<' ) && /* Fixed in 5.3.0 :) */ - ( version_compare( PHP_VERSION, '5.2.1', '>=' ) || php_uname( 's' ) == 'Windows NT' ) ) - { - # Hack to work around PHP's flawed invocation of cmd.exe - # http://news.php.net/php.internals/21796 - # Windows 9x doesn't accept any kind of quotes - $cmd = '"' . $cmd . '"'; - } - } elseif ( php_uname( 's' ) == 'Linux' ) { - $time = intval( $wgMaxShellTime ); - $mem = intval( $wgMaxShellMemory ); - $filesize = intval( $wgMaxShellFileSize ); + 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"; @@ -2879,21 +2829,29 @@ function wfInitShellLocale() { } /** - * Generate a shell-escaped command line string to run a maintenance script. + * Alias to wfShellWikiCmd() + * @see wfShellWikiCmd() + */ +function wfShellMaintenanceCmd( $script, array $parameters = array(), array $options = array() ) { + return wfShellWikiCmd( $script, $parameters, $options ); +} + +/** + * Generate a shell-escaped command line string to run a MediaWiki cli script. * Note that $parameters should be a flat array and an option with an argument * should consist of two consecutive items in the array (do not use "--option value"). - * @param $script string MediaWiki maintenance script path + * @param $script string MediaWiki cli script path * @param $parameters Array Arguments and options to the script * @param $options Array Associative array of options: * 'php': The path to the php executable * 'wrapper': Path to a PHP wrapper to handle the maintenance script * @return Array */ -function wfShellMaintenanceCmd( $script, array $parameters = array(), array $options = array() ) { +function wfShellWikiCmd( $script, array $parameters = array(), array $options = array() ) { global $wgPhpCli; // Give site config file a chance to run the script in a wrapper. // The caller may likely want to call wfBasename() on $script. - wfRunHooks( 'wfShellMaintenanceCmd', array( &$script, &$parameters, &$options ) ); + wfRunHooks( 'wfShellWikiCmd', array( &$script, &$parameters, &$options ) ); $cmd = isset( $options['php'] ) ? array( $options['php'] ) : array( $wgPhpCli ); if ( isset( $options['wrapper'] ) ) { $cmd[] = $options['wrapper']; @@ -3096,11 +3054,11 @@ function wfUseMW( $req_ver ) { /** * Return the final portion of a pathname. - * Reimplemented because PHP5's basename() is buggy with multibyte text. + * Reimplemented because PHP5's "basename()" is buggy with multibyte text. * http://bugs.php.net/bug.php?id=33898 * * PHP's basename() only considers '\' a pathchar on Windows and Netware. - * We'll consider it so always, as we don't want \s in our Unix paths either. + * 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 @@ -3294,11 +3252,6 @@ function wfHttpOnlySafe() { /** * Check if there is sufficent entropy in php's built-in session generation - * PHP's built-in session entropy is enabled if: - * - entropy_file is set or you're on Windows with php 5.3.3+ - * - AND entropy_length is > 0 - * We treat it as disabled if it doesn't have an entropy length of at least 32 - * * @return bool true = there is sufficient entropy */ function wfCheckEntropy() { @@ -3319,6 +3272,10 @@ function wfFixSessionID() { return; } + // PHP's built-in session entropy is enabled if: + // - entropy_file is set or you're on Windows with php 5.3.3+ + // - AND entropy_length is > 0 + // We treat it as disabled if it doesn't have an entropy length of at least 32 $entropyEnabled = wfCheckEntropy(); // If built-in entropy is not enabled or not sufficient override php's built in session id generation code @@ -3334,21 +3291,10 @@ function wfFixSessionID() { * @param $sessionId Bool */ function wfSetupSession( $sessionId = false ) { - global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain, + global $wgSessionsInMemcached, $wgSessionsInObjectCache, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly, $wgSessionHandler; - if( $wgSessionsInMemcached ) { - if ( !defined( 'MW_COMPILED' ) ) { - global $IP; - require_once( "$IP/includes/cache/MemcachedSessions.php" ); - } - session_set_save_handler( 'memsess_open', 'memsess_close', 'memsess_read', - 'memsess_write', 'memsess_destroy', 'memsess_gc' ); - - // It's necessary to register a shutdown function to call session_write_close(), - // because by the time the request shutdown function for the session module is - // called, $wgMemc has already been destroyed. Shutdown functions registered - // this way are called before object destruction. - register_shutdown_function( 'memsess_write_close' ); + if( $wgSessionsInObjectCache || $wgSessionsInMemcached ) { + ObjectCacheSessionHandler::install(); } elseif( $wgSessionHandler && $wgSessionHandler != ini_get( 'session.save_handler' ) ) { # Only set this if $wgSessionHandler isn't null and session.save_handler # hasn't already been set to the desired value (that causes errors) @@ -3507,7 +3453,7 @@ function &wfGetLBFactory() { * Shortcut for RepoGroup::singleton()->findFile() * * @param $title String or Title object - * @param $options Associative array of options: + * @param $options array 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. @@ -3531,7 +3477,7 @@ function wfFindFile( $title, $options = array() ) { * Returns a valid placeholder object if the file does not exist. * * @param $title Title|String - * @return File|null A File, or null if passed an invalid Title + * @return LocalFile|null A File, or null if passed an invalid Title */ function wfLocalFile( $title ) { return RepoGroup::singleton()->getLocalRepo()->newFile( $title ); @@ -3563,19 +3509,26 @@ function wfQueriesMustScale() { /** * Get the path to a specified script file, respecting file * extensions; this is a wrapper around $wgScriptExtension etc. + * except for 'index' and 'load' which use $wgScript/$wgLoadScript * * @param $script String: script filename, sans extension * @return String */ function wfScript( $script = 'index' ) { - global $wgScriptPath, $wgScriptExtension; - return "{$wgScriptPath}/{$script}{$wgScriptExtension}"; + global $wgScriptPath, $wgScriptExtension, $wgScript, $wgLoadScript; + if ( $script === 'index' ) { + return $wgScript; + } else if ( $script === 'load' ) { + return $wgLoadScript; + } else { + return "{$wgScriptPath}/{$script}{$wgScriptExtension}"; + } } /** * Get the script URL. * - * @return script URL + * @return string script URL */ function wfGetScriptUrl() { if( isset( $_SERVER['SCRIPT_NAME'] ) ) { @@ -3689,25 +3642,29 @@ 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. */ function wfGenerateToken( $salt = '' ) { + wfDeprecated( __METHOD__, '1.20' ); $salt = serialize( $salt ); return md5( mt_rand( 0, 0x7fffffff ) . $salt ); } /** * Replace all invalid characters with - + * Additional characters can be defined in $wgIllegalFileChars (see bug 20489) + * By default, $wgIllegalFileChars = ':' * * @param $name Mixed: filename to process * @return String */ function wfStripIllegalFilenameChars( $name ) { global $wgIllegalFileChars; + $illegalFileChars = $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : ''; $name = wfBaseName( $name ); $name = preg_replace( - "/[^" . Title::legalChars() . "]" . - ( $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : '' ) . - "/", + "/[^" . Title::legalChars() . "]" . $illegalFileChars . "/", '-', $name ); @@ -3845,6 +3802,16 @@ function wfGetParserCacheStorage() { return ObjectCache::getInstance( $wgParserCacheType ); } +/** + * Get the cache object used by the language converter + * + * @return BagOStuff + */ +function wfGetLangConverterCacheStorage() { + global $wgLanguageConverterCacheType; + return ObjectCache::getInstance( $wgLanguageConverterCacheType ); +} + /** * Call hook functions defined in $wgHooks * @@ -3868,7 +3835,7 @@ function wfRunHooks( $event, $args = array() ) { * because php might make it negative. * * @throws MWException if $data not long enough, or if unpack fails - * @return Associative array of the extracted data + * @return array Associative array of the extracted data */ function wfUnpack( $format, $data, $length=false ) { if ( $length !== false ) { @@ -3891,3 +3858,84 @@ function wfUnpack( $format, $data, $length=false ) { } return $result; } + +/** + * Determine if an image exists on the 'bad image list'. + * + * The format of MediaWiki:Bad_image_list is as follows: + * * Only list items (lines starting with "*") are considered + * * The first link on a line must be a link to a bad image + * * 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 $contextTitle Title|bool the page on which the image occurs, if known + * @param $blacklist string wikitext of a file blacklist + * @return bool + */ +function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) { + static $badImageCache = null; // based on bad_image_list msg + wfProfileIn( __METHOD__ ); + + # Handle redirects + $redirectTitle = RepoGroup::singleton()->checkRedirect( Title::makeTitle( NS_FILE, $name ) ); + if( $redirectTitle ) { + $name = $redirectTitle->getDbKey(); + } + + # Run the extension hook + $bad = false; + if( !wfRunHooks( 'BadImage', array( $name, &$bad ) ) ) { + wfProfileOut( __METHOD__ ); + return $bad; + } + + $cacheable = ( $blacklist === null ); + if( $cacheable && $badImageCache !== null ) { + $badImages = $badImageCache; + } else { // cache miss + if ( $blacklist === null ) { + $blacklist = wfMessage( 'bad_image_list' )->inContentLanguage()->plain(); // site list + } + # Build the list now + $badImages = array(); + $lines = explode( "\n", $blacklist ); + foreach( $lines as $line ) { + # List items only + if ( substr( $line, 0, 1 ) !== '*' ) { + continue; + } + + # Find all links + $m = array(); + if ( !preg_match_all( '/\[\[:?(.*?)\]\]/', $line, $m ) ) { + continue; + } + + $exceptions = array(); + $imageDBkey = false; + foreach ( $m[1] as $i => $titleText ) { + $title = Title::newFromText( $titleText ); + if ( !is_null( $title ) ) { + if ( $i == 0 ) { + $imageDBkey = $title->getDBkey(); + } else { + $exceptions[$title->getPrefixedDBkey()] = true; + } + } + } + + if ( $imageDBkey !== false ) { + $badImages[$imageDBkey] = $exceptions; + } + } + if ( $cacheable ) { + $badImageCache = $badImages; + } + } + + $contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false; + $bad = isset( $badImages[$name] ) && !isset( $badImages[$name][$contextKey] ); + wfProfileOut( __METHOD__ ); + return $bad; +} diff --git a/includes/HTMLForm.php b/includes/HTMLForm.php index 7326bf5c..5c00b9f6 100644 --- a/includes/HTMLForm.php +++ b/includes/HTMLForm.php @@ -1,4 +1,25 @@ $info, * where $info is an Associative Array with any of the following: * @@ -30,13 +55,14 @@ * the message. * 'label' -- alternatively, a raw text message. Overridden by * label-message + * 'help' -- message text for a message to use as a help text. * 'help-message' -- message key for a message to use as a help text. * can be an array of msg key and then parameters to * the message. - * Overwrites 'help-messages'. + * Overwrites 'help-messages' and 'help'. * 'help-messages' -- array of message key. As above, each item can * be an array of msg key and then parameters. - * Overwrites 'help-message'. + * Overwrites 'help'. * 'required' -- passed through to the object, indicating that it * is a required field. * 'size' -- the length of text fields @@ -51,6 +77,19 @@ * (eg one without the "wp" prefix), specify it here and * it will be used without modification. * + * Since 1.20, you can chain mutators to ease the form generation: + * @par Example: + * @code + * $form = new HTMLForm( $someFields ); + * $form->setMethod( 'get' ) + * ->setWrapperLegendMsg( 'message-key' ) + * ->suppressReset() + * ->prepareForm() + * ->displayForm(); + * @endcode + * Note that you will have prepareForm and displayForm at the end. Other + * methods call done after that would simply not be part of the form :( + * * TODO: Document 'section' / 'subsection' stuff */ class HTMLForm extends ContextSource { @@ -111,7 +150,7 @@ class HTMLForm extends ContextSource { /** * Form action URL. false means we will use the URL to set Title * @since 1.19 - * @var false|string + * @var bool|string */ protected $mAction = false; @@ -120,16 +159,33 @@ class HTMLForm extends ContextSource { protected $mButtons = array(); protected $mWrapperLegend = false; - + /** * If true, sections that contain both fields and subsections will * render their subsections before their fields. - * + * * Subclasses may set this to false to render subsections after fields * instead. */ protected $mSubSectionBeforeFields = true; + /** + * Format in which to display form. For viable options, + * @see $availableDisplayFormats + * @var String + */ + protected $displayFormat = 'table'; + + /** + * Available formats in which to display the form + * @var Array + */ + protected $availableDisplayFormats = array( + 'table', + 'div', + 'raw', + ); + /** * Build a new HTMLForm from an array of field attributes * @param $descriptor Array of Field constructs, as described above @@ -138,13 +194,13 @@ class HTMLForm extends ContextSource { * @param $messagePrefix String a prefix to go in front of default messages */ public function __construct( $descriptor, /*IContextSource*/ $context = null, $messagePrefix = '' ) { - if( $context instanceof IContextSource ){ + if ( $context instanceof IContextSource ) { $this->setContext( $context ); $this->mTitle = false; // We don't need them to set a title $this->mMessagePrefix = $messagePrefix; } else { // B/C since 1.18 - if( is_string( $context ) && $messagePrefix === '' ){ + if ( is_string( $context ) && $messagePrefix === '' ) { // it's actually $messagePrefix $this->mMessagePrefix = $context; } @@ -188,6 +244,30 @@ class HTMLForm extends ContextSource { $this->mFieldTree = $loadedDescriptor; } + /** + * Set format in which to display the form + * @param $format String the name of the format to use, must be one of + * $this->availableDisplayFormats + * @since 1.20 + * @return HTMLForm $this for chaining calls (since 1.20) + */ + public function setDisplayFormat( $format ) { + if ( !in_array( $format, $this->availableDisplayFormats ) ) { + throw new MWException ( 'Display format must be one of ' . print_r( $this->availableDisplayFormats, true ) ); + } + $this->displayFormat = $format; + return $this; + } + + /** + * Getter for displayFormat + * @since 1.20 + * @return String + */ + public function getDisplayFormat() { + return $this->displayFormat; + } + /** * Add the HTMLForm-specific JavaScript, if it hasn't been * done already. @@ -217,13 +297,22 @@ class HTMLForm extends ContextSource { $descriptor['fieldname'] = $fieldname; + # TODO + # This will throw a fatal error whenever someone try to use + # 'class' to feed a CSS class instead of 'cssclass'. Would be + # great to avoid the fatal error and show a nice error. $obj = new $class( $descriptor ); return $obj; } /** - * Prepare form for submission + * Prepare form for submission. + * + * @attention When doing method chaining, that should be the very last + * method call before displayForm(). + * + * @return HTMLForm $this for chaining calls (since 1.20) */ function prepareForm() { # Check if we have the info we need @@ -233,6 +322,7 @@ class HTMLForm extends ContextSource { # Load data from the request. $this->loadData(); + return $this; } /** @@ -249,7 +339,7 @@ class HTMLForm extends ContextSource { $editToken = $this->getRequest()->getVal( 'wpEditToken' ); if ( $this->getUser()->isLoggedIn() || $editToken != null ) { // Session tokens for logged-out users have no security value. - // However, if the user gave one, check it in order to give a nice + // However, if the user gave one, check it in order to give a nice // "session expired" error instead of "permission denied" or such. $submit = $this->getUser()->matchEditToken( $editToken ); } else { @@ -266,7 +356,7 @@ class HTMLForm extends ContextSource { /** * The here's-one-I-made-earlier option: do the submission if - * posted, or display the form with or without funky valiation + * posted, or display the form with or without funky validation * errors * @return Bool or Status whether submission was successful. */ @@ -274,7 +364,7 @@ class HTMLForm extends ContextSource { $this->prepareForm(); $result = $this->tryAuthorizedSubmit(); - if ( $result === true || ( $result instanceof Status && $result->isGood() ) ){ + if ( $result === true || ( $result instanceof Status && $result->isGood() ) ) { return $result; } @@ -307,6 +397,9 @@ class HTMLForm extends ContextSource { } $callback = $this->mSubmitCallback; + if ( !is_callable( $callback ) ) { + throw new MWException( 'HTMLForm: no submit callback provided. Use setSubmitCallback() to set one.' ); + } $data = $this->filterDataForSubmit( $this->mFieldData ); @@ -322,45 +415,60 @@ class HTMLForm extends ContextSource { * 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. + * @return HTMLForm $this for chaining calls (since 1.20) */ function setSubmitCallback( $cb ) { $this->mSubmitCallback = $cb; + return $this; } /** * Set a message to display on a validation error. - * @param $msg Mixed String or Array of valid inputs to wfMsgExt() + * @param $msg Mixed String or Array of valid inputs to wfMessage() * (so each entry can be either a String or Array) + * @return HTMLForm $this for chaining calls (since 1.20) */ function setValidationErrorMessage( $msg ) { $this->mValidationErrorMessage = $msg; + return $this; } /** * Set the introductory message, overwriting any existing message. * @param $msg String complete text of message to display + * @return HTMLForm $this for chaining calls (since 1.20) */ function setIntro( $msg ) { $this->setPreText( $msg ); + return $this; } /** * Set the introductory message, overwriting any existing message. * @since 1.19 * @param $msg String complete text of message to display + * @return HTMLForm $this for chaining calls (since 1.20) */ - function setPreText( $msg ) { $this->mPre = $msg; } + function setPreText( $msg ) { + $this->mPre = $msg; + return $this; + } /** * Add introductory text. * @param $msg String complete text of message to display + * @return HTMLForm $this for chaining calls (since 1.20) */ - function addPreText( $msg ) { $this->mPre .= $msg; } + function addPreText( $msg ) { + $this->mPre .= $msg; + return $this; + } /** * 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 + * @return HTMLForm $this for chaining calls (since 1.20) */ function addHeaderText( $msg, $section = null ) { if ( is_null( $section ) ) { @@ -371,6 +479,7 @@ class HTMLForm extends ContextSource { } $this->mSectionHeaders[$section] .= $msg; } + return $this; } /** @@ -378,6 +487,7 @@ class HTMLForm extends ContextSource { * @since 1.19 * @param $msg String complete text of message to display * @param $section The section to add the header to + * @return HTMLForm $this for chaining calls (since 1.20) */ function setHeaderText( $msg, $section = null ) { if ( is_null( $section ) ) { @@ -385,12 +495,14 @@ class HTMLForm extends ContextSource { } else { $this->mSectionHeaders[$section] = $msg; } + return $this; } /** * 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 + * @return HTMLForm $this for chaining calls (since 1.20) */ function addFooterText( $msg, $section = null ) { if ( is_null( $section ) ) { @@ -401,6 +513,7 @@ class HTMLForm extends ContextSource { } $this->mSectionFooters[$section] .= $msg; } + return $this; } /** @@ -408,6 +521,7 @@ class HTMLForm extends ContextSource { * @since 1.19 * @param $msg String complete text of message to display * @param $section string The section to add the footer text to + * @return HTMLForm $this for chaining calls (since 1.20) */ function setFooterText( $msg, $section = null ) { if ( is_null( $section ) ) { @@ -415,39 +529,65 @@ class HTMLForm extends ContextSource { } else { $this->mSectionFooters[$section] = $msg; } + return $this; } /** * Add text to the end of the display. * @param $msg String complete text of message to display + * @return HTMLForm $this for chaining calls (since 1.20) */ - function addPostText( $msg ) { $this->mPost .= $msg; } + function addPostText( $msg ) { + $this->mPost .= $msg; + return $this; + } /** * Set text at the end of the display. * @param $msg String complete text of message to display + * @return HTMLForm $this for chaining calls (since 1.20) */ - function setPostText( $msg ) { $this->mPost = $msg; } + function setPostText( $msg ) { + $this->mPost = $msg; + return $this; + } /** * 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 $attribs Array + * @return HTMLForm $this for chaining calls (since 1.20) */ public function addHiddenField( $name, $value, $attribs = array() ) { $attribs += array( 'name' => $name ); $this->mHiddenFields[] = array( $value, $attribs ); + return $this; } + /** + * 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 $attribs Array + * @return HTMLForm $this for chaining calls (since 1.20) + */ public function addButton( $name, $value, $id = null, $attribs = null ) { $this->mButtons[] = compact( 'name', 'value', 'id', 'attribs' ); + return $this; } /** * Display the form (sending to $wgOut), with an appropriate error * message or stack of messages, and any validation errors, etc. + * + * @attention You should call prepareForm() before calling this function. + * Moreover, when doing method chaining this should be the very last method + * call just after prepareForm(). + * * @param $submitResult Mixed output from HTMLForm::trySubmit() + * @return Nothing, should be last call */ function displayForm( $submitResult ) { $this->getOutput()->addHTML( $this->getHTML( $submitResult ) ); @@ -478,7 +618,7 @@ class HTMLForm extends ContextSource { } /** - * Wrap the form innards in an actual
element + * Wrap the form innards in an actual "" element * @param $html String HTML contents to wrap. * @return String wrapped HTML. */ @@ -511,15 +651,15 @@ class HTMLForm extends ContextSource { * @return String HTML. */ function getHiddenFields() { - global $wgUsePathInfo; + global $wgArticlePath; $html = ''; - if( $this->getMethod() == 'post' ){ + if ( $this->getMethod() == 'post' ) { $html .= Html::hidden( 'wpEditToken', $this->getUser()->getEditToken(), array( 'id' => 'wpEditToken' ) ) . "\n"; $html .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . "\n"; } - if ( !$wgUsePathInfo && $this->getMethod() == 'get' ) { + if ( strpos( $wgArticlePath, '?' ) !== false && $this->getMethod() == 'get' ) { $html .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . "\n"; } @@ -560,7 +700,7 @@ class HTMLForm extends ContextSource { 'input', array( 'type' => 'reset', - 'value' => wfMsg( 'htmlform-reset' ) + 'value' => $this->msg( 'htmlform-reset' )->text() ) ) . "\n"; } @@ -620,7 +760,7 @@ class HTMLForm extends ContextSource { /** * Format a stack of error messages into a single HTML string * @param $errors Array of message keys/values - * @return String HTML, a
    list of errors + * @return String HTML, a "
      " list of errors */ public static function formatErrors( $errors ) { $errorstr = ''; @@ -636,7 +776,7 @@ class HTMLForm extends ContextSource { $errorstr .= Html::rawElement( 'li', array(), - wfMsgExt( $msg, array( 'parseinline' ), $error ) + wfMessage( $msg, $error )->parse() ); } @@ -648,84 +788,115 @@ class HTMLForm extends ContextSource { /** * Set the text for the submit button * @param $t String plaintext. + * @return HTMLForm $this for chaining calls (since 1.20) */ function setSubmitText( $t ) { $this->mSubmitText = $t; + return $this; } /** * Set the text for the submit button to a message * @since 1.19 * @param $msg String message key + * @return HTMLForm $this for chaining calls (since 1.20) */ public function setSubmitTextMsg( $msg ) { - return $this->setSubmitText( $this->msg( $msg )->escaped() ); + $this->setSubmitText( $this->msg( $msg )->text() ); + return $this; } /** * Get the text for the submit button, either customised or a default. - * @return unknown_type + * @return string */ function getSubmitText() { return $this->mSubmitText ? $this->mSubmitText - : wfMsg( 'htmlform-submit' ); + : $this->msg( 'htmlform-submit' )->text(); } + /** + * @param $name String Submit button name + * @return HTMLForm $this for chaining calls (since 1.20) + */ public function setSubmitName( $name ) { $this->mSubmitName = $name; + return $this; } + /** + * @param $name String Tooltip for the submit button + * @return HTMLForm $this for chaining calls (since 1.20) + */ public function setSubmitTooltip( $name ) { $this->mSubmitTooltip = $name; + return $this; } /** * Set the id for the submit button. * @param $t String. * @todo FIXME: Integrity of $t is *not* validated + * @return HTMLForm $this for chaining calls (since 1.20) */ function setSubmitID( $t ) { $this->mSubmitID = $t; + return $this; } + /** + * @param $id String DOM id for the form + * @return HTMLForm $this for chaining calls (since 1.20) + */ public function setId( $id ) { $this->mId = $id; + return $this; } /** - * Prompt the whole form to be wrapped in a
      , with - * this text as its element. - * @param $legend String HTML to go inside the element. + * Prompt the whole form to be wrapped in a "
      ", with + * this text as its "" element. + * @param $legend String HTML to go inside the "" element. * Will be escaped + * @return HTMLForm $this for chaining calls (since 1.20) */ - public function setWrapperLegend( $legend ) { $this->mWrapperLegend = $legend; } + public function setWrapperLegend( $legend ) { + $this->mWrapperLegend = $legend; + return $this; + } /** - * Prompt the whole form to be wrapped in a
      , with - * this message as its element. + * Prompt the whole form to be wrapped in a "
      ", with + * this message as its "" element. * @since 1.19 * @param $msg String message key + * @return HTMLForm $this for chaining calls (since 1.20) */ public function setWrapperLegendMsg( $msg ) { - return $this->setWrapperLegend( $this->msg( $msg )->escaped() ); + $this->setWrapperLegend( $this->msg( $msg )->text() ); + return $this; } /** * Set the prefix for various default messages - * TODO: currently only used for the
      legend on forms + * @todo currently only used for the "
      " legend on forms * with multiple sections; should be used elsewhre? * @param $p String + * @return HTMLForm $this for chaining calls (since 1.20) */ function setMessagePrefix( $p ) { $this->mMessagePrefix = $p; + return $this; } /** * Set the title for form submission * @param $t Title of page the form is on/should be posted to + * @return HTMLForm $this for chaining calls (since 1.20) */ function setTitle( $t ) { $this->mTitle = $t; + return $this; } /** @@ -741,36 +912,43 @@ class HTMLForm extends ContextSource { /** * Set the method used to submit the form * @param $method String + * @return HTMLForm $this for chaining calls (since 1.20) */ - public function setMethod( $method='post' ){ + public function setMethod( $method = 'post' ) { $this->mMethod = $method; + return $this; } - public function getMethod(){ + public function getMethod() { return $this->mMethod; } /** - * TODO: Document + * @todo Document * @param $fields array[]|HTMLFormField[] array of fields (either arrays or objects) - * @param $sectionName string ID attribute of the
'; + $ret .= ''; $colContents = array(); # Kind of like array_flip() here, but we keep duplicates in an @@ -508,7 +526,7 @@ class CategoryViewer extends ContextSource { if ( $first && $char === $prevchar ) { # We're continuing a previous chunk at the top of a new # column, so add " cont." after the letter. - $ret .= ' ' . wfMsgHtml( 'listingcontinuesabbrev' ); + $ret .= ' ' . wfMessage( 'listingcontinuesabbrev' )->escaped(); } $ret .= "\n"; @@ -558,7 +576,7 @@ class CategoryViewer extends ContextSource { * @return String HTML */ private function pagingLinks( $first, $last, $type = '' ) { - $prevLink = wfMessage( 'prevn' )->numParams( $this->limit )->escaped(); + $prevLink = $this->msg( 'prevn' )->numParams( $this->limit )->escaped(); if ( $first != '' ) { $prevQuery = $this->query; @@ -572,7 +590,7 @@ class CategoryViewer extends ContextSource { ); } - $nextLink = wfMessage( 'nextn' )->numParams( $this->limit )->escaped(); + $nextLink = $this->msg( 'nextn' )->numParams( $this->limit )->escaped(); if ( $last != '' ) { $lastQuery = $this->query; @@ -586,7 +604,7 @@ class CategoryViewer extends ContextSource { ); } - return "($prevLink) ($nextLink)"; + return $this->msg('categoryviewer-pagedlinks')->rawParams($prevLink, $nextLink)->escaped(); } /** @@ -670,8 +688,8 @@ class CategoryViewer extends ContextSource { $this->cat->refreshCounts(); } else { # Case 3: hopeless. Don't give a total count at all. - return wfMessage( "category-$type-count-limited" )->numParams( $rescnt )->parseAsBlock(); + return $this->msg( "category-$type-count-limited" )->numParams( $rescnt )->parseAsBlock(); } - return wfMessage( "category-$type-count" )->numParams( $rescnt, $totalcnt )->parseAsBlock(); + return $this->msg( "category-$type-count" )->numParams( $rescnt, $totalcnt )->parseAsBlock(); } } diff --git a/includes/Categoryfinder.php b/includes/Categoryfinder.php index 4a8ed709..e2b6a0ca 100644 --- a/includes/Categoryfinder.php +++ b/includes/Categoryfinder.php @@ -1,4 +1,25 @@ rawParams( $wgLang->commaList( $displayTags ) )->text(); $markers = Xml::tags( 'span', array( 'class' => 'mw-tag-markers' ), $markers ); return array( $markers, $classes ); @@ -209,17 +227,17 @@ class ChangeTags { if ( !$wgUseTagFilter || !count( self::listDefinedTags() ) ) return $fullForm ? '' : array(); - $data = array( Html::rawElement( 'label', array( 'for' => 'tagfilter' ), wfMsgExt( 'tag-filter', 'parseinline' ) ), - Xml::input( 'tagfilter', 20, $selected ) ); + $data = array( Html::rawElement( 'label', array( 'for' => 'tagfilter' ), wfMessage( 'tag-filter' )->parse() ), + Xml::input( 'tagfilter', 20, $selected, array( 'class' => 'mw-tagfilter-input' ) ) ); if ( !$fullForm ) { return $data; } $html = implode( ' ', $data ); - $html .= "\n" . Xml::element( 'input', array( 'type' => 'submit', 'value' => wfMsg( 'tag-filter-submit' ) ) ); + $html .= "\n" . Xml::element( 'input', array( 'type' => 'submit', 'value' => wfMessage( 'tag-filter-submit' )->text() ) ); $html .= "\n" . Html::hidden( 'title', $title->getPrefixedText() ); - $html = Xml::tags( 'form', array( 'action' => $title->getLocalURL(), 'method' => 'get' ), $html ); + $html = Xml::tags( 'form', array( 'action' => $title->getLocalURL(), 'class' => 'mw-tagfilter-form', 'method' => 'get' ), $html ); return $html; } diff --git a/includes/ChangesFeed.php b/includes/ChangesFeed.php index bcedf2f3..ee4c2d64 100644 --- a/includes/ChangesFeed.php +++ b/includes/ChangesFeed.php @@ -1,4 +1,24 @@ format ) ) { - return; + return null; } $optionsHash = md5( serialize( $opts->getAllValues() ) ) . $wgRenderHashAppend; @@ -107,7 +127,7 @@ class ChangesFeed { * @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 - * @return feed's content on cache hit or false on cache miss + * @return string|bool feed's content on cache hit or false on cache miss */ public function loadFromCache( $lastmod, $timekey, $key ) { global $wgFeedCacheTimeout, $wgOut, $messageMemc; @@ -186,7 +206,7 @@ class ChangesFeed { FeedUtils::formatDiff( $obj ), $url, $obj->rc_timestamp, - ($obj->rc_deleted & Revision::DELETED_USER) ? wfMsgHtml('rev-deleted-user') : $obj->rc_user_text, + ( $obj->rc_deleted & Revision::DELETED_USER ) ? wfMessage( 'rev-deleted-user' )->escaped() : $obj->rc_user_text, $talkpage ); $feed->outItem( $item ); diff --git a/includes/ChangesList.php b/includes/ChangesList.php index fd97e0cb..84677124 100644 --- a/includes/ChangesList.php +++ b/includes/ChangesList.php @@ -1,10 +1,27 @@ tag + * Sets the list to use a "
  • " tag * @param $value Boolean */ public function setWatchlistDivs( $value = true ) { @@ -106,7 +123,7 @@ class ChangesList extends ContextSource { if( !isset( $this->message ) ) { foreach ( explode( ' ', 'cur diff hist last blocklink history ' . 'semicolon-separator pipe-separator' ) as $msg ) { - $this->message[$msg] = wfMsgExt( $msg, array( 'escapenoentities' ) ); + $this->message[$msg] = $this->msg( $msg )->escaped(); } } } @@ -128,7 +145,7 @@ class ChangesList extends ContextSource { } /** - * Provide the element appropriate to a given abbreviated flag, + * Provide the "" element appropriate to a given abbreviated flag, * namely the flag indicating a new page, a minor edit, a bot edit, or an * unpatrolled edit. By default in English it will contain "N", "m", "b", * "!" respectively, plus it will have an appropriate title and class. @@ -146,8 +163,8 @@ class ChangesList extends ContextSource { 'unpatrolled' => array( 'unpatrolledletter', 'recentchanges-label-unpatrolled' ), ); foreach( $messages as &$value ) { - $value[0] = wfMsgExt( $value[0], 'escapenoentities' ); - $value[1] = wfMsgExt( $value[1], 'escapenoentities' ); + $value[0] = wfMessage( $value[0] )->escaped(); + $value[1] = wfMessage( $value[1] )->escaped(); } } @@ -175,6 +192,7 @@ class ChangesList extends ContextSource { $this->rcCacheIndex = 0; $this->lastdate = ''; $this->rclistOpen = false; + $this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' ); return ''; } @@ -182,22 +200,31 @@ class ChangesList extends ContextSource { * Show formatted char difference * @param $old Integer: bytes * @param $new Integer: bytes + * @param $context IContextSource context to use * @return String */ - public static function showCharacterDifference( $old, $new ) { - global $wgRCChangedSizeThreshold, $wgLang, $wgMiserMode; + public static function showCharacterDifference( $old, $new, IContextSource $context = null ) { + global $wgRCChangedSizeThreshold, $wgMiserMode; + + if ( !$context ) { + $context = RequestContext::getMain(); + } + + $new = (int)$new; + $old = (int)$old; $szdiff = $new - $old; - $code = $wgLang->getCode(); + $lang = $context->getLanguage(); + $code = $lang->getCode(); static $fastCharDiff = array(); if ( !isset($fastCharDiff[$code]) ) { - $fastCharDiff[$code] = $wgMiserMode || wfMsgNoTrans( 'rc-change-size' ) === '$1'; + $fastCharDiff[$code] = $wgMiserMode || $context->msg( 'rc-change-size' )->plain() === '$1'; } - $formattedSize = $wgLang->formatNum($szdiff); + $formattedSize = $lang->formatNum( $szdiff ); if ( !$fastCharDiff[$code] ) { - $formattedSize = wfMsgExt( 'rc-change-size', array( 'parsemag' ), $formattedSize ); + $formattedSize = $context->msg( 'rc-change-size', $formattedSize )->text(); } if( abs( $szdiff ) > abs( $wgRCChangedSizeThreshold ) ) { @@ -217,11 +244,34 @@ class ChangesList extends ContextSource { $formattedSizeClass = 'mw-plusminus-neg'; } - $formattedTotalSize = wfMsgExt( 'rc-change-size-new', 'parsemag', $wgLang->formatNum( $new ) ); + $formattedTotalSize = $context->msg( 'rc-change-size-new' )->numParams( $new )->text(); return Html::element( $tag, array( 'dir' => 'ltr', 'class' => $formattedSizeClass, 'title' => $formattedTotalSize ), - wfMessage( 'parentheses', $formattedSize )->plain() ) . $wgLang->getDirMark(); + $context->msg( 'parentheses', $formattedSize )->plain() ) . $lang->getDirMark(); + } + + /** + * Format the character difference of one or several changes. + * + * @param $old RecentChange + * @param $new RecentChange last change to use, if not provided, $old will be used + * @return string HTML fragment + */ + public function formatCharacterDifference( RecentChange $old, RecentChange $new = null ) { + $oldlen = $old->mAttribs['rc_old_len']; + + if ( $new ) { + $newlen = $new->mAttribs['rc_new_len']; + } else { + $newlen = $old->mAttribs['rc_new_len']; + } + + if( $oldlen === null || $newlen === null ) { + return ''; + } + + return self::showCharacterDifference( $oldlen, $newlen, $this->getContext() ); } /** @@ -238,7 +288,7 @@ class ChangesList extends ContextSource { public function insertDateHeader( &$s, $rc_timestamp ) { # Make date header if necessary - $date = $this->getLanguage()->date( $rc_timestamp, true, true ); + $date = $this->getLanguage()->userDate( $rc_timestamp, $this->getUser() ); if( $date != $this->lastdate ) { if( $this->lastdate != '' ) { $s .= "\n"; @@ -252,7 +302,7 @@ class ChangesList extends ContextSource { public function insertLog( &$s, $title, $logtype ) { $page = new LogPage( $logtype ); $logname = $page->getName()->escaped(); - $s .= '(' . Linker::linkKnown( $title, $logname ) . ')'; + $s .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $title, $logname ) )->escaped(); } /** @@ -284,9 +334,9 @@ class ChangesList extends ContextSource { $query ); } - $s .= '(' . $diffLink . $this->message['pipe-separator']; + $diffhist = $diffLink . $this->message['pipe-separator']; # History link - $s .= Linker::linkKnown( + $diffhist .= Linker::linkKnown( $rc->getTitle(), $this->message['hist'], array(), @@ -295,7 +345,7 @@ class ChangesList extends ContextSource { 'action' => 'history' ) ); - $s .= ') . . '; + $s .= $this->msg( 'parentheses' )->rawParams( $diffhist )->escaped() . ' . . '; } /** @@ -316,16 +366,14 @@ class ChangesList extends ContextSource { $articlelink = Linker::linkKnown( $rc->getTitle(), null, - array(), + array( 'class' => 'mw-changeslist-title' ), $params ); if( $this->isDeleted($rc,Revision::DELETED_TEXT) ) { $articlelink = '' . $articlelink . ''; } - # Bolden pages watched by this user - if( $watched ) { - $articlelink = "{$articlelink}"; - } + # To allow for boldening pages watched by this user + $articlelink = "{$articlelink}"; # RTL/LTR marker $articlelink .= $this->getLanguage()->getDirMark(); @@ -340,8 +388,8 @@ class ChangesList extends ContextSource { * @param $rc RecentChange */ public function insertTimestamp( &$s, $rc ) { - $s .= $this->message['semicolon-separator'] . - $this->getLanguage()->time( $rc->mAttribs['rc_timestamp'], true, true ) . ' . . '; + $s .= $this->message['semicolon-separator'] . '' . + $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() ) . ' . . '; } /** @@ -352,7 +400,7 @@ class ChangesList extends ContextSource { */ public function insertUserRelatedLinks( &$s, &$rc ) { if( $this->isDeleted( $rc, Revision::DELETED_USER ) ) { - $s .= ' ' . wfMsgHtml( 'rev-deleted-user' ) . ''; + $s .= ' ' . $this->msg( 'rev-deleted-user' )->escaped() . ''; } else { $s .= $this->getLanguage()->getDirMark() . Linker::userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] ); @@ -364,22 +412,25 @@ class ChangesList extends ContextSource { * Insert a formatted action * * @param $rc RecentChange + * @return string */ public function insertLogEntry( $rc ) { $formatter = LogFormatter::newFromRow( $rc->mAttribs ); + $formatter->setContext( $this->getContext() ); $formatter->setShowUserToolLinks( true ); $mark = $this->getLanguage()->getDirMark(); return $formatter->getActionText() . " $mark" . $formatter->getComment(); } - /** + /** * Insert a formatted comment * @param $rc RecentChange + * @return string */ public function insertComment( $rc ) { if( $rc->mAttribs['rc_type'] != RC_MOVE && $rc->mAttribs['rc_type'] != RC_MOVE_OVER_REDIRECT ) { if( $this->isDeleted( $rc, Revision::DELETED_COMMENT ) ) { - return ' ' . wfMsgHtml( 'rev-deleted-comment' ) . ''; + return ' ' . $this->msg( 'rev-deleted-comment' )->escaped() . ''; } else { return Linker::commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() ); } @@ -397,13 +448,13 @@ class ChangesList extends ContextSource { /** * Returns the string which indicates the number of watching users + * @return string */ protected function numberofWatchingusers( $count ) { static $cache = array(); if( $count > 0 ) { if( !isset( $cache[$count] ) ) { - $cache[$count] = wfMsgExt( 'number_of_watching_users_RCview', - array('parsemag', 'escape' ), $this->getLanguage()->formatNum( $count ) ); + $cache[$count] = $this->msg( 'number_of_watching_users_RCview' )->numParams( $count )->escaped(); } return $cache[$count]; } else { @@ -456,7 +507,7 @@ class ChangesList extends ContextSource { * @param $rc RecentChange */ public function insertRollback( &$s, &$rc ) { - if( !$rc->mAttribs['rc_new'] && $rc->mAttribs['rc_this_oldid'] && $rc->mAttribs['rc_cur_id'] ) { + if( $rc->mAttribs['rc_type'] != RC_NEW && $rc->mAttribs['rc_this_oldid'] && $rc->mAttribs['rc_cur_id'] ) { $page = $rc->getTitle(); /** Check for rollback and edit permissions, disallow special pages, and only * show a link on the top-most revision */ @@ -497,7 +548,7 @@ class ChangesList extends ContextSource { if ( !$rc->mAttribs['rc_patrolled'] ) { if ( $this->getUser()->useRCPatrol() ) { $unpatrolled = true; - } elseif ( $this->getUser()->useNPPatrol() && $rc->mAttribs['rc_new'] ) { + } elseif ( $this->getUser()->useNPPatrol() && $rc->mAttribs['rc_type'] == RC_NEW ) { $unpatrolled = true; } } @@ -513,7 +564,10 @@ class OldChangesList extends ChangesList { /** * Format a line using the old system (aka without any javascript). * - * @param $rc RecentChange + * @param $rc RecentChange, passed by reference + * @param $watched Bool (default false) + * @param $linenumber Int (default null) + * @return string */ public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) { global $wgRCShowChangedSize; @@ -537,11 +591,15 @@ class OldChangesList extends ChangesList { } } + // Indicate watched status on the line to allow for more + // comprehensive styling. + $classes[] = $watched ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched'; + // Moved pages (very very old, not supported anymore) if( $rc->mAttribs['rc_type'] == RC_MOVE || $rc->mAttribs['rc_type'] == RC_MOVE_OVER_REDIRECT ) { // Log entries } elseif( $rc->mAttribs['rc_log_type'] ) { - $logtitle = Title::newFromText( 'Log/'.$rc->mAttribs['rc_log_type'], NS_SPECIAL ); + $logtitle = SpecialPage::getTitleFor( 'Log', $rc->mAttribs['rc_log_type'] ); $this->insertLog( $s, $logtitle, $rc->mAttribs['rc_log_type'] ); // Log entries (old format) or log targets, and special pages } elseif( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) { @@ -555,7 +613,7 @@ class OldChangesList extends ChangesList { # M, N, b and ! (minor, new, bot and unpatrolled) $s .= $this->recentChangesFlags( array( - 'newpage' => $rc->mAttribs['rc_new'], + 'newpage' => $rc->mAttribs['rc_type'] == RC_NEW, 'minor' => $rc->mAttribs['rc_minor'], 'unpatrolled' => $unpatrolled, 'bot' => $rc->mAttribs['rc_bot'] @@ -567,10 +625,10 @@ class OldChangesList extends ChangesList { # Edit/log timestamp $this->insertTimestamp( $s, $rc ); # Bytes added or removed - if( $wgRCShowChangedSize ) { - $cd = $rc->getCharacterDifference(); - if( $cd != '' ) { - $s .= "$cd . . "; + if ( $wgRCShowChangedSize ) { + $cd = $this->formatCharacterDifference( $rc ); + if ( $cd !== '' ) { + $s .= $cd . ' . . '; } } @@ -593,8 +651,7 @@ class OldChangesList extends ChangesList { # How many users watch this page if( $rc->numberofWatchingusers > 0 ) { - $s .= ' ' . wfMsgExt( 'number_of_watching_users_RCview', - array( 'parsemag', 'escape' ), $this->getLanguage()->formatNum( $rc->numberofWatchingusers ) ); + $s .= ' ' . $this->numberofWatchingusers( $rc->numberofWatchingusers ); } if( $this->watchlist ) { @@ -646,7 +703,7 @@ class EnhancedChangesList extends ChangesList { $curIdEq = array( 'curid' => $rc->mAttribs['rc_cur_id'] ); # If it's a new day, add the headline and flush the cache - $date = $this->getLanguage()->date( $rc->mAttribs['rc_timestamp'], true ); + $date = $this->getLanguage()->userDate( $rc->mAttribs['rc_timestamp'], $this->getUser() ); $ret = ''; if( $date != $this->lastdate ) { # Process current cache @@ -675,7 +732,7 @@ class EnhancedChangesList extends ChangesList { $logtitle = SpecialPage::getTitleFor( 'Log', $logType ); $logpage = new LogPage( $logType ); $logname = $logpage->getName()->escaped(); - $clink = '(' . Linker::linkKnown( $logtitle, $logname ) . ')'; + $clink = $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $logtitle, $logname ) )->escaped(); } else { $clink = Linker::link( $rc->getTitle() ); } @@ -694,7 +751,7 @@ class EnhancedChangesList extends ChangesList { $showdifflinks = false; } - $time = $this->getLanguage()->time( $rc->mAttribs['rc_timestamp'], true, true ); + $time = $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() ); $rc->watched = $watched; $rc->link = $clink; $rc->timestamp = $time; @@ -743,7 +800,7 @@ class EnhancedChangesList extends ChangesList { # Make user links if( $this->isDeleted( $rc, Revision::DELETED_USER ) ) { - $rc->userlink = ' ' . wfMsgHtml( 'rev-deleted-user' ) . ''; + $rc->userlink = ' ' . $this->msg( 'rev-deleted-user' )->escaped() . ''; } else { $rc->userlink = Linker::userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] ); $rc->usertalklink = Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] ); @@ -779,6 +836,7 @@ class EnhancedChangesList extends ChangesList { /** * Enhanced RC group + * @return string */ protected function recentChangesBlockGroup( $block ) { global $wgRCShowChangedSize; @@ -786,14 +844,16 @@ class EnhancedChangesList extends ChangesList { wfProfileIn( __METHOD__ ); # Add the namespace and title of the block as part of the class + $classes = array( 'mw-collapsible', 'mw-collapsed', 'mw-enhanced-rc' ); if ( $block[0]->mAttribs['rc_log_type'] ) { # Log entry - $classes = 'mw-collapsible mw-collapsed mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-log-' + $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-' . $block[0]->mAttribs['rc_log_type'] . '-' . $block[0]->mAttribs['rc_title'] ); } else { - $classes = 'mw-collapsible mw-collapsed mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-ns' + $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns' . $block[0]->mAttribs['rc_namespace'] . '-' . $block[0]->mAttribs['rc_title'] ); } + $classes[] = $block[0]->watched ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched'; $r = Html::openElement( 'table', array( 'class' => $classes ) ) . Html::openElement( 'tr' ); @@ -808,7 +868,7 @@ class EnhancedChangesList extends ChangesList { $allLogs = true; foreach( $block as $rcObj ) { $oldid = $rcObj->mAttribs['rc_last_oldid']; - if( $rcObj->mAttribs['rc_new'] ) { + if( $rcObj->mAttribs['rc_type'] == RC_NEW ) { $isnew = true; } // If all log actions to this page were hidden, then don't @@ -847,24 +907,17 @@ class EnhancedChangesList extends ChangesList { $text = $userlink; $text .= $this->getLanguage()->getDirMark(); if( $count > 1 ) { - $text .= ' (' . $this->getLanguage()->formatNum( $count ) . '×)'; + $text .= ' ' . $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->formatNum( $count ) . '×' )->escaped(); } array_push( $users, $text ); } - $users = ' [' . - implode( $this->message['semicolon-separator'], $users ) . ']'; + $users = ' ' + . $this->msg( 'brackets' )->rawParams( + implode( $this->message['semicolon-separator'], $users ) + )->escaped() . ''; - # Title for tags - $expandTitle = htmlspecialchars( wfMsg( 'rc-enhanced-expand' ) ); - $closeTitle = htmlspecialchars( wfMsg( 'rc-enhanced-hide' ) ); - - $tl = "" - . "" - . "{$this->sideArrow()}" - . "" - . "{$this->downArrow()}" - . ""; + $tl = ''; $r .= "
  • $tl
    '.$this->spacerArrow(); $r .= '
    '; $r .= $this->recentChangesFlags( array( - 'newpage' => $rcObj->mAttribs['rc_new'], + 'newpage' => $type == RC_NEW, 'minor' => $rcObj->mAttribs['rc_minor'], 'unpatrolled' => $rcObj->unpatrolled, 'bot' => $rcObj->mAttribs['rc_bot'], @@ -1008,17 +1064,16 @@ class EnhancedChangesList extends ChangesList { $r .= $link . ''; if ( !$type == RC_LOG || $type == RC_NEW ) { - $r .= ' ('; - $r .= $rcObj->curlink; - $r .= $this->message['pipe-separator']; - $r .= $rcObj->lastlink; - $r .= ')'; + $r .= ' ' . $this->msg( 'parentheses' )->rawParams( $rcObj->curlink . $this->message['pipe-separator'] . $rcObj->lastlink )->escaped(); } - $r .= ' . . '; + $r .= ' . . '; # Character diff - if( $wgRCShowChangedSize && $rcObj->getCharacterDifference() ) { - $r .= $rcObj->getCharacterDifference() . ' . . ' ; + if ( $wgRCShowChangedSize ) { + $cd = $this->formatCharacterDifference( $rcObj ); + if ( $cd !== '' ) { + $r .= $cd . ' . . '; + } } if ( $rcObj->mAttribs['rc_type'] == RC_LOG ) { @@ -1051,7 +1106,7 @@ class EnhancedChangesList extends ChangesList { * @param $dir String: one of '', 'd', 'l', 'r' * @param $alt String: text * @param $title String: text - * @return String: HTML tag + * @return String: HTML "" tag */ protected function arrow( $dir, $alt='', $title='' ) { global $wgStylePath; @@ -1064,26 +1119,25 @@ class EnhancedChangesList extends ChangesList { /** * Generate HTML for a right- or left-facing arrow, * depending on language direction. - * @return String: HTML tag + * @return String: HTML "" tag */ protected function sideArrow() { - global $wgLang; - $dir = $wgLang->isRTL() ? 'l' : 'r'; - return $this->arrow( $dir, '+', wfMsg( 'rc-enhanced-expand' ) ); + $dir = $this->getLanguage()->isRTL() ? 'l' : 'r'; + return $this->arrow( $dir, '+', $this->msg( 'rc-enhanced-expand' )->text() ); } /** * Generate HTML for a down-facing arrow * depending on language direction. - * @return String: HTML tag + * @return String: HTML "" tag */ protected function downArrow() { - return $this->arrow( 'd', '-', wfMsg( 'rc-enhanced-hide' ) ); + return $this->arrow( 'd', '-', $this->msg( 'rc-enhanced-hide' )->text() ); } /** * Generate HTML for a spacer image - * @return String: HTML tag + * @return String: HTML "" tag */ protected function spacerArrow() { return $this->arrow( '', codepointToUtf8( 0xa0 ) ); // non-breaking space @@ -1103,18 +1157,20 @@ class EnhancedChangesList extends ChangesList { $type = $rcObj->mAttribs['rc_type']; $logType = $rcObj->mAttribs['rc_log_type']; + $classes = array( 'mw-enhanced-rc' ); if( $logType ) { # Log entry - $classes = 'mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-log-' + $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-' . $logType . '-' . $rcObj->mAttribs['rc_title'] ); } else { - $classes = 'mw-enhanced-rc ' . Sanitizer::escapeClass( 'mw-changeslist-ns' . + $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns' . $rcObj->mAttribs['rc_namespace'] . '-' . $rcObj->mAttribs['rc_title'] ); } + $classes[] = $rcObj->watched ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched'; $r = Html::openElement( 'table', array( 'class' => $classes ) ) . Html::openElement( 'tr' ); - $r .= '' . $this->spacerArrow(); + $r .= ''; # Flag and Timestamp if( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) { $r .= '    '; // 4 flags -> 4 spaces @@ -1129,39 +1185,41 @@ class EnhancedChangesList extends ChangesList { $r .= ' '.$rcObj->timestamp.' '; # Article or log link if( $logType ) { - $logtitle = SpecialPage::getTitleFor( 'Log', $logType ); - $logname = LogPage::logName( $logType ); - $r .= '(' . Linker::linkKnown( $logtitle, htmlspecialchars( $logname ) ) . ')'; + $logPage = new LogPage( $logType ); + $logTitle = SpecialPage::getTitleFor( 'Log', $logType ); + $logName = $logPage->getName()->escaped(); + $r .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $logTitle, $logName ) )->escaped(); } else { $this->insertArticleLink( $r, $rcObj, $rcObj->unpatrolled, $rcObj->watched ); } # Diff and hist links if ( $type != RC_LOG ) { - $r .= ' ('. $rcObj->difflink . $this->message['pipe-separator']; $query['action'] = 'history'; - $r .= Linker::linkKnown( + $r .= ' ' . $this->msg( 'parentheses' )->rawParams( $rcObj->difflink . $this->message['pipe-separator'] . Linker::linkKnown( $rcObj->getTitle(), $this->message['hist'], array(), $query - ) . ')'; + ) )->escaped(); } - $r .= ' . . '; + $r .= ' . . '; # Character diff - if( $wgRCShowChangedSize && ($cd = $rcObj->getCharacterDifference()) ) { - $r .= "$cd . . "; + if ( $wgRCShowChangedSize ) { + $cd = $this->formatCharacterDifference( $rcObj ); + if ( $cd !== '' ) { + $r .= $cd . ' . . '; + } } if ( $type == RC_LOG ) { $r .= $this->insertLogEntry( $rcObj ); - } else { + } else { $r .= ' '.$rcObj->userlink . $rcObj->usertalklink; $r .= $this->insertComment( $rcObj ); - $r .= $this->insertRollback( $r, $rcObj ); + $this->insertRollback( $r, $rcObj ); } # Tags - $classes = explode( ' ', $classes ); $this->insertTags( $r, $rcObj, $classes ); # Show how many people are watching this if enabled $r .= $this->numberofWatchingusers($rcObj->numberofWatchingusers); diff --git a/includes/Collation.php b/includes/Collation.php index 0c510b78..ad2b94b1 100644 --- a/includes/Collation.php +++ b/includes/Collation.php @@ -1,4 +1,24 @@ parse(); @@ -371,6 +392,7 @@ 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. + * @return array */ function findDeletionRegion( $pathName ) { if ( !isset( $this->pathInfo[$pathName] ) ) { @@ -428,6 +450,7 @@ class ConfEditor { * or semicolon. * * The end position is the past-the-end (end + 1) value as per convention. + * @return array */ function findValueRegion( $pathName ) { if ( !isset( $this->pathInfo[$pathName] ) ) { @@ -444,6 +467,7 @@ class ConfEditor { * Find the path name of the last element in the array. * If the array is empty, this will return the \@extra interstitial element. * If the specified path is not found or is not an array, it will return false. + * @return bool|int|string */ function findLastArrayElement( $path ) { // Try for a real element @@ -480,6 +504,7 @@ class ConfEditor { * Find the path name of first element in the array. * If the array is empty, this will return the \@extra interstitial element. * If the specified path is not found or is not an array, it will return false. + * @return bool|int|string */ function findFirstArrayElement( $path ) { // Try for an ordinary element @@ -504,6 +529,7 @@ class ConfEditor { /** * Get the indent string which sits after a given start position. * Returns false if the position is not at the start of the line. + * @return array */ function getIndent( $pos, $key = false, $arrowPos = false ) { $arrowIndent = ' '; @@ -725,6 +751,7 @@ class ConfEditor { /** * Create a ConfEditorToken from an element of token_get_all() + * @return ConfEditorToken */ function newTokenObj( $internalToken ) { if ( is_array( $internalToken ) ) { @@ -776,6 +803,7 @@ class ConfEditor { /** * Get the token $offset steps ahead of the current position. * $offset may be negative, to get tokens behind the current position. + * @return ConfEditorToken */ function getTokenAhead( $offset ) { $pos = $this->pos + $offset; @@ -821,6 +849,7 @@ class ConfEditor { /** * Pop a state from the state stack. + * @return mixed */ function popState() { return array_pop( $this->stateStack ); @@ -829,6 +858,7 @@ class ConfEditor { /** * Returns true if the user input path is valid. * This exists to allow "/" and "@" to be reserved for string path keys + * @return bool */ function validatePath( $path ) { return strpos( $path, '/' ) === false && substr( $path, 0, 1 ) != '@'; @@ -949,6 +979,7 @@ class ConfEditor { /** * Get a readable name for the given token type. + * @return string */ function getTypeName( $type ) { if ( is_int( $type ) ) { @@ -962,6 +993,7 @@ class ConfEditor { * Looks ahead to see if the given type is the next token type, starting * from the current position plus the given offset. Skips any intervening * whitespace. + * @return bool */ function isAhead( $type, $offset = 0 ) { $ahead = $offset; diff --git a/includes/Cookie.php b/includes/Cookie.php index 76739ccc..7984d63e 100644 --- a/includes/Cookie.php +++ b/includes/Cookie.php @@ -1,6 +1,24 @@ =' ) ) ) { diff --git a/includes/DataUpdate.php b/includes/DataUpdate.php new file mode 100644 index 00000000..377b64c0 --- /dev/null +++ b/includes/DataUpdate.php @@ -0,0 +1,124 @@ +beginTransaction(); + $open_transactions[] = $update; + } + + // do work + foreach ( $updates as $update ) { + $update->doUpdate(); + } + + // commit transactions + while ( count( $open_transactions ) > 0 ) { + $trans = array_pop( $open_transactions ); + $trans->commitTransaction(); + } + } catch ( Exception $ex ) { + $exception = $ex; + wfDebug( "Caught exception, will rethrow after rollback: " . $ex->getMessage() ); + } + + // rollback remaining transactions + while ( count( $open_transactions ) > 0 ) { + $trans = array_pop( $open_transactions ); + $trans->rollbackTransaction(); + } + + if ( $exception ) { + throw $exception; // rethrow after cleanup + } + } + +} diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index ef1ef402..8216beb8 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -1,6 +1,7 @@ + * @par Example: + * @code * $wgServer = 'http://example.com'; - * + * @endcode * * This is usually detected correctly by MediaWiki. If MediaWiki detects the * wrong server, it will redirect incorrectly after you save a page. In that @@ -110,28 +136,6 @@ $wgUsePathInfo = */ $wgScriptExtension = '.php'; -/** - * The URL path to index.php. - * - * Will default to "{$wgScriptPath}/index{$wgScriptExtension}" in Setup.php - */ -$wgScript = false; - -/** - * The URL path to redirect.php. This is a script that is used by the Nostalgia - * skin. - * - * Will default to "{$wgScriptPath}/redirect{$wgScriptExtension}" in Setup.php - */ -$wgRedirectScript = false; - -/** - * The URL path to load.php. - * - * Defaults to "{$wgScriptPath}/load{$wgScriptExtension}". - */ -$wgLoadScript = false; - /**@}*/ @@ -154,7 +158,30 @@ $wgLoadScript = false; */ /** - * The URL path of the skins directory. Will default to "{$wgScriptPath}/skins" in Setup.php + * The URL path to index.php. + * + * Defaults to "{$wgScriptPath}/index{$wgScriptExtension}". + */ +$wgScript = false; + +/** + * The URL path to redirect.php. This is a script that is used by the Nostalgia + * skin. + * + * Defaults to "{$wgScriptPath}/redirect{$wgScriptExtension}". + */ +$wgRedirectScript = false; + +/** + * The URL path to load.php. + * + * Defaults to "{$wgScriptPath}/load{$wgScriptExtension}". + */ +$wgLoadScript = false; + +/** + * The URL path of the skins directory. + * Defaults to "{$wgScriptPath}/skins". */ $wgStylePath = false; $wgStyleSheetPath = &$wgStylePath; @@ -173,7 +200,8 @@ $wgLocalStylePath = false; $wgExtensionAssetsPath = false; /** - * Filesystem stylesheets directory. Will default to "{$IP}/skins" in Setup.php + * Filesystem stylesheets directory. + * Defaults to "{$IP}/skins". */ $wgStyleDirectory = false; @@ -181,29 +209,31 @@ $wgStyleDirectory = false; * The URL path for primary article page views. This path should contain $1, * which is replaced by the article title. * - * Will default to "{$wgScript}/$1" or "{$wgScript}?title=$1" in Setup.php, + * Defaults to "{$wgScript}/$1" or "{$wgScript}?title=$1", * depending on $wgUsePathInfo. */ $wgArticlePath = false; /** - * The URL path for the images directory. Will default to "{$wgScriptPath}/images" in Setup.php + * The URL path for the images directory. + * Defaults to "{$wgScriptPath}/images". */ $wgUploadPath = false; /** - * The maximum age of temporary (incomplete) uploaded files + * The filesystem path of the images directory. Defaults to "{$IP}/images". */ -$wgUploadStashMaxAge = 6 * 3600; // 6 hours +$wgUploadDirectory = false; /** - * The filesystem path of the images directory. Defaults to "{$IP}/images". + * Directory where the cached page will be saved. + * Defaults to "{$wgUploadDirectory}/cache". */ -$wgUploadDirectory = false; +$wgFileCacheDirectory = false; /** * The URL path of the wiki logo. The logo size should be 135x135 pixels. - * Will default to "{$wgStylePath}/common/images/wiki.png" in Setup.php + * Defaults to "{$wgStylePath}/common/images/wiki.png". */ $wgLogo = false; @@ -222,7 +252,16 @@ $wgAppleTouchIcon = false; * The local filesystem path to a temporary directory. This is not required to * be web accessible. * - * Will default to "{$wgUploadDirectory}/tmp" in Setup.php + * When this setting is set to false, its value will be set through a call + * to wfTempDir(). See that methods implementation for the actual detection + * logic. + * + * Developers should use the global function wfTempDir() instead of this + * variable. + * + * @see wfTempDir() + * @note Default changed to false in MediaWiki 1.20. + * */ $wgTmpDirectory = false; @@ -242,11 +281,16 @@ $wgUploadStashScalerBaseUrl = false; /** * To set 'pretty' URL paths for actions other than - * plain page views, add to this array. For instance: + * plain page views, add to this array. + * + * @par Example: + * Set pretty URL for the edit action: + * @code * 'edit' => "$wgScriptPath/edit/$1" + * @endcode * - * There must be an appropriate script or rewrite rule - * in place to handle these URLs. + * There must be an appropriate script or rewrite rule in place to handle these + * URLs. */ $wgActionPaths = array(); @@ -260,11 +304,16 @@ $wgActionPaths = array(); /** Uploads have to be specially set up to be secure */ $wgEnableUploads = false; +/** + * The maximum age of temporary (incomplete) uploaded files + */ +$wgUploadStashMaxAge = 6 * 3600; // 6 hours + /** Allows to move images and other media files */ $wgAllowImageMoving = true; /** - * These are additional characters that should be replaced with '-' in file names + * These are additional characters that should be replaced with '-' in filenames */ $wgIllegalFileChars = ":"; @@ -274,9 +323,10 @@ $wgIllegalFileChars = ":"; $wgFileStore = array(); /** - * What directory to place deleted uploads in + * What directory to place deleted uploads in. + * Defaults to "{$wgUploadDirectory}/deleted". */ -$wgDeletedDirectory = false; // Defaults to $wgUploadDirectory/deleted +$wgDeletedDirectory = false; /** * Set this to true if you use img_auth and want the user to see details on why access failed. @@ -308,11 +358,15 @@ $wgImgAuthPublicTest = true; * * For most core repos: * - zones Associative array of zone names that each map to an array with: - * container : backend container name the zone is in - * directory : root path within container for the zone - * Zones default to using - as the - * container name and the container root as the zone directory. - * - url Base public URL + * 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 + * (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. + * Nesting of zone locations within other zones should be avoided. + * - url Public zone URL. The 'zones' settings take precedence. * - hashLevels The number of directory levels for hash-based division of files * - thumbScriptUrl The URL for thumb.php (optional, not recommended) * - transformVia404 Whether to skip media file transformation on parse and rely on a 404 @@ -329,9 +383,11 @@ $wgImgAuthPublicTest = true; * is 0644. * - directory The local filesystem directory where public files are stored. Not used for * some remote repos. - * - thumbDir The base thumbnail directory. Defaults to /thumb. - * - thumbUrl The base thumbnail URL. Defaults to /thumb. - * + * - thumbDir The base thumbnail directory. Defaults to "/thumb". + * - thumbUrl The base thumbnail URL. Defaults to "/thumb". + * - isPrivate Set this if measures should always be taken to keep the files private. + * One should not trust this to assure that the files are not web readable; + * the server configuration should be done manually depending on the backend. * * These settings describe a foreign MediaWiki installation. They are optional, and will be ignored * for local repositories: @@ -343,7 +399,9 @@ $wgImgAuthPublicTest = true; * * - articleUrl Equivalent to $wgArticlePath, e.g. http://en.wikipedia.org/wiki/$1 * - fetchDescription Fetch the text of the remote file description page. Equivalent to - * $wgFetchCommonsDescriptions. + * $wgFetchCommonsDescriptions. + * - abbrvThreshold File names over this size will use the short form of thumbnail names. + * Short thumbnail names only have the width, parameters, and the extension. * * ForeignDBRepo: * - dbType, dbServer, dbUser, dbPassword, dbName, dbFlags @@ -380,10 +438,11 @@ $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) + * - '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. */ $wgFileBackends = array(); @@ -391,8 +450,8 @@ $wgFileBackends = array(); /** * Array of configuration arrays for each lock manager. * Each backend configuration has the following parameters: - * 'name' : A unique name for the lock manger - * 'class' : The lock manger class to use + * - 'name' : A unique name for the lock manager + * - 'class' : The lock manger class to use * Additional parameters are specific to the class used. */ $wgLockManagers = array(); @@ -401,12 +460,13 @@ $wgLockManagers = array(); * Show EXIF data, on by default if available. * Requires PHP's EXIF extension: http://www.php.net/manual/en/ref.exif.php * - * NOTE FOR WINDOWS USERS: - * To enable EXIF functions, add the following lines to the - * "Windows extensions" section of php.ini: - * + * @note FOR WINDOWS USERS: + * To enable EXIF functions, add the following lines to the "Windows + * extensions" section of php.ini: + * @code{.ini} * extension=extensions/php_mbstring.dll * extension=extensions/php_exif.dll + * @endcode */ $wgShowEXIF = function_exists( 'exif_read_data' ); @@ -431,40 +491,64 @@ $wgUpdateCompatibleMetadata = false; * $wgForeignFileRepos variable. */ $wgUseSharedUploads = false; + /** Full path on the web server where shared uploads can be found */ $wgSharedUploadPath = "http://commons.wikimedia.org/shared/images"; + /** Fetch commons image description pages and display them on the local wiki? */ $wgFetchCommonsDescriptions = false; + /** Path on the file system where shared uploads can be found. */ $wgSharedUploadDirectory = "/var/www/wiki3/images"; + /** DB name with metadata about shared directory. Set this to false if the uploads do not come from a wiki. */ $wgSharedUploadDBname = false; + /** Optional table prefix used in database. */ $wgSharedUploadDBprefix = ''; + /** Cache shared metadata in memcached. Don't do this if the commons wiki is in a different memcached domain */ $wgCacheSharedUploads = true; + /** -* Allow for upload to be copied from an URL. Requires Special:Upload?source=web -* The timeout for copy uploads is set by $wgHTTPTimeout. -*/ + * Allow for upload to be copied from an URL. + * The timeout for copy uploads is set by $wgHTTPTimeout. + * You have to assign the user right 'upload_by_url' to a user group, to use this. + */ $wgAllowCopyUploads = false; + /** * Allow asynchronous copy uploads. * This feature is experimental and broken as of r81612. */ $wgAllowAsyncCopyUploads = false; +/** + * A list of domains copy uploads can come from + * + * @since 1.20 + */ +$wgCopyUploadsDomains = array(); + +/** + * Proxy to use for copy upload requests. + * @since 1.20 + */ +$wgCopyUploadProxy = false; + /** * Max size for uploads, in bytes. If not set to an array, applies to all * uploads. If set to an array, per upload type maximums can be set, using the * file and url keys. If the * key is set this value will be used as maximum * for non-specified types. * - * For example: + * @par Example: + * @code * $wgMaxUploadSize = array( * '*' => 250 * 1024, * 'url' => 500 * 1024, * ); + * @endcode * Sets the maximum for all uploads to 250 kB except for upload-by-url, which * will have a maximum of 500 kB. * @@ -474,27 +558,37 @@ $wgMaxUploadSize = 1024*1024*100; # 100MB /** * Point the upload navigation link to an external URL * Useful if you want to use a shared repository by default - * without disabling local uploads (use $wgEnableUploads = false for that) - * e.g. $wgUploadNavigationUrl = 'http://commons.wikimedia.org/wiki/Special:Upload'; + * without disabling local uploads (use $wgEnableUploads = false for that). + * + * @par Example: + * @code + * $wgUploadNavigationUrl = 'http://commons.wikimedia.org/wiki/Special:Upload'; + * @endcode */ $wgUploadNavigationUrl = false; /** * Point the upload link for missing files to an external URL, as with - * $wgUploadNavigationUrl. The URL will get (?|&)wpDestFile= + * $wgUploadNavigationUrl. The URL will get "(?|&)wpDestFile=" * appended to it as appropriate. */ $wgUploadMissingFileUrl = false; /** - * Give a path here to use thumb.php for thumbnail generation on client request, instead of - * generating them on render and outputting a static URL. This is necessary if some of your - * apache servers don't have read/write access to the thumbnail path. + * Give a path here to use thumb.php for thumbnail generation on client + * request, instead of generating them on render and outputting a static URL. + * This is necessary if some of your apache servers don't have read/write + * access to the thumbnail path. * - * Example: + * @par Example: + * @code * $wgThumbnailScriptPath = "{$wgScriptPath}/thumb{$wgScriptExtension}"; + * @endcode */ $wgThumbnailScriptPath = false; +/** + * @see $wgThumbnailScriptPath + */ $wgSharedThumbnailScriptPath = false; /** @@ -507,7 +601,8 @@ $wgSharedThumbnailScriptPath = false; * maintenance/rebuildImages.php to register them in the database. This is no * longer recommended, use maintenance/importImages.php instead. * - * Note that this variable may be ignored if $wgLocalFileRepo is set. + * @note That this variable may be ignored if $wgLocalFileRepo is set. + * @todo Deprecate the setting and ultimately remove it from Core. */ $wgHashedUploadDirectory = true; @@ -532,13 +627,17 @@ $wgRepositoryBaseUrl = "http://commons.wikimedia.org/wiki/File:"; * This is the list of preferred extensions for uploading files. Uploading files * with extensions not in this list will trigger a warning. * - * WARNING: If you add any OpenOffice or Microsoft Office file formats here, + * @warning If you add any OpenOffice or Microsoft Office file formats here, * such as odt or doc, and untrusted users are allowed to upload files, then * your wiki will be vulnerable to cross-site request forgery (CSRF). */ $wgFileExtensions = array( 'png', 'gif', 'jpg', 'jpeg' ); -/** Files with these extensions will never be allowed as uploads. */ +/** + * Files with these extensions will never be allowed as uploads. + * An array of file extensions to blacklist. You should append to this array + * if you want to blacklist additional files. + * */ $wgFileBlacklist = array( # HTML may contain cookie-stealing JavaScript and web bugs 'html', 'htm', 'js', 'jsb', 'mhtml', 'mht', 'xhtml', 'xht', @@ -576,7 +675,7 @@ $wgAllowJavaUploads = false; /** * This is a flag to determine whether or not to check file extensions on upload. * - * WARNING: setting this to false is insecure for public wikis. + * @warning Setting this to false is insecure for public wikis. */ $wgCheckFileExtensions = true; @@ -584,18 +683,21 @@ $wgCheckFileExtensions = true; * If this is turned off, users may override the warning for files not covered * by $wgFileExtensions. * - * WARNING: setting this to false is insecure for public wikis. + * @warning Setting this to false is insecure for public wikis. */ $wgStrictFileExtensions = true; /** * Setting this to true will disable the upload system's checks for HTML/JavaScript. - * THIS IS VERY DANGEROUS on a publicly editable site, so USE wgGroupPermissions - * TO RESTRICT UPLOADING to only those that you trust + * + * @warning THIS IS VERY DANGEROUS on a publicly editable site, so USE + * $wgGroupPermissions TO RESTRICT UPLOADING to only those that you trust */ $wgDisableUploadScriptChecks = false; -/** Warn if uploaded files are larger than this (in bytes), or false to disable*/ +/** + * Warn if uploaded files are larger than this (in bytes), or false to disable + */ $wgUploadSizeWarning = false; /** @@ -622,18 +724,18 @@ $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 ); /** @@ -667,17 +769,18 @@ $wgImageMagickTempDir = false; * %s will be replaced with the source path, %d with the destination * %w and %h will be replaced with the width and height. * - * Example for GraphicMagick: - * + * @par Example for GraphicMagick: + * @code * $wgCustomConvertCommand = "gm convert %s -resize %wx%h %d" - * + * @endcode * * Leave as false to skip this. */ $wgCustomConvertCommand = false; /** - * Some tests and extensions use exiv2 to manipulate the EXIF metadata in some image formats. + * Some tests and extensions use exiv2 to manipulate the EXIF metadata in some + * image formats. */ $wgExiv2Command = '/usr/bin/exiv2'; @@ -699,22 +802,31 @@ $wgSVGConverters = array( 'imgserv' => '$path/imgserv-wrapper -i svg -o png -w$width $input $output', 'ImagickExt' => array( 'SvgHandler::rasterizeImagickExt' ), ); + /** Pick a converter defined in $wgSVGConverters */ $wgSVGConverter = 'ImageMagick'; + /** If not in the executable PATH, specify the SVG converter path. */ $wgSVGConverterPath = ''; + /** Don't scale a SVG larger than this */ $wgSVGMaxSize = 2048; + /** Don't read SVG metadata beyond this point. - * Default is 1024*256 bytes */ + * Default is 1024*256 bytes + */ $wgSVGMetadataCutoff = 262144; /** - * MediaWiki will reject HTMLesque tags in uploaded files due to idiotic browsers which can't - * perform basic stuff like MIME detection and which are vulnerable to further idiots uploading - * crap files as images. When this directive is on, will be allowed in files with - * an "image/svg+xml" MIME type. You should leave this disabled if your web server is misconfigured - * and doesn't send appropriate MIME types for SVG images. + * Disallow <title> element in SVG files. + * + * MediaWiki will reject HTMLesque tags in uploaded files due to idiotic + * browsers which can not perform basic stuff like MIME detection and which are + * vulnerable to further idiots uploading crap files as images. + * + * When this directive is on, "<title>" will be allowed in files with an + * "image/svg+xml" MIME type. You should leave this disabled if your web server + * is misconfigured and doesn't send appropriate MIME types for SVG images. */ $wgAllowTitlesInSVG = false; @@ -744,13 +856,13 @@ $wgMaxAnimatedGifArea = 1.25e7; * For inline display, we need to convert to PNG or JPEG. * Note scaling should work with ImageMagick, but may not with GD scaling. * - * Example: - * <code> + * @par Example: + * @code * // PNG is lossless, but inefficient for photos * $wgTiffThumbnailType = array( 'png', 'image/png' ); * // JPEG is good for photos, but has no transparency support. Bad for diagrams. * $wgTiffThumbnailType = array( 'jpg', 'image/jpeg' ); - * </code> + * @endcode */ $wgTiffThumbnailType = false; @@ -763,7 +875,7 @@ $wgMaxAnimatedGifArea = 1.25e7; $wgThumbnailEpoch = '20030516000000'; /** - * If set, inline scaled images will still produce <img> tags ready for + * If set, inline scaled images will still produce "<img>" tags ready for * output instead of showing an error message. * * This may be useful if errors are transitory, especially if the site @@ -855,20 +967,6 @@ $wgAntivirusSetup = array( 'messagepattern' => '/.*?:(.*)/sim', ), - - #setup for f-prot - 'f-prot' => array ( - 'command' => "f-prot ", - - 'codemap' => array ( - "0" => AV_NO_VIRUS, # no virus - "3" => AV_VIRUS_FOUND, # virus found - "6" => AV_VIRUS_FOUND, # virus found - "*" => AV_SCAN_FAILED, # else scan failed - ), - - 'messagepattern' => '/.*?Infection:(.*)$/m', - ), ); @@ -898,10 +996,11 @@ $wgLoadFileinfoExtension = false; * the mime type to standard output. * The name of the file to process will be appended to the command given here. * If not set or NULL, mime_content_type will be used if available. - * Example: - * <code> + * + * @par Example: + * @code * #$wgMimeDetectorCommand = "file -bi"; # use external mime detector (Linux) - * </code> + * @endcode */ $wgMimeDetectorCommand = null; @@ -937,8 +1036,7 @@ $wgImageLimits = array( array( 640, 480 ), array( 800, 600 ), array( 1024, 768 ), - array( 1280, 1024 ), - array( 10000, 10000 ) + array( 1280, 1024 ) ); /** @@ -956,7 +1054,7 @@ $wgThumbLimits = array( ); /** - * Default parameters for the <gallery> tag + * Default parameters for the "<gallery>" tag */ $wgGalleryOptions = array ( 'imagesPerRow' => 0, // Default number of images per-row in the gallery. 0 -> Adapt to screensize @@ -979,7 +1077,10 @@ $wgThumbUpright = 0.75; $wgDirectoryMode = 0777; /** - * DJVU settings + * @name DJVU settings + * @{ + */ +/** * Path of the djvudump executable * Enable this and $wgDjvuRenderer to enable djvu rendering */ @@ -1004,15 +1105,18 @@ $wgDjvuTxt = null; * Path of the djvutoxml executable * This works like djvudump except much, much slower as of version 3.5. * - * For now I recommend you use djvudump instead. The djvuxml output is + * For now we recommend you use djvudump instead. The djvuxml output is * probably more stable, so we'll switch back to it as soon as they fix * the efficiency problem. * http://sourceforge.net/tracker/index.php?func=detail&aid=1704049&group_id=32953&atid=406583 + * + * @par Example: + * @code + * $wgDjvuToXML = 'djvutoxml'; + * @endcode */ -# $wgDjvuToXML = 'djvutoxml'; $wgDjvuToXML = null; - /** * Shell command for the DJVU post processor * Default: pnmtopng, since ddjvu generates ppm output @@ -1023,6 +1127,7 @@ $wgDjvuPostProcessor = 'pnmtojpeg'; * File extension for the DJVU post processor output */ $wgDjvuOutputExtension = 'jpg'; +/** @} */ # end of DJvu } /** @} */ # end of file uploads } @@ -1099,17 +1204,21 @@ $wgNewPasswordExpiry = 3600 * 24 * 7; $wgUserEmailConfirmationTokenExpiry = 7 * 24 * 60 * 60; /** - * SMTP Mode + * SMTP Mode. + * * For using a direct (authenticated) SMTP server connection. * Default to false or fill an array : - * <code> - * "host" => 'SMTP domain', - * "IDHost" => 'domain for MessageID', - * "port" => "25", - * "auth" => true/false, - * "username" => user, - * "password" => password - * </code> + * + * @code + * $wgSMTP = array( + * 'host' => 'SMTP domain', + * 'IDHost' => 'domain for MessageID', + * 'port' => '25', + * 'auth' => [true|false], + * 'username' => [SMTP username], + * 'password' => [SMTP password], + * ); + * @endcode */ $wgSMTP = false; @@ -1131,9 +1240,9 @@ $wgEnotifFromEditor = false; # It call this to be a "user-preferences-option (UPO)" /** - * Require email authentication before sending mail to an email addres. This is - * highly recommended. It prevents MediaWiki from being used as an open spam - * relay. + * Require email authentication before sending mail to an email address. + * This is highly recommended. It prevents MediaWiki from being used as an open + * spam relay. */ $wgEmailAuthentication = true; @@ -1211,6 +1320,10 @@ $wgDBuser = 'wikiuser'; $wgDBpassword = ''; /** Database type */ $wgDBtype = 'mysql'; +/** Whether to use SSL in DB connection. */ +$wgDBssl = false; +/** Whether to use compression in DB connection. */ +$wgDBcompress = false; /** Separate username for maintenance tasks. Leave as null to use the default. */ $wgDBadminuser = null; @@ -1295,6 +1408,9 @@ $wgSharedTables = array( 'user', 'user_properties' ); * - DBO_TRX -- wrap entire request in a transaction * - DBO_IGNORE -- ignore errors (not useful in LocalSettings.php) * - DBO_NOBUFFER -- turn off buffering (not useful in LocalSettings.php) + * - DBO_PERSISTENT -- enables persistent database connections + * - DBO_SSL -- uses SSL/TLS encryption in database connections, if available + * - DBO_COMPRESS -- uses internal compression in database connections, if available * * - max lag: (optional) Maximum replication lag before a slave will taken out of rotation * - max threads: (optional) Maximum number of running threads @@ -1311,9 +1427,9 @@ $wgSharedTables = array( 'user', 'user_properties' ); * accidental misconfiguration or MediaWiki bugs, set read_only=1 on all your * slaves in my.cnf. You can set read_only mode at runtime using: * - * <code> + * @code * SET @@read_only=1; - * </code> + * @endcode * * Since the effect of writing to a slave is so damaging and difficult to clean * up, we at Wikimedia set read_only=1 in my.cnf on all our DB servers, even @@ -1339,23 +1455,41 @@ $wgMasterWaitTimeout = 10; /** File to log database errors to */ $wgDBerrorLog = false; +/** + * Timezone to use in the error log. + * Defaults to the wiki timezone ($wgLocaltimezone). + * + * A list of useable timezones can found at: + * http://php.net/manual/en/timezones.php + * + * @par Examples: + * @code + * $wgLocaltimezone = 'UTC'; + * $wgLocaltimezone = 'GMT'; + * $wgLocaltimezone = 'PST8PDT'; + * $wgLocaltimezone = 'Europe/Sweden'; + * $wgLocaltimezone = 'CET'; + * @endcode + * + * @since 1.20 + */ +$wgDBerrorLogTZ = false; + /** When to give an error message */ $wgDBClusterTimeout = 10; /** - * Scale load balancer polling time so that under overload conditions, the database server - * receives a SHOW STATUS query at an average interval of this many microseconds + * Scale load balancer polling time so that under overload conditions, the + * database server receives a SHOW STATUS query at an average interval of this + * many microseconds */ $wgDBAvgStatusPoll = 2000; -/** Set to true if using InnoDB tables */ -$wgDBtransactions = false; - /** * Set to true to engage MySQL 4.1/5.0 charset-related features; * for now will just cause sending of 'SET NAMES=utf8' on connect. * - * WARNING: THIS IS EXPERIMENTAL! + * @warning THIS IS EXPERIMENTAL! * * May break if you're not using the table defs from mysql5/tables.sql. * May break if you're upgrading an existing wiki if set differently. @@ -1408,19 +1542,30 @@ $wgCompressRevisions = false; /** * External stores allow including content - * from non database sources following URL links + * from non database sources following URL links. * * Short names of ExternalStore classes may be specified in an array here: + * @code * $wgExternalStores = array("http","file","custom")... + * @endcode * * CAUTION: Access to database might lead to code execution */ $wgExternalStores = false; /** - * An array of external mysql servers, e.g. - * $wgExternalServers = array( 'cluster1' => array( 'srv28', 'srv29', 'srv30' ) ); - * Used by LBFactory_Simple, may be ignored if $wgLBFactoryConf is set to another class. + * An array of external MySQL servers. + * + * @par Example: + * Create a cluster named 'cluster1' containing three servers: + * @code + * $wgExternalServers = array( + * 'cluster1' => array( 'srv28', 'srv29', 'srv30' ) + * ); + * @endcode + * + * Used by LBFactory_Simple, may be ignored if $wgLBFactoryConf is set to + * another class. */ $wgExternalServers = array(); @@ -1429,9 +1574,12 @@ $wgExternalServers = array(); * Part of a URL, e.g. DB://cluster1 * * Can be an array instead of a single string, to enable data distribution. Keys - * must be consecutive integers, starting at zero. Example: + * must be consecutive integers, starting at zero. * + * @par Example: + * @code * $wgDefaultExternalStore = array( 'DB://cluster1', 'DB://cluster2' ); + * @endcode * * @var array */ @@ -1471,17 +1619,10 @@ $wgUseDumbLinkUpdate = false; /** * Anti-lock flags - bitfield - * - ALF_PRELOAD_LINKS: - * Preload links during link update for save - * - ALF_PRELOAD_EXISTENCE: - * Preload cur_id during replaceLinkHolders * - ALF_NO_LINK_LOCK: * Don't use locking reads when updating the link table. This is * necessary for wikis with a high edit rate for performance * reasons, but may cause link table inconsistency - * - ALF_NO_BLOCK_LOCK: - * As for ALF_LINK_LOCK, this flag is a necessity for high-traffic - * wikis. */ $wgAntiLockFlags = 0; @@ -1551,12 +1692,30 @@ $wgMessageCacheType = CACHE_ANYTHING; */ $wgParserCacheType = CACHE_ANYTHING; +/** + * The cache type for storing session data. Used if $wgSessionsInObjectCache is true. + * + * For available types see $wgMainCacheType. + */ +$wgSessionCacheType = CACHE_ANYTHING; + +/** + * The cache type for storing language conversion tables, + * which are used when parsing certain text and interface messages. + * + * For available types see $wgMainCacheType. + * + * @since 1.20 + */ +$wgLanguageConverterCacheType = CACHE_ANYTHING; + /** * Advanced object cache configuration. * * Use this to define the class names and constructor parameters which are used * for the various cache types. Custom cache types may be defined here and - * referenced from $wgMainCacheType, $wgMessageCacheType or $wgParserCacheType. + * referenced from $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType, + * or $wgLanguageConverterCacheType. * * The format is an associative array where the key is a cache identifier, and * the value is an associative array of parameters. The "class" parameter is the @@ -1580,27 +1739,43 @@ $wgObjectCaches = array( 'xcache' => array( 'class' => 'XCacheBagOStuff' ), 'wincache' => array( 'class' => 'WinCacheBagOStuff' ), 'memcached-php' => array( 'class' => 'MemcachedPhpBagOStuff' ), + 'memcached-pecl' => array( 'class' => 'MemcachedPeclBagOStuff' ), 'hash' => array( 'class' => 'HashBagOStuff' ), ); /** - * The expiry time for the parser cache, in seconds. The default is 86.4k - * seconds, otherwise known as a day. + * The expiry time for the parser cache, in seconds. + * The default is 86400 (one day). */ $wgParserCacheExpireTime = 86400; /** - * Select which DBA handler <http://www.php.net/manual/en/dba.requirements.php> to use as CACHE_DBA backend + * Select which DBA handler <http://www.php.net/manual/en/dba.requirements.php> + * to use as CACHE_DBA backend. */ $wgDBAhandler = 'db3'; /** - * Store sessions in MemCached. This can be useful to improve performance, or to - * avoid the locking behaviour of PHP's default session handler, which tends to - * prevent multiple requests for the same user from acting concurrently. + * Deprecated alias for $wgSessionsInObjectCache. + * + * @deprecated 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 + * PHP's default session handler, which tends to prevent multiple requests for + * the same user from acting concurrently. + */ +$wgSessionsInObjectCache = false; + +/** + * The expiry time to use for session storage when $wgSessionsInObjectCache is + * enabled, in seconds. + */ +$wgObjectCacheSessionExpiry = 3600; + /** * This is used for setting php's session.save_handler. In practice, you will * almost never need to change this ever. Other options might be 'user' or @@ -1624,7 +1799,7 @@ $wgMemCachedPersistent = false; /** * Read/write timeout for MemCached server communication, in microseconds. */ -$wgMemCachedTimeout = 100000; +$wgMemCachedTimeout = 500000; /** * Set this to true to make a local copy of the message cache, for use in @@ -1633,9 +1808,9 @@ $wgMemCachedTimeout = 100000; $wgUseLocalMessageCache = false; /** - * Defines format of local cache - * true - Serialized object - * false - PHP source file (Warning - security risk) + * Defines format of local cache. + * - true: Serialized object + * - false: PHP source file (Warning - security risk) */ $wgLocalMessageCacheSerialized = true; @@ -1648,23 +1823,23 @@ $wgAdaptiveMessageCache = false; /** * Localisation cache configuration. Associative array with keys: - * class: The class to use. May be overridden by extensions. + * class: The class to use. May be overridden by extensions. * - * store: The location to store cache data. May be 'files', 'db' or - * 'detect'. If set to "files", data will be in CDB files. If set - * to "db", data will be stored to the database. If set to - * "detect", files will be used if $wgCacheDirectory is set, - * otherwise the database will be used. + * store: The location to store cache data. May be 'files', 'db' or + * 'detect'. If set to "files", data will be in CDB files. If set + * to "db", data will be stored to the database. If set to + * "detect", files will be used if $wgCacheDirectory is set, + * otherwise the database will be used. * - * storeClass: The class name for the underlying storage. If set to a class - * name, it overrides the "store" setting. + * storeClass: The class name for the underlying storage. If set to a class + * name, it overrides the "store" setting. * - * storeDirectory: If the store class puts its data in files, this is the - * directory it will use. If this is false, $wgCacheDirectory - * will be used. + * storeDirectory: If the store class puts its data in files, this is the + * directory it will use. If this is false, $wgCacheDirectory + * will be used. * - * manualRecache: Set this to true to disable cache updates on web requests. - * Use maintenance/rebuildLocalisationCache.php instead. + * manualRecache: Set this to true to disable cache updates on web requests. + * Use maintenance/rebuildLocalisationCache.php instead. */ $wgLocalisationCacheConf = array( 'class' => 'LocalisationCache', @@ -1679,14 +1854,17 @@ $wgCachePages = true; /** * Set this to current time to invalidate all prior cached pages. Affects both - * client- and server-side caching. + * client-side and server-side caching. * You can get the current date on your server by using the command: + * @verbatim * date +%Y%m%d%H%M%S + * @endverbatim */ $wgCacheEpoch = '20030516000000'; /** * Bump this number when changing the global style sheets and JavaScript. + * * It should be appended in the query string of static CSS and JS includes, * to ensure that client-side caches do not keep obsolete copies of global * styles. @@ -1702,12 +1880,6 @@ $wgStyleVersion = '303'; */ $wgUseFileCache = false; -/** - * Directory where the cached page will be saved. - * Will default to "{$wgUploadDirectory}/cache" in Setup.php - */ -$wgFileCacheDirectory = false; - /** * Depth of the subdirectory hierarchy to be created under * $wgFileCacheDirectory. The subdirectories will be named based on @@ -1752,8 +1924,6 @@ $wgSidebarCacheExpiry = 86400; /** * When using the file cache, we can store the cached HTML gzipped to save disk * space. Pages will then also be served compressed to clients that support it. - * THIS IS NOT COMPATIBLE with ob_gzhandler which is now enabled if supported in - * the default LocalSettings.php! If you enable this, remove that setting first. * * Requires zlib support enabled in PHP. */ @@ -1820,10 +1990,12 @@ $wgUseXVO = false; $wgVaryOnXFP = false; /** - * Internal server name as known to Squid, if different. Example: - * <code> + * Internal server name as known to Squid, if different. + * + * @par Example: + * @code * $wgInternalServer = 'http://yourinternal.tld:8000'; - * </code> + * @endcode */ $wgInternalServer = false; @@ -1859,23 +2031,62 @@ $wgSquidServersNoPurge = array(); /** Maximum number of titles to purge in any one client operation */ $wgMaxSquidPurgeTitles = 400; +/** + * Routing configuration for HTCP multicast purging. Add elements here to + * enable HTCP and determine which purges are sent where. If set to an empty + * array, HTCP is disabled. + * + * Each key in this array is a regular expression to match against the purged + * URL, or an empty string to match all URLs. The purged URL is matched against + * the regexes in the order specified, and the first rule whose regex matches + * is used. + * + * Example configuration to send purges for upload.wikimedia.org to one + * multicast group and all other purges to another: + * @code + * $wgHTCPMulticastRouting = array( + * '|^https?://upload\.wikimedia\.org|' => array( + * 'host' => '239.128.0.113', + * 'port' => 4827, + * ), + * '' => array( + * 'host' => '239.128.0.112', + * 'port' => 4827, + * ), + * ); + * @endcode + * + * @since 1.20 + * + * @see $wgHTCPMulticastTTL + */ +$wgHTCPMulticastRouting = array(); + /** * HTCP multicast address. Set this to a multicast IP address to enable HTCP. * * Note that MediaWiki uses the old non-RFC compliant HTCP format, which was * present in the earliest Squid implementations of the protocol. + * + * This setting is DEPRECATED in favor of $wgHTCPMulticastRouting , and kept + * for backwards compatibility only. If $wgHTCPMulticastRouting is set, this + * setting is ignored. If $wgHTCPMulticastRouting is not set and this setting + * is, it is used to populate $wgHTCPMulticastRouting. + * + * @deprecated in favor of $wgHTCPMulticastRouting */ $wgHTCPMulticastAddress = false; /** * HTCP multicast port. + * @deprecated in favor of $wgHTCPMulticastRouting * @see $wgHTCPMulticastAddress */ $wgHTCPPort = 4827; /** * HTCP multicast TTL. - * @see $wgHTCPMulticastAddress + * @see $wgHTCPMulticastRouting */ $wgHTCPMulticastTTL = 1; @@ -1894,11 +2105,12 @@ $wgLanguageCode = 'en'; /** * Some languages need different word forms, usually for different cases. - * Used in Language::convertGrammar(). Example: + * Used in Language::convertGrammar(). * - * <code> + * @par Example: + * @code * $wgGrammarForms['en']['genitive']['car'] = 'car\'s'; - * </code> + * @endcode */ $wgGrammarForms = array(); @@ -1981,7 +2193,7 @@ $wgAllUnicodeFixes = false; * converting a wiki from MediaWiki 1.4 or earlier to UTF-8 without the * burdensome mass conversion of old text data. * - * NOTE! This DOES NOT touch any fields other than old_text.Titles, comments, + * @note This DOES NOT touch any fields other than old_text. Titles, comments, * user names, etc still must be converted en masse in the database before * continuing as a UTF-8 wiki. */ @@ -2090,28 +2302,27 @@ $wgCanonicalLanguageLinks = true; $wgDefaultLanguageVariant = false; /** - * Disabled variants array of language variant conversion. Example: - * <code> + * Disabled variants array of language variant conversion. + * + * @par Example: + * @code * $wgDisabledVariants[] = 'zh-mo'; * $wgDisabledVariants[] = 'zh-my'; - * </code> - * - * or: - * - * <code> - * $wgDisabledVariants = array('zh-mo', 'zh-my'); - * </code> + * @endcode */ $wgDisabledVariants = array(); /** * Like $wgArticlePath, but on multi-variant wikis, this provides a * path format that describes which parts of the URL contain the - * language variant. For Example: + * language variant. * - * $wgLanguageCode = 'sr'; - * $wgVariantArticlePath = '/$2/$1'; - * $wgArticlePath = '/wiki/$1'; + * @par Example: + * @code + * $wgLanguageCode = 'sr'; + * $wgVariantArticlePath = '/$2/$1'; + * $wgArticlePath = '/wiki/$1'; + * @endcode * * A link to /wiki/ would be redirected to /sr/Главна_страна * @@ -2128,19 +2339,23 @@ $wgVariantArticlePath = false; $wgLoginLanguageSelector = false; /** - * When translating messages with wfMsg(), it is not always clear what should - * be considered UI messages and what should be content messages. + * When translating messages with wfMessage(), it is not always clear what + * should be considered UI messages and what should be content messages. * * For example, for the English Wikipedia, there should be only one 'mainpage', * so when getting the link for 'mainpage', we should treat it as site content - * and call wfMsgForContent(), but for rendering the text of the link, we call - * wfMsg(). The code behaves this way by default. However, sites like the - * Wikimedia Commons do offer different versions of 'mainpage' and the like for - * different languages. This array provides a way to override the default - * behavior. For example, to allow language-specific main page and community - * portal, set - * - * $wgForceUIMsgAsContentMsg = array( 'mainpage', 'portal-url' ); + * and call ->inContentLanguage()->text(), but for rendering the text of the + * link, we call ->text(). The code behaves this way by default. However, + * sites like the Wikimedia Commons do offer different versions of 'mainpage' + * and the like for different languages. This array provides a way to override + * the default behavior. + * + * @par Example: + * To allow language-specific main page and community + * portal: + * @code + * $wgForceUIMsgAsContentMsg = array( 'mainpage', 'portal-url' ); + * @endcode */ $wgForceUIMsgAsContentMsg = array(); @@ -2155,13 +2370,17 @@ $wgForceUIMsgAsContentMsg = array(); * Timezones can be translated by editing MediaWiki messages of type * timezone-nameinlowercase like timezone-utc. * - * Examples: - * <code> + * A list of useable timezones can found at: + * http://php.net/manual/en/timezones.php + * + * @par Examples: + * @code + * $wgLocaltimezone = 'UTC'; * $wgLocaltimezone = 'GMT'; * $wgLocaltimezone = 'PST8PDT'; * $wgLocaltimezone = 'Europe/Sweden'; * $wgLocaltimezone = 'CET'; - * </code> + * @endcode */ $wgLocaltimezone = null; @@ -2182,7 +2401,7 @@ $wgLocalTZoffset = null; * language variant conversion is disabled in interface messages. Setting this * to true re-enables it. * - * This variable should be removed (implicitly false) in 1.20 or earlier. + * @todo This variable should be removed (implicitly false) in 1.20 or earlier. */ $wgBug34832TransitionalRollback = true; @@ -2254,11 +2473,6 @@ $wgAllowRdfaAttributes = false; */ $wgAllowMicrodataAttributes = false; -/** - * Cleanup as much presentational html like valign -> css vertical-align as we can - */ -$wgCleanupPresentationalAttributes = true; - /** * Should we try to make our HTML output well-formed XML? If set to false, * output will be a few bytes shorter, and the HTML will arguably be more @@ -2279,10 +2493,14 @@ $wgWellFormedXml = true; /** * Permit other namespaces in addition to the w3.org default. - * Use the prefix for the key and the namespace for the value. For - * example: + * + * Use the prefix for the key and the namespace for the value. + * + * @par Example: + * @code * $wgXhtmlNamespaces['svg'] = 'http://www.w3.org/2000/svg'; - * Normally we wouldn't have to define this in the root <html> + * @endCode + * Normally we wouldn't have to define this in the root "<html>" * element, but IE needs it there in some circumstances. * * This is ignored if $wgHtml5 is true, for the same reason as @@ -2293,7 +2511,7 @@ $wgXhtmlNamespaces = array(); /** * Show IP address, for non-logged in users. It's necessary to switch this off * for some forms of caching. - * Will disable file cache. + * @warning Will disable file cache. */ $wgShowIPinHeader = true; @@ -2464,15 +2682,16 @@ $wgExperimentalHtmlIds = false; * The value should be either a string or an array. If it is a string it will be output * directly as html, however some skins may choose to ignore it. An array is the preferred format * for the icon, the following keys are used: - * src: An absolute url to the image to use for the icon, this is recommended + * - src: An absolute url to the image to use for the icon, this is recommended * but not required, however some skins will ignore icons without an image - * url: The url to use in the <a> arround the text or icon, if not set an <a> will not be outputted - * alt: This is the text form of the icon, it will be displayed without an image in + * - url: The url to use in the a element arround 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. - * width and height: If the icon specified by src is not of the standard size + * - width and height: If the icon specified by src is not of the standard size * you can specify the size of image to use with these keys. * Otherwise they will default to the standard 88x31. + * @todo Reformat documentation. */ $wgFooterIcons = array( "copyright" => array( @@ -2488,23 +2707,24 @@ $wgFooterIcons = array( ); /** - * Login / create account link behavior when it's possible for anonymous users to create an account - * true = use a combined login / create account link - * false = split login and create account into two separate links + * Login / create account link behavior when it's possible for anonymous users + * to create an account. + * - true = use a combined login / create account link + * - false = split login and create account into two separate links */ -$wgUseCombinedLoginLink = true; +$wgUseCombinedLoginLink = false; /** - * Search form behavior for Vector skin only - * true = use an icon search button - * false = use Go & Search buttons + * Search form look for Vector skin only. + * - true = use an icon search button + * - false = use Go & Search buttons */ $wgVectorUseSimpleSearch = false; /** - * Watch and unwatch as an icon rather than a link for Vector skin only - * true = use an icon watch/unwatch button - * false = use watch/unwatch text link + * Watch and unwatch as an icon rather than a link for Vector skin only. + * - true = use an icon watch/unwatch button + * - false = use watch/unwatch text link */ $wgVectorUseIconWatch = false; @@ -2534,6 +2754,16 @@ $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. + * + * @since 1.20 + */ +$wgShowRollbackEditCount = 10; + /** @} */ # End of output format settings } /*************************************************************************//** @@ -2542,17 +2772,21 @@ $wgSend404Code = true; */ /** - * Client-side resource modules. Extensions should add their module definitions - * here. + * Client-side resource modules. + * + * Extensions should add their resource loader module definitions + * to the $wgResourceModules variable. * - * Example: + * @par Example: + * @code * $wgResourceModules['ext.myExtension'] = array( * 'scripts' => 'myExtension.js', * 'styles' => 'myExtension.css', * 'dependencies' => array( 'jquery.cookie', 'jquery.tabIndex' ), - * 'localBasePath' => dirname( __FILE__ ), + * 'localBasePath' => __DIR__, * 'remoteExtPath' => 'MyExtension', * ); + * @endcode */ $wgResourceModules = array(); @@ -2561,22 +2795,26 @@ $wgResourceModules = array(); * built-in source that is not in this array, but defined by * ResourceLoader::__construct() so that it cannot be unset. * - * Example: + * @par Example: + * @code * $wgResourceLoaderSources['foo'] = array( * 'loadScript' => 'http://example.org/w/load.php', * 'apiScript' => 'http://example.org/w/api.php' * ); + * @endcode */ $wgResourceLoaderSources = array(); /** - * Default 'remoteBasePath' value for resource loader modules. + * Default 'remoteBasePath' value for instances of ResourceLoaderFileModule. * If not set, then $wgScriptPath will be used as a fallback. */ $wgResourceBasePath = null; /** - * Maximum time in seconds to cache resources served by the resource loader + * Maximum time in seconds to cache resources served by the resource loader. + * + * @todo Document array structure */ $wgResourceLoaderMaxage = array( 'versioned' => array( @@ -2592,8 +2830,9 @@ $wgResourceLoaderMaxage = array( ); /** - * The default debug mode (on/off) for of ResourceLoader requests. This will still - * be overridden when the debug URL parameter is used. + * The default debug mode (on/off) for of ResourceLoader requests. + * + * This will still be overridden when the debug URL parameter is used. */ $wgResourceLoaderDebug = false; @@ -2619,33 +2858,54 @@ $wgResourceLoaderMinifierMaxLineLength = 1000; /** * Whether to include the mediawiki.legacy JS library (old wikibits.js), and its - * dependencies + * dependencies. */ $wgIncludeLegacyJavaScript = true; /** - * Whether to preload the mediawiki.util module as blocking module in the top queue. - * Before MediaWiki 1.19, modules used to load slower/less asynchronous which allowed - * modules to lack dependencies on 'popular' modules that were likely loaded already. + * Whether to preload the mediawiki.util module as blocking module in the top + * queue. + * + * Before MediaWiki 1.19, modules used to load slower/less asynchronous which + * allowed modules to lack dependencies on 'popular' modules that were likely + * loaded already. + * * This setting is to aid scripts during migration by providing mediawiki.util * unconditionally (which was the most commonly missed dependency). - * It doesn't cover all missing dependencies obviously but should fix most of them. + * It doesn't cover all missing dependencies obviously but should fix most of + * them. + * * This should be removed at some point after site/user scripts have been fixed. - * Enable this if your wiki has a large amount of user/site scripts that are lacking - * dependencies. + * Enable this if your wiki has a large amount of user/site scripts that are + * lacking dependencies. + * @todo Deprecate */ $wgPreloadJavaScriptMwUtil = false; /** - * Whether or not to assing configuration variables to the global window object. - * If this is set to false, old code using deprecated variables like: - * " if ( window.wgRestrictionEdit ) ..." + * Whether or not to assign configuration variables to the global window object. + * + * If this is set to false, old code using deprecated variables will no longer + * work. + * + * @par Example of legacy code: + * @code{,js} + * if ( window.wgRestrictionEdit ) { ... } + * @endcode * or: - * " if ( wgIsArticle ) ..." - * will no longer work and needs to use mw.config instead. For example: - * " if ( mw.config.exists('wgRestrictionEdit') )" - * or - * " if ( mw.config.get('wgIsArticle') )". + * @code{,js} + * if ( wgIsArticle ) { ... } + * @endcode + * + * Instead, one needs to use mw.config. + * @par Example using mw.config global configuration: + * @code{,js} + * if ( mw.config.exists('wgRestrictionEdit') ) { ... } + * @endcode + * or: + * @code{,js} + * if ( mw.config.get('wgIsArticle') ) { ... } + * @endcode */ $wgLegacyJavaScriptGlobals = true; @@ -2663,8 +2923,8 @@ $wgLegacyJavaScriptGlobals = true; $wgResourceLoaderMaxQueryLength = -1; /** - * If set to true, JavaScript modules loaded from wiki pages will be parsed prior - * to minification to validate it. + * If set to true, JavaScript modules loaded from wiki pages will be parsed + * prior to minification to validate it. * * Parse errors will result in a JS exception being thrown during module load, * which avoids breaking other modules loaded in the same request. @@ -2682,7 +2942,7 @@ $wgResourceLoaderValidateJS = true; $wgResourceLoaderValidateStaticJS = false; /** - * If set to true, asynchronous loading of bottom-queue scripts in the <head> + * If set to true, asynchronous loading of bottom-queue scripts in the "<head>" * will be enabled. This is an experimental feature that's supposed to make * JavaScript load faster. */ @@ -2718,19 +2978,25 @@ $wgMetaNamespaceTalk = false; * names of existing namespaces. Extensions developers should use * $wgCanonicalNamespaceNames. * - * PLEASE NOTE: Once you delete a namespace, the pages in that namespace will + * @warning Once you delete a namespace, the pages in that namespace will * no longer be accessible. If you rename it, then you can access them through * the new namespace name. * * Custom namespaces should start at 100 to avoid conflicting with standard * namespaces, and should always follow the even/odd main/talk pattern. + * + * @par Example: + * @code + * $wgExtraNamespaces = array( + * 100 => "Hilfe", + * 101 => "Hilfe_Diskussion", + * 102 => "Aide", + * 103 => "Discussion_Aide" + * ); + * @endcode + * + * @todo Add a note about maintenance/namespaceDupes.php */ -# $wgExtraNamespaces = array( -# 100 => "Hilfe", -# 101 => "Hilfe_Diskussion", -# 102 => "Aide", -# 103 => "Discussion_Aide" -# ); $wgExtraNamespaces = array(); /** @@ -2742,18 +3008,22 @@ $wgExtraNamespaces = array(); $wgExtraGenderNamespaces = array(); /** - * Namespace aliases + * Namespace aliases. + * * These are alternate names for the primary localised namespace names, which * are defined by $wgExtraNamespaces and the language file. If a page is * requested with such a prefix, the request will be redirected to the primary * name. * * Set this to a map from namespace names to IDs. - * Example: + * + * @par Example: + * @code * $wgNamespaceAliases = array( * 'Wikipedian' => NS_USER, * 'Help' => 100, * ); + * @endcode */ $wgNamespaceAliases = array(); @@ -2768,8 +3038,8 @@ $wgNamespaceAliases = array(); * - + Enabled by default, but doesn't work with path to query rewrite rules, corrupted by apache * - ? Enabled by default, but doesn't work with path to PATH_INFO rewrites * - * All three of these punctuation problems can be avoided by using an alias, instead of a - * rewrite rule of either variety. + * All three of these punctuation problems can be avoided by using an alias, + * instead of a rewrite rule of either variety. * * The problem with % is that when using a path to query rewrite rule, URLs are * double-unescaped: once by Apache's path conversion code, and again by PHP. So @@ -2795,33 +3065,47 @@ $wgLocalInterwiki = false; */ $wgInterwikiExpiry = 10800; -/** Interwiki caching settings. - $wgInterwikiCache specifies path to constant database file - This cdb database is generated by dumpInterwiki from maintenance - and has such key formats: - dbname:key - a simple key (e.g. enwiki:meta) - _sitename:key - site-scope key (e.g. wiktionary:meta) - __global:key - global-scope key (e.g. __global:meta) - __sites:dbname - site mapping (e.g. __sites:enwiki) - Sites mapping just specifies site name, other keys provide - "local url" data layout. - $wgInterwikiScopes specify number of domains to check for messages: - 1 - Just wiki(db)-level - 2 - wiki and global levels - 3 - site levels - $wgInterwikiFallbackSite - if unable to resolve from cache +/** + * @name Interwiki caching settings. + * @{ + */ +/** + *$wgInterwikiCache specifies path to constant database file. + * + * This cdb database is generated by dumpInterwiki from maintenance and has + * such key formats: + * - dbname:key - a simple key (e.g. enwiki:meta) + * - _sitename:key - site-scope key (e.g. wiktionary:meta) + * - __global:key - global-scope key (e.g. __global:meta) + * - __sites:dbname - site mapping (e.g. __sites:enwiki) + * + * Sites mapping just specifies site name, other keys provide "local url" + * data layout. */ $wgInterwikiCache = false; +/** + * Specify number of domains to check for messages. + * - 1: Just wiki(db)-level + * - 2: wiki and global levels + * - 3: site levels + */ $wgInterwikiScopes = 3; +/** + * $wgInterwikiFallbackSite - if unable to resolve from cache + */ $wgInterwikiFallbackSite = 'wiki'; +/** @} */ # end of Interwiki caching settings. /** * If local interwikis are set up which allow redirects, * set this regexp to restrict URLs which will be displayed * as 'redirected from' links. * + * @par Example: * It might look something like this: + * @code * $wgRedirectSources = '!^https?://[a-z-]+\.wikipedia\.org/!'; + * @endcode * * Leave at false to avoid displaying any incoming redirect markers. * This does not affect intra-wiki redirects, which don't change @@ -2831,7 +3115,8 @@ $wgRedirectSources = false; /** * Set this to false to avoid forcing the first letter of links to capitals. - * WARNING: may break links! This makes links COMPLETELY case-sensitive. Links + * + * @warning may break links! This makes links COMPLETELY case-sensitive. Links * appearing with a capital at the beginning of a sentence will *not* go to the * same place as links in the middle of a sentence using a lowercase initial. */ @@ -2845,7 +3130,11 @@ $wgCapitalLinks = true; * associated content namespaces, the values for those are ignored in favor of the * subject namespace's setting. Setting for NS_MEDIA is taken automatically from * NS_FILE. - * EX: $wgCapitalLinkOverrides[ NS_FILE ] = false; + * + * @par Example: + * @code + * $wgCapitalLinkOverrides[ NS_FILE ] = false; + * @endcode */ $wgCapitalLinkOverrides = array(); @@ -2930,10 +3219,18 @@ $wgParserConf = array( $wgMaxTocLevel = 999; /** - * A complexity limit on template expansion + * A complexity limit on template expansion: the maximum number of nodes visited + * by PPFrame::expand() */ $wgMaxPPNodeCount = 1000000; +/** + * A complexity limit on template expansion: the maximum number of nodes + * generated by Preprocessor::preprocessToObj() + */ +$wgMaxGeneratedPPNodeCount = 1000000; + + /** * Maximum recursion depth for templates within templates. * The current parser adds two levels to the PHP call stack for each template, @@ -2978,11 +3275,11 @@ $wgAllowExternalImages = false; * You can use this to set up a trusted, simple repository of images. * You may also specify an array of strings to allow multiple sites * - * Examples: - * <code> + * @par Examples: + * @code * $wgAllowExternalImagesFrom = 'http://127.0.0.1/'; * $wgAllowExternalImagesFrom = array( 'http://127.0.0.1/', 'http://example.com' ); - * </code> + * @endcode */ $wgAllowExternalImagesFrom = ''; @@ -2997,7 +3294,7 @@ $wgAllowExternalImagesFrom = ''; $wgEnableImageWhitelist = true; /** - * A different approach to the above: simply allow the <img> tag to be used. + * A different approach to the above: simply allow the "<img>" tag to be used. * This allows you to specify alt text and other attributes, copy-paste HTML to * your wiki more easily, etc. However, allowing external images in any manner * will allow anyone with editing rights to snoop on your visitors' IP @@ -3039,7 +3336,7 @@ $wgTidyInternal = extension_loaded( 'tidy' ); */ $wgDebugTidy = false; -/** Allow raw, unchecked HTML in <html>...</html> sections. +/** Allow raw, unchecked HTML in "<html>...</html>" sections. * THIS IS VERY DANGEROUS on a publicly editable site, so USE wgGroupPermissions * TO RESTRICT EDITING to only those that you trust */ @@ -3245,7 +3542,6 @@ $wgDefaultUserOptions = array( 'gender' => 'unknown', 'hideminor' => 0, 'hidepatrolled' => 0, - 'highlightbroken' => 1, 'imagesize' => 2, 'justify' => 0, 'math' => 1, @@ -3308,7 +3604,7 @@ $wgInvalidUsernameCharacters = '@'; /** * Character used as a delimiter when testing for interwiki userrights * (In Special:UserRights, it is possible to modify users on different - * databases if the delimiter is used, e.g. Someuser@enwiki). + * databases if the delimiter is used, e.g. "Someuser@enwiki"). * * It is recommended that you have this delimiter in * $wgInvalidUsernameCharacters above, or you will not be able to @@ -3405,12 +3701,19 @@ $wgSysopEmailBans = true; * Limits on the possible sizes of range blocks. * * CIDR notation is hard to understand, it's easy to mistakenly assume that a - * /1 is a small range and a /31 is a large range. Setting this to half the - * number of bits avoids such errors. + * /1 is a small range and a /31 is a large range. For IPv4, setting a limit of + * half the number of bits avoids such errors, and allows entire ISPs to be + * blocked using a small number of range blocks. + * + * For IPv6, RFC 3177 recommends that a /48 be allocated to every residential + * customer, so range blocks larger than /64 (half the number of bits) will + * plainly be required. RFC 4692 implies that a very large ISP may be + * allocated a /19 if a generous HD-Ratio of 0.8 is used, so we will use that + * as our limit. As of 2012, blocking the whole world would require a /4 range. */ $wgBlockCIDRLimit = array( 'IPv4' => 16, # Blocks larger than a /16 (64k addresses) will not be allowed - 'IPv6' => 64, # 2^64 = ~1.8x10^19 addresses + 'IPv6' => 19, ); /** @@ -3423,18 +3726,19 @@ $wgBlockCIDRLimit = array( $wgBlockDisablesLogin = false; /** - * Pages anonymous user may see as an array, e.g. + * Pages anonymous user may see, set as an array of pages titles. * - * <code> + * @par Example: + * @code * $wgWhitelistRead = array ( "Main Page", "Wikipedia:Help"); - * </code> + * @endcode * * Special:Userlogin and Special:ChangePassword are always whitelisted. * - * NOTE: This will only work if $wgGroupPermissions['*']['read'] is false -- + * @note This will only work if $wgGroupPermissions['*']['read'] is false -- * see below. Otherwise, ALL pages are accessible, regardless of this setting. * - * Also note that this will only protect _pages in the wiki_. Uploaded files + * @note Also that this will only protect _pages in the wiki_. Uploaded files * will remain readable. You can use img_auth.php to protect uploaded files, * see http://www.mediawiki.org/wiki/Manual:Image_Authorization */ @@ -3448,6 +3752,7 @@ $wgEmailConfirmToEdit = false; /** * Permission keys given to users in each group. + * * This is an array where the keys are all groups and each value is an * array of the format (right => boolean). * @@ -3538,7 +3843,6 @@ $wgGroupPermissions['sysop']['reupload'] = true; $wgGroupPermissions['sysop']['reupload-shared'] = true; $wgGroupPermissions['sysop']['unwatchedpages'] = true; $wgGroupPermissions['sysop']['autoconfirmed'] = true; -$wgGroupPermissions['sysop']['upload_by_url'] = true; $wgGroupPermissions['sysop']['ipblock-exempt'] = true; $wgGroupPermissions['sysop']['blockemail'] = true; $wgGroupPermissions['sysop']['markbotedits'] = true; @@ -3548,6 +3852,7 @@ $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; // Permission to change users' group assignments @@ -3558,6 +3863,7 @@ $wgGroupPermissions['bureaucrat']['noratelimit'] = 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; // To hide usernames from users and Sysops #$wgGroupPermissions['suppress']['hideuser'] = true; @@ -3578,6 +3884,7 @@ $wgGroupPermissions['bureaucrat']['noratelimit'] = true; /** * Permission keys revoked from users in each group. + * * This acts the same way as wgGroupPermissions above, except that * if the user is in a group here, the permission will be removed from them. * @@ -3595,16 +3902,20 @@ $wgImplicitGroups = array( '*', 'user', 'autoconfirmed' ); * A map of group names that the user is in, to group names that those users * are allowed to add or revoke. * - * Setting the list of groups to add or revoke to true is equivalent to "any group". - * - * For example, to allow sysops to add themselves to the "bot" group: + * Setting the list of groups to add or revoke to true is equivalent to "any + * group". * + * @par Example: + * To allow sysops to add themselves to the "bot" group: + * @code * $wgGroupsAddToSelf = array( 'sysop' => array( 'bot' ) ); + * @endcode * + * @par Example: * Implicit groups may be used for the source group, for instance: - * + * @code * $wgGroupsRemoveFromSelf = array( '*' => true ); - * + * @endcode * This allows users in the '*' group (i.e. any user) to remove themselves from * any group that they happen to be in. * @@ -3640,13 +3951,16 @@ $wgRestrictionLevels = array( '', 'autoconfirmed', 'sysop' ); * namespace. If you list more than one permission, a user must * have all of them to edit pages in that namespace. * - * Note: NS_MEDIAWIKI is implicitly restricted to editinterface. + * @note NS_MEDIAWIKI is implicitly restricted to 'editinterface'. */ $wgNamespaceProtection = array(); /** * Pages in namespaces in this array can not be used as templates. - * Elements must be numeric namespace ids. + * + * Elements MUST be numeric namespace ids, you can safely use the MediaWiki + * 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. */ @@ -3662,11 +3976,15 @@ $wgNonincludableNamespaces = array(); * * When left at 0, all registered accounts will pass. * - * Example: - * <code> + * @par Example: + * Set automatic confirmation to 10 minutes (which is 600 seconds): + * @code * $wgAutoConfirmAge = 600; // ten minutes + * @endcode + * Set age to one day: + * @code * $wgAutoConfirmAge = 3600*24; // one day - * </code> + * @endcode */ $wgAutoConfirmAge = 0; @@ -3674,14 +3992,18 @@ $wgAutoConfirmAge = 0; * Number of edits an account requires before it is autoconfirmed. * Passing both this AND the time requirement is needed. Example: * - * <code> + * @par Example: + * @code * $wgAutoConfirmCount = 50; - * </code> + * @endcode */ $wgAutoConfirmCount = 0; /** * Automatically add a usergroup to any user who matches certain conditions. + * + * @todo Redocument $wgAutopromote + * * The format is * array( '&' or '|' or '^' or '!', cond1, cond2, ... ) * where cond1, cond2, ... are themselves conditions; *OR* @@ -3709,14 +4031,19 @@ $wgAutopromote = array( /** * Automatically add a usergroup to any user who matches certain conditions. + * * Does not add the user to the group again if it has been removed. * Also, does not remove the group if the user no longer meets the criteria. * - * The format is + * The format is: + * @code * array( event => criteria, ... ) - * where event is - * 'onEdit' (when user edits) or 'onView' (when user views the wiki) - * and criteria has the same format as $wgAutopromote + * @endcode + * Where event is either: + * - 'onEdit' (when user edits) + * - 'onView' (when user views the wiki) + * + * Criteria has the same format as $wgAutopromote * * @see $wgAutopromote * @since 1.18 @@ -3734,16 +4061,23 @@ $wgAutopromoteOnceLogInRC = true; /** * $wgAddGroups and $wgRemoveGroups can be used to give finer control over who - * can assign which groups at Special:Userrights. Example configuration: + * can assign which groups at Special:Userrights. * + * @par Example: + * Bureaucrats can add any group: * @code - * // Bureaucrat can add any group * $wgAddGroups['bureaucrat'] = true; - * // Bureaucrats can only remove bots and sysops + * @endcode + * Bureaucrats can only remove bots and sysops: + * @code * $wgRemoveGroups['bureaucrat'] = array( 'bot', 'sysop' ); - * // Sysops can make bots + * @endcode + * Sysops can make bots: + * @code * $wgAddGroups['sysop'] = array( 'bot' ); - * // Sysops can disable other sysops in an emergency, and disable bots + * @endcode + * Sysops can disable other sysops in an emergency, and disable bots: + * @code * $wgRemoveGroups['sysop'] = array( 'sysop', 'bot' ); * @endcode */ @@ -3763,8 +4097,10 @@ $wgAvailableRights = array(); */ $wgDeleteRevisionsLimit = 0; -/** Number of accounts each IP address may create, 0 to disable. - * Requires memcached */ +/** + * Number of accounts each IP address may create, 0 to disable. + * + * @warning Requires memcached */ $wgAccountCreationThrottle = 0; /** @@ -3774,8 +4110,9 @@ $wgAccountCreationThrottle = 0; * There's no administrator override on-wiki, so be careful what you set. :) * May be an array of regexes or a single string for backwards compatibility. * - * See http://en.wikipedia.org/wiki/Regular_expression - * Note that each regex needs a beginning/end delimiter, eg: # or / + * @see http://en.wikipedia.org/wiki/Regular_expression + * + * @note Each regex needs a beginning/end delimiter, eg: # or / */ $wgSpamRegex = array(); @@ -3783,54 +4120,46 @@ $wgSpamRegex = array(); $wgSummarySpamRegex = array(); /** - * Similarly you can get a function to do the job. The function will be given - * the following args: - * - a Title object for the article the edit is made on - * - the text submitted in the textarea (wpTextbox1) - * - the section number. - * The return should be boolean indicating whether the edit matched some evilness: - * - true : block it - * - false : let it through - * - * @deprecated since 1.17 Use hooks. See SpamBlacklist extension. - * @var $wgFilterCallback bool|string|Closure - */ -$wgFilterCallback = false; - -/** - * Whether to use DNS blacklists in $wgDnsBlacklistUrls to check for open proxies + * Whether to use DNS blacklists in $wgDnsBlacklistUrls to check for open + * proxies * @since 1.16 */ $wgEnableDnsBlacklist = false; /** - * @deprecated since 1.17 Use $wgEnableDnsBlacklist instead, only kept for backward - * compatibility + * @deprecated since 1.17 Use $wgEnableDnsBlacklist instead, only kept for + * backward compatibility. */ $wgEnableSorbs = false; /** - * List of DNS blacklists to use, if $wgEnableDnsBlacklist is true. This is an - * array of either a URL or an array with the URL and a key (should the blacklist - * require a key). For example: + * List of DNS blacklists to use, if $wgEnableDnsBlacklist is true. + * + * This is an array of either a URL or an array with the URL and a key (should + * the blacklist require a key). + * + * @par Example: * @code * $wgDnsBlacklistUrls = array( * // String containing URL - * 'http.dnsbl.sorbs.net', + * 'http.dnsbl.sorbs.net.', * // Array with URL and key, for services that require a key - * array( 'dnsbl.httpbl.net', 'mykey' ), + * array( 'dnsbl.httpbl.net.', 'mykey' ), * // Array with just the URL. While this works, it is recommended that you * // just use a string as shown above - * array( 'opm.tornevall.org' ) + * array( 'opm.tornevall.org.' ) * ); * @endcode + * + * @note You should end the domain name with a . to avoid searching your + * eventual domain search suffixes. * @since 1.16 */ $wgDnsBlacklistUrls = array( 'http.dnsbl.sorbs.net.' ); /** - * @deprecated since 1.17 Use $wgDnsBlacklistUrls instead, only kept for backward - * compatibility + * @deprecated since 1.17 Use $wgDnsBlacklistUrls instead, only kept for + * backward compatibility. */ $wgSorbsUrl = array(); @@ -3841,13 +4170,24 @@ $wgSorbsUrl = array(); $wgProxyWhitelist = array(); /** - * Simple rate limiter options to brake edit floods. Maximum number actions - * allowed in the given number of seconds; after that the violating client re- - * ceives HTTP 500 error pages until the period elapses. + * Simple rate limiter options to brake edit floods. + * + * Maximum number actions allowed in the given number of seconds; after that + * the violating client receives HTTP 500 error pages until the period + * elapses. + * + * @par Example: + * To set a generic maximum of 4 hits in 60 seconds: + * @code + * $wgRateLimits = array( 4, 60 ); + * @endcode * - * array( 4, 60 ) for a maximum of 4 hits in 60 seconds. + * You could also limit per action and then type of users. See the inline + * code for a template to use. * - * This option set is experimental and likely to change. Requires memcached. + * This option set is experimental and likely to change. + * + * @warning Requires memcached. */ $wgRateLimits = array( 'edit' => array( @@ -3896,7 +4236,8 @@ $wgQueryPageDefaultLimit = 50; /** * Limit password attempts to X attempts per Y seconds per IP per account. - * Requires memcached. + * + * @warning Requires memcached. */ $wgPasswordAttemptThrottle = array( 'count' => 5, 'seconds' => 300 ); @@ -3911,10 +4252,10 @@ $wgPasswordAttemptThrottle = array( 'count' => 5, 'seconds' => 300 ); * If you enable this, every editor's IP address will be scanned for open HTTP * proxies. * - * Don't enable this. Many sysops will report "hostile TCP port scans" to your - * ISP and ask for your server to be shut down. - * + * @warning Don't enable this. Many sysops will report "hostile TCP port scans" + * to your ISP and ask for your server to be shut down. * You have been warned. + * */ $wgBlockOpenProxies = false; /** Port we want to scan for a proxy */ @@ -4048,21 +4389,28 @@ $wgDebugRedirects = false; /** * If true, log debugging data from action=raw and load.php. - * This is normally false to avoid overlapping debug entries due to gen=css and - * gen=js requests. + * This is normally false to avoid overlapping debug entries due to gen=css + * and gen=js requests. */ $wgDebugRawPage = false; /** * Send debug data to an HTML comment in the output. * - * This may occasionally be useful when supporting a non-technical end-user. It's - * more secure than exposing the debug log file to the web, since the output only - * contains private data for the current user. But it's not ideal for development - * use since data is lost on fatal errors and redirects. + * This may occasionally be useful when supporting a non-technical end-user. + * It's more secure than exposing the debug log file to the web, since the + * output only contains private data for the current user. But it's not ideal + * for development use since data is lost on fatal errors and redirects. */ $wgDebugComments = false; +/** + * Extensive database transaction state debugging + * + * @since 1.20 + */ +$wgDebugDBTransactions = false; + /** * Write SQL queries to the debug log */ @@ -4119,11 +4467,23 @@ $wgShowExceptionDetails = false; */ $wgShowDBErrorBacktrace = false; +/** + * If true, send the exception backtrace to the error log + */ +$wgLogExceptionBacktrace = true; + /** * Expose backend server host names through the API and various HTML comments */ $wgShowHostnames = false; +/** + * Override server hostname detection with a hardcoded value. + * Should be a string, default false. + * @since 1.20 + */ +$wgOverrideHostname = false; + /** * If set to true MediaWiki will throw notices for some possible error * conditions and for deprecated functions. @@ -4135,7 +4495,7 @@ $wgDevelopmentWarnings = false; * development warnings will not be generated for deprecations added in releases * after the limit. */ -$wgDeprecationReleaseLimit = '1.17'; +$wgDeprecationReleaseLimit = false; /** Only record profiling info for pages that took longer than this */ $wgProfileLimit = 0.0; @@ -4200,6 +4560,14 @@ $wgAggregateStatsID = false; */ $wgDisableCounters = false; +/** + * Set this to an integer to only do synchronous site_stats updates + * one every *this many* updates. The other requests go into pending + * delta values in $wgMemc. Make sure that $wgMemc is a global cache. + * If set to -1, updates *only* go to $wgMemc (useful for daemons). + */ +$wgSiteStatsAsyncFactor = false; + /** * Parser test suite files to be run by parserTests.php when no specific * filename is passed to it. @@ -4228,7 +4596,7 @@ $wgParserTestFiles = array( * ); */ $wgParserTestRemote = false; - + /** * Allow running of javascript test suites via [[Special:JavaScriptTest]] (such as QUnit). */ @@ -4239,7 +4607,17 @@ $wgEnableJavaScriptTest = false; */ $wgJavaScriptTestConfig = array( 'qunit' => array( + // Page where documentation can be found relevant to the QUnit test suite being ran. + // Used in the intro paragraph on [[Special:JavaScriptTest/qunit]] for the + // documentation link in the "javascripttest-qunit-intro" message. 'documentation' => '//www.mediawiki.org/wiki/Manual:JavaScript_unit_testing', + // If you are submitting the QUnit test suite to a TestSwarm instance, + // point this to the "inject.js" script of that instance. This is was registers + // the QUnit hooks to extract the test results and push them back up into the + // TestSwarm database. + // @example 'http://localhost/testswarm/js/inject.js' + // @example '//integration.mediawiki.org/testswarm/js/inject.js' + 'testswarm-injectjs' => false, ), ); @@ -4306,17 +4684,11 @@ $wgCountTotalSearchHits = false; */ $wgOpenSearchTemplate = false; -/** - * Enable suggestions while typing in search boxes - * (results are passed around in OpenSearch format) - * Requires $wgEnableOpenSearchSuggest = true; - */ -$wgEnableMWSuggest = false; - /** * Enable OpenSearch suggestions requested by MediaWiki. Set this to - * false if you've disabled MWSuggest or another suggestion script and - * want reduce load caused by cached scripts pulling suggestions. + * false if you've disabled scripts that use api?action=opensearch and + * want reduce load caused by cached scripts still pulling suggestions. + * It will let the API fallback by responding with an empty array. */ $wgEnableOpenSearchSuggest = true; @@ -4325,14 +4697,6 @@ $wgEnableOpenSearchSuggest = true; */ $wgSearchSuggestCacheExpiry = 1200; -/** - * Template for internal MediaWiki suggestion engine, defaults to API action=opensearch - * - * Placeholders: {searchTerms}, {namespaces}, {dbname} - * - */ -$wgMWSuggestTemplate = false; - /** * If you've disabled search semi-permanently, this also disables updates to the * table. If you ever re-enable, be sure to rebuild the search table. @@ -4340,12 +4704,13 @@ $wgMWSuggestTemplate = false; $wgDisableSearchUpdate = false; /** - * List of namespaces which are searched by default. Example: + * List of namespaces which are searched by default. * - * <code> + * @par Example: + * @code * $wgNamespacesToBeSearchedDefault[NS_MAIN] = true; * $wgNamespacesToBeSearchedDefault[NS_PROJECT] = true; - * </code> + * @endcode */ $wgNamespacesToBeSearchedDefault = array( NS_MAIN => true, @@ -4353,9 +4718,9 @@ $wgNamespacesToBeSearchedDefault = array( /** * Namespaces to be searched when user clicks the "Help" tab - * on Special:Search + * on Special:Search. * - * Same format as $wgNamespacesToBeSearchedDefault + * Same format as $wgNamespacesToBeSearchedDefault. */ $wgNamespacesToBeSearchedHelp = array( NS_PROJECT => true, @@ -4363,8 +4728,10 @@ $wgNamespacesToBeSearchedHelp = array( ); /** - * If set to true the 'searcheverything' preference will be effective only for logged-in users. - * Useful for big wikis to maintain different search profiles for anonymous and logged-in users. + * If set to true the 'searcheverything' preference will be effective only for + * logged-in users. + * Useful for big wikis to maintain different search profiles for anonymous and + * logged-in users. * */ $wgSearchEverythingOnlyLoggedIn = false; @@ -4380,18 +4747,22 @@ $wgDisableInternalSearch = false; * If the URL includes '$1', this will be replaced with the URL-encoded * search term. * - * For example, to forward to Google you'd have something like: - * $wgSearchForwardUrl = 'http://www.google.com/search?q=$1' . - * '&domains=http://example.com' . - * '&sitesearch=http://example.com' . - * '&ie=utf-8&oe=utf-8'; + * @par Example: + * 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'; + * @endcode */ $wgSearchForwardUrl = null; /** - * Search form behavior - * true = use Go & Search buttons - * false = use Go button & Advanced search link + * Search form behavior. + * - true = use Go & Search buttons + * - false = use Go button & Advanced search link */ $wgUseTwoButtonsSearchForm = true; @@ -4408,11 +4779,13 @@ $wgSitemapNamespaces = false; * maintenance/generateSitemap.php script. * * This should be a map of namespace IDs to priority - * Example: + * @par Example: + * @code * $wgSitemapNamespacesPriorities = array( * NS_USER => '0.9', * NS_HELP => '0.0', * ); + * @endcode */ $wgSitemapNamespacesPriorities = false; @@ -4530,6 +4903,22 @@ $wgReadOnlyFile = false; */ $wgUpgradeKey = false; +/** + * Map GIT repository URLs to viewer URLs to provide links in Special:Version + * + * Key is a pattern passed to preg_match() and preg_replace(), + * without the delimiters (which are #) and must match the whole URL. + * The value is the replacement for the key (it can contain $1, etc.) + * %h will be replaced by the short SHA-1 (7 first chars) and %H by the + * full SHA-1 of the HEAD revision. + * + * @since 1.20 + */ +$wgGitRepositoryViewers = array( + 'https://gerrit.wikimedia.org/r/p/(.*)' => 'https://gerrit.wikimedia.org/r/gitweb?p=$1;h=%H', + 'ssh://(?:[a-z0-9_]+@)?gerrit.wikimedia.org:29418/(.*)' => 'https://gerrit.wikimedia.org/r/gitweb?p=$1;h=%H', +); + /** @} */ # End of maintenance } /************************************************************************//** @@ -4627,18 +5016,23 @@ $wgFeedDiffCutoff = 32768; /** Override the site's default RSS/ATOM feed for recentchanges that appears on * every page. Some sites might have a different feed they'd like to promote * instead of the RC feed (maybe like a "Recent New Articles" or "Breaking news" one). - * Ex: $wgSiteFeed['format'] = "http://example.com/somefeed.xml"; Format can be one - * of either 'rss' or 'atom'. + * Should be a format as key (either 'rss' or 'atom') and an URL to the feed + * as value. + * @par Example: + * Configure the 'atom' feed to http://example.com/somefeed.xml + * @code + * $wgSiteFeed['atom'] = "http://example.com/somefeed.xml"; + * @endcode */ $wgOverrideSiteFeed = array(); /** - * Available feeds objects + * Available feeds objects. * Should probably only be defined when a page is syndicated ie when - * $wgOut->isSyndicated() is true + * $wgOut->isSyndicated() is true. */ $wgFeedClasses = array( - 'rss' => 'RSSFeed', + 'rss' => 'RSSFeed', 'atom' => 'AtomFeed', ); @@ -4797,9 +5191,9 @@ $wgExportAllowListContributors = false; * can become *insanely large* and could easily break your wiki, * it's disabled by default for now. * - * There's a HARD CODED limit of 5 levels of recursion to prevent a - * crazy-big export from being done by someone setting the depth - * number too high. In other words, last resort safety net. + * @warning There's a HARD CODED limit of 5 levels of recursion to prevent a + * crazy-big export from being done by someone setting the depth number too + * high. In other words, last resort safety net. */ $wgExportMaxLinkDepth = 0; @@ -4821,7 +5215,8 @@ $wgExportAllowAll = false; */ /** - * A list of callback functions which are called once MediaWiki is fully initialised + * A list of callback functions which are called once MediaWiki is fully + * initialised */ $wgExtensionFunctions = array(); @@ -4836,9 +5231,10 @@ $wgExtensionFunctions = array(); * Variables defined in extensions will override conflicting variables defined * in the core. * - * Example: - * $wgExtensionMessagesFiles['ConfirmEdit'] = dirname(__FILE__).'/ConfirmEdit.i18n.php'; - * + * @par Example: + * @code + * $wgExtensionMessagesFiles['ConfirmEdit'] = __DIR__.'/ConfirmEdit.i18n.php'; + * @endcode */ $wgExtensionMessagesFiles = array(); @@ -4852,7 +5248,9 @@ $wgExtensionMessagesFiles = array(); * Registration is done with $pout->addOutputHook( $tag, $data ). * * The callback has the form: + * @code * function outputHook( $outputPage, $parserOutput, $data ) { ... } + * @endcode */ $wgParserOutputHooks = array(); @@ -4883,7 +5281,7 @@ $wgAutoloadClasses = array(); * urls, descriptions and pointers to localized description msgs. Note that * the version, url, description and descriptionmsg key can be omitted. * - * <code> + * @code * $wgExtensionCredits[$type][] = array( * 'name' => 'Example extension', * 'version' => 1.9, @@ -4893,7 +5291,7 @@ $wgAutoloadClasses = array(); * 'description' => 'An example extension', * 'descriptionmsg' => 'exampleextension-desc', * ); - * </code> + * @endcode * * Where $type is 'specialpage', 'parserhook', 'variable', 'media' or 'other'. * Where 'descriptionmsg' can be an array with message key and parameters: @@ -4909,12 +5307,30 @@ $wgAuth = null; /** * Global list of hooks. - * Add a hook by doing: + * + * The key is one of the events made available by MediaWiki, you can find + * a description for most of them in docs/hooks.txt. The array is used + * internally by Hook:run(). + * + * The value can be one of: + * + * - A function name: + * @code * $wgHooks['event_name'][] = $function; - * or: + * @endcode + * - A function with some data: + * @code * $wgHooks['event_name'][] = array($function, $data); - * or: + * @endcode + * - A an object method: + * @code * $wgHooks['event_name'][] = array($object, 'method'); + * @endcode + * + * @warning You should always append to an event array or you will end up + * deleting a previous registered hook. + * + * @todo Does it support PHP closures? */ $wgHooks = array(); @@ -5068,17 +5484,19 @@ $wgLogRestrictions = array( * * See $wgLogTypes for a list of available log types. * - * For example: + * @par Example: + * @code * $wgFilterLogTypes => array( * 'move' => true, * 'import' => false, * ); + * @endcode * * Will display show/hide links for the move and import logs. Move logs will be * hidden by default unless the link is clicked. Import logs will be shown by * default, and hidden when the link is clicked. * - * A message of the form log-show-hide-<type> should be added, and will be used + * A message of the form log-show-hide-[type] should be added, and will be used * for the link text. */ $wgFilterLogTypes = array( @@ -5091,7 +5509,7 @@ $wgFilterLogTypes = array( * * Extensions with custom log types may add to this array. * - * Since 1.19, if you follow the naming convention log-name-TYPE, + * @since 1.19, if you follow the naming convention log-name-TYPE, * where TYPE is your log type, yoy don't need to use this array. */ $wgLogNames = array( @@ -5114,7 +5532,7 @@ $wgLogNames = array( * * Extensions with custom log types may add to this array. * - * Since 1.19, if you follow the naming convention log-description-TYPE, + * @since 1.19, if you follow the naming convention log-description-TYPE, * where TYPE is your log type, yoy don't need to use this array. */ $wgLogHeaders = array( @@ -5164,10 +5582,12 @@ $wgLogActions = array( * @see LogFormatter */ $wgLogActionsHandlers = array( - // move, move_redir - 'move/*' => 'MoveLogFormatter', - // delete, restore, revision, event - 'delete/*' => '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', @@ -5266,6 +5686,7 @@ $wgSpecialPageGroups = array( 'Mostlinkedtemplates' => 'highuse', 'Mostcategories' => 'highuse', 'Mostimages' => 'highuse', + 'Mostinterwikis' => 'highuse', 'Mostrevisions' => 'highuse', 'Allpages' => 'pages', @@ -5328,7 +5749,7 @@ $wgMaxRedirectLinksRetrieved = 500; */ /** - * Array of allowed values for the title=foo&action=<action> parameter. Syntax is: + * Array of allowed values for the "title=foo&action=<action>" parameter. Syntax is: * 'foo' => 'ClassName' Load the specified class which subclasses Action * 'foo' => true Load the class FooAction which subclasses Action * If something is specified in the getActionOverrides() @@ -5364,11 +5785,6 @@ $wgActions = array( */ $wgDisabledActions = array(); -/** - * Allow the "info" action, very inefficient at the moment - */ -$wgAllowPageInfo = false; - /** @} */ # end actions } /*************************************************************************//** @@ -5393,8 +5809,10 @@ $wgDefaultRobotPolicy = 'index,follow'; * URLs, so search engine spiders risk getting lost in a maze of twisty special * pages, all alike, and never reaching your actual content. * - * Example: + * @par Example: + * @code * $wgNamespaceRobotPolicies = array( NS_TALK => 'noindex' ); + * @endcode */ $wgNamespaceRobotPolicies = array(); @@ -5402,10 +5820,18 @@ $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. - * Example: - * $wgArticleRobotPolicies = array( 'Main Page' => 'noindex,follow', - * 'User:Bob' => 'index,follow' ); - * Example that DOES NOT WORK because the names are not canonical text forms: + * + * @par Example: + * @code + * $wgArticleRobotPolicies = array( + * 'Main Page' => 'noindex,follow', + * 'User:Bob' => 'index,follow', + * ); + * @endcode + * + * @par Example that DOES NOT WORK because the names are not canonical text + * forms: + * @code * $wgArticleRobotPolicies = array( * # Underscore, not space! * 'Main_Page' => 'noindex,follow', @@ -5414,6 +5840,7 @@ $wgNamespaceRobotPolicies = array(); * # Needs to be "Abc", not "abc" (unless $wgCapitalLinks is false for that namespace)! * 'abc' => 'noindex,nofollow' * ); + * @endcode */ $wgArticleRobotPolicies = array(); @@ -5421,8 +5848,11 @@ $wgArticleRobotPolicies = array(); * An array of namespace keys in which the __INDEX__/__NOINDEX__ magic words * will not function, so users can't decide whether pages in that namespace are * indexed by search engines. If set to null, default to $wgContentNamespaces. - * Example: + * + * @par Example: + * @code * $wgExemptFromUserRobotsControl = array( NS_MAIN, NS_TALK, NS_PROJECT ); + * @endcode */ $wgExemptFromUserRobotsControl = null; @@ -5452,9 +5882,10 @@ $wgEnableAPI = true; $wgEnableWriteAPI = true; /** - * API module extensions + * API module extensions. * Associative array mapping module name to class name. * Extension modules may override the core modules. + * @todo Describe each of the variables, group them and add examples */ $wgAPIModules = array(); $wgAPIMetaModules = array(); @@ -5469,7 +5900,7 @@ $wgAPIMaxDBRows = 5000; /** * The maximum size (in bytes) of an API result. - * Don't set this lower than $wgMaxArticleSize*1024 + * @warning Do not set this lower than $wgMaxArticleSize*1024 */ $wgAPIMaxResultSize = 8388608; @@ -5524,17 +5955,18 @@ $wgAjaxLicensePreview = true; * This is currently only used by the API (requests to api.php) * $wgCrossSiteAJAXdomains can be set using a wildcard syntax: * - * '*' matches any number of characters - * '?' matches any 1 character - * - * Example: - $wgCrossSiteAJAXdomains = array( - 'www.mediawiki.org', - '*.wikipedia.org', - '*.wikimedia.org', - '*.wiktionary.org', - ); + * - '*' matches any number of characters + * - '?' matches any 1 character * + * @par Example: + * @code + * $wgCrossSiteAJAXdomains = array( + * 'www.mediawiki.org', + * '*.wikipedia.org', + * '*.wikimedia.org', + * '*.wiktionary.org', + * ); + * @endcode */ $wgCrossSiteAJAXdomains = array(); @@ -5638,7 +6070,7 @@ $wgUpdateRowsPerQuery = 100; /** * The build directory for HipHop compilation. - * Defaults to $IP/maintenance/hiphop/build. + * Defaults to '$IP/maintenance/hiphop/build'. */ $wgHipHopBuildDirectory = false; @@ -5658,8 +6090,9 @@ $wgHipHopCompilerProcs = 'detect'; * * To compile extensions with HipHop, set $wgExtensionsDirectory correctly, * and use code like: - * + * @code * require( MWInit::extensionSetupPath( 'Extension/Extension.php' ) ); + * @endcode * * to include the extension setup file from LocalSettings.php. It is not * necessary to set this variable unless you use MWInit::extensionSetupPath(). @@ -5681,6 +6114,19 @@ $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 * @{ @@ -5691,9 +6137,11 @@ $wgExternalDiffEngine = false; /** * Disable redirects to special pages and interwiki redirects, which use a 302 - * and have no "redirected from" link. Note this is only for articles with #Redirect - * in them. URL's containing a local interwiki prefix (or a non-canonical special - * page name) are still hard redirected regardless of this setting. + * and have no "redirected from" link. + * + * @note This is only for articles with #REDIRECT in them. URL's containing a + * local interwiki prefix (or a non-canonical special page name) are still hard + * redirected regardless of this setting. */ $wgDisableHardRedirects = false; @@ -5704,8 +6152,8 @@ $wgDisableHardRedirects = false; $wgLinkHolderBatchSize = 1000; /** - * By default MediaWiki does not register links pointing to same server in externallinks dataset, - * use this value to override: + * By default MediaWiki does not register links pointing to same server in + * externallinks dataset, use this value to override: */ $wgRegisterInternalExternals = false; @@ -5733,8 +6181,10 @@ $wgRedirectOnLogin = null; * This configuration array maps pool types to an associative array. The only * defined key in the associative array is "class", which gives the class name. * The remaining elements are passed through to the class as constructor - * parameters. Example: + * parameters. * + * @par Example: + * @code * $wgPoolCounterConf = array( 'ArticleView' => array( * 'class' => 'PoolCounter_Client', * 'timeout' => 15, // wait timeout in seconds @@ -5742,6 +6192,7 @@ $wgRedirectOnLogin = null; * 'maxqueue' => 50, // maximum number of total threads in each pool * ... any extension-specific options... * ); + * @endcode */ $wgPoolCounterConf = null; @@ -5759,6 +6210,13 @@ $wgSeleniumConfigFile = null; $wgDBtestuser = ''; //db user that has permission to create and drop the test databases only $wgDBtestpassword = ''; +/** + * Whether the user must enter their password to change their e-mail address + * + * @since 1.20 + */ +$wgRequirePasswordforEmailChange = true; + /** * 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 262994e3..b4989a69 100644 --- a/includes/DeferredUpdates.php +++ b/includes/DeferredUpdates.php @@ -1,4 +1,25 @@ <?php +/** + * Interface and manager for deferred updates. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + /** * Interface that deferrable updates should implement. Basically required so we * can validate input on DeferredUpdates::addUpdate() @@ -67,10 +88,19 @@ class DeferredUpdates { } foreach ( $updates as $update ) { - $update->doUpdate(); + try { + $update->doUpdate(); - if ( $doCommit && $dbw->trxLevel() ) { - $dbw->commit( __METHOD__ ); + if ( $doCommit && $dbw->trxLevel() ) { + $dbw->commit( __METHOD__ ); + } + } catch ( MWException $e ) { + // We don't want exceptions thrown during deferred updates to + // be reported to the user since the output is already sent. + // Instead we just log them. + if ( !$e instanceof ErrorPageError ) { + wfDebugLog( 'exception', $e->getLogMessage() ); + } } } diff --git a/includes/Defines.php b/includes/Defines.php index 26deb2ba..be9f9816 100644 --- a/includes/Defines.php +++ b/includes/Defines.php @@ -6,9 +6,28 @@ * since this file will not be executed during request startup for a compiled * MediaWiki. * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file */ +/** + * @defgroup Constants MediaWiki constants + */ + /** * Version constants for the benefit of extensions */ @@ -25,6 +44,8 @@ define( 'DBO_DEFAULT', 16 ); define( 'DBO_PERSISTENT', 32 ); define( 'DBO_SYSDBA', 64 ); //for oracle maintenance define( 'DBO_DDLMODE', 128 ); // when using schema files: mostly for Oracle +define( 'DBO_SSL', 256 ); +define( 'DBO_COMPRESS', 512 ); /**@}*/ /**@{ @@ -125,8 +146,8 @@ define( 'AV_SCAN_FAILED', false ); #scan failed (scanner not found or error in * Anti-lock flags * See DefaultSettings.php for a description */ -define( 'ALF_PRELOAD_LINKS', 1 ); -define( 'ALF_PRELOAD_EXISTENCE', 2 ); +define( 'ALF_PRELOAD_LINKS', 1 ); // unused +define( 'ALF_PRELOAD_EXISTENCE', 2 ); // unused define( 'ALF_NO_LINK_LOCK', 4 ); define( 'ALF_NO_BLOCK_LOCK', 8 ); /**@}*/ @@ -184,7 +205,7 @@ define( 'LIST_SET_PREPARED', 8); // List of (?, ?, ?) for DatabaseIbm_db2 /** * Unicode and normalisation related */ -require_once dirname(__FILE__).'/normal/UtfNormalDefines.php'; +require_once __DIR__.'/normal/UtfNormalDefines.php'; /**@{ * Hook support constants diff --git a/includes/DeprecatedGlobal.php b/includes/DeprecatedGlobal.php new file mode 100644 index 00000000..4d7b9689 --- /dev/null +++ b/includes/DeprecatedGlobal.php @@ -0,0 +1,55 @@ +<?php +/** + * Delayed loading of deprecated global objects. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @file + */ + +/** + * Class to allow throwing wfDeprecated warnings + * when people use globals that we do not want them to. + * (For example like $wgArticle) + */ + +class DeprecatedGlobal extends StubObject { + // The m's are to stay consistent with parent class. + protected $mRealValue, $mVersion; + + function __construct( $name, $realValue, $version = false ) { + parent::__construct( $name ); + $this->mRealValue = $realValue; + $this->mVersion = $version; + } + + function _newObject() { + /* Put the caller offset for wfDeprecated as 6, as + * that gives the function that uses this object, since: + * 1 = this function ( _newObject ) + * 2 = StubObject::_unstub + * 3 = StubObject::_call + * 4 = StubObject::__call + * 5 = DeprecatedGlobal::<method of global called> + * 6 = Actual function using the global. + * Of course its theoretically possible to have other call + * sequences for this method, but that seems to be + * rather unlikely. + */ + wfDeprecated( '$' . $this->mGlobal, $this->mVersion, false, 6 ); + return $this->mRealValue; + } +} diff --git a/includes/EditPage.php b/includes/EditPage.php index d00d9114..b762cad1 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -1,6 +1,22 @@ <?php /** - * Contains the EditPage class + * Page edition user interface. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * * @file */ @@ -36,11 +52,6 @@ class EditPage { */ const AS_HOOK_ERROR = 210; - /** - * Status: The filter function set in $wgFilterCallback returned true (= block it) - */ - const AS_FILTERING = 211; - /** * Status: A hook function returned an error */ @@ -144,6 +155,11 @@ class EditPage { */ const AS_IMAGE_REDIRECT_LOGGED = 234; + /** + * HTML id and name for the beginning of the edit form. + */ + const EDITFORM_ID = 'editform'; + /** * @var Article */ @@ -183,6 +199,12 @@ class EditPage { */ var $mParserOutput; + /** + * Has a summary been preset using GET parameter &summary= ? + * @var Bool + */ + var $hasPresetSummary = false; + var $mBaseRevision = false; var $mShowSummaryField = true; @@ -282,7 +304,7 @@ class EditPage { } wfProfileIn( __METHOD__ ); - wfDebug( __METHOD__.": enter\n" ); + wfDebug( __METHOD__ . ": enter\n" ); // If they used redlink=1 and the page exists, redirect to the main article if ( $wgRequest->getBool( 'redlink' ) && $this->mTitle->exists() ) { @@ -333,7 +355,7 @@ class EditPage { return; } - wfProfileIn( __METHOD__."-business-end" ); + wfProfileIn( __METHOD__ . "-business-end" ); $this->isConflict = false; // css / js subpages of user pages get a special treatment @@ -355,7 +377,7 @@ class EditPage { if ( 'save' == $this->formtype ) { if ( !$this->attemptSave() ) { - wfProfileOut( __METHOD__."-business-end" ); + wfProfileOut( __METHOD__ . "-business-end" ); wfProfileOut( __METHOD__ ); return; } @@ -366,18 +388,18 @@ class EditPage { if ( 'initial' == $this->formtype || $this->firsttime ) { if ( $this->initialiseForm() === false ) { $this->noSuchSectionPage(); - wfProfileOut( __METHOD__."-business-end" ); + wfProfileOut( __METHOD__ . "-business-end" ); wfProfileOut( __METHOD__ ); return; } - if ( !$this->mTitle->getArticleId() ) + if ( !$this->mTitle->getArticleID() ) wfRunHooks( 'EditFormPreloadText', array( &$this->textbox1, &$this->mTitle ) ); else wfRunHooks( 'EditFormInitialText', array( $this ) ); } $this->showEditForm(); - wfProfileOut( __METHOD__."-business-end" ); + wfProfileOut( __METHOD__ . "-business-end" ); wfProfileOut( __METHOD__ ); } @@ -394,7 +416,7 @@ class EditPage { } # Ignore some permissions errors when a user is just previewing/viewing diffs $remove = array(); - foreach( $permErrors as $error ) { + foreach ( $permErrors as $error ) { if ( ( $this->preview || $this->diff ) && ( $error[0] == 'blockedtext' || $error[0] == 'autoblockedtext' ) ) { @@ -469,7 +491,7 @@ class EditPage { */ function readOnlyPage( $source = null, $protected = false, $reasons = array(), $action = null ) { wfDeprecated( __METHOD__, '1.19' ); - + global $wgRequest, $wgOut; if ( $wgRequest->getBool( 'redlink' ) ) { // The edit page was reached via a red link. @@ -501,7 +523,7 @@ class EditPage { // Standard preference behaviour return true; } elseif ( !$this->mTitle->exists() && - isset($wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()]) && + isset( $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] ) && $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] ) { // Categories are special @@ -518,7 +540,7 @@ class EditPage { * @return bool */ protected function isWrongCaseCssJsPage() { - if( $this->mTitle->isCssJsSubpage() ) { + if ( $this->mTitle->isCssJsSubpage() ) { $name = $this->mTitle->getSkinFromCssJsSubpage(); $skins = array_merge( array_keys( Skin::getSkinNames() ), @@ -558,31 +580,31 @@ class EditPage { # Also remove trailing whitespace, but don't remove _initial_ # whitespace from the text boxes. This may be significant formatting. $this->textbox1 = $this->safeUnicodeInput( $request, 'wpTextbox1' ); - if ( !$request->getCheck('wpTextbox2') ) { + if ( !$request->getCheck( 'wpTextbox2' ) ) { // Skip this if wpTextbox2 has input, it indicates that we came // from a conflict page with raw page text, not a custom form // modified by subclasses - wfProfileIn( get_class($this)."::importContentFormData" ); + wfProfileIn( get_class( $this ) . "::importContentFormData" ); $textbox1 = $this->importContentFormData( $request ); - if ( isset($textbox1) ) + if ( isset( $textbox1 ) ) $this->textbox1 = $textbox1; - wfProfileOut( get_class($this)."::importContentFormData" ); + wfProfileOut( get_class( $this ) . "::importContentFormData" ); } - # Truncate for whole multibyte characters. +5 bytes for ellipsis - $this->summary = $wgLang->truncate( $request->getText( 'wpSummary' ), 250 ); + # Truncate for whole multibyte characters + $this->summary = $wgLang->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 # section titles. $this->summary = preg_replace( '/^\s*=+\s*(.*?)\s*=+\s*$/', '$1', $this->summary ); - + # Treat sectiontitle the same way as summary. # Note that wpSectionTitle is not yet a part of the actual edit form, as wpSummary is # 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' ), 250 ); + $this->sectiontitle = $wgLang->truncate( $request->getText( 'wpSectionTitle' ), 255 ); $this->sectiontitle = preg_replace( '/^\s*=+\s*(.*?)\s*=+\s*$/', '$1', $this->sectiontitle ); $this->edittime = $request->getVal( 'wpEdittime' ); @@ -650,7 +672,7 @@ class EditPage { { $this->allowBlankSummary = true; } else { - $this->allowBlankSummary = $request->getBool( 'wpIgnoreBlankSummary' ) || !$wgUser->getOption( 'forceeditsummary'); + $this->allowBlankSummary = $request->getBool( 'wpIgnoreBlankSummary' ) || !$wgUser->getOption( 'forceeditsummary' ); } $this->autoSumm = $request->getText( 'wpAutoSummary' ); @@ -669,7 +691,7 @@ class EditPage { $this->minoredit = false; $this->watchthis = $request->getBool( 'watchthis', false ); // Watch may be overriden 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) if ( $this->section == 'new' && $request->getVal( 'preloadtitle' ) ) { @@ -679,6 +701,9 @@ class EditPage { } elseif ( $this->section != 'new' && $request->getVal( 'summary' ) ) { $this->summary = $request->getText( 'summary' ); + if ( $this->summary !== '' ) { + $this->hasPresetSummary = true; + } } if ( $request->getVal( 'minor' ) ) { @@ -731,7 +756,7 @@ class EditPage { } elseif ( $wgUser->getOption( 'watchcreations' ) && !$this->mTitle->exists() ) { # Watch creations $this->watchthis = true; - } elseif ( $this->mTitle->userIsWatching() ) { + } elseif ( $wgUser->isWatched( $this->mTitle ) ) { # Already watched $this->watchthis = true; } @@ -796,7 +821,7 @@ class EditPage { # Otherwise, $text will be left as-is. if ( !is_null( $undorev ) && !is_null( $oldrev ) && $undorev->getPage() == $oldrev->getPage() && - $undorev->getPage() == $this->mTitle->getArticleId() && + $undorev->getPage() == $this->mTitle->getArticleID() && !$undorev->isDeleted( Revision::DELETED_TEXT ) && !$oldrev->isDeleted( Revision::DELETED_TEXT ) ) { @@ -810,12 +835,13 @@ class EditPage { # If we just undid one rev, use an autosummary $firstrev = $oldrev->getNext(); - if ( $firstrev->getId() == $undo ) { - $undoSummary = wfMsgForContent( 'undo-summary', $undo, $undorev->getUserText() ); + if ( $firstrev && $firstrev->getId() == $undo ) { + $undoSummary = wfMessage( 'undo-summary', $undo, $undorev->getUserText() )->inContentLanguage()->text(); if ( $this->summary === '' ) { $this->summary = $undoSummary; } else { - $this->summary = $undoSummary . wfMsgForContent( 'colon-separator' ) . $this->summary; + $this->summary = $undoSummary . wfMessage( 'colon-separator' ) + ->inContentLanguage()->text() . $this->summary; } $this->undidRev = $undo; } @@ -830,7 +856,7 @@ class EditPage { $class = ( $undoMsg == 'success' ? '' : 'error ' ) . "mw-undo-{$undoMsg}"; $this->editFormPageTop .= $wgOut->parse( "<div class=\"{$class}\">" . - wfMsgNoTrans( 'undo-' . $undoMsg ) . '</div>', true, /* interface */true ); + wfMessage( 'undo-' . $undoMsg )->plain() . '</div>', true, /* interface */true ); } if ( $text === false ) { @@ -852,7 +878,7 @@ class EditPage { * * This difers from Article::getContent() that when a missing revision is * encountered the result will be an empty string and not the - * 'missing-article' message. + * 'missing-revision' message. * * @since 1.19 * @return string @@ -907,7 +933,7 @@ class EditPage { if ( !empty( $this->mPreloadText ) ) { return $this->mPreloadText; } - + if ( $preload === '' ) { return ''; } @@ -959,7 +985,6 @@ class EditPage { $bot = $wgUser->isAllowed( 'bot' ) && $this->bot; $status = $this->internalAttemptSave( $resultDetails, $bot ); // 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; } @@ -976,7 +1001,6 @@ class EditPage { return true; case self::AS_HOOK_ERROR: - case self::AS_FILTERING: return false; case self::AS_SUCCESS_NEW_ARTICLE: @@ -1011,7 +1035,7 @@ class EditPage { return false; case self::AS_BLOCKED_PAGE_FOR_USER: - throw new UserBlockedError( $wgUser->mBlock ); + throw new UserBlockedError( $wgUser->getBlock() ); case self::AS_IMAGE_REDIRECT_ANON: case self::AS_IMAGE_REDIRECT_LOGGED: @@ -1031,8 +1055,15 @@ class EditPage { $permission = $this->mTitle->isTalkPage() ? 'createtalk' : 'createpage'; throw new PermissionsError( $permission ); + default: + // We don't recognize $status->value. The only way that can happen + // is if an extension hook aborted from inside ArticleSave. + // Render the status object into $this->hookError + // FIXME this sucks, we should just use the Status object throughout + $this->hookError = '<div class="error">' . $status->getWikitext() . + '</div>'; + return true; } - return false; } /** @@ -1048,8 +1079,7 @@ class EditPage { * AS_CONTENT_TOO_BIG and AS_BLOCKED_PAGE_FOR_USER. All that stuff needs to be cleaned up some time. */ function internalAttemptSave( &$result, $bot = false ) { - global $wgFilterCallback, $wgUser, $wgRequest, $wgParser; - global $wgMaxArticleSize; + global $wgUser, $wgRequest, $wgParser, $wgMaxArticleSize; $status = Status::newGood(); @@ -1095,13 +1125,6 @@ class EditPage { wfProfileOut( __METHOD__ ); return $status; } - if ( $wgFilterCallback && is_callable( $wgFilterCallback ) && $wgFilterCallback( $this->mTitle, $this->textbox1, $this->section, $this->hookError, $this->summary ) ) { - # Error messages or other handling should be performed by the filter function - $status->setResult( false, self::AS_FILTERING ); - wfProfileOut( __METHOD__ . '-checks' ); - wfProfileOut( __METHOD__ ); - return $status; - } if ( !wfRunHooks( 'EditFilter', array( $this, $this->textbox1, $this->section, &$this->hookError, $this->summary ) ) ) { # Error messages etc. could be handled within the hook... $status->fatal( 'hookaborted' ); @@ -1179,9 +1202,10 @@ class EditPage { wfProfileOut( __METHOD__ . '-checks' ); - # If article is new, insert it. - $aid = $this->mTitle->getArticleID( Title::GAID_FOR_UPDATE ); - $new = ( $aid == 0 ); + # Load the page data from the master. If anything changes in the meantime, + # we detect it by using page_latest like a token in a 1 try compare-and-swap. + $this->mArticle->loadPageData( 'fromdbmaster' ); + $new = !$this->mArticle->exists(); if ( $new ) { // Late check for create permission, just in case *PARANOIA* @@ -1215,44 +1239,37 @@ class EditPage { return $status; } - # Handle the user preference to force summaries here. Check if it's not a redirect. - if ( !$this->allowBlankSummary && !Title::newFromRedirect( $this->textbox1 ) ) { - if ( md5( $this->summary ) == $this->autoSumm ) { - $this->missingSummary = true; - $status->fatal( 'missingsummary' ); // or 'missingcommentheader' if $section == 'new'. Blegh - $status->value = self::AS_SUMMARY_NEEDED; - wfProfileOut( __METHOD__ ); - return $status; - } - } - $text = $this->textbox1; $result['sectionanchor'] = ''; if ( $this->section == 'new' ) { if ( $this->sectiontitle !== '' ) { // Insert the section title above the content. - $text = wfMsgForContent( 'newsectionheaderdefaultlevel', $this->sectiontitle ) . "\n\n" . $text; - + $text = wfMessage( 'newsectionheaderdefaultlevel', $this->sectiontitle ) + ->inContentLanguage()->text() . "\n\n" . $text; + // Jump to the new section $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle ); - + // If no edit summary was specified, create one automatically from the section // title and have it link to the new section. Otherwise, respect the summary as // passed. if ( $this->summary === '' ) { $cleanSectionTitle = $wgParser->stripSectionName( $this->sectiontitle ); - $this->summary = wfMsgForContent( 'newsectionsummary', $cleanSectionTitle ); + $this->summary = wfMessage( 'newsectionsummary' ) + ->rawParams( $cleanSectionTitle )->inContentLanguage()->text(); } } elseif ( $this->summary !== '' ) { // Insert the section title above the content. - $text = wfMsgForContent( 'newsectionheaderdefaultlevel', $this->summary ) . "\n\n" . $text; - + $text = wfMessage( 'newsectionheaderdefaultlevel', $this->summary ) + ->inContentLanguage()->text() . "\n\n" . $text; + // Jump to the new section $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText( $this->summary ); // Create a link to the new section from the edit summary. $cleanSummary = $wgParser->stripSectionName( $this->summary ); - $this->summary = wfMsgForContent( 'newsectionsummary', $cleanSummary ); + $this->summary = wfMessage( 'newsectionsummary' ) + ->rawParams( $cleanSummary )->inContentLanguage()->text(); } } @@ -1261,10 +1278,7 @@ class EditPage { } else { # 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 ) { @@ -1279,15 +1293,15 @@ class EditPage { } else { // New comment; suppress conflict. $this->isConflict = false; - wfDebug( __METHOD__ .": conflict suppressed; new section\n" ); + wfDebug( __METHOD__ . ": conflict suppressed; new section\n" ); } - } elseif ( $this->section == '' && $this->userWasLastToEdit( $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; } } - + // If sectiontitle is set, use it, otherwise use the summary as the section title (for // backwards compatibility with old forms/bots). if ( $this->sectiontitle !== '' ) { @@ -1295,7 +1309,7 @@ class EditPage { } else { $sectionTitle = $this->summary; } - + 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 ); @@ -1385,14 +1399,16 @@ class EditPage { // passed. if ( $this->summary === '' ) { $cleanSectionTitle = $wgParser->stripSectionName( $this->sectiontitle ); - $this->summary = wfMsgForContent( 'newsectionsummary', $cleanSectionTitle ); + $this->summary = wfMessage( 'newsectionsummary' ) + ->rawParams( $cleanSectionTitle )->inContentLanguage()->text(); } } elseif ( $this->summary !== '' ) { $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $this->summary ); # This is a new section, so create a link to the new section # in the revision summary. $cleanSummary = $wgParser->stripSectionName( $this->summary ); - $this->summary = wfMsgForContent( 'newsectionsummary', $cleanSummary ); + $this->summary = wfMessage( 'newsectionsummary' ) + ->rawParams( $cleanSummary )->inContentLanguage()->text(); } } elseif ( $this->section != '' ) { # Try to get a section anchor from the section source, redirect to edited section if header found @@ -1440,8 +1456,17 @@ class EditPage { wfProfileOut( __METHOD__ ); return $status; } else { - $this->isConflict = true; - $doEditStatus->value = self::AS_END; // Destroys data doEdit() put in $status->value but who cares + // Failure from doEdit() + // Show the edit conflict page for certain recognized errors from doEdit(), + // but don't show it for errors from extension hooks + $errors = $doEditStatus->getErrorsArray(); + if ( in_array( $errors[0][0], array( 'edit-gone-missing', 'edit-conflict', + 'edit-already-exists' ) ) ) + { + $this->isConflict = true; + // Destroys data doEdit() put in $status->value but who cares + $doEditStatus->value = self::AS_END; + } wfProfileOut( __METHOD__ ); return $doEditStatus; } @@ -1452,56 +1477,27 @@ class EditPage { */ protected function commitWatch() { global $wgUser; - if ( $this->watchthis xor $this->mTitle->userIsWatching() ) { + if ( $wgUser->isLoggedIn() && $this->watchthis != $wgUser->isWatched( $this->mTitle ) ) { $dbw = wfGetDB( DB_MASTER ); - $dbw->begin(); + $dbw->begin( __METHOD__ ); if ( $this->watchthis ) { WatchAction::doWatch( $this->mTitle, $wgUser ); } else { WatchAction::doUnwatch( $this->mTitle, $wgUser ); } - $dbw->commit(); + $dbw->commit( __METHOD__ ); } } - /** - * Check if no edits were made by other users since - * the time a user started editing the page. Limit to - * 50 revisions for the sake of performance. - * - * @param $id int - * @param $edittime string - * - * @return bool - */ - protected function userWasLastToEdit( $id, $edittime ) { - if( !$id ) return false; - $dbw = wfGetDB( DB_MASTER ); - $res = $dbw->select( 'revision', - 'rev_user', - array( - 'rev_page' => $this->mTitle->getArticleId(), - 'rev_timestamp > '.$dbw->addQuotes( $dbw->timestamp($edittime) ) - ), - __METHOD__, - array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ) ); - foreach ( $res as $row ) { - if( $row->rev_user != $id ) { - return false; - } - } - return true; - } - /** * @private * @todo document * - * @parma $editText string + * @param $editText string * * @return bool */ - function mergeChangesInto( &$editText ){ + function mergeChangesInto( &$editText ) { wfProfileIn( __METHOD__ ); $db = wfGetDB( DB_MASTER ); @@ -1552,7 +1548,7 @@ class EditPage { * * @param $text string * - * @return string|false matching string or false + * @return string|bool matching string or false */ public static function matchSpamRegex( $text ) { global $wgSpamRegex; @@ -1564,9 +1560,9 @@ class EditPage { /** * Check given input text against $wgSpamRegex, and return the text of the first match. * - * @parma $text string + * @param $text string * - * @return string|false matching string or false + * @return string|bool matching string or false */ public static function matchSummarySpamRegex( $text ) { global $wgSummarySpamRegex; @@ -1580,9 +1576,9 @@ class EditPage { * @return bool|string */ protected static function matchSpamRegexInternal( $text, $regexes ) { - foreach( $regexes as $regex ) { + foreach ( $regexes as $regex ) { $matches = array(); - if( preg_match( $regex, $text, $matches ) ) { + if ( preg_match( $regex, $text, $matches ) ) { return $matches[0]; } } @@ -1595,7 +1591,7 @@ class EditPage { $wgOut->addModules( 'mediawiki.action.edit' ); if ( $wgUser->getOption( 'uselivepreview', false ) ) { - $wgOut->addModules( 'mediawiki.legacy.preview' ); + $wgOut->addModules( 'mediawiki.action.edit.preview' ); } // Bug #19334: textarea jumps when editing articles in IE8 $wgOut->addStyle( 'common/IE80Fixes.css', 'screen', 'IE 8' ); @@ -1605,21 +1601,21 @@ class EditPage { # Enabled article-related sidebar, toplinks, etc. $wgOut->setArticleRelated( true ); + $contextTitle = $this->getContextTitle(); if ( $this->isConflict ) { - $wgOut->setPageTitle( wfMessage( 'editconflict', $this->getContextTitle()->getPrefixedText() ) ); - } elseif ( $this->section != '' ) { + $msg = 'editconflict'; + } elseif ( $contextTitle->exists() && $this->section != '' ) { $msg = $this->section == 'new' ? 'editingcomment' : 'editingsection'; - $wgOut->setPageTitle( wfMessage( $msg, $this->getContextTitle()->getPrefixedText() ) ); } else { - # Use the title defined by DISPLAYTITLE magic word when present - if ( isset( $this->mParserOutput ) - && ( $dt = $this->mParserOutput->getDisplayTitle() ) !== false ) { - $title = $dt; - } else { - $title = $this->getContextTitle()->getPrefixedText(); - } - $wgOut->setPageTitle( wfMessage( 'editing', $title ) ); + $msg = $contextTitle->exists() || ( $contextTitle->getNamespace() == NS_MEDIAWIKI && $contextTitle->getDefaultMessageText() !== false ) ? + 'editing' : 'creating'; + } + # Use the title defined by DISPLAYTITLE magic word when present + $displayTitle = isset( $this->mParserOutput ) ? $this->mParserOutput->getDisplayTitle() : false; + if ( $displayTitle === false ) { + $displayTitle = $contextTitle->getPrefixedText(); } + $wgOut->setPageTitle( wfMessage( $msg, $displayTitle ) ); } /** @@ -1636,6 +1632,24 @@ class EditPage { if ( $namespace == NS_MEDIAWIKI ) { # Show a warning if editing an interface message $wgOut->wrapWikiMsg( "<div class='mw-editinginterface'>\n$1\n</div>", 'editinginterface' ); + } else if( $namespace == NS_FILE ) { + # Show a hint to shared repo + $file = wfFindFile( $this->mTitle ); + if( $file && !$file->isLocal() ) { + $descUrl = $file->getDescriptionUrl(); + # there must be a description url to show a hint to shared repo + if( $descUrl ) { + if( !$this->mTitle->exists() ) { + $wgOut->wrapWikiMsg( "<div class=\"mw-sharedupload-desc-create\">\n$1\n</div>", array ( + 'sharedupload-desc-create', $file->getRepo()->getDisplayName(), $descUrl + ) ); + } else { + $wgOut->wrapWikiMsg( "<div class=\"mw-sharedupload-desc-edit\">\n$1\n</div>", array( + 'sharedupload-desc-edit', $file->getRepo()->getDisplayName(), $descUrl + ) ); + } + } + } } # Show a warning message when someone creates/edits a user (talk) page but the user does not exist @@ -1645,7 +1659,7 @@ class EditPage { $username = $parts[0]; $user = User::newFromName( $username, false /* allow IP users*/ ); $ip = User::isIP( $username ); - if ( !($user && $user->isLoggedIn()) && !$ip ) { # User does not exist + if ( !( $user && $user->isLoggedIn() ) && !$ip ) { # User does not exist $wgOut->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n$1\n</div>", array( 'userpage-userdoesnotexist', wfEscapeWikiText( $username ) ) ); } elseif ( $user->isBlocked() ) { # Show log extract if the user is currently blocked @@ -1679,7 +1693,7 @@ class EditPage { '', array( 'lim' => 10, 'conds' => array( "log_action != 'revision'" ), 'showIfEmpty' => false, - 'msgKey' => array( 'recreate-moveddeleted-warn') ) + 'msgKey' => array( 'recreate-moveddeleted-warn' ) ) ); } } @@ -1715,16 +1729,16 @@ class EditPage { wfProfileIn( __METHOD__ ); - #need to parse the preview early so that we know which templates are used, - #otherwise users with "show preview after edit box" will get a blank list - #we parse this near the beginning so that setHeaders can do the title - #setting work instead of leaving it in getPreviewText + # need to parse the preview early so that we know which templates are used, + # otherwise users with "show preview after edit box" will get a blank list + # we parse this near the beginning so that setHeaders can do the title + # setting work instead of leaving it in getPreviewText $previewOutput = ''; if ( $this->formtype == 'preview' ) { $previewOutput = $this->getPreviewText(); } - wfRunHooks( 'EditPage::showEditForm:initial', array( &$this ) ); + wfRunHooks( 'EditPage::showEditForm:initial', array( &$this, &$wgOut ) ); $this->setHeaders(); @@ -1753,7 +1767,7 @@ class EditPage { } } - $wgOut->addHTML( Html::openElement( 'form', array( 'id' => 'editform', 'name' => 'editform', + $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' ) ) ); @@ -1777,14 +1791,19 @@ class EditPage { : 'confirmrecreate'; $wgOut->addHTML( '<div class="mw-confirm-recreate">' . - wfMsgExt( $key, 'parseinline', $username, "<nowiki>$comment</nowiki>" ) . - Xml::checkLabel( wfMsg( 'recreate' ), 'wpRecreate', 'wpRecreate', false, + wfMessage( $key, $username, "<nowiki>$comment</nowiki>" )->parse() . + Xml::checkLabel( wfMessage( 'recreate' )->text(), 'wpRecreate', 'wpRecreate', false, array( 'title' => Linker::titleAttrib( 'recreate' ), 'tabindex' => 1, 'id' => 'wpRecreate' ) ) . '</div>' ); } + # When the summary is hidden, also hide them on preview/show changes + if( $this->nosummary ) { + $wgOut->addHTML( Html::hidden( 'nosummary', true ) ); + } + # If a blank edit summary was previously provided, and the appropriate # user preference is active, pass a hidden tag as wpIgnoreBlankSummary. This will stop the # user being bounced back more than once in the event that a summary @@ -1796,6 +1815,17 @@ class EditPage { $wgOut->addHTML( Html::hidden( 'wpIgnoreBlankSummary', true ) ); } + if ( $this->undidRev ) { + $wgOut->addHTML( Html::hidden( 'wpUndidRevision', $this->undidRev ) ); + } + + if ( $this->hasPresetSummary ) { + // If a summary has been preset using &summary= we dont want to prompt for + // a different summary. Only prompt for a summary if the summary is blanked. + // (Bug 17416) + $this->autoSumm = md5( '' ); + } + $autosumm = $this->autoSumm ? $this->autoSumm : md5( $this->summary ); $wgOut->addHTML( Html::hidden( 'wpAutoSummary', $autosumm ) ); @@ -1827,10 +1857,6 @@ class EditPage { $wgOut->addHTML( $this->editFormTextAfterContent ); - $wgOut->addWikiText( $this->getCopywarn() ); - - $wgOut->addHTML( $this->editFormTextAfterWarn ); - $this->showStandardInputs(); $this->showFormAfterText(); @@ -1870,7 +1896,7 @@ class EditPage { preg_match( "/^(=+)(.+)\\1\\s*(\n|$)/i", $text, $matches ); if ( !empty( $matches[2] ) ) { global $wgParser; - return $wgParser->stripSectionName(trim($matches[2])); + return $wgParser->stripSectionName( trim( $matches[2] ) ); } else { return false; } @@ -1884,8 +1910,8 @@ class EditPage { } # Optional notices on a per-namespace and per-page basis - $editnotice_ns = 'editnotice-'.$this->mTitle->getNamespace(); - $editnotice_ns_message = wfMessage( $editnotice_ns )->inContentLanguage(); + $editnotice_ns = 'editnotice-' . $this->mTitle->getNamespace(); + $editnotice_ns_message = wfMessage( $editnotice_ns ); if ( $editnotice_ns_message->exists() ) { $wgOut->addWikiText( $editnotice_ns_message->plain() ); } @@ -1893,16 +1919,16 @@ class EditPage { $parts = explode( '/', $this->mTitle->getDBkey() ); $editnotice_base = $editnotice_ns; while ( count( $parts ) > 0 ) { - $editnotice_base .= '-'.array_shift( $parts ); - $editnotice_base_msg = wfMessage( $editnotice_base )->inContentLanguage(); + $editnotice_base .= '-' . array_shift( $parts ); + $editnotice_base_msg = wfMessage( $editnotice_base ); if ( $editnotice_base_msg->exists() ) { - $wgOut->addWikiText( $editnotice_base_msg->plain() ); + $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 )->inContentLanguage(); + $editnoticeMsg = wfMessage( $editnoticeText ); if ( $editnoticeMsg->exists() ) { $wgOut->addWikiText( $editnoticeMsg->plain() ); } @@ -1968,8 +1994,7 @@ class EditPage { // Something went wrong $wgOut->wrapWikiMsg( "<div class='errorbox'>\n$1\n</div>\n", - array( 'missing-article', $this->mTitle->getPrefixedText(), - wfMsgNoTrans( 'missingarticle-rev', $this->oldid ) ) ); + array( 'missing-revision', $this->oldid ) ); } } } @@ -2010,12 +2035,12 @@ class EditPage { } if ( $this->mTitle->isCascadeProtected() ) { # Is this page under cascading protection from some source pages? - list($cascadeSources, /* $restrictions */) = $this->mTitle->getCascadeProtectionSources(); + list( $cascadeSources, /* $restrictions */ ) = $this->mTitle->getCascadeProtectionSources(); $notice = "<div class='mw-cascadeprotectedwarning'>\n$1\n"; $cascadeSourcesCount = count( $cascadeSources ); if ( $cascadeSourcesCount > 0 ) { # Explain, and list the titles responsible - foreach( $cascadeSources as $page ) { + foreach ( $cascadeSources as $page ) { $notice .= '* [[:' . $page->getPrefixedText() . "]]\n"; } } @@ -2024,7 +2049,7 @@ class EditPage { } if ( !$this->mTitle->exists() && $this->mTitle->getRestrictions( 'create' ) ) { LogEventsList::showLogExtract( $wgOut, 'protect', $this->mTitle, '', - array( 'lim' => 1, + array( 'lim' => 1, 'showIfEmpty' => false, 'msgKey' => array( 'titleprotectedwarning' ), 'wrap' => "<div class=\"mw-titleprotectedwarning\">\n$1</div>" ) ); @@ -2038,14 +2063,17 @@ class EditPage { $wgOut->wrapWikiMsg( "<div class='error' id='mw-edit-longpageerror'>\n$1\n</div>", array( 'longpageerror', $wgLang->formatNum( $this->kblength ), $wgLang->formatNum( $wgMaxArticleSize ) ) ); } else { - if( !wfMessage('longpage-hint')->isDisabled() ) { + if ( !wfMessage( 'longpage-hint' )->isDisabled() ) { $wgOut->wrapWikiMsg( "<div id='mw-edit-longpage-hint'>\n$1\n</div>", array( 'longpage-hint', $wgLang->formatSize( strlen( $this->textbox1 ) ), strlen( $this->textbox1 ) ) ); } } + # Add header copyright warning + $this->showHeaderCopyrightWarning(); } + /** * Standard summary input and label (wgSummary), abstracted so EditPage * subclasses may reorganize the form. @@ -2060,9 +2088,9 @@ class EditPage { * * @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 250 and to make it use UTF-8 bytes, not characters. - $inputAttrs = ( is_array($inputAttrs) ? $inputAttrs : array() ) + array( + 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. + $inputAttrs = ( is_array( $inputAttrs ) ? $inputAttrs : array() ) + array( 'id' => 'wpSummary', 'maxlength' => '200', 'tabindex' => '1', @@ -2070,7 +2098,7 @@ class EditPage { 'spellcheck' => 'true', ) + Linker::tooltipAndAccesskeyAttribs( 'summary' ); - $spanLabelAttrs = ( is_array($spanLabelAttrs) ? $spanLabelAttrs : array() ) + array( + $spanLabelAttrs = ( is_array( $spanLabelAttrs ) ? $spanLabelAttrs : array() ) + array( 'class' => $this->missingSummary ? 'mw-summarymissed' : 'mw-summary', 'id' => "wpSummaryLabel" ); @@ -2107,9 +2135,9 @@ class EditPage { } } $summary = $wgContLang->recodeForEdit( $summary ); - $labelText = wfMsgExt( $isSubjectPreview ? 'subject' : 'summary', 'parseinline' ); - list($label, $input) = $this->getSummaryInput($summary, $labelText, array( 'class' => $summaryClass ), array()); - $wgOut->addHTML("{$label} {$input}"); + $labelText = wfMessage( $isSubjectPreview ? 'subject' : 'summary' )->parse(); + list( $label, $input ) = $this->getSummaryInput( $summary, $labelText, array( 'class' => $summaryClass ), array() ); + $wgOut->addHTML( "{$label} {$input}" ); } /** @@ -2126,11 +2154,12 @@ class EditPage { global $wgParser; if ( $isSubjectPreview ) - $summary = wfMsgForContent( 'newsectionsummary', $wgParser->stripSectionName( $summary ) ); + $summary = wfMessage( 'newsectionsummary', $wgParser->stripSectionName( $summary ) ) + ->inContentLanguage()->text(); $message = $isSubjectPreview ? 'subject-preview' : 'summary-preview'; - $summary = wfMsgExt( $message, 'parseinline' ) . Linker::commentBlock( $summary, $this->mTitle, $isSubjectPreview ); + $summary = wfMessage( $message )->parse() . Linker::commentBlock( $summary, $this->mTitle, $isSubjectPreview ); return Xml::tags( 'div', array( 'class' => 'mw-summary-preview' ), $summary ); } @@ -2146,7 +2175,7 @@ class EditPage { HTML ); if ( !$this->checkUnicodeCompliantBrowser() ) - $wgOut->addHTML(Html::hidden( 'safemode', '1' )); + $wgOut->addHTML( Html::hidden( 'safemode', '1' ) ); } protected function showFormAfterText() { @@ -2183,7 +2212,7 @@ HTML * The $textoverride method can be used by subclasses overriding showContentForm * to pass back to this method. * - * @param $customAttribs An array of html attributes to use in the textarea + * @param $customAttribs array of html attributes to use in the textarea * @param $textoverride String: optional text to override $this->textarea1 with */ protected function showTextbox1( $customAttribs = null, $textoverride = null ) { @@ -2230,7 +2259,7 @@ HTML global $wgOut, $wgUser; $wikitext = $this->safeUnicodeOutput( $content ); - if ( strval($wikitext) !== '' ) { + if ( strval( $wikitext ) !== '' ) { // Ensure there's a newline at the end, otherwise adding lines // is awkward. // But don't add a newline if the ext is empty, or Firefox in XHTML @@ -2272,7 +2301,7 @@ HTML $wgOut->addHTML( '</div>' ); - if ( $this->formtype == 'diff') { + if ( $this->formtype == 'diff' ) { $this->showDiff(); } } @@ -2285,12 +2314,12 @@ HTML */ protected function showPreview( $text ) { global $wgOut; - if ( $this->mTitle->getNamespace() == NS_CATEGORY) { + if ( $this->mTitle->getNamespace() == NS_CATEGORY ) { $this->mArticle->openShowCategory(); } # This hook seems slightly odd here, but makes things more # consistent for extensions. - wfRunHooks( 'OutputPageBeforeHTML',array( &$wgOut, &$text ) ); + wfRunHooks( 'OutputPageBeforeHTML', array( &$wgOut, &$text ) ); $wgOut->addHTML( $text ); if ( $this->mTitle->getNamespace() == NS_CATEGORY ) { $this->mArticle->closeShowCategory(); @@ -2307,7 +2336,16 @@ HTML function showDiff() { global $wgUser, $wgContLang, $wgParser, $wgOut; - $oldtext = $this->mArticle->getRawText(); + $oldtitlemsg = 'currentrev'; + # if message does not exist, show diff against the preloaded default + if( $this->mTitle->getNamespace() == NS_MEDIAWIKI && !$this->mTitle->exists() ) { + $oldtext = $this->mTitle->getDefaultMessageText(); + if( $oldtext !== false ) { + $oldtitlemsg = 'defaultmessagetext'; + } + } else { + $oldtext = $this->mArticle->getRawText(); + } $newtext = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary, $this->edittime ); @@ -2317,8 +2355,8 @@ HTML $newtext = $wgParser->preSaveTransform( $newtext, $this->mTitle, $wgUser, $popts ); if ( $oldtext !== false || $newtext != '' ) { - $oldtitle = wfMsgExt( 'currentrev', array( 'parseinline' ) ); - $newtitle = wfMsgExt( 'yourtext', array( 'parseinline' ) ); + $oldtitle = wfMessage( $oldtitlemsg )->parse(); + $newtitle = wfMessage( 'yourtext' )->parse(); $de = new DifferenceEngine( $this->mArticle->getContext() ); $de->setText( $oldtext, $newtext ); @@ -2331,6 +2369,18 @@ HTML $wgOut->addHTML( '<div id="wikiDiff">' . $difftext . '</div>' ); } + /** + * Show the header copyright warning. + */ + protected function showHeaderCopyrightWarning() { + $msg = 'editpage-head-copy-warn'; + if ( !wfMessage( $msg )->isDisabled() ) { + global $wgOut; + $wgOut->wrapWikiMsg( "<div class='editpage-head-copywarn'>\n$1\n</div>", + 'editpage-head-copy-warn' ); + } + } + /** * Give a chance for site and per-namespace customizations of * terms of service summary link that might exist separately @@ -2342,7 +2392,7 @@ HTML protected function showTosSummary() { $msg = 'editpage-tos-summary'; wfRunHooks( 'EditPageTosSummary', array( $this->mTitle, &$msg ) ); - if( !wfMessage( $msg )->isDisabled() ) { + if ( !wfMessage( $msg )->isDisabled() ) { global $wgOut; $wgOut->addHTML( '<div class="mw-tos-summary">' ); $wgOut->addWikiMsg( $msg ); @@ -2357,21 +2407,30 @@ HTML '</div>' ); } + /** + * Get the copyright warning + * + * Renamed to getCopyrightWarning(), old name kept around for backwards compatibility + */ protected function getCopywarn() { + return self::getCopyrightWarning( $this->mTitle ); + } + + public static function getCopyrightWarning( $title ) { global $wgRightsText; if ( $wgRightsText ) { $copywarnMsg = array( 'copyrightwarning', - '[[' . wfMsgForContent( 'copyrightpage' ) . ']]', + '[[' . wfMessage( 'copyrightpage' )->inContentLanguage()->text() . ']]', $wgRightsText ); } else { $copywarnMsg = array( 'copyrightwarning2', - '[[' . wfMsgForContent( 'copyrightpage' ) . ']]' ); + '[[' . wfMessage( 'copyrightpage' )->inContentLanguage()->text() . ']]' ); } // Allow for site and per-namespace customization of contribution/copyright notice. - wfRunHooks( 'EditPageCopyrightWarning', array( $this->mTitle, &$copywarnMsg ) ); + wfRunHooks( 'EditPageCopyrightWarning', array( $title, &$copywarnMsg ) ); return "<div id=\"editpage-copywarn\">\n" . - call_user_func_array("wfMsgNoTrans", $copywarnMsg) . "\n</div>"; + call_user_func_array( 'wfMessage', $copywarnMsg )->plain() . "\n</div>"; } protected function showStandardInputs( &$tabindex = 2 ) { @@ -2386,18 +2445,24 @@ HTML $checkboxes = $this->getCheckboxes( $tabindex, array( 'minor' => $this->minoredit, 'watch' => $this->watchthis ) ); $wgOut->addHTML( "<div class='editCheckboxes'>" . implode( $checkboxes, "\n" ) . "</div>\n" ); + + // Show copyright warning. + $wgOut->addWikiText( $this->getCopywarn() ); + $wgOut->addHTML( $this->editFormTextAfterWarn ); + $wgOut->addHTML( "<div class='editButtons'>\n" ); $wgOut->addHTML( implode( $this->getEditButtons( $tabindex ), "\n" ) . "\n" ); $cancel = $this->getCancelLink(); if ( $cancel !== '' ) { - $cancel .= wfMsgExt( 'pipe-separator' , 'escapenoentities' ); - } - $edithelpurl = Skin::makeInternalOrExternalUrl( wfMsgForContent( 'edithelppage' ) ); - $edithelp = '<a target="helpwindow" href="'.$edithelpurl.'">'. - htmlspecialchars( wfMsg( 'edithelp' ) ).'</a> '. - htmlspecialchars( wfMsg( 'newwindow' ) ); - $wgOut->addHTML( " <span class='editHelp'>{$cancel}{$edithelp}</span>\n" ); + $cancel .= wfMessage( 'pipe-separator' )->text(); + } + $edithelpurl = Skin::makeInternalOrExternalUrl( wfMessage( 'edithelppage' )->inContentLanguage()->text() ); + $edithelp = '<a target="helpwindow" href="' . $edithelpurl . '">' . + wfMessage( 'edithelp' )->escaped() . '</a> ' . + wfMessage( 'newwindow' )->parse(); + $wgOut->addHTML( " <span class='cancelLink'>{$cancel}</span>\n" ); + $wgOut->addHTML( " <span class='editHelp'>{$edithelp}</span>\n" ); $wgOut->addHTML( "</div><!-- editButtons -->\n</div><!-- editOptions -->\n" ); } @@ -2413,7 +2478,10 @@ HTML $de = new DifferenceEngine( $this->mArticle->getContext() ); $de->setText( $this->textbox2, $this->textbox1 ); - $de->showDiff( wfMsgExt( 'yourtext', 'parseinline' ), wfMsg( 'storedversion' ) ); + $de->showDiff( + wfMessage( 'yourtext' )->parse(), + wfMessage( 'storedversion' )->text() + ); $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourtext" ); $this->showTextbox2(); @@ -2431,7 +2499,7 @@ HTML return Linker::linkKnown( $this->getContextTitle(), - wfMsgExt( 'cancel', array( 'parseinline' ) ), + wfMessage( 'cancel' )->parse(), array( 'id' => 'mw-editform-cancel' ), $cancelParams ); @@ -2499,11 +2567,11 @@ HTML array( 'LIMIT' => 1, 'ORDER BY' => 'log_timestamp DESC' ) ); // Quick paranoid permission checks... - if( is_object( $data ) ) { - if( $data->log_deleted & LogPage::DELETED_USER ) - $data->user_name = wfMsgHtml( 'rev-deleted-user' ); - if( $data->log_deleted & LogPage::DELETED_COMMENT ) - $data->log_comment = wfMsgHtml( 'rev-deleted-comment' ); + if ( is_object( $data ) ) { + if ( $data->log_deleted & LogPage::DELETED_USER ) + $data->user_name = wfMessage( 'rev-deleted-user' )->escaped(); + if ( $data->log_deleted & LogPage::DELETED_COMMENT ) + $data->log_comment = wfMessage( 'rev-deleted-comment' )->escaped(); } return $data; } @@ -2513,7 +2581,7 @@ HTML * @return string */ function getPreviewText() { - global $wgOut, $wgUser, $wgParser, $wgRawHtml; + global $wgOut, $wgUser, $wgParser, $wgRawHtml, $wgLang; wfProfileIn( __METHOD__ ); @@ -2526,7 +2594,7 @@ HTML // string, which happens when you initially edit // a category page, due to automatic preview-on-open. $parsedNote = $wgOut->parse( "<div class='previewnote'>" . - wfMsg( 'session_fail_preview_html' ) . "</div>", true, /* interface */true ); + wfMessage( 'session_fail_preview_html' )->text() . "</div>", true, /* interface */true ); } wfProfileOut( __METHOD__ ); return $parsedNote; @@ -2534,29 +2602,28 @@ HTML if ( $this->mTriedSave && !$this->mTokenOk ) { if ( $this->mTokenOkExceptSuffix ) { - $note = wfMsg( 'token_suffix_mismatch' ); + $note = wfMessage( 'token_suffix_mismatch' )->plain(); } else { - $note = wfMsg( 'session_fail_preview' ); + $note = wfMessage( 'session_fail_preview' )->plain(); } } elseif ( $this->incompleteForm ) { - $note = wfMsg( 'edit_form_incomplete' ); + $note = wfMessage( 'edit_form_incomplete' )->plain(); } else { - $note = wfMsg( 'previewnote' ); + $note = wfMessage( 'previewnote' )->plain() . + ' [[#' . self::EDITFORM_ID . '|' . $wgLang->getArrow() . ' ' . wfMessage( 'continue-editing' )->text() . ']]'; } - $parserOptions = ParserOptions::newFromUser( $wgUser ); + $parserOptions = $this->mArticle->makeParserOptions( $this->mArticle->getContext() ); + $parserOptions->setEditSection( false ); - $parserOptions->setTidy( true ); $parserOptions->setIsPreview( true ); - $parserOptions->setIsSectionPreview( !is_null($this->section) && $this->section !== '' ); + $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !== '' ); # don't parse non-wikitext pages, show message about preview - # XXX: stupid php bug won't let us use $this->getContextTitle()->isCssJsSubpage() here -- This note has been there since r3530. Sure the bug was fixed time ago? - - if ( $this->isCssJsSubpage || !$this->mTitle->isWikitextPage() ) { - if( $this->mTitle->isCssJsSubpage() ) { + if ( $this->mTitle->isCssJsSubpage() || !$this->mTitle->isWikitextPage() ) { + if ( $this->mTitle->isCssJsSubpage() ) { $level = 'user'; - } elseif( $this->mTitle->isCssOrJsPage() ) { + } elseif ( $this->mTitle->isCssOrJsPage() ) { $level = 'site'; } else { $level = false; @@ -2564,64 +2631,66 @@ HTML # Used messages to make sure grep find them: # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview - if( $level ) { - if (preg_match( "/\\.css$/", $this->mTitle->getText() ) ) { - $previewtext = "<div id='mw-{$level}csspreview'>\n" . wfMsg( "{$level}csspreview" ) . "\n</div>"; - $class = "mw-code mw-css"; - } elseif (preg_match( "/\\.js$/", $this->mTitle->getText() ) ) { - $previewtext = "<div id='mw-{$level}jspreview'>\n" . wfMsg( "{$level}jspreview" ) . "\n</div>"; - $class = "mw-code mw-js"; + $class = 'mw-code'; + if ( $level ) { + if ( preg_match( "/\\.css$/", $this->mTitle->getText() ) ) { + $previewtext = "<div id='mw-{$level}csspreview'>\n" . wfMessage( "{$level}csspreview" )->text() . "\n</div>"; + $class .= " mw-css"; + } elseif ( preg_match( "/\\.js$/", $this->mTitle->getText() ) ) { + $previewtext = "<div id='mw-{$level}jspreview'>\n" . wfMessage( "{$level}jspreview" )->text() . "\n</div>"; + $class .= " mw-js"; } else { throw new MWException( 'A CSS/JS (sub)page but which is not css nor js!' ); } + $parserOutput = $wgParser->parse( $previewtext, $this->mTitle, $parserOptions ); + $previewHTML = $parserOutput->getText(); + } else { + $previewHTML = ''; } - $parserOutput = $wgParser->parse( $previewtext, $this->mTitle, $parserOptions ); - $previewHTML = $parserOutput->mText; $previewHTML .= "<pre class=\"$class\" dir=\"ltr\">\n" . htmlspecialchars( $this->textbox1 ) . "\n</pre>\n"; } else { - $rt = Title::newFromRedirectArray( $this->textbox1 ); - if ( $rt ) { - $previewHTML = $this->mArticle->viewRedirect( $rt, false ); - } else { - $toparse = $this->textbox1; - - # If we're adding a comment, we need to show the - # summary as the headline - if ( $this->section == "new" && $this->summary != "" ) { - $toparse = wfMsgForContent( 'newsectionheaderdefaultlevel', $this->summary ) . "\n\n" . $toparse; - } + $toparse = $this->textbox1; - wfRunHooks( 'EditPageGetPreviewText', array( $this, &$toparse ) ); + # 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; + } - $parserOptions->enableLimitReport(); + wfRunHooks( 'EditPageGetPreviewText', array( $this, &$toparse ) ); - $toparse = $wgParser->preSaveTransform( $toparse, $this->mTitle, $wgUser, $parserOptions ); - $parserOutput = $wgParser->parse( $toparse, $this->mTitle, $parserOptions ); + $toparse = $wgParser->preSaveTransform( $toparse, $this->mTitle, $wgUser, $parserOptions ); + $parserOutput = $wgParser->parse( $toparse, $this->mTitle, $parserOptions ); + $rt = Title::newFromRedirectArray( $this->textbox1 ); + if ( $rt ) { + $previewHTML = $this->mArticle->viewRedirect( $rt, false ); + } else { $previewHTML = $parserOutput->getText(); - $this->mParserOutput = $parserOutput; - $wgOut->addParserOutputNoText( $parserOutput ); + } - if ( count( $parserOutput->getWarnings() ) ) { - $note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() ); - } + $this->mParserOutput = $parserOutput; + $wgOut->addParserOutputNoText( $parserOutput ); + + if ( count( $parserOutput->getWarnings() ) ) { + $note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() ); } } - if( $this->isConflict ) { - $conflict = '<h2 id="mw-previewconflict">' . htmlspecialchars( wfMsg( 'previewconflict' ) ) . "</h2>\n"; + if ( $this->isConflict ) { + $conflict = '<h2 id="mw-previewconflict">' . wfMessage( 'previewconflict' )->escaped() . "</h2>\n"; } else { $conflict = '<hr />'; } $previewhead = "<div class='previewnote'>\n" . - '<h2 id="mw-previewheader">' . htmlspecialchars( wfMsg( 'preview' ) ) . "</h2>" . + '<h2 id="mw-previewheader">' . wfMessage( 'preview' )->escaped() . "</h2>" . $wgOut->parse( $note, true, /* interface */true ) . $conflict . "</div>\n"; $pageLang = $this->mTitle->getPageLanguage(); $attribs = array( 'lang' => $pageLang->getCode(), 'dir' => $pageLang->getDir(), - 'class' => 'mw-content-'.$pageLang->getDir() ); + 'class' => 'mw-content-' . $pageLang->getDir() ); $previewHTML = Html::rawElement( 'div', $attribs, $previewHTML ); wfProfileOut( __METHOD__ ); @@ -2637,9 +2706,9 @@ HTML if ( !isset( $this->mParserOutput ) ) { return $templates; } - foreach( $this->mParserOutput->getTemplates() as $ns => $template) { - foreach( array_keys( $template ) as $dbk ) { - $templates[] = Title::makeTitle($ns, $dbk); + foreach ( $this->mParserOutput->getTemplates() as $ns => $template ) { + foreach ( array_keys( $template ) as $dbk ) { + $templates[] = Title::makeTitle( $ns, $dbk ); } } return $templates; @@ -2680,8 +2749,8 @@ HTML 'id' => 'mw-editbutton-bold', 'open' => '\'\'\'', 'close' => '\'\'\'', - 'sample' => wfMsg( 'bold_sample' ), - 'tip' => wfMsg( 'bold_tip' ), + 'sample' => wfMessage( 'bold_sample' )->text(), + 'tip' => wfMessage( 'bold_tip' )->text(), 'key' => 'B' ), array( @@ -2689,8 +2758,8 @@ HTML 'id' => 'mw-editbutton-italic', 'open' => '\'\'', 'close' => '\'\'', - 'sample' => wfMsg( 'italic_sample' ), - 'tip' => wfMsg( 'italic_tip' ), + 'sample' => wfMessage( 'italic_sample' )->text(), + 'tip' => wfMessage( 'italic_tip' )->text(), 'key' => 'I' ), array( @@ -2698,8 +2767,8 @@ HTML 'id' => 'mw-editbutton-link', 'open' => '[[', 'close' => ']]', - 'sample' => wfMsg( 'link_sample' ), - 'tip' => wfMsg( 'link_tip' ), + 'sample' => wfMessage( 'link_sample' )->text(), + 'tip' => wfMessage( 'link_tip' )->text(), 'key' => 'L' ), array( @@ -2707,8 +2776,8 @@ HTML 'id' => 'mw-editbutton-extlink', 'open' => '[', 'close' => ']', - 'sample' => wfMsg( 'extlink_sample' ), - 'tip' => wfMsg( 'extlink_tip' ), + 'sample' => wfMessage( 'extlink_sample' )->text(), + 'tip' => wfMessage( 'extlink_tip' )->text(), 'key' => 'X' ), array( @@ -2716,8 +2785,8 @@ HTML 'id' => 'mw-editbutton-headline', 'open' => "\n== ", 'close' => " ==\n", - 'sample' => wfMsg( 'headline_sample' ), - 'tip' => wfMsg( 'headline_tip' ), + 'sample' => wfMessage( 'headline_sample' )->text(), + 'tip' => wfMessage( 'headline_tip' )->text(), 'key' => 'H' ), $imagesAvailable ? array( @@ -2725,8 +2794,8 @@ HTML 'id' => 'mw-editbutton-image', 'open' => '[[' . $wgContLang->getNsText( NS_FILE ) . ':', 'close' => ']]', - 'sample' => wfMsg( 'image_sample' ), - 'tip' => wfMsg( 'image_tip' ), + 'sample' => wfMessage( 'image_sample' )->text(), + 'tip' => wfMessage( 'image_tip' )->text(), 'key' => 'D', ) : false, $imagesAvailable ? array( @@ -2734,17 +2803,17 @@ HTML 'id' => 'mw-editbutton-media', 'open' => '[[' . $wgContLang->getNsText( NS_MEDIA ) . ':', 'close' => ']]', - 'sample' => wfMsg( 'media_sample' ), - 'tip' => wfMsg( 'media_tip' ), + 'sample' => wfMessage( 'media_sample' )->text(), + 'tip' => wfMessage( 'media_tip' )->text(), 'key' => 'M' ) : false, - $wgUseTeX ? array( + $wgUseTeX ? array( 'image' => $wgLang->getImageFile( 'button-math' ), 'id' => 'mw-editbutton-math', 'open' => "<math>", 'close' => "</math>", - 'sample' => wfMsg( 'math_sample' ), - 'tip' => wfMsg( 'math_tip' ), + 'sample' => wfMessage( 'math_sample' )->text(), + 'tip' => wfMessage( 'math_tip' )->text(), 'key' => 'C' ) : false, array( @@ -2752,8 +2821,8 @@ HTML 'id' => 'mw-editbutton-nowiki', 'open' => "<nowiki>", 'close' => "</nowiki>", - 'sample' => wfMsg( 'nowiki_sample' ), - 'tip' => wfMsg( 'nowiki_tip' ), + 'sample' => wfMessage( 'nowiki_sample' )->text(), + 'tip' => wfMessage( 'nowiki_tip' )->text(), 'key' => 'N' ), array( @@ -2762,7 +2831,7 @@ HTML 'open' => '--~~~~', 'close' => '', 'sample' => '', - 'tip' => wfMsg( 'sig_tip' ), + 'tip' => wfMessage( 'sig_tip' )->text(), 'key' => 'Y' ), array( @@ -2771,7 +2840,7 @@ HTML 'open' => "\n----\n", 'close' => '', 'sample' => '', - 'tip' => wfMsg( 'hr_tip' ), + 'tip' => wfMessage( 'hr_tip' )->text(), 'key' => 'R' ) ); @@ -2797,7 +2866,7 @@ HTML $script .= Xml::encodeJsCall( 'mw.toolbar.addButton', $params ); } - + // This used to be called on DOMReady from mediawiki.action.edit, which // ended up causing race conditions with the setup code above. $script .= "\n" . @@ -2818,7 +2887,7 @@ HTML * Returns an array of html code of the following checkboxes: * minor and watch * - * @param $tabindex Current tabindex + * @param $tabindex int Current tabindex * @param $checked Array of checkbox => bool, where bool indicates the checked * status of the checkbox * @@ -2832,11 +2901,11 @@ HTML // don't show the minor edit checkbox if it's a new page or section if ( !$this->isNew ) { $checkboxes['minor'] = ''; - $minorLabel = wfMsgExt( 'minoredit', array( 'parseinline' ) ); + $minorLabel = wfMessage( 'minoredit' )->parse(); if ( $wgUser->isAllowed( 'minoredit' ) ) { $attribs = array( 'tabindex' => ++$tabindex, - 'accesskey' => wfMsg( 'accesskey-minoredit' ), + 'accesskey' => wfMessage( 'accesskey-minoredit' )->text(), 'id' => 'wpMinoredit', ); $checkboxes['minor'] = @@ -2847,12 +2916,12 @@ HTML } } - $watchLabel = wfMsgExt( 'watchthis', array( 'parseinline' ) ); + $watchLabel = wfMessage( 'watchthis' )->parse(); $checkboxes['watch'] = ''; if ( $wgUser->isLoggedIn() ) { $attribs = array( 'tabindex' => ++$tabindex, - 'accesskey' => wfMsg( 'accesskey-watch' ), + 'accesskey' => wfMessage( 'accesskey-watch' )->text(), 'id' => 'wpWatchthis', ); $checkboxes['watch'] = @@ -2869,7 +2938,7 @@ HTML * Returns an array of html code of the following buttons: * save, diff, preview and live * - * @param $tabindex Current tabindex + * @param $tabindex int Current tabindex * * @return array */ @@ -2881,11 +2950,11 @@ HTML 'name' => 'wpSave', 'type' => 'submit', 'tabindex' => ++$tabindex, - 'value' => wfMsg( 'savearticle' ), - 'accesskey' => wfMsg( 'accesskey-save' ), - 'title' => wfMsg( 'tooltip-save' ).' ['.wfMsg( 'accesskey-save' ).']', + 'value' => wfMessage( 'savearticle' )->text(), + 'accesskey' => wfMessage( 'accesskey-save' )->text(), + 'title' => wfMessage( 'tooltip-save' )->text() . ' [' . wfMessage( 'accesskey-save' )->text() . ']', ); - $buttons['save'] = Xml::element('input', $temp, ''); + $buttons['save'] = Xml::element( 'input', $temp, '' ); ++$tabindex; // use the same for preview and live preview $temp = array( @@ -2893,9 +2962,9 @@ HTML 'name' => 'wpPreview', 'type' => 'submit', 'tabindex' => $tabindex, - 'value' => wfMsg( 'showpreview' ), - 'accesskey' => wfMsg( 'accesskey-preview' ), - 'title' => wfMsg( 'tooltip-preview' ) . ' [' . wfMsg( 'accesskey-preview' ) . ']', + 'value' => wfMessage( 'showpreview' )->text(), + 'accesskey' => wfMessage( 'accesskey-preview' )->text(), + 'title' => wfMessage( 'tooltip-preview' )->text() . ' [' . wfMessage( 'accesskey-preview' )->text() . ']', ); $buttons['preview'] = Xml::element( 'input', $temp, '' ); $buttons['live'] = ''; @@ -2905,9 +2974,9 @@ HTML 'name' => 'wpDiff', 'type' => 'submit', 'tabindex' => ++$tabindex, - 'value' => wfMsg( 'showdiff' ), - 'accesskey' => wfMsg( 'accesskey-diff' ), - 'title' => wfMsg( 'tooltip-diff' ) . ' [' . wfMsg( 'accesskey-diff' ) . ']', + 'value' => wfMessage( 'showdiff' )->text(), + 'accesskey' => wfMessage( 'accesskey-diff' )->text(), + 'title' => wfMessage( 'tooltip-diff' )->text() . ' [' . wfMessage( 'accesskey-diff' )->text() . ']', ); $buttons['diff'] = Xml::element( 'input', $temp, '' ); @@ -2923,8 +2992,8 @@ HTML * failure, etc). * * @todo This doesn't include category or interlanguage links. - * Would need to enhance it a bit, <s>maybe wrap them in XML - * or something...</s> that might also require more skin + * Would need to enhance it a bit, "<s>maybe wrap them in XML + * or something...</s>" that might also require more skin * initialization, so check whether that's a problem. */ function livePreview() { @@ -2954,7 +3023,7 @@ HTML wfDeprecated( __METHOD__, '1.19' ); global $wgUser; - throw new UserBlockedError( $wgUser->mBlock ); + throw new UserBlockedError( $wgUser->getBlock() ); } /** @@ -2988,7 +3057,7 @@ HTML $wgOut->prepareErrorPage( wfMessage( 'nosuchsectiontitle' ) ); - $res = wfMsgExt( 'nosuchsectiontext', 'parse', $this->section ); + $res = wfMessage( 'nosuchsectiontext', $this->section )->parseAsBlock(); wfRunHooks( 'EditPageNoSuchSection', array( &$this, &$res ) ); $wgOut->addHTML( $res ); @@ -2998,12 +3067,12 @@ HTML /** * Produce the stock "your edit contains spam" page * - * @param $match Text which triggered one or more filters + * @param $match string Text which triggered one or more filters * @deprecated since 1.17 Use method spamPageWithContent() instead */ static function spamPage( $match = false ) { wfDeprecated( __METHOD__, '1.17' ); - + global $wgOut, $wgTitle; $wgOut->prepareErrorPage( wfMessage( 'spamprotectiontitle' ) ); @@ -3021,12 +3090,15 @@ HTML /** * Show "your edit contains spam" page with your diff and text * - * @param $match Text which triggered one or more filters + * @param $match string|Array|bool Text (or array of texts) which triggered one or more filters */ public function spamPageWithContent( $match = false ) { - global $wgOut; + global $wgOut, $wgLang; $this->textbox2 = $this->textbox1; + if( is_array( $match ) ){ + $match = $wgLang->listToText( $match ); + } $wgOut->prepareErrorPage( wfMessage( 'spamprotectiontitle' ) ); $wgOut->addHTML( '<div id="spamprotected">' ); @@ -3037,9 +3109,7 @@ HTML $wgOut->addHTML( '</div>' ); $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourdiff" ); - $de = new DifferenceEngine( $this->mArticle->getContext() ); - $de->setText( $this->getCurrentText(), $this->textbox2 ); - $de->showDiff( wfMsg( "storedversion" ), wfMsgExt( 'yourtext', 'parseinline' ) ); + $this->showDiff(); $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourtext" ); $this->showTextbox2(); @@ -3066,14 +3136,16 @@ HTML * @private */ function checkUnicodeCompliantBrowser() { - global $wgBrowserBlackList; - if ( empty( $_SERVER["HTTP_USER_AGENT"] ) ) { + global $wgBrowserBlackList, $wgRequest; + + $currentbrowser = $wgRequest->getHeader( 'User-Agent' ); + if ( $currentbrowser === false ) { // No User-Agent header sent? Trust it by default... return true; } - $currentbrowser = $_SERVER["HTTP_USER_AGENT"]; + foreach ( $wgBrowserBlackList as $browser ) { - if ( preg_match($browser, $currentbrowser) ) { + if ( preg_match( $browser, $currentbrowser ) ) { return false; } } @@ -3144,25 +3216,25 @@ HTML $bytesleft = 0; $result = ""; $working = 0; - for( $i = 0; $i < strlen( $invalue ); $i++ ) { + for ( $i = 0; $i < strlen( $invalue ); $i++ ) { $bytevalue = ord( $invalue[$i] ); - if ( $bytevalue <= 0x7F ) { //0xxx xxxx + if ( $bytevalue <= 0x7F ) { // 0xxx xxxx $result .= chr( $bytevalue ); $bytesleft = 0; - } elseif ( $bytevalue <= 0xBF ) { //10xx xxxx + } elseif ( $bytevalue <= 0xBF ) { // 10xx xxxx $working = $working << 6; - $working += ($bytevalue & 0x3F); + $working += ( $bytevalue & 0x3F ); $bytesleft--; if ( $bytesleft <= 0 ) { $result .= "&#x" . strtoupper( dechex( $working ) ) . ";"; } - } elseif ( $bytevalue <= 0xDF ) { //110x xxxx + } elseif ( $bytevalue <= 0xDF ) { // 110x xxxx $working = $bytevalue & 0x1F; $bytesleft = 1; - } elseif ( $bytevalue <= 0xEF ) { //1110 xxxx + } elseif ( $bytevalue <= 0xEF ) { // 1110 xxxx $working = $bytevalue & 0x0F; $bytesleft = 2; - } else { //1111 0xxx + } else { // 1111 0xxx $working = $bytevalue & 0x07; $bytesleft = 3; } @@ -3181,20 +3253,20 @@ HTML */ function unmakesafe( $invalue ) { $result = ""; - for( $i = 0; $i < strlen( $invalue ); $i++ ) { - if ( ( substr( $invalue, $i, 3 ) == "&#x" ) && ( $invalue[$i+3] != '0' ) ) { + for ( $i = 0; $i < strlen( $invalue ); $i++ ) { + if ( ( substr( $invalue, $i, 3 ) == "&#x" ) && ( $invalue[$i + 3] != '0' ) ) { $i += 3; $hexstring = ""; do { $hexstring .= $invalue[$i]; $i++; - } while( ctype_xdigit( $invalue[$i] ) && ( $i < strlen( $invalue ) ) ); + } while ( ctype_xdigit( $invalue[$i] ) && ( $i < strlen( $invalue ) ) ); // Do some sanity checks. These aren't needed for reversability, // 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) ) { - $codepoint = hexdec($hexstring); + if ( ( substr( $invalue, $i, 1 ) == ";" ) and ( strlen( $hexstring ) <= 6 ) ) { + $codepoint = hexdec( $hexstring ); $result .= codepointToUtf8( $codepoint ); } else { $result .= "&#x" . $hexstring . substr( $invalue, $i, 1 ); diff --git a/includes/Exception.php b/includes/Exception.php index 3bd89b6e..714f73e8 100644 --- a/includes/Exception.php +++ b/includes/Exception.php @@ -1,6 +1,21 @@ <?php /** - * Exception class and handler + * Exception class and handler. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html * * @file */ @@ -15,8 +30,11 @@ * @ingroup Exception */ class MWException extends Exception { + var $logId; + /** - * Should the exception use $wgOut to output the error ? + * Should the exception use $wgOut to output the error? + * * @return bool */ function useOutputPage() { @@ -27,7 +45,8 @@ class MWException extends Exception { } /** - * Can the extension use wfMsg() to get i18n messages ? + * Can the extension use the Message class/wfMessage to get i18n-ed messages? + * * @return bool */ function useMessageCache() { @@ -45,19 +64,19 @@ 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 - * @return Mixed: string to output or null if any hook has been called + * @param $name string: class name of the exception + * @param $args array: 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() ) { global $wgExceptionHooks; if ( !isset( $wgExceptionHooks ) || !is_array( $wgExceptionHooks ) ) { - return; // Just silently ignore + return null; // Just silently ignore } if ( !array_key_exists( $name, $wgExceptionHooks ) || !is_array( $wgExceptionHooks[ $name ] ) ) { - return; + return null; } $hooks = $wgExceptionHooks[ $name ]; @@ -74,22 +93,23 @@ class MWException extends Exception { return $result; } } + return null; } /** * Get a message from i18n * - * @param $key String: message name - * @param $fallback String: default message if the message cache can't be + * @param $key string: message name + * @param $fallback string: 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 + * @return string message with arguments replaced */ function msg( $key, $fallback /*[, params...] */ ) { $args = array_slice( func_get_args(), 2 ); if ( $this->useMessageCache() ) { - return wfMsgNoTrans( $key, $args ); + return wfMessage( $key, $args )->plain(); } else { return wfMsgReplaceArgs( $fallback, $args ); } @@ -100,7 +120,7 @@ class MWException extends Exception { * backtrace to the error, otherwise show a message to ask to set it to true * to show that information. * - * @return String html to output + * @return string html to output */ function getHTML() { global $wgShowExceptionDetails; @@ -110,15 +130,22 @@ class MWException extends Exception { '</p><p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) ) . "</p>\n"; } else { - return "<p>Set <b><tt>\$wgShowExceptionDetails = true;</tt></b> " . + return + "<div class=\"errorbox\">" . + '[' . $this->getLogId() . '] ' . + gmdate( 'Y-m-d H:i:s' ) . + ": Fatal exception of type " . get_class( $this ) . "</div>\n" . + "<!-- Set \$wgShowExceptionDetails = true; " . "at the bottom of LocalSettings.php to show detailed " . - "debugging information.</p>"; + "debugging information. -->"; } } /** + * Get the text to display when reporting the error on the command line. * If $wgShowExceptionDetails is true, return a text message with a * backtrace to the error. + * * @return string */ function getText() { @@ -134,22 +161,38 @@ class MWException extends Exception { } /** - * Return titles of this error page - * @return String + * Return the title of the page when reporting this error in a HTTP response. + * + * @return string */ function getPageTitle() { return $this->msg( 'internalerror', "Internal error" ); } + /** + * Get a random ID for this error. + * This allows to link the exception to its correspoding log entry when + * $wgShowExceptionDetails is set to false. + * + * @return string + */ + function getLogId() { + if ( $this->logId === null ) { + $this->logId = wfRandomString( 8 ); + } + return $this->logId; + } + /** * Return the requested URL and point to file and line number from which the - * exception occured + * exception occurred * - * @return String + * @return string */ function getLogMessage() { global $wgRequest; + $id = $this->getLogId(); $file = $this->getFile(); $line = $this->getLine(); $message = $this->getMessage(); @@ -163,10 +206,12 @@ class MWException extends Exception { $url = '[no req]'; } - return "$url Exception from line $line of $file: $message"; + return "[$id] $url Exception from line $line of $file: $message"; } - /** Output the exception report using HTML */ + /** + * Output the exception report using HTML. + */ function reportHTML() { global $wgOut; if ( $this->useOutputPage() ) { @@ -182,13 +227,19 @@ class MWException extends Exception { $wgOut->output(); } else { header( "Content-Type: text/html; charset=utf-8" ); + echo "<!doctype html>\n" . + '<html><head>' . + '<title>' . htmlspecialchars( $this->getPageTitle() ) . '' . + "\n"; + $hookResult = $this->runHooks( get_class( $this ) . "Raw" ); if ( $hookResult ) { - die( $hookResult ); + echo $hookResult; + } else { + echo $this->getHTML(); } - echo $this->getHTML(); - die(1); + echo "\n"; } } @@ -197,21 +248,35 @@ class MWException extends Exception { * It will be either HTML or plain text based on isCommandLine(). */ function report() { + global $wgLogExceptionBacktrace; $log = $this->getLogMessage(); if ( $log ) { - wfDebugLog( 'exception', $log ); + if ( $wgLogExceptionBacktrace ) { + wfDebugLog( 'exception', $log . "\n" . $this->getTraceAsString() . "\n" ); + } else { + wfDebugLog( 'exception', $log ); + } } - if ( self::isCommandLine() ) { + 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() ); + } elseif ( self::isCommandLine() ) { MWExceptionHandler::printError( $this->getText() ); } else { + header( "HTTP/1.1 500 MediaWiki exception" ); + header( "Status: 500 MediaWiki exception", true ); + $this->reportHTML(); } } /** - * @static + * Check whether we are in command line mode or not to report the exception + * in the correct format. + * * @return bool */ static function isCommandLine() { @@ -222,6 +287,8 @@ class MWException extends Exception { /** * Exception class which takes an HTML error message, and does not * produce a backtrace. Replacement for OutputPage::fatalError(). + * + * @since 1.7 * @ingroup Exception */ class FatalError extends MWException { @@ -242,14 +309,20 @@ class FatalError extends MWException { } /** - * An error page which can definitely be safely rendered using the OutputPage + * An error page which can definitely be safely rendered using the OutputPage. + * + * @since 1.7 * @ingroup Exception */ class ErrorPageError extends MWException { public $title, $msg, $params; /** - * Note: these arguments are keys into wfMsg(), not text! + * 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() */ function __construct( $title, $msg, $params = null ) { $this->title = $title; @@ -259,14 +332,13 @@ class ErrorPageError extends MWException { if( $msg instanceof Message ){ parent::__construct( $msg ); } else { - parent::__construct( wfMsg( $msg ) ); + parent::__construct( wfMessage( $msg )->text() ); } } function report() { global $wgOut; - $wgOut->showErrorPage( $this->title, $this->msg, $this->params ); $wgOut->output(); } @@ -276,12 +348,14 @@ class ErrorPageError extends MWException { * Show an error page on a badtitle. * Similar to ErrorPage, but emit a 400 HTTP error code to let mobile * browser it is not really a valid content. + * + * @since 1.19 + * @ingroup Exception */ class BadTitleError extends ErrorPageError { - /** - * @param $msg string A message key (default: 'badtitletext') - * @param $params Array parameter to wfMsg() + * @param $msg string|Message A message key (default: 'badtitletext') + * @param $params Array parameter to wfMessage() */ function __construct( $msg = 'badtitletext', $params = null ) { parent::__construct( 'badtitle', $msg, $params ); @@ -305,6 +379,8 @@ class BadTitleError extends ErrorPageError { /** * Show an error when a user tries to do something they do not have the necessary * permissions for. + * + * @since 1.18 * @ingroup Exception */ class PermissionsError extends ErrorPageError { @@ -341,7 +417,9 @@ class PermissionsError extends ErrorPageError { /** * Show an error when the wiki is locked/read-only and the user tries to do - * something that requires write access + * something that requires write access. + * + * @since 1.18 * @ingroup Exception */ class ReadOnlyError extends ErrorPageError { @@ -355,7 +433,9 @@ class ReadOnlyError extends ErrorPageError { } /** - * Show an error when the user hits a rate limit + * Show an error when the user hits a rate limit. + * + * @since 1.18 * @ingroup Exception */ class ThrottledError extends ErrorPageError { @@ -369,12 +449,14 @@ class ThrottledError extends ErrorPageError { public function report(){ global $wgOut; $wgOut->setStatusCode( 503 ); - return parent::report(); + parent::report(); } } /** - * Show an error when the user tries to do something whilst blocked + * Show an error when the user tries to do something whilst blocked. + * + * @since 1.18 * @ingroup Exception */ class UserBlockedError extends ErrorPageError { @@ -391,7 +473,7 @@ class UserBlockedError extends ErrorPageError { $reason = $block->mReason; if( $reason == '' ) { - $reason = wfMsg( 'blockednoreason' ); + $reason = wfMessage( 'blockednoreason' )->text(); } /* $ip returns who *is* being blocked, $intended contains who was meant to be blocked. @@ -415,10 +497,58 @@ 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 + * 'exception-nologin' as a title and 'exception-nologin-text' for the message. + * @see bug 37627 + * @since 1.20 + * + * @par Example: + * @code + * 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. + * + * @par Example: + * @code + * if( $user->isAnon ) { + * throw new UserNotLoggedIn( 'action-require-loggedin' ); + * } + * @endcode + * + * @ingroup Exception + */ +class UserNotLoggedIn extends ErrorPageError { + + /** + * @param $reasonMsg A message key containing the reason for the error. + * Optional, default: 'exception-nologin-text' + * @param $titleMsg A message key to set the page title. + * Optional, default: 'exception-nologin' + * @param $params Parameters to wfMessage(). + * Optiona, default: null + */ + public function __construct( + $reasonMsg = 'exception-nologin-text', + $titleMsg = 'exception-nologin', + $params = null + ) { + parent::__construct( $titleMsg, $reasonMsg, $params ); + } +} + /** * Show an error that looks like an HTTP server error. * Replacement for wfHttpError(). * + * @since 1.19 * @ingroup Exception */ class HttpError extends MWException { @@ -438,7 +568,7 @@ class HttpError extends MWException { $this->content = $content; } - public function reportHTML() { + public function report() { $httpMessage = HttpStatus::getMessage( $this->httpCode ); header( "Status: {$this->httpCode} {$httpMessage}" ); @@ -458,7 +588,7 @@ class HttpError extends MWException { $content = htmlspecialchars( $this->content ); } - print "\n". + print "\n". "$header\n" . "

    $header

    $content

    \n"; } @@ -508,7 +638,7 @@ class MWExceptionHandler { if ( $cmdLine ) { self::printError( $message ); } else { - self::escapeEchoAndDie( $message ); + echo nl2br( htmlspecialchars( $message ) ) . "\n"; } } } else { @@ -522,7 +652,7 @@ class MWExceptionHandler { if ( $cmdLine ) { self::printError( $message ); } else { - self::escapeEchoAndDie( $message ); + echo nl2br( htmlspecialchars( $message ) ) . "\n"; } } } @@ -530,7 +660,8 @@ class MWExceptionHandler { /** * Print a message, if possible to STDERR. * Use this in command line mode only (see isCommandLine) - * @param $message String Failure text + * + * @param $message string 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). @@ -542,16 +673,6 @@ class MWExceptionHandler { } } - /** - * Print a message after escaping it and converting newlines to
    - * Use this for non-command line failures - * @param $message String Failure text - */ - private static function escapeEchoAndDie( $message ) { - echo nl2br( htmlspecialchars( $message ) ) . "\n"; - die(1); - } - /** * Exception handler which simulates the appropriate catch() handling: * diff --git a/includes/Export.php b/includes/Export.php index 7773d03c..f01fb237 100644 --- a/includes/Export.php +++ b/includes/Export.php @@ -49,6 +49,23 @@ class WikiExporter { const TEXT = 0; const STUB = 1; + var $buffer; + + var $text; + + /** + * @var DumpOutput + */ + var $sink; + + /** + * Returns the export schema version. + * @return string + */ + public static function schemaVersion() { + return "0.7"; + } + /** * If using WikiExporter::STREAM to stream a large amount of data, * provide a database connection which is not managed by @@ -103,7 +120,7 @@ class WikiExporter { * the most recent version. */ public function allPages() { - return $this->dumpFrom( '' ); + $this->dumpFrom( '' ); } /** @@ -118,7 +135,7 @@ class WikiExporter { if ( $end ) { $condition .= ' AND page_id < ' . intval( $end ); } - return $this->dumpFrom( $condition ); + $this->dumpFrom( $condition ); } /** @@ -133,27 +150,34 @@ class WikiExporter { if ( $end ) { $condition .= ' AND rev_id < ' . intval( $end ); } - return $this->dumpFrom( $condition ); + $this->dumpFrom( $condition ); } /** * @param $title Title */ public function pageByTitle( $title ) { - return $this->dumpFrom( + $this->dumpFrom( 'page_namespace=' . $title->getNamespace() . ' AND page_title=' . $this->db->addQuotes( $title->getDBkey() ) ); } + /** + * @param $name string + * @throws MWException + */ public function pageByName( $name ) { $title = Title::newFromText( $name ); if ( is_null( $title ) ) { throw new MWException( "Can't export invalid title" ); } else { - return $this->pageByTitle( $title ); + $this->pageByTitle( $title ); } } + /** + * @param $names array + */ public function pagesByName( $names ) { foreach ( $names as $name ) { $this->pageByName( $name ); @@ -161,20 +185,28 @@ class WikiExporter { } public function allLogs() { - return $this->dumpFrom( '' ); + $this->dumpFrom( '' ); } + /** + * @param $start int + * @param $end int + */ public function logsByRange( $start, $end ) { $condition = 'log_id >= ' . intval( $start ); if ( $end ) { $condition .= ' AND log_id < ' . intval( $end ); } - return $this->dumpFrom( $condition ); + $this->dumpFrom( $condition ); } - # Generates the distinct list of authors of an article - # Not called by default (depends on $this->list_authors) - # Can be set by Special:Export when not exporting whole history + /** + * Generates the distinct list of authors of an article + * Not called by default (depends on $this->list_authors) + * Can be set by Special:Export when not exporting whole history + * + * @param $cond + */ protected function do_list_authors( $cond ) { wfProfileIn( __METHOD__ ); $this->author_list = ""; @@ -205,13 +237,15 @@ class WikiExporter { wfProfileOut( __METHOD__ ); } + /** + * @param $cond string + * @throws MWException + * @throws Exception + */ protected function dumpFrom( $cond = '' ) { wfProfileIn( __METHOD__ ); # For logging dumps... if ( $this->history & self::LOGS ) { - if ( $this->buffer == WikiExporter::STREAM ) { - $prev = $this->db->bufferResults( false ); - } $where = array( 'user_id = log_user' ); # Hide private logs $hideLogs = LogEventsList::getExcludeClause( $this->db ); @@ -220,16 +254,49 @@ class WikiExporter { if ( $cond ) $where[] = $cond; # Get logging table name for logging.* clause $logging = $this->db->tableName( 'logging' ); - $result = $this->db->select( array( 'logging', 'user' ), - array( "{$logging}.*", 'user_name' ), // grab the user name - $where, - __METHOD__, - array( 'ORDER BY' => 'log_id', 'USE INDEX' => array( 'logging' => 'PRIMARY' ) ) - ); - $wrapper = $this->db->resultObject( $result ); - $this->outputLogStream( $wrapper ); + if ( $this->buffer == WikiExporter::STREAM ) { - $this->db->bufferResults( $prev ); + $prev = $this->db->bufferResults( false ); + } + $wrapper = null; // Assuring $wrapper is not undefined, if exception occurs early + try { + $result = $this->db->select( array( 'logging', 'user' ), + array( "{$logging}.*", 'user_name' ), // grab the user name + $where, + __METHOD__, + array( 'ORDER BY' => 'log_id', 'USE INDEX' => array( 'logging' => 'PRIMARY' ) ) + ); + $wrapper = $this->db->resultObject( $result ); + $this->outputLogStream( $wrapper ); + if ( $this->buffer == WikiExporter::STREAM ) { + $this->db->bufferResults( $prev ); + } + } catch ( Exception $e ) { + // Throwing the exception does not reliably free the resultset, and + // would also leave the connection in unbuffered mode. + + // Freeing result + try { + if ( $wrapper ) { + $wrapper->free(); + } + } catch ( Exception $e2 ) { + // Already in panic mode -> ignoring $e2 as $e has + // higher priority + } + + // Putting database back in previous buffer mode + try { + if ( $this->buffer == WikiExporter::STREAM ) { + $this->db->bufferResults( $prev ); + } + } catch ( Exception $e2 ) { + // Already in panic mode -> ignoring $e2 as $e has + // higher priority + } + + // Inform caller about problem + throw $e; } # For page dumps... } else { @@ -279,7 +346,7 @@ class WikiExporter { } elseif ( $this->history & WikiExporter::RANGE ) { # Dump of revisions within a specified range $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page' ); - $opts['ORDER BY'] = 'rev_page ASC, rev_id ASC'; + $opts['ORDER BY'] = array( 'rev_page ASC', 'rev_id ASC' ); } else { # Uknown history specification parameter? wfProfileOut( __METHOD__ ); @@ -300,20 +367,46 @@ class WikiExporter { $prev = $this->db->bufferResults( false ); } - wfRunHooks( 'ModifyExportQuery', + $wrapper = null; // Assuring $wrapper is not undefined, if exception occurs early + try { + wfRunHooks( 'ModifyExportQuery', array( $this->db, &$tables, &$cond, &$opts, &$join ) ); - # Do the query! - $result = $this->db->select( $tables, '*', $cond, __METHOD__, $opts, $join ); - $wrapper = $this->db->resultObject( $result ); - # Output dump results - $this->outputPageStream( $wrapper ); - if ( $this->list_authors ) { + # Do the query! + $result = $this->db->select( $tables, '*', $cond, __METHOD__, $opts, $join ); + $wrapper = $this->db->resultObject( $result ); + # Output dump results $this->outputPageStream( $wrapper ); - } - if ( $this->buffer == WikiExporter::STREAM ) { - $this->db->bufferResults( $prev ); + if ( $this->buffer == WikiExporter::STREAM ) { + $this->db->bufferResults( $prev ); + } + } catch ( Exception $e ) { + // Throwing the exception does not reliably free the resultset, and + // would also leave the connection in unbuffered mode. + + // Freeing result + try { + if ( $wrapper ) { + $wrapper->free(); + } + } catch ( Exception $e2 ) { + // Already in panic mode -> ignoring $e2 as $e has + // higher priority + } + + // Putting database back in previous buffer mode + try { + if ( $this->buffer == WikiExporter::STREAM ) { + $this->db->bufferResults( $prev ); + } + } catch ( Exception $e2 ) { + // Already in panic mode -> ignoring $e2 as $e has + // higher priority + } + + // Inform caller about problem + throw $e; } } wfProfileOut( __METHOD__ ); @@ -324,7 +417,7 @@ class WikiExporter { * The result set should be sorted/grouped by page to avoid duplicate * page records in the output. * - * The result set will be freed once complete. Should be safe for + * Should be safe for * streaming (non-buffered) queries, as long as it was made on a * separate database connection not managed by LoadBalancer; some * blob storage types will make queries to pull source data. @@ -363,6 +456,9 @@ class WikiExporter { } } + /** + * @param $resultset array + */ protected function outputLogStream( $resultset ) { foreach ( $resultset as $row ) { $output = $this->writer->writeLogItem( $row ); @@ -377,14 +473,16 @@ class WikiExporter { class XmlDumpWriter { /** * Returns the export schema version. + * @deprecated in 1.20; use WikiExporter::schemaVersion() instead * @return string */ function schemaVersion() { - return "0.6"; + wfDeprecated( __METHOD__, '1.20' ); + return WikiExporter::schemaVersion(); } /** - * Opens the XML output stream's root element. + * Opens the XML output stream's root "" element. * This does not include an xml directive, so is safe to include * as a subelement in a larger XML stream. Namespace and XML Schema * references are included. @@ -395,7 +493,7 @@ class XmlDumpWriter { */ function openStream() { global $wgLanguageCode; - $ver = $this->schemaVersion(); + $ver = WikiExporter::schemaVersion(); return Xml::element( 'mediawiki', array( 'xmlns' => "http://www.mediawiki.org/xml/export-$ver/", 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance", @@ -408,6 +506,9 @@ class XmlDumpWriter { $this->siteInfo(); } + /** + * @return string + */ function siteInfo() { $info = array( $this->sitename(), @@ -420,20 +521,32 @@ class XmlDumpWriter { "\n \n"; } + /** + * @return string + */ function sitename() { global $wgSitename; return Xml::element( 'sitename', array(), $wgSitename ); } + /** + * @return string + */ function generator() { global $wgVersion; return Xml::element( 'generator', array(), "MediaWiki $wgVersion" ); } + /** + * @return string + */ function homelink() { return Xml::element( 'base', array(), Title::newMainPage()->getCanonicalUrl() ); } + /** + * @return string + */ function caseSetting() { global $wgCapitalLinks; // "case-insensitive" option is reserved for future @@ -441,6 +554,9 @@ class XmlDumpWriter { return Xml::element( 'case', array(), $sensitivity ); } + /** + * @return string + */ function namespaces() { global $wgContLang; $spaces = "\n"; @@ -466,7 +582,7 @@ class XmlDumpWriter { } /** - * Opens a section on the output stream, with data + * Opens a "" section on the output stream, with data * from the given database row. * * @param $row object @@ -486,13 +602,7 @@ class XmlDumpWriter { $out .= ' ' . Xml::element( 'redirect', array( 'title' => self::canonicalTitle( $redirect ) ) ) . "\n"; } } - - if ( $row->rev_sha1 ) { - $out .= " " . Xml::element('sha1', null, strval($row->rev_sha1) ) . "\n"; - } else { - $out .= " \n"; - } - + if ( $row->page_restrictions != '' ) { $out .= ' ' . Xml::element( 'restrictions', array(), strval( $row->page_restrictions ) ) . "\n"; @@ -504,16 +614,17 @@ class XmlDumpWriter { } /** - * Closes a section on the output stream. + * Closes a "" section on the output stream. * * @access private + * @return string */ function closePage() { return " \n"; } /** - * Dumps a section on the output stream, with + * Dumps a "" section on the output stream, with * data filled in from the given database row. * * @param $row object @@ -525,6 +636,9 @@ class XmlDumpWriter { $out = " \n"; $out .= " " . Xml::element( 'id', null, strval( $row->rev_id ) ) . "\n"; + if( $row->rev_parent_id ) { + $out .= " " . Xml::element( 'parentid', null, strval( $row->rev_parent_id ) ) . "\n"; + } $out .= $this->writeTimestamp( $row->rev_timestamp ); @@ -540,7 +654,13 @@ class XmlDumpWriter { if ( $row->rev_deleted & Revision::DELETED_COMMENT ) { $out .= " " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n"; } elseif ( $row->rev_comment != '' ) { - $out .= " " . Xml::elementClean( 'comment', null, strval( $row->rev_comment ) ) . "\n"; + $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 = ''; @@ -568,7 +688,7 @@ class XmlDumpWriter { } /** - * Dumps a section on the output stream, with + * Dumps a "" section on the output stream, with * data filled in from the given database row. * * @param $row object @@ -578,64 +698,78 @@ class XmlDumpWriter { function writeLogItem( $row ) { wfProfileIn( __METHOD__ ); - $out = " \n"; - $out .= " " . Xml::element( 'id', null, strval( $row->log_id ) ) . "\n"; + $out = " \n"; + $out .= " " . Xml::element( 'id', null, strval( $row->log_id ) ) . "\n"; - $out .= $this->writeTimestamp( $row->log_timestamp ); + $out .= $this->writeTimestamp( $row->log_timestamp, " " ); if ( $row->log_deleted & LogPage::DELETED_USER ) { - $out .= " " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n"; + $out .= " " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n"; } else { - $out .= $this->writeContributor( $row->log_user, $row->user_name ); + $out .= $this->writeContributor( $row->log_user, $row->user_name, " " ); } if ( $row->log_deleted & LogPage::DELETED_COMMENT ) { - $out .= " " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n"; + $out .= " " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n"; } elseif ( $row->log_comment != '' ) { - $out .= " " . Xml::elementClean( 'comment', null, strval( $row->log_comment ) ) . "\n"; + $out .= " " . Xml::elementClean( 'comment', null, strval( $row->log_comment ) ) . "\n"; } - $out .= " " . Xml::element( 'type', null, strval( $row->log_type ) ) . "\n"; - $out .= " " . Xml::element( 'action', null, strval( $row->log_action ) ) . "\n"; + $out .= " " . Xml::element( 'type', null, strval( $row->log_type ) ) . "\n"; + $out .= " " . Xml::element( 'action', null, strval( $row->log_action ) ) . "\n"; if ( $row->log_deleted & LogPage::DELETED_ACTION ) { - $out .= " " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n"; + $out .= " " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n"; } else { $title = Title::makeTitle( $row->log_namespace, $row->log_title ); - $out .= " " . Xml::elementClean( 'logtitle', null, self::canonicalTitle( $title ) ) . "\n"; - $out .= " " . Xml::elementClean( 'params', + $out .= " " . Xml::elementClean( 'logtitle', null, self::canonicalTitle( $title ) ) . "\n"; + $out .= " " . Xml::elementClean( 'params', array( 'xml:space' => 'preserve' ), strval( $row->log_params ) ) . "\n"; } - $out .= " \n"; + $out .= " \n"; wfProfileOut( __METHOD__ ); return $out; } - function writeTimestamp( $timestamp ) { + /** + * @param $timestamp string + * @param $indent string Default to six spaces + * @return string + */ + function writeTimestamp( $timestamp, $indent = " " ) { $ts = wfTimestamp( TS_ISO_8601, $timestamp ); - return " " . Xml::element( 'timestamp', null, $ts ) . "\n"; + return $indent . Xml::element( 'timestamp', null, $ts ) . "\n"; } - function writeContributor( $id, $text ) { - $out = " \n"; + /** + * @param $id + * @param $text string + * @param $indent string Default to six spaces + * @return string + */ + function writeContributor( $id, $text, $indent = " " ) { + $out = $indent . "\n"; if ( $id || !IP::isValid( $text ) ) { - $out .= " " . Xml::elementClean( 'username', null, strval( $text ) ) . "\n"; - $out .= " " . Xml::element( 'id', null, strval( $id ) ) . "\n"; + $out .= $indent . " " . Xml::elementClean( 'username', null, strval( $text ) ) . "\n"; + $out .= $indent . " " . Xml::element( 'id', null, strval( $id ) ) . "\n"; } else { - $out .= " " . Xml::elementClean( 'ip', null, strval( $text ) ) . "\n"; + $out .= $indent . " " . Xml::elementClean( 'ip', null, strval( $text ) ) . "\n"; } - $out .= " \n"; + $out .= $indent . "\n"; return $out; } /** * Warning! This data is potentially inconsistent. :( + * @param $row + * @param $dumpContents bool + * @return string */ function writeUploads( $row, $dumpContents = false ) { - if ( $row->page_namespace == NS_IMAGE ) { + if ( $row->page_namespace == NS_FILE ) { $img = wfLocalFile( $row->page_title ); if ( $img && $img->exists() ) { $out = ''; @@ -670,10 +804,15 @@ class XmlDumpWriter { } else { $contents = ''; } + if ( $file->isDeleted( File::DELETED_COMMENT ) ) { + $comment = Xml::element( 'comment', array( 'deleted' => 'deleted' ) ); + } else { + $comment = Xml::elementClean( 'comment', null, $file->getDescription() ); + } return " \n" . $this->writeTimestamp( $file->getTimestamp() ) . $this->writeContributor( $file->getUser( 'id' ), $file->getUser( 'text' ) ) . - " " . Xml::elementClean( 'comment', null, $file->getDescription() ) . "\n" . + " " . $comment . "\n" . " " . Xml::element( 'filename', null, $file->getName() ) . "\n" . $archiveName . " " . Xml::element( 'src', null, $file->getCanonicalUrl() ) . "\n" . @@ -688,7 +827,7 @@ class XmlDumpWriter { * Return prefixed text form of title, but using the content language's * canonical namespace. This skips any special-casing such as gendered * user namespaces -- which while useful, are not yet listed in the - * XML data so are unsafe in export. + * XML "" data so are unsafe in export. * * @param Title $title * @return string @@ -716,32 +855,55 @@ class XmlDumpWriter { * @ingroup Dump */ class DumpOutput { + + /** + * @param $string string + */ function writeOpenStream( $string ) { $this->write( $string ); } + /** + * @param $string string + */ function writeCloseStream( $string ) { $this->write( $string ); } + /** + * @param $page + * @param $string string + */ function writeOpenPage( $page, $string ) { $this->write( $string ); } + /** + * @param $string string + */ function writeClosePage( $string ) { $this->write( $string ); } + /** + * @param $rev + * @param $string string + */ function writeRevision( $rev, $string ) { $this->write( $string ); } + /** + * @param $rev + * @param $string string + */ function writeLogItem( $rev, $string ) { $this->write( $string ); } /** * Override to write to a different stream type. + * @param $string string * @return bool */ function write( $string ) { @@ -773,6 +935,7 @@ class DumpOutput { /** * Returns the name of the file or files which are * being written to, if there are any. + * @return null */ function getFilenames() { return NULL; @@ -784,27 +947,56 @@ class DumpOutput { * @ingroup Dump */ class DumpFileOutput extends DumpOutput { - protected $handle, $filename; + protected $handle = false, $filename; + /** + * @param $file + */ function __construct( $file ) { $this->handle = fopen( $file, "wt" ); $this->filename = $file; } + /** + * @param $string string + */ + function writeCloseStream( $string ) { + parent::writeCloseStream( $string ); + if ( $this->handle ) { + fclose( $this->handle ); + $this->handle = false; + } + } + + /** + * @param $string string + */ function write( $string ) { fputs( $this->handle, $string ); } + /** + * @param $newname + */ function closeRenameAndReopen( $newname ) { $this->closeAndRename( $newname, true ); } + /** + * @param $newname + * @throws MWException + */ function renameOrException( $newname ) { if (! rename( $this->filename, $newname ) ) { throw new MWException( __METHOD__ . ": rename of file {$this->filename} to $newname failed\n" ); } } + /** + * @param $newname array + * @return mixed + * @throws MWException + */ function checkRenameArgCount( $newname ) { if ( is_array( $newname ) ) { if ( count( $newname ) > 1 ) { @@ -816,10 +1008,17 @@ class DumpFileOutput extends DumpOutput { return $newname; } + /** + * @param $newname mixed + * @param $open bool + */ function closeAndRename( $newname, $open = false ) { $newname = $this->checkRenameArgCount( $newname ); if ( $newname ) { - fclose( $this->handle ); + if ( $this->handle ) { + fclose( $this->handle ); + $this->handle = false; + } $this->renameOrException( $newname ); if ( $open ) { $this->handle = fopen( $this->filename, "wt" ); @@ -827,6 +1026,9 @@ class DumpFileOutput extends DumpOutput { } } + /** + * @return string|null + */ function getFilenames() { return $this->filename; } @@ -840,7 +1042,12 @@ class DumpFileOutput extends DumpOutput { */ class DumpPipeOutput extends DumpFileOutput { protected $command, $filename; + protected $procOpenResource = false; + /** + * @param $command + * @param $file null + */ function __construct( $command, $file = null ) { if ( !is_null( $file ) ) { $command .= " > " . wfEscapeShellArg( $file ); @@ -851,6 +1058,20 @@ class DumpPipeOutput extends DumpFileOutput { $this->filename = $file; } + /** + * @param $string string + */ + function writeCloseStream( $string ) { + parent::writeCloseStream( $string ); + if ( $this->procOpenResource ) { + proc_close( $this->procOpenResource ); + $this->procOpenResource = false; + } + } + + /** + * @param $command + */ function startCommand( $command ) { $spec = array( 0 => array( "pipe", "r" ), @@ -860,15 +1081,28 @@ class DumpPipeOutput extends DumpFileOutput { $this->handle = $pipes[0]; } + /** + * @param mixed $newname + */ function closeRenameAndReopen( $newname ) { $this->closeAndRename( $newname, true ); } + /** + * @param $newname mixed + * @param $open bool + */ function closeAndRename( $newname, $open = false ) { $newname = $this->checkRenameArgCount( $newname ); if ( $newname ) { - fclose( $this->handle ); - proc_close( $this->procOpenResource ); + if ( $this->handle ) { + fclose( $this->handle ); + $this->handle = false; + } + if ( $this->procOpenResource ) { + proc_close( $this->procOpenResource ); + $this->procOpenResource = false; + } $this->renameOrException( $newname ); if ( $open ) { $command = $this->command; @@ -885,6 +1119,10 @@ class DumpPipeOutput extends DumpFileOutput { * @ingroup Dump */ class DumpGZipOutput extends DumpPipeOutput { + + /** + * @param $file string + */ function __construct( $file ) { parent::__construct( "gzip", $file ); } @@ -895,6 +1133,10 @@ class DumpGZipOutput extends DumpPipeOutput { * @ingroup Dump */ class DumpBZip2Output extends DumpPipeOutput { + + /** + * @param $file string + */ function __construct( $file ) { parent::__construct( "bzip2", $file ); } @@ -905,12 +1147,20 @@ class DumpBZip2Output extends DumpPipeOutput { * @ingroup Dump */ class Dump7ZipOutput extends DumpPipeOutput { + + /** + * @param $file string + */ function __construct( $file ) { $command = $this->setup7zCommand( $file ); parent::__construct( $command ); $this->filename = $file; } + /** + * @param $file string + * @return string + */ function setup7zCommand( $file ) { $command = "7za a -bd -si " . wfEscapeShellArg( $file ); // Suppress annoying useless crap from p7zip @@ -919,6 +1169,10 @@ class Dump7ZipOutput extends DumpPipeOutput { return( $command ); } + /** + * @param $newname string + * @param $open bool + */ function closeAndRename( $newname, $open = false ) { $newname = $this->checkRenameArgCount( $newname ); if ( $newname ) { @@ -933,8 +1187,6 @@ class Dump7ZipOutput extends DumpPipeOutput { } } - - /** * Dump output filter class. * This just does output filtering and streaming; XML formatting is done @@ -942,18 +1194,44 @@ class Dump7ZipOutput extends DumpPipeOutput { * @ingroup Dump */ class DumpFilter { + + /** + * @var DumpOutput + * FIXME will need to be made protected whenever legacy code + * is updated. + */ + public $sink; + + /** + * @var bool + */ + protected $sendingThisPage; + + /** + * @param $sink DumpOutput + */ function __construct( &$sink ) { $this->sink =& $sink; } + /** + * @param $string string + */ function writeOpenStream( $string ) { $this->sink->writeOpenStream( $string ); } + /** + * @param $string string + */ function writeCloseStream( $string ) { $this->sink->writeCloseStream( $string ); } + /** + * @param $page + * @param $string string + */ function writeOpenPage( $page, $string ) { $this->sendingThisPage = $this->pass( $page, $string ); if ( $this->sendingThisPage ) { @@ -961,6 +1239,9 @@ class DumpFilter { } } + /** + * @param $string string + */ function writeClosePage( $string ) { if ( $this->sendingThisPage ) { $this->sink->writeClosePage( $string ); @@ -968,30 +1249,49 @@ class DumpFilter { } } + /** + * @param $rev + * @param $string string + */ function writeRevision( $rev, $string ) { if ( $this->sendingThisPage ) { $this->sink->writeRevision( $rev, $string ); } } + /** + * @param $rev + * @param $string string + */ function writeLogItem( $rev, $string ) { $this->sink->writeRevision( $rev, $string ); } + /** + * @param $newname string + */ function closeRenameAndReopen( $newname ) { $this->sink->closeRenameAndReopen( $newname ); } + /** + * @param $newname string + * @param $open bool + */ function closeAndRename( $newname, $open = false ) { $this->sink->closeAndRename( $newname, $open ); } + /** + * @return array + */ function getFilenames() { return $this->sink->getFilenames(); } /** * Override for page-based filter types. + * @param $page * @return bool */ function pass( $page ) { @@ -1004,6 +1304,11 @@ class DumpFilter { * @ingroup Dump */ class DumpNotalkFilter extends DumpFilter { + + /** + * @param $page + * @return bool + */ function pass( $page ) { return !MWNamespace::isTalk( $page->page_namespace ); } @@ -1017,6 +1322,10 @@ class DumpNamespaceFilter extends DumpFilter { var $invert = false; var $namespaces = array(); + /** + * @param $sink DumpOutput + * @param $param + */ function __construct( &$sink, $param ) { parent::__construct( $sink ); @@ -1059,6 +1368,10 @@ class DumpNamespaceFilter extends DumpFilter { } } + /** + * @param $page + * @return bool + */ function pass( $page ) { $match = isset( $this->namespaces[$page->page_namespace] ); return $this->invert xor $match; @@ -1073,11 +1386,18 @@ class DumpNamespaceFilter extends DumpFilter { class DumpLatestFilter extends DumpFilter { var $page, $pageString, $rev, $revString; + /** + * @param $page + * @param $string string + */ function writeOpenPage( $page, $string ) { $this->page = $page; $this->pageString = $string; } + /** + * @param $string string + */ function writeClosePage( $string ) { if ( $this->rev ) { $this->sink->writeOpenPage( $this->page, $this->pageString ); @@ -1090,6 +1410,10 @@ class DumpLatestFilter extends DumpFilter { $this->pageString = null; } + /** + * @param $rev + * @param $string string + */ function writeRevision( $rev, $string ) { if ( $rev->rev_id == $this->page->page_latest ) { $this->rev = $rev; @@ -1103,51 +1427,82 @@ class DumpLatestFilter extends DumpFilter { * @ingroup Dump */ class DumpMultiWriter { + + /** + * @param $sinks + */ function __construct( $sinks ) { $this->sinks = $sinks; $this->count = count( $sinks ); } + /** + * @param $string string + */ function writeOpenStream( $string ) { for ( $i = 0; $i < $this->count; $i++ ) { $this->sinks[$i]->writeOpenStream( $string ); } } + /** + * @param $string string + */ function writeCloseStream( $string ) { for ( $i = 0; $i < $this->count; $i++ ) { $this->sinks[$i]->writeCloseStream( $string ); } } + /** + * @param $page + * @param $string string + */ function writeOpenPage( $page, $string ) { for ( $i = 0; $i < $this->count; $i++ ) { $this->sinks[$i]->writeOpenPage( $page, $string ); } } + /** + * @param $string + */ function writeClosePage( $string ) { for ( $i = 0; $i < $this->count; $i++ ) { $this->sinks[$i]->writeClosePage( $string ); } } + /** + * @param $rev + * @param $string + */ function writeRevision( $rev, $string ) { for ( $i = 0; $i < $this->count; $i++ ) { $this->sinks[$i]->writeRevision( $rev, $string ); } } + /** + * @param $newnames + */ function closeRenameAndReopen( $newnames ) { $this->closeAndRename( $newnames, true ); } + /** + * @param $newnames array + * @param bool $open + */ function closeAndRename( $newnames, $open = false ) { for ( $i = 0; $i < $this->count; $i++ ) { $this->sinks[$i]->closeAndRename( $newnames[$i], $open ); } } + /** + * @return array + */ function getFilenames() { $filenames = array(); for ( $i = 0; $i < $this->count; $i++ ) { @@ -1158,6 +1513,10 @@ class DumpMultiWriter { } +/** + * @param $string string + * @return string + */ function xmlsafe( $string ) { wfProfileIn( __FUNCTION__ ); diff --git a/includes/ExternalEdit.php b/includes/ExternalEdit.php index b8704758..34683253 100644 --- a/includes/ExternalEdit.php +++ b/includes/ExternalEdit.php @@ -80,10 +80,16 @@ class ExternalEdit extends ContextSource { } elseif ( $this->getRequest()->getVal( 'mode' ) == 'file' ) { $type = "Edit file"; $image = wfLocalFile( $this->getTitle() ); - $urls = array( 'File' => array( - 'Extension' => $image->getExtension(), - 'URL' => $image->getCanonicalURL() - ) ); + if ( $image ) { + $urls = array( + 'File' => array( + 'Extension' => $image->getExtension(), + 'URL' => $image->getCanonicalURL() + ) + ); + } else{ + $urls = array(); + } } else { $type = "Edit text"; # *.wiki file extension is used by some editors for syntax diff --git a/includes/ExternalStore.php b/includes/ExternalStore.php index 3bee6ed8..61d4ef7c 100644 --- a/includes/ExternalStore.php +++ b/includes/ExternalStore.php @@ -1,4 +1,25 @@ getFlag( DBO_TRX ) ) { - $dbw->commit(); + $dbw->commit( __METHOD__ ); } return "DB://$cluster/$id"; } diff --git a/includes/ExternalStoreHttp.php b/includes/ExternalStoreHttp.php index 092ff7d8..311e32b3 100644 --- a/includes/ExternalStoreHttp.php +++ b/includes/ExternalStoreHttp.php @@ -1,4 +1,24 @@ error(); } function isCssSubpage() { $this->error(); } function isJsSubpage() { $this->error(); } - function userCanEditCssJsSubpage() { $this->error(); } function userCanEditCssSubpage() { $this->error(); } function userCanEditJsSubpage() { $this->error(); } function isCascadeProtected() { $this->error(); } @@ -88,8 +107,6 @@ class FakeTitle extends Title { function moveNoAuth( &$nt ) { $this->error(); } function isValidMoveOperation( &$nt, $auth = true, $reason = '' ) { $this->error(); } function moveTo( &$nt, $auth = true, $reason = '', $createRedirect = true ) { $this->error(); } - function moveOverExistingRedirect( &$nt, $reason = '', $createRedirect = true ) { $this->error(); } - function moveToNewTitle( &$nt, $reason = '', $createRedirect = true ) { $this->error(); } function moveSubpages( $nt, $auth = true, $reason = '', $createRedirect = true ) { $this->error(); } function isSingleRevRedirect() { $this->error(); } function isValidMoveTarget( $nt ) { $this->error(); } diff --git a/includes/Fallback.php b/includes/Fallback.php index b517cd16..4b138c11 100644 --- a/includes/Fallback.php +++ b/includes/Fallback.php @@ -1,6 +1,7 @@ "; + * @endcode + * @param $item */ - function outHeader() { - # print ""; - } + abstract public function outHeader(); /** * Generate an item + * @par Example: + * @code + * print "..."; + * @endcode * @param $item */ - function outItem( $item ) { - # print "..."; - } + abstract public function outItem( $item ); /** * Generate Footer of the feed + * @par Example: + * @code + * print ""; + * @endcode */ - function outFooter() { - # print ""; - } - /**#@-*/ + abstract public function outFooter(); /** * Setup and send HTTP headers. Don't send any content; @@ -334,6 +335,7 @@ class RSSFeed extends ChannelFeed { class AtomFeed extends ChannelFeed { /** * @todo document + * @return string */ function formatTime( $ts ) { // need to use RFC 822 time format at least for rss2.0 diff --git a/includes/FeedUtils.php b/includes/FeedUtils.php index cf42329b..11b2675d 100644 --- a/includes/FeedUtils.php +++ b/includes/FeedUtils.php @@ -1,4 +1,25 @@ rc_last_oldid, $row->rc_this_oldid, $timestamp, ($row->rc_deleted & Revision::DELETED_COMMENT) - ? wfMsgHtml('rev-deleted-comment') + ? wfMessage('rev-deleted-comment')->escaped() : $row->rc_comment, $actiontext ); @@ -108,21 +129,22 @@ class FeedUtils { if( $oldid ) { wfProfileIn( __METHOD__."-dodiff" ); - #$diffText = $de->getDiff( wfMsg( 'revisionasof', + #$diffText = $de->getDiff( wfMessage( 'revisionasof', # $wgLang->timeanddate( $timestamp ), # $wgLang->date( $timestamp ), - # $wgLang->time( $timestamp ) ), - # wfMsg( 'currentrev' ) ); + # $wgLang->time( $timestamp ) )->text(), + # wfMessage( 'currentrev' )->text() ); + $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( - wfMsg( 'previousrevision' ), // hack - wfMsg( 'revisionasof', + wfMessage( 'previousrevision' )->text(), // hack + wfMessage( 'revisionasof', $wgLang->timeanddate( $timestamp ), $wgLang->date( $timestamp ), - $wgLang->time( $timestamp ) ) ); + $wgLang->time( $timestamp ) )->text() ); } if ( $wgFeedDiffCutoff <= 0 || ( strlen( $diffText ) > $wgFeedDiffCutoff ) ) { @@ -148,7 +170,7 @@ class FeedUtils { // Omit large new page diffs, bug 29110 $diffText = self::getDiffLink( $title, $newid ); } else { - $diffText = '

    ' . wfMsg( 'newpage' ) . '

    ' . + $diffText = '

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

    ' . '
    ' . nl2br( htmlspecialchars( $newtext ) ) . '
    '; } } @@ -165,6 +187,7 @@ class FeedUtils { * @param $title Title object: used to generate the diff URL * @param $newid Integer newid for this diff * @param $oldid Integer|null oldid for the diff. Null means it is a new article + * @return string */ protected static function getDiffLink( Title $title, $newid, $oldid = null ) { $queryParameters = ($oldid == null) @@ -173,7 +196,7 @@ class FeedUtils { $diffUrl = $title->getFullUrl( $queryParameters ); $diffLink = Html::element( 'a', array( 'href' => $diffUrl ), - wfMsgForContent( 'showdiff' ) ); + wfMessage( 'showdiff' )->inContentLanguage()->text() ); return $diffLink; } diff --git a/includes/FileDeleteForm.php b/includes/FileDeleteForm.php index 11f9aea5..e75ad729 100644 --- a/includes/FileDeleteForm.php +++ b/includes/FileDeleteForm.php @@ -1,10 +1,31 @@ + * @ingroup Media + */ /** * File deletion user interface * * @ingroup Media - * @author Rob Church */ class FileDeleteForm { @@ -80,7 +101,8 @@ class FileDeleteForm { $reason = $deleteReason; } elseif ( $deleteReason != '' ) { // Entry from drop down menu + additional comment - $reason = $deleteReasonList . wfMsgForContent( 'colon-separator' ) . $deleteReason; + $reason = $deleteReasonList . wfMessage( 'colon-separator' ) + ->inContentLanguage()->text() . $deleteReason; } else { $reason = $deleteReasonList; } @@ -89,9 +111,7 @@ class FileDeleteForm { if( !$status->isGood() ) { $wgOut->addHTML( '

    ' . $this->prepareMessage( 'filedeleteerror-short' ) . "

    \n" ); - $wgOut->addHTML( '' ); - $wgOut->addWikiText( $status->getWikiText( 'filedeleteerror-short', 'filedeleteerror-long' ) ); - $wgOut->addHTML( '' ); + $wgOut->addWikiText( '
    ' . $status->getWikiText( 'filedeleteerror-short', 'filedeleteerror-long' ) . '
    ' ); } if( $status->ok ) { $wgOut->setPageTitle( wfMessage( 'actioncomplete' ) ); @@ -100,10 +120,12 @@ class FileDeleteForm { // file, otherwise go back to the description page $wgOut->addReturnTo( $this->oldimage ? $this->title : Title::newMainPage() ); - if ( $wgRequest->getCheck( 'wpWatch' ) && $wgUser->isLoggedIn() ) { - WatchAction::doWatch( $this->title, $wgUser ); - } elseif ( $this->title->userIsWatching() ) { - WatchAction::doUnwatch( $this->title, $wgUser ); + if ( $wgUser->isLoggedIn() && $wgRequest->getCheck( 'wpWatch' ) != $wgUser->isWatched( $this->title ) ) { + if ( $wgRequest->getCheck( 'wpWatch' ) ) { + WatchAction::doWatch( $this->title, $wgUser ); + } else { + WatchAction::doUnwatch( $this->title, $wgUser ); + } } } return; @@ -122,6 +144,7 @@ class FileDeleteForm { * @param $reason String: reason of the deletion * @param $suppress Boolean: whether to mark all deleted versions as restricted * @param $user User object performing the request + * @return bool|Status */ public static function doDelete( &$title, &$file, &$oldimage, $reason, $suppress, User $user = null ) { if ( $user === null ) { @@ -134,12 +157,20 @@ class FileDeleteForm { $status = $file->deleteOld( $oldimage, $reason, $suppress ); if( $status->ok ) { // Need to do a log item - $log = new LogPage( 'delete' ); - $logComment = wfMsgForContent( 'deletedrevision', $oldimage ); + $logComment = wfMessage( 'deletedrevision', $oldimage )->inContentLanguage()->text(); if( trim( $reason ) != '' ) { - $logComment .= wfMsgForContent( 'colon-separator' ) . $reason; + $logComment .= wfMessage( 'colon-separator' ) + ->inContentLanguage()->text() . $reason; } - $log->addEntry( 'delete', $title, $logComment ); + + $logtype = $suppress ? 'suppress' : 'delete'; + + $logEntry = new ManualLogEntry( $logtype, 'delete' ); + $logEntry->setPerformer( $user ); + $logEntry->setTarget( $title ); + $logEntry->setComment( $logComment ); + $logid = $logEntry->insert(); + $logEntry->publish( $logid ); } } else { $status = Status::newFatal( 'cannotdelete', @@ -150,17 +181,20 @@ class FileDeleteForm { try { // delete the associated article first $error = ''; - if ( $page->doDeleteArticleReal( $reason, $suppress, 0, false, $error, $user ) >= WikiPage::DELETE_SUCCESS ) { + $deleteStatus = $page->doDeleteArticleReal( $reason, $suppress, 0, false, $error, $user ); + // doDeleteArticleReal() returns a non-fatal error status if the page + // or revision is missing, so check for isOK() rather than isGood() + if ( $deleteStatus->isOK() ) { $status = $file->delete( $reason, $suppress ); if( $status->isOK() ) { - $dbw->commit(); + $dbw->commit( __METHOD__ ); } else { - $dbw->rollback(); + $dbw->rollback( __METHOD__ ); } } } catch ( MWException $e ) { // rollback before returning to prevent UI from displaying incorrect "View or restore N deleted edits?" - $dbw->rollback(); + $dbw->rollback( __METHOD__ ); throw $e; } } @@ -182,7 +216,7 @@ class FileDeleteForm { $suppress = "
    " . - Xml::checkLabel( wfMsg( 'revdelete-suppress' ), + Xml::checkLabel( wfMessage( 'revdelete-suppress' )->text(), 'wpSuppress', 'wpSuppress', false, array( 'tabindex' => '3' ) ) . "
    " . - Xml::label( wfMsg( 'filedelete-comment' ), 'wpDeleteReasonList' ) . + Xml::label( wfMessage( 'filedelete-comment' )->text(), 'wpDeleteReasonList' ) . " " . - Xml::listDropDown( 'wpDeleteReasonList', - wfMsgForContent( 'filedelete-reason-dropdown' ), - wfMsgForContent( 'filedelete-reason-otherlist' ), '', 'wpReasonDropDown', 1 ) . + Xml::listDropDown( + 'wpDeleteReasonList', + wfMessage( 'filedelete-reason-dropdown' )->inContentLanguage()->text(), + wfMessage( 'filedelete-reason-otherlist' )->inContentLanguage()->text(), + '', + 'wpReasonDropDown', + 1 + ) . "
    " . - Xml::label( wfMsg( 'filedelete-otherreason' ), 'wpReason' ) . + Xml::label( wfMessage( 'filedelete-otherreason' )->text(), 'wpReason' ) . " " . Xml::input( 'wpReason', 60, $wgRequest->getText( 'wpReason' ), @@ -223,7 +262,7 @@ class FileDeleteForm {
    " . - Xml::checkLabel( wfMsg( 'watchthis' ), + Xml::checkLabel( wfMessage( 'watchthis' )->text(), 'wpWatch', 'wpWatch', $checkWatch, array( 'tabindex' => '3' ) ) . "
    " . - Xml::submitButton( wfMsg( 'filedelete-submit' ), + Xml::submitButton( wfMessage( 'filedelete-submit' )->text(), array( 'name' => 'mw-filedelete-submit', 'id' => 'mw-filedelete-submit', 'tabindex' => '4' ) ) . "
    tag for this section, ignored if empty - * @param $fieldsetIDPrefix string ID prefix for the
    tag of each subsection, ignored if empty + * @param $sectionName string ID attribute of the "
    " tag for this section, ignored if empty + * @param $fieldsetIDPrefix string ID prefix for the "
    " tag of each subsection, ignored if empty * @return String */ - function displaySection( $fields, $sectionName = '', $fieldsetIDPrefix = '' ) { - $tableHtml = ''; + public function displaySection( $fields, $sectionName = '', $fieldsetIDPrefix = '' ) { + $displayFormat = $this->getDisplayFormat(); + + $html = ''; $subsectionHtml = ''; - $hasLeftColumn = false; + $hasLabel = false; + + $getFieldHtmlMethod = ( $displayFormat == 'table' ) ? 'getTableRow' : 'get' . ucfirst( $displayFormat ); foreach ( $fields as $key => $value ) { - if ( is_object( $value ) ) { + if ( $value instanceof HTMLFormField ) { $v = empty( $value->mParams['nodata'] ) ? $this->mFieldData[$key] : $value->getDefault(); - $tableHtml .= $value->getTableRow( $v ); + $html .= $value->$getFieldHtmlMethod( $v ); - if ( $value->getLabel() != ' ' ) { - $hasLeftColumn = true; + $labelValue = trim( $value->getLabel() ); + if ( $labelValue != ' ' && $labelValue !== '' ) { + $hasLabel = true; } } elseif ( is_array( $value ) ) { $section = $this->displaySection( $value, $key ); @@ -789,27 +967,33 @@ class HTMLForm extends ContextSource { } } - $classes = array(); + if ( $displayFormat !== 'raw' ) { + $classes = array(); - if ( !$hasLeftColumn ) { // Avoid strange spacing when no labels exist - $classes[] = 'mw-htmlform-nolabel'; - } + if ( !$hasLabel ) { // Avoid strange spacing when no labels exist + $classes[] = 'mw-htmlform-nolabel'; + } - $attribs = array( - 'class' => implode( ' ', $classes ), - ); + $attribs = array( + 'class' => implode( ' ', $classes ), + ); - if ( $sectionName ) { - $attribs['id'] = Sanitizer::escapeId( "mw-htmlform-$sectionName" ); - } + if ( $sectionName ) { + $attribs['id'] = Sanitizer::escapeId( "mw-htmlform-$sectionName" ); + } - $tableHtml = Html::rawElement( 'table', $attribs, - Html::rawElement( 'tbody', array(), "\n$tableHtml\n" ) ) . "\n"; + if ( $displayFormat === 'table' ) { + $html = Html::rawElement( 'table', $attribs, + Html::rawElement( 'tbody', array(), "\n$html\n" ) ) . "\n"; + } elseif ( $displayFormat === 'div' ) { + $html = Html::rawElement( 'div', $attribs, "\n$html\n" ); + } + } if ( $this->mSubSectionBeforeFields ) { - return $subsectionHtml . "\n" . $tableHtml; + return $subsectionHtml . "\n" . $html; } else { - return $tableHtml . "\n" . $subsectionHtml; + return $html . "\n" . $subsectionHtml; } } @@ -842,9 +1026,11 @@ class HTMLForm extends ContextSource { * Stop a reset button being shown for this form * @param $suppressReset Bool set to false to re-enable the * button again + * @return HTMLForm $this for chaining calls (since 1.20) */ function suppressReset( $suppressReset = true ) { $this->mShowReset = !$suppressReset; + return $this; } /** @@ -852,20 +1038,20 @@ class HTMLForm extends ContextSource { * to the form as a whole, after it's submitted but before it's * processed. * @param $data - * @return unknown_type + * @return */ function filterDataForSubmit( $data ) { return $data; } /** - * Get a string to go in the of a section fieldset. Override this if you - * want something more complicated + * Get a string to go in the "" of a section fieldset. + * Override this if you want something more complicated. * @param $key String * @return String */ public function getLegend( $key ) { - return wfMsg( "{$this->mMessagePrefix}-$key" ); + return $this->msg( "{$this->mMessagePrefix}-$key" )->text(); } /** @@ -874,10 +1060,12 @@ class HTMLForm extends ContextSource { * * @since 1.19 * - * @param string|false $action + * @param string|bool $action + * @return HTMLForm $this for chaining calls (since 1.20) */ public function setAction( $action ) { $this->mAction = $action; + return $this; } } @@ -912,6 +1100,28 @@ abstract class HTMLFormField { */ abstract function getInputHTML( $value ); + /** + * Get a translated interface message + * + * This is a wrapper arround $this->mParent->msg() if $this->mParent is set + * and wfMessage() otherwise. + * + * Parameters are the same as wfMessage(). + * + * @return Message object + */ + function msg() { + $args = func_get_args(); + + if ( $this->mParent ) { + $callback = array( $this->mParent, 'msg' ); + } else { + $callback = 'wfMessage'; + } + + return call_user_func_array( $callback, $args ); + } + /** * Override this function to add specific validation checks on the * field input. Don't forget to call parent::validate() to ensure @@ -921,8 +1131,8 @@ abstract class HTMLFormField { * @return Mixed Bool true on success, or String error to display. */ function validate( $value, $alldata ) { - if ( isset( $this->mParams['required'] ) && $value === '' ) { - return wfMsgExt( 'htmlform-required', 'parseinline' ); + if ( isset( $this->mParams['required'] ) && $this->mParams['required'] !== false && $value === '' ) { + return $this->msg( 'htmlform-required' )->parse(); } if ( isset( $this->mValidationCallback ) ) { @@ -982,7 +1192,7 @@ abstract class HTMLFormField { $msgInfo = array(); } - $this->mLabel = wfMsgExt( $msg, 'parseinline', $msgInfo ); + $this->mLabel = wfMessage( $msg, $msgInfo )->parse(); } elseif ( isset( $params['label'] ) ) { $this->mLabel = $params['label']; } @@ -1026,7 +1236,7 @@ abstract class HTMLFormField { $this->mFilterCallback = $params['filter-callback']; } - if ( isset( $params['flatlist'] ) ){ + if ( isset( $params['flatlist'] ) ) { $this->mClass .= ' mw-htmlform-flatlist'; } } @@ -1038,35 +1248,27 @@ abstract class HTMLFormField { * @return String complete HTML table row. */ function getTableRow( $value ) { - # Check for invalid data. - - $errors = $this->validate( $value, $this->mParent->mFieldData ); - + list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value ); + $inputHtml = $this->getInputHTML( $value ); + $fieldType = get_class( $this ); + $helptext = $this->getHelpTextHtmlTable( $this->getHelpText() ); $cellAttributes = array(); - $verticalLabel = false; - if ( !empty($this->mParams['vertical-label']) ) { + if ( !empty( $this->mParams['vertical-label'] ) ) { $cellAttributes['colspan'] = 2; $verticalLabel = true; - } - - if ( $errors === true || ( !$this->mParent->getRequest()->wasPosted() && ( $this->mParent->getMethod() == 'post' ) ) ) { - $errors = ''; - $errorClass = ''; } else { - $errors = self::formatErrors( $errors ); - $errorClass = 'mw-htmlform-invalid-input'; + $verticalLabel = false; } $label = $this->getLabelHtml( $cellAttributes ); + $field = Html::rawElement( 'td', array( 'class' => 'mw-input' ) + $cellAttributes, - $this->getInputHTML( $value ) . "\n$errors" + $inputHtml . "\n$errors" ); - $fieldType = get_class( $this ); - if ( $verticalLabel ) { $html = Html::rawElement( 'tr', array( 'class' => 'mw-htmlform-vertical-label' ), $label ); @@ -1079,40 +1281,159 @@ abstract class HTMLFormField { $label . $field ); } + return $html . $helptext; + } + + /** + * Get the complete div for the input, including help text, + * labels, and whatever. + * @since 1.20 + * @param $value String the value to set the input to. + * @return String complete HTML table row. + */ + public function getDiv( $value ) { + list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value ); + $inputHtml = $this->getInputHTML( $value ); + $fieldType = get_class( $this ); + $helptext = $this->getHelpTextHtmlDiv( $this->getHelpText() ); + $cellAttributes = array(); + $label = $this->getLabelHtml( $cellAttributes ); + + $field = Html::rawElement( + 'div', + array( 'class' => 'mw-input' ) + $cellAttributes, + $inputHtml . "\n$errors" + ); + $html = Html::rawElement( 'div', + array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass" ), + $label . $field ); + $html .= $helptext; + return $html; + } + + /** + * Get the complete raw fields for the input, including help text, + * labels, and whatever. + * @since 1.20 + * @param $value String the value to set the input to. + * @return String complete HTML table row. + */ + public function getRaw( $value ) { + list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value ); + $inputHtml = $this->getInputHTML( $value ); + $fieldType = get_class( $this ); + $helptext = $this->getHelpTextHtmlRaw( $this->getHelpText() ); + $cellAttributes = array(); + $label = $this->getLabelHtml( $cellAttributes ); + + $html = "\n$errors"; + $html .= $label; + $html .= $inputHtml; + $html .= $helptext; + return $html; + } + + /** + * Generate help text HTML in table format + * @since 1.20 + * @param $helptext String|null + * @return String + */ + public function getHelpTextHtmlTable( $helptext ) { + if ( is_null( $helptext ) ) { + return ''; + } + + $row = Html::rawElement( + 'td', + array( 'colspan' => 2, 'class' => 'htmlform-tip' ), + $helptext + ); + $row = Html::rawElement( 'tr', array(), $row ); + return $row; + } + + /** + * Generate help text HTML in div format + * @since 1.20 + * @param $helptext String|null + * @return String + */ + public function getHelpTextHtmlDiv( $helptext ) { + if ( is_null( $helptext ) ) { + return ''; + } + + $div = Html::rawElement( 'div', array( 'class' => 'htmlform-tip' ), $helptext ); + return $div; + } + + /** + * Generate help text HTML formatted for raw output + * @since 1.20 + * @param $helptext String|null + * @return String + */ + public function getHelpTextHtmlRaw( $helptext ) { + return $this->getHelpTextHtmlDiv( $helptext ); + } + + /** + * Determine the help text to display + * @since 1.20 + * @return String + */ + public function getHelpText() { $helptext = null; if ( isset( $this->mParams['help-message'] ) ) { - $msg = wfMessage( $this->mParams['help-message'] ); - if ( $msg->exists() ) { - $helptext = $msg->parse(); - } - } elseif ( isset( $this->mParams['help-messages'] ) ) { - # help-message can be passed a message key (string) or an array containing - # a message key and additional parameters. This makes it impossible to pass - # an array of message key - foreach( $this->mParams['help-messages'] as $name ) { - $msg = wfMessage( $name ); - if( $msg->exists() ) { - $helptext .= $msg->parse(); // append message + $this->mParams['help-messages'] = array( $this->mParams['help-message'] ); + } + + if ( isset( $this->mParams['help-messages'] ) ) { + foreach ( $this->mParams['help-messages'] as $name ) { + $helpMessage = (array)$name; + $msg = $this->msg( array_shift( $helpMessage ), $helpMessage ); + + if ( $msg->exists() ) { + if ( is_null( $helptext ) ) { + $helptext = ''; + } else { + $helptext .= $this->msg( 'word-separator' )->escaped(); // some space + } + $helptext .= $msg->parse(); // Append message } } - } elseif ( isset( $this->mParams['help'] ) ) { + } + elseif ( isset( $this->mParams['help'] ) ) { $helptext = $this->mParams['help']; } + return $helptext; + } - if ( !is_null( $helptext ) ) { - $row = Html::rawElement( 'td', array( 'colspan' => 2, 'class' => 'htmlform-tip' ), - $helptext ); - $row = Html::rawElement( 'tr', array(), $row ); - $html .= "$row\n"; - } + /** + * Determine form errors to display and their classes + * @since 1.20 + * @param $value String the value of the input + * @return Array + */ + public function getErrorsAndErrorClass( $value ) { + $errors = $this->validate( $value, $this->mParent->mFieldData ); - return $html; + if ( $errors === true || ( !$this->mParent->getRequest()->wasPosted() && ( $this->mParent->getMethod() == 'post' ) ) ) { + $errors = ''; + $errorClass = ''; + } else { + $errors = self::formatErrors( $errors ); + $errorClass = 'mw-htmlform-invalid-input'; + } + return array( $errors, $errorClass ); } function getLabel() { return $this->mLabel; } + function getLabelHtml( $cellAttributes = array() ) { # Don't output a for= attribute for labels with no associated input. # Kind of hacky here, possibly we don't want these to be
    \n"; + } + + /** + * @since 1.20 + */ + public function getDiv( $value ) { + $msg = $this->formatMsg(); + return '
    ' . $msg->parseAsBlock() . '
    '; + } + + /** + * @since 1.20 + */ + public function getRaw( $value ) { + return $this->getDiv( $value ); + } + + protected function formatMsg() { if ( empty( $this->mParams['message'] ) ) { - $msg = wfMessage( 'edittools' ); + $msg = $this->msg( 'edittools' ); } else { - $msg = wfMessage( $this->mParams['message'] ); + $msg = $this->msg( $this->mParams['message'] ); if ( $msg->isDisabled() ) { - $msg = wfMessage( 'edittools' ); + $msg = $this->msg( 'edittools' ); } } $msg->inContentLanguage(); - - - return '\n"; + return $msg; } } diff --git a/includes/HistoryBlob.php b/includes/HistoryBlob.php index f707b3f6..bb8ec5e3 100644 --- a/includes/HistoryBlob.php +++ b/includes/HistoryBlob.php @@ -1,5 +1,25 @@ mHash = $hash; @@ -298,7 +318,7 @@ class HistoryBlobCurStub { } /** - * @return string|false + * @return string|bool */ function getText() { $dbr = wfGetDB( DB_SLAVE ); @@ -515,13 +535,11 @@ class DiffHistoryBlob implements HistoryBlob { $header = unpack( 'Vofp/Vcsize', substr( $diff, 0, 8 ) ); - # Check the checksum if mhash is available - if ( extension_loaded( 'mhash' ) ) { - $ofp = mhash( MHASH_ADLER32, $base ); - if ( $ofp !== substr( $diff, 0, 4 ) ) { - wfDebug( __METHOD__. ": incorrect base checksum\n" ); - return false; - } + # Check the checksum if hash/mhash is available + $ofp = $this->xdiffAdler32( $base ); + if ( $ofp !== false && $ofp !== substr( $diff, 0, 4 ) ) { + wfDebug( __METHOD__. ": incorrect base checksum\n" ); + return false; } if ( $header['csize'] != strlen( $base ) ) { wfDebug( __METHOD__. ": incorrect base length\n" ); @@ -560,6 +578,30 @@ class DiffHistoryBlob implements HistoryBlob { return $out; } + /** + * Compute a binary "Adler-32" checksum as defined by LibXDiff, i.e. with + * the bytes backwards and initialised with 0 instead of 1. See bug 34428. + * + * Returns false if no hashing library is available + */ + function xdiffAdler32( $s ) { + static $init; + if ( $init === null ) { + $init = str_repeat( "\xf0", 205 ) . "\xee" . str_repeat( "\xf0", 67 ) . "\x02"; + } + // The real Adler-32 checksum of $init is zero, so it initialises the + // state to zero, as it is at the start of LibXDiff's checksum + // algorithm. Appending the subject string then simulates LibXDiff. + if ( function_exists( 'hash' ) ) { + $hash = hash( 'adler32', $init . $s, true ); + } elseif ( function_exists( 'mhash' ) ) { + $hash = mhash( MHASH_ADLER32, $init . $s ); + } else { + return false; + } + return strrev( $hash ); + } + function uncompress() { if ( !$this->mDiffs ) { return; diff --git a/includes/Hooks.php b/includes/Hooks.php index e1c1d50b..bc39f2fc 100644 --- a/includes/Hooks.php +++ b/includes/Hooks.php @@ -259,7 +259,7 @@ class Hooks { /** * This REALLY should be protected... but it's public for compatibility * - * @param $errno Unused + * @param $errno int Unused * @param $errstr String: error message * @return Boolean: false */ diff --git a/includes/Html.php b/includes/Html.php index c61a1baf..83af24af 100644 --- a/includes/Html.php +++ b/includes/Html.php @@ -211,6 +211,23 @@ class Html { 'search', ); + if( $wgHtml5 ) { + $validTypes = array_merge( $validTypes, array( + 'datetime', + 'datetime-local', + 'date', + 'month', + 'time', + 'week', + 'number', + 'range', + 'email', + 'url', + 'search', + 'tel', + 'color', + ) ); + } if ( isset( $attribs['type'] ) && !in_array( $attribs['type'], $validTypes ) ) { unset( $attribs['type'] ); @@ -286,6 +303,8 @@ class Html { return $attribs; } + # Whenever altering this array, please provide a covering test case + # in HtmlTest::provideElementsWithAttributesHavingDefaultValues static $attribDefaults = array( 'area' => array( 'shape' => 'rect' ), 'button' => array( @@ -306,7 +325,6 @@ class Html { 'input' => array( 'formaction' => 'GET', 'type' => 'text', - 'value' => '', ), 'keygen' => array( 'keytype' => 'rsa' ), 'link' => array( 'media' => 'all' ), @@ -325,7 +343,11 @@ class Html { foreach ( $attribs as $attrib => $value ) { $lcattrib = strtolower( $attrib ); - $value = strval( $value ); + if( is_array( $value ) ) { + $value = implode( ' ', $value ); + } else { + $value = strval( $value ); + } # Simple checks using $attribDefaults if ( isset( $attribDefaults[$element][$lcattrib] ) && @@ -343,6 +365,29 @@ class Html { && strval( $attribs['type'] ) == 'text/css' ) { unset( $attribs['type'] ); } + if ( $element === 'input' ) { + $type = isset( $attribs['type'] ) ? $attribs['type'] : null; + $value = isset( $attribs['value'] ) ? $attribs['value'] : null; + if ( $type === 'checkbox' || $type === 'radio' ) { + // The default value for checkboxes and radio buttons is 'on' + // not ''. By stripping value="" we break radio boxes that + // actually wants empty values. + if ( $value === 'on' ) { + unset( $attribs['value'] ); + } + } elseif ( $type === 'submit' ) { + // The default value for submit appears to be "Submit" but + // let's not bother stripping out localized text that matches + // that. + } else { + // The default value for nearly every other field type is '' + // The 'range' and 'color' types use different defaults but + // stripping a value="" does not hurt them. + if ( $value === '' ) { + unset( $attribs['value'] ); + } + } + } if ( $element === 'select' && isset( $attribs['size'] ) ) { if ( in_array( 'multiple', $attribs ) || ( isset( $attribs['multiple'] ) && $attribs['multiple'] !== false ) @@ -548,9 +593,10 @@ class Html { } /** - * Output a ' or (for - * XML) literal "]]>". + * Output a "" or (for XML) literal "]]>". * * @param $contents string JavaScript * @return string Raw HTML @@ -572,8 +618,8 @@ class Html { } /** - * Output a . + * Output a "". * * @param $url string * @return string Raw HTML @@ -591,9 +637,9 @@ class Html { } /** - * Output a ' (admittedly unlikely). + * contains literal "" (admittedly unlikely). * * @param $contents string CSS * @param $media mixed A media type string, like 'screen' @@ -613,7 +659,7 @@ class Html { } /** - * Output a linking to the given URL for the given + * Output a "" linking to the given URL for the given * media type (if any). * * @param $url string @@ -630,7 +676,7 @@ class Html { } /** - * Convenience function to produce an element. This supports the + * Convenience function to produce an "" element. This supports the * new HTML5 input types and attributes, and will silently strip them if * $wgHtml5 is false. * @@ -663,11 +709,12 @@ class Html { } /** - * Convenience function to produce an element. This supports leaving - * out the cols= and rows= which Xml requires and are required by HTML4/XHTML - * but not required by HTML5 and will silently set cols="" and rows="" if - * $wgHtml5 is false and cols and rows are omitted (HTML4 validates present - * but empty cols="" and rows="" as valid). + * Convenience function to produce an "" element. + * + * This supports leaving out the cols= and rows= which Xml requires and are + * required by HTML4/XHTML but not required by HTML5 and will silently set + * cols="" and rows="" if $wgHtml5 is false and cols and rows are omitted + * (HTML4 validates present but empty cols="" and rows="" as valid). * * @param $name string name attribute * @param $value string value attribute @@ -706,8 +753,10 @@ class Html { * * @param $params array: * - selected: [optional] Id of namespace which should be pre-selected - * - all: [optional] Value of item for "all namespaces". If null or unset, no
    ' + . '
    ' + . $msg->parseAsBlock() + . "
    ' - . '
    ' - . $msg->parseAsBlock() - . "
    \n"; foreach ( $metadata as $type => $stuff ) { foreach ( $stuff as $v ) { @@ -259,12 +287,16 @@ class ImagePage extends Article { } protected function openShowImage() { - global $wgOut, $wgUser, $wgImageLimits, $wgRequest, - $wgLang, $wgEnableUploads, $wgSend404Code; + global $wgImageLimits, $wgEnableUploads, $wgSend404Code; $this->loadFile(); + $out = $this->getContext()->getOutput(); + $user = $this->getContext()->getUser(); + $lang = $this->getContext()->getLanguage(); + $dirmark = $lang->getDirMarkEntity(); + $request = $this->getContext()->getRequest(); - $sizeSel = intval( $wgUser->getOption( 'imagesize' ) ); + $sizeSel = intval( $user->getOption( 'imagesize' ) ); if ( !isset( $wgImageLimits[$sizeSel] ) ) { $sizeSel = User::getDefaultOption( 'imagesize' ); @@ -278,11 +310,10 @@ class ImagePage extends Article { $max = $wgImageLimits[$sizeSel]; $maxWidth = $max[0]; $maxHeight = $max[1]; - $dirmark = $wgLang->getDirMark(); if ( $this->displayImg->exists() ) { # image - $page = $wgRequest->getIntOrNull( 'page' ); + $page = $request->getIntOrNull( 'page' ); if ( is_null( $page ) ) { $params = array(); $page = 1; @@ -294,15 +325,15 @@ class ImagePage extends Article { $height_orig = $this->displayImg->getHeight( $page ); $height = $height_orig; - $longDesc = wfMsg( 'parentheses', $this->displayImg->getLongDesc() ); + $longDesc = wfMessage( 'parentheses', $this->displayImg->getLongDesc() )->text(); - wfRunHooks( 'ImageOpenShowImageInlineBefore', array( &$this, &$wgOut ) ); + wfRunHooks( 'ImageOpenShowImageInlineBefore', array( &$this, &$out ) ); if ( $this->displayImg->allowInlineDisplay() ) { # image # "Download high res version" link below the image - # $msgsize = wfMsgHtml( 'file-info-size', $width_orig, $height_orig, Linker::formatSize( $this->displayImg->getSize() ), $mime ); + # $msgsize = wfMessage( 'file-info-size', $width_orig, $height_orig, Linker::formatSize( $this->displayImg->getSize() ), $mime )->escaped(); # We'll show a thumbnail of this image if ( $width > $maxWidth || $height > $maxHeight ) { # Calculate the thumbnail size. @@ -318,21 +349,33 @@ class ImagePage extends Article { # Note that $height <= $maxHeight now, but might not be identical # because of rounding. } - $msgbig = wfMsgHtml( 'show-big-image' ); + $msgbig = wfMessage( 'show-big-image' )->escaped(); + if ( $this->displayImg->getRepo()->canTransformVia404() ) { + $thumbSizes = $wgImageLimits; + } else { + # Creating thumb links triggers thumbnail generation. + # Just generate the thumb for the current users prefs. + $thumbOption = $user->getOption( 'thumbsize' ); + $thumbSizes = array( isset( $wgImageLimits[$thumbOption] ) + ? $wgImageLimits[$thumbOption] + : $wgImageLimits[User::getDefaultOption( 'thumbsize' )] ); + } + # Generate thumbnails or thumbnail links as needed... $otherSizes = array(); - foreach ( $wgImageLimits as $size ) { - if ( $size[0] < $width_orig && $size[1] < $height_orig && - $size[0] != $width && $size[1] != $height ) { + foreach ( $thumbSizes as $size ) { + if ( $size[0] < $width_orig && $size[1] < $height_orig + && $size[0] != $width && $size[1] != $height ) + { $otherSizes[] = $this->makeSizeLink( $params, $size[0], $size[1] ); } } $msgsmall = wfMessage( 'show-big-image-preview' )-> rawParams( $this->makeSizeLink( $params, $width, $height ) )-> parse(); - if ( count( $otherSizes ) && $this->displayImg->getRepo()->canTransformVia404() ) { + if ( count( $otherSizes ) ) { $msgsmall .= ' ' . Html::rawElement( 'span', array( 'class' => 'mw-filepage-other-resolutions' ), - wfMessage( 'show-big-image-other' )->rawParams( $wgLang->pipeList( $otherSizes ) )-> + wfMessage( 'show-big-image-other' )->rawParams( $lang->pipeList( $otherSizes ) )-> params( count( $otherSizes ) )->parse() ); } @@ -340,6 +383,9 @@ class ImagePage extends Article { # Some sort of audio file that doesn't have dimensions # Don't output a no hi res message for such a file $msgsmall = ''; + } elseif ( $this->displayImg->isVectorized() ) { + # For vectorized images, full size is just the frame size + $msgsmall = ''; } else { # Image is small enough to show full size on image page $msgsmall = wfMessage( 'file-nohires' )->parse(); @@ -354,7 +400,7 @@ class ImagePage extends Article { $isMulti = $this->displayImg->isMultipage() && $this->displayImg->pageCount() > 1; if ( $isMulti ) { - $wgOut->addHTML( '
    ' - . ( $this->current->isLocal() && ( $wgUser->isAllowedAny( 'delete', 'deletedhistory' ) ) ? '' : '' ) - . '' - . ( $this->showThumb ? '' : '' ) - . '' - . '' - . '' + . ( $this->current->isLocal() && ( $this->getUser()->isAllowedAny( 'delete', 'deletedhistory' ) ) ? '' : '' ) + . '' + . ( $this->showThumb ? '' : '' ) + . '' + . '' + . '' . "\n"; } @@ -922,43 +1001,45 @@ class ImageHistoryList { * @return string */ public function imageHistoryLine( $iscur, $file ) { - global $wgUser, $wgLang, $wgContLang; + global $wgContLang; + $user = $this->getUser(); + $lang = $this->getLanguage(); $timestamp = wfTimestamp( TS_MW, $file->getTimestamp() ); $img = $iscur ? $file->getName() : $file->getArchiveName(); - $user = $file->getUser( 'id' ); - $usertext = $file->getUser( 'text' ); - $description = $file->getDescription(); + $userId = $file->getUser( 'id' ); + $userText = $file->getUser( 'text' ); + $description = $file->getDescription( File::FOR_THIS_USER, $user ); $local = $this->current->isLocal(); $row = $selected = ''; // Deletion link - if ( $local && ( $wgUser->isAllowedAny( 'delete', 'deletedhistory' ) ) ) { + if ( $local && ( $user->isAllowedAny( 'delete', 'deletedhistory' ) ) ) { $row .= '"; @@ -1037,27 +1118,33 @@ class ImageHistoryList { // Image dimensions + size $row .= ''; // Uploading user $row .= ''; // Don't show deleted descriptions if ( $file->isDeleted( File::DELETED_COMMENT ) ) { - $row .= ''; + $row .= ''; } else { $row .= ''; } @@ -1074,9 +1161,11 @@ class ImageHistoryList { * @return string */ protected function getThumbForLine( $file ) { - global $wgLang; - - if ( $file->allowInlineDisplay() && $file->userCan( File::DELETED_FILE ) && !$file->isDeleted( File::DELETED_FILE ) ) { + $lang = $this->getLanguage(); + $user = $this->getUser(); + if ( $file->allowInlineDisplay() && $file->userCan( File::DELETED_FILE,$user ) + && !$file->isDeleted( File::DELETED_FILE ) ) + { $params = array( 'width' => '120', 'height' => '120', @@ -1085,20 +1174,20 @@ class ImageHistoryList { $thumbnail = $file->transform( $params ); $options = array( - 'alt' => wfMsg( 'filehist-thumbtext', - $wgLang->timeanddate( $timestamp, true ), - $wgLang->date( $timestamp, true ), - $wgLang->time( $timestamp, true ) ), + 'alt' => $this->msg( 'filehist-thumbtext', + $lang->userTimeAndDate( $timestamp, $user ), + $lang->userDate( $timestamp, $user ), + $lang->userTime( $timestamp, $user ) )->text(), 'file-link' => true, ); if ( !$thumbnail ) { - return wfMsgHtml( 'filehist-nothumb' ); + return $this->msg( 'filehist-nothumb' )->escaped(); } return $thumbnail->toHtml( $options ); } else { - return wfMsgHtml( 'filehist-nothumb' ); + return $this->msg( 'filehist-nothumb' )->escaped(); } } @@ -1162,6 +1251,7 @@ class ImageHistoryPseudoPager extends ReverseChronologicalPager { } /** + * @param $row object * @return string */ function formatRow( $row ) { diff --git a/includes/ImageQueryPage.php b/includes/ImageQueryPage.php index f46974b2..f9f6ceed 100644 --- a/includes/ImageQueryPage.php +++ b/includes/ImageQueryPage.php @@ -1,4 +1,25 @@ * http://www.mediawiki.org/ @@ -33,7 +33,7 @@ class WikiImporter { private $reader = null; private $mLogItemCallback, $mUploadCallback, $mRevisionCallback, $mPageCallback; - private $mSiteInfoCallback, $mTargetNamespace, $mPageOutCallback; + private $mSiteInfoCallback, $mTargetNamespace, $mTargetRootPage, $mPageOutCallback; private $mNoticeCallback, $mDebug; private $mImportUploads, $mImageBasePath; private $mNoUpdates = false; @@ -199,6 +199,39 @@ class WikiImporter { } } + /** + * Set a target root page under which all pages are imported + * @param $rootpage + * @return status object + */ + public function setTargetRootPage( $rootpage ) { + $status = Status::newGood(); + if( is_null( $rootpage ) ) { + // No rootpage + $this->mTargetRootPage = null; + } elseif( $rootpage !== '' ) { + $rootpage = rtrim( $rootpage, '/' ); //avoid double slashes + $title = Title::newFromText( $rootpage, !is_null( $this->mTargetNamespace ) ? $this->mTargetNamespace : NS_MAIN ); + if( !$title || $title->isExternal() ) { + $status->fatal( 'import-rootpage-invalid' ); + } else { + if( !MWNamespace::hasSubpages( $title->getNamespace() ) ) { + global $wgContLang; + + $displayNSText = $title->getNamespace() == NS_MAIN + ? wfMessage( 'blanknamespace' )->text() + : $wgContLang->getNsText( $title->getNamespace() ); + $status->fatal( 'import-rootpage-nosubpage', $displayNSText ); + } else { + // set namespace to 'all', so the namespace check in processTitle() can passed + $this->setTargetNamespace( null ); + $this->mTargetRootPage = $title->getPrefixedDBKey(); + } + } + } + return $status; + } + /** * @param $dir */ @@ -275,7 +308,7 @@ class WikiImporter { } /** - * Notify the callback function when a new is reached. + * Notify the callback function when a new "" is reached. * @param $title Title */ function pageCallback( $title ) { @@ -285,7 +318,7 @@ class WikiImporter { } /** - * Notify the callback function when a is closed. + * Notify the callback function when a "" is closed. * @param $title Title * @param $origTitle Title * @param $revCount Integer @@ -301,7 +334,8 @@ class WikiImporter { /** * Notify the callback function of a revision - * @param $revision A WikiRevision object + * @param $revision WikiRevision object + * @return bool|mixed */ private function revisionCallback( $revision ) { if ( isset( $this->mRevisionCallback ) ) { @@ -314,7 +348,8 @@ class WikiImporter { /** * Notify the callback function of a new log item - * @param $revision A WikiRevision object + * @param $revision WikiRevision object + * @return bool|mixed */ private function logItemCallback( $revision ) { if ( isset( $this->mLogItemCallback ) ) { @@ -394,6 +429,7 @@ class WikiImporter { /** * Primary entry point + * @return bool */ public function doImport() { $this->reader->read(); @@ -783,9 +819,14 @@ class WikiImporter { $origTitle = Title::newFromText( $workTitle ); if( !is_null( $this->mTargetNamespace ) && !is_null( $origTitle ) ) { - $title = Title::makeTitle( $this->mTargetNamespace, + # makeTitleSafe, because $origTitle can have a interwiki (different setting of interwiki map) + # and than dbKey can begin with a lowercase char + $title = Title::makeTitleSafe( $this->mTargetNamespace, $origTitle->getDBkey() ); } else { + if( !is_null( $this->mTargetRootPage ) ) { + $workTitle = $this->mTargetRootPage . '/' . $workTitle; + } $title = Title::newFromText( $workTitle ); } @@ -826,7 +867,7 @@ class UploadSourceAdapter { * @return string */ static function registerSource( $source ) { - $id = wfGenerateToken(); + $id = wfRandomString(); self::$sourceRegistrations[$id] = $source; diff --git a/includes/Init.php b/includes/Init.php index 72c10543..a8540f2c 100644 --- a/includes/Init.php +++ b/includes/Init.php @@ -1,4 +1,24 @@ * @copyright Copyright © 2005, Ævar Arnfjörð Bjarmason * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later */ +/** + * A License class for use on Special:Upload + */ class Licenses extends HTMLFormField { /** * @var string @@ -34,7 +52,7 @@ class Licenses extends HTMLFormField { public function __construct( $params ) { parent::__construct( $params ); - $this->msg = empty( $params['licenses'] ) ? wfMsgForContent( 'licenses' ) : $params['licenses']; + $this->msg = empty( $params['licenses'] ) ? wfMessage( 'licenses' )->inContentLanguage()->plain() : $params['licenses']; $this->selected = null; $this->makeLicenses(); @@ -102,7 +120,7 @@ class Licenses extends HTMLFormField { foreach ( $tagset as $key => $val ) if ( is_array( $val ) ) { $this->html .= $this->outputOption( - $this->msg( $key ), '', + $key, '', array( 'disabled' => 'disabled', 'style' => 'color: GrayText', // for MSIE @@ -112,7 +130,7 @@ class Licenses extends HTMLFormField { $this->makeHtml( $val, $depth + 1 ); } else { $this->html .= $this->outputOption( - $this->msg( $val->text ), $val->template, + $val->text, $val->template, array( 'title' => '{{' . $val->template . '}}' ), $depth ); @@ -120,13 +138,15 @@ class Licenses extends HTMLFormField { } /** - * @param $text + * @param $message * @param $value * @param $attribs null * @param $depth int * @return string */ - protected function outputOption( $text, $value, $attribs = null, $depth = 0 ) { + protected function outputOption( $message, $value, $attribs = null, $depth = 0 ) { + $msgObj = $this->msg( $message ); + $text = $msgObj->exists() ? $msgObj->text() : $message; $attribs['value'] = $value; if ( $value === $this->selected ) $attribs['selected'] = 'selected'; @@ -134,15 +154,6 @@ class Licenses extends HTMLFormField { return str_repeat( "\t", $depth ) . Xml::element( 'option', $attribs, $val ) . "\n"; } - /** - * @param $str string - * @return String - */ - protected function msg( $str ) { - $msg = wfMessage( $str ); - return $msg->exists() ? $msg->text() : $str; - } - /**#@-*/ /** @@ -164,7 +175,7 @@ class Licenses extends HTMLFormField { public function getInputHTML( $value ) { $this->selected = $value; - $this->html = $this->outputOption( wfMsg( 'nolicense' ), '', + $this->html = $this->outputOption( wfMessage( 'nolicense' )->text(), '', (bool)$this->selected ? null : array( 'selected' => 'selected' ) ); $this->makeHtml( $this->getLicenses() ); diff --git a/includes/LinkFilter.php b/includes/LinkFilter.php index af7680fb..214f4959 100644 --- a/includes/LinkFilter.php +++ b/includes/LinkFilter.php @@ -1,4 +1,25 @@ isRedirect() ) { # Page is a redirect $colour = 'mw-redirect'; - } elseif ( $threshold > 0 && - $t->exists() && $t->getLength() < $threshold && - $t->isContentPage() ) { + } elseif ( $threshold > 0 && $t->isContentPage() && + $t->exists() && $t->getLength() < $threshold + ) { # Page is a stub $colour = 'stub'; } @@ -173,6 +198,12 @@ class Linker { wfProfileOut( __METHOD__ ); return "$html"; } + + if( is_string( $query ) ) { + // some functions withing core using this still hand over query strings + wfDeprecated( __METHOD__ . ' with parameter $query as string (should be array)', '1.20' ); + $query = wfCgiToArray( $query ); + } $options = (array)$options; $dummy = new DummyLinker; // dummy linker instance for bc on the hooks @@ -229,6 +260,7 @@ class Linker { /** * Identical to link(), except $options defaults to 'known'. + * @return string */ public static function linkKnown( $target, $html = null, $customAttribs = array(), @@ -243,6 +275,7 @@ class Linker { * @param $target Title * @param $query Array: query parameters * @param $options Array + * @return String */ private static function linkUrl( $target, $query, $options ) { wfProfileIn( __METHOD__ ); @@ -312,7 +345,7 @@ class Linker { } elseif ( in_array( 'known', $options ) ) { $defaults['title'] = $target->getPrefixedText(); } else { - $defaults['title'] = wfMsg( 'red-link-title', $target->getPrefixedText() ); + $defaults['title'] = wfMessage( 'red-link-title', $target->getPrefixedText() )->text(); } # Finally, merge the custom attribs with the default ones, and iterate @@ -380,6 +413,11 @@ class Linker { * despite $query not being used. * * @param $nt Title + * @param $html String [optional] + * @param $query String [optional] + * @param $trail String [optional] + * @param $prefix String [optional] + * * * @return string */ @@ -391,6 +429,31 @@ class Linker { return "{$prefix}{$html}{$inside}{$trail}"; } + /** + * Get a message saying that an invalid title was encountered. + * This should be called after a method like Title::makeTitleSafe() returned + * a value indicating that the title object is invalid. + * + * @param $context IContextSource context to use to get the messages + * @param $namespace int Namespace number + * @param $title string Text of the title, without the namespace part + */ + public static function getInvalidTitleDescription( IContextSource $context, $namespace, $title ) { + global $wgContLang; + + // First we check whether the namespace exists or not. + if ( MWNamespace::exists( $namespace ) ) { + if ( $namespace == NS_MAIN ) { + $name = $context->msg( 'blanknamespace' )->text(); + } else { + $name = $wgContLang->getFormattedNsText( $namespace ); + } + return $context->msg( 'invalidtitle-knownnamespace', $namespace, $name, $title )->text(); + } else { + return $context->msg( 'invalidtitle-unknownnamespace', $namespace, $title )->text(); + } + } + /** * @param $title Title * @return Title @@ -456,7 +519,8 @@ class Linker { * Given parameters derived from [[Image:Foo|options...]], generate the * HTML that that syntax inserts in the page. * - * @param $title Title object + * @param $parser Parser object + * @param $title Title object of the file (not the currently viewed page) * @param $file File object, or false if it doesn't exist * @param $frameParams Array: associative array of parameters external to the media handler. * Boolean parameters are indicated by presence or absence, the value is arbitrary and @@ -472,6 +536,7 @@ class Linker { * valign Vertical alignment (baseline, sub, super, top, text-top, middle, * bottom, text-bottom) * alt Alternate text for image (i.e. alt attribute). Plain text. + * class HTML for image classes. Plain text. * caption HTML for image caption. * link-url URL to link to * link-title Title object to link to @@ -483,9 +548,10 @@ class Linker { * @param $time String: timestamp of the file, set as false for current * @param $query String: query params for desc url * @param $widthOption: Used by the parser to remember the user preference thumbnailsize + * @since 1.20 * @return String: HTML for an image, with links, wrappers, etc. */ - public static function makeImageLink2( Title $title, $file, $frameParams = array(), + public static function makeImageLink( /*Parser*/ $parser, Title $title, $file, $frameParams = array(), $handlerParams = array(), $time = false, $query = "", $widthOption = null ) { $res = null; @@ -515,6 +581,9 @@ class Linker { if ( !isset( $fp['title'] ) ) { $fp['title'] = ''; } + if ( !isset( $fp['class'] ) ) { + $fp['class'] = ''; + } $prefix = $postfix = ''; @@ -558,16 +627,20 @@ class Linker { } if ( isset( $fp['thumbnail'] ) || isset( $fp['manualthumb'] ) || isset( $fp['framed'] ) ) { - global $wgContLang; - # Create a thumbnail. Alignment depends on language - # writing direction, # right aligned for left-to-right- - # languages ("Western languages"), left-aligned - # for right-to-left-languages ("Semitic languages") + # Create a thumbnail. Alignment depends on the writing direction of + # the page content language (right-aligned for LTR languages, + # left-aligned for RTL languages) # - # If thumbnail width has not been provided, it is set + # If a thumbnail width has not been provided, it is set # to the default user option as specified in Language*.php if ( $fp['align'] == '' ) { - $fp['align'] = $wgContLang->alignEnd(); + if( $parser instanceof Parser ) { + $fp['align'] = $parser->getTargetLanguage()->alignEnd(); + } else { + # backwards compatibility, remove with makeImageLink2() + global $wgContLang; + $fp['align'] = $wgContLang->alignEnd(); + } } return $prefix . self::makeThumbLink2( $title, $file, $fp, $hp, $time, $query ) . $postfix; } @@ -594,9 +667,12 @@ class Linker { $params = array( 'alt' => $fp['alt'], 'title' => $fp['title'], - 'valign' => isset( $fp['valign'] ) ? $fp['valign'] : false , - 'img-class' => isset( $fp['border'] ) ? 'thumbborder' : false ); - $params = self::getImageLinkMTOParams( $fp, $query ) + $params; + 'valign' => isset( $fp['valign'] ) ? $fp['valign'] : false, + 'img-class' => $fp['class'] ); + if ( isset( $fp['border'] ) ) { + $params['img-class'] .= ( $params['img-class'] !== '' ) ? ' thumbborder' : 'thumbborder'; + } + $params = self::getImageLinkMTOParams( $fp, $query, $parser ) + $params; $s = $thumb->toHtml( $params ); } @@ -606,19 +682,38 @@ class Linker { return str_replace( "\n", ' ', $prefix . $s . $postfix ); } + /** + * See makeImageLink() + * When this function is removed, remove if( $parser instanceof Parser ) check there too + * @deprecated since 1.20 + */ + public static function makeImageLink2( Title $title, $file, $frameParams = array(), + $handlerParams = array(), $time = false, $query = "", $widthOption = null ) { + return self::makeImageLink( null, $title, $file, $frameParams, + $handlerParams, $time, $query, $widthOption ); + } + /** * Get the link parameters for MediaTransformOutput::toHtml() from given * frame parameters supplied by the Parser. - * @param $frameParams The frame parameters - * @param $query An optional query string to add to description page links + * @param $frameParams array The frame parameters + * @param $query string An optional query string to add to description page links + * @return array */ - private static function getImageLinkMTOParams( $frameParams, $query = '' ) { + private static function getImageLinkMTOParams( $frameParams, $query = '', $parser = null ) { $mtoParams = array(); if ( isset( $frameParams['link-url'] ) && $frameParams['link-url'] !== '' ) { $mtoParams['custom-url-link'] = $frameParams['link-url']; if ( isset( $frameParams['link-target'] ) ) { $mtoParams['custom-target-link'] = $frameParams['link-target']; } + if ( $parser ) { + $extLinkAttrs = $parser->getExternalLinkAttribs( $frameParams['link-url'] ); + foreach ( $extLinkAttrs as $name => $val ) { + // Currently could include 'rel' and 'target' + $mtoParams['parser-extlink-'.$name] = $val; + } + } } elseif ( isset( $frameParams['link-title'] ) && $frameParams['link-title'] !== '' ) { $mtoParams['custom-title-link'] = self::normaliseSpecialPage( $frameParams['link-title'] ); } elseif ( !empty( $frameParams['no-link'] ) ) { @@ -640,6 +735,7 @@ class Linker { * @param $params Array * @param $framed Boolean * @param $manualthumb String + * @return mixed */ public static function makeThumbLinkObj( Title $title, $file, $label = '', $alt, $align = 'right', $params = array(), $framed = false , $manualthumb = "" ) @@ -736,13 +832,14 @@ class Linker { $s .= self::makeBrokenImageLinkObj( $title, $fp['title'], '', '', '', $time == true ); $zoomIcon = ''; } elseif ( !$thumb ) { - $s .= htmlspecialchars( wfMsg( 'thumbnail_error', '' ) ); + $s .= wfMessage( 'thumbnail_error', '' )->escaped(); $zoomIcon = ''; } else { $params = array( 'alt' => $fp['alt'], 'title' => $fp['title'], - 'img-class' => 'thumbimage' ); + 'img-class' => ( isset( $fp['class'] ) && $fp['class'] !== '' ) ? $fp['class'] . ' thumbimage' : 'thumbimage' + ); $params = self::getImageLinkMTOParams( $fp, $query ) + $params; $s .= $thumb->toHtml( $params ); if ( isset( $fp['framed'] ) ) { @@ -752,7 +849,7 @@ class Linker { Html::rawElement( 'a', array( 'href' => $url, 'class' => 'internal', - 'title' => wfMsg( 'thumbnail-more' ) ), + 'title' => wfMessage( 'thumbnail-more' )->text() ), Html::element( 'img', array( 'src' => $wgStylePath . '/common/images/magnify-clip' . ( $wgContLang->isRTL() ? '-rtl' : '' ) . '.png', 'width' => 15, @@ -848,7 +945,7 @@ class Linker { * This will make a broken link if $file is false. * * @param $title Title object. - * @param $file File|false mixed File object or false + * @param $file File|bool mixed File object or false * @param $html String: pre-sanitized HTML * @return String: HTML * @@ -882,7 +979,7 @@ class Linker { $key = strtolower( $name ); } - return self::linkKnown( SpecialPage::getTitleFor( $name ) , wfMsg( $key ) ); + return self::linkKnown( SpecialPage::getTitleFor( $name ) , wfMessage( $key )->text() ); } /** @@ -892,6 +989,7 @@ class Linker { * @param $escape Boolean: do we escape the link text? * @param $linktype String: type of external link. Gets added to the classes * @param $attribs Array of extra attributes to + * @return string */ public static function makeExternalLink( $url, $text, $escape = true, $linktype = '', $attribs = array() ) { $class = "external"; @@ -923,7 +1021,7 @@ class Linker { * @param $userName String: user name in database. * @param $altUserName String: text to display instead of the user name (optional) * @return String: HTML fragment - * @since 1.19 Method exists for a long time. $displayText was added in 1.19. + * @since 1.19 Method exists for a long time. $altUserName was added in 1.19. */ public static function userLink( $userId, $userName, $altUserName = false ) { if ( $userId == 0 ) { @@ -973,7 +1071,7 @@ class Linker { } $contribsPage = SpecialPage::getTitleFor( 'Contributions', $userText ); - $items[] = self::link( $contribsPage, wfMsgHtml( 'contribslink' ), $attribs ); + $items[] = self::link( $contribsPage, wfMessage( 'contribslink' )->escaped(), $attribs ); } if ( $blockable && $wgUser->isAllowed( 'block' ) ) { $items[] = self::blockLink( $userId, $userText ); @@ -986,7 +1084,10 @@ class Linker { wfRunHooks( 'UserToolLinksEdit', array( $userId, $userText, &$items ) ); if ( $items ) { - return ' (' . $wgLang->pipeList( $items ) . ')'; + return wfMessage( 'word-separator' )->plain() + . '' + . wfMessage( 'parentheses' )->rawParams( $wgLang->pipeList( $items ) )->escaped() + . ''; } else { return ''; } @@ -997,6 +1098,7 @@ class Linker { * @param $userId Integer: user identifier * @param $userText String: user name or IP address * @param $edits Integer: user edit count (optional, for performance) + * @return String */ public static function userToolLinksRedContribs( $userId, $userText, $edits = null ) { return self::userToolLinks( $userId, $userText, true, 0, $edits ); @@ -1010,7 +1112,7 @@ class Linker { */ public static function userTalkLink( $userId, $userText ) { $userTalkPage = Title::makeTitle( NS_USER_TALK, $userText ); - $userTalkLink = self::link( $userTalkPage, wfMsgHtml( 'talkpagelinktext' ) ); + $userTalkLink = self::link( $userTalkPage, wfMessage( 'talkpagelinktext' )->escaped() ); return $userTalkLink; } @@ -1021,7 +1123,7 @@ class Linker { */ public static function blockLink( $userId, $userText ) { $blockPage = SpecialPage::getTitleFor( 'Block', $userText ); - $blockLink = self::link( $blockPage, wfMsgHtml( 'blocklink' ) ); + $blockLink = self::link( $blockPage, wfMessage( 'blocklink' )->escaped() ); return $blockLink; } @@ -1032,7 +1134,7 @@ class Linker { */ public static function emailLink( $userId, $userText ) { $emailPage = SpecialPage::getTitleFor( 'Emailuser', $userText ); - $emailLink = self::link( $emailPage, wfMsgHtml( 'emaillink' ) ); + $emailLink = self::link( $emailPage, wfMessage( 'emaillink' )->escaped() ); return $emailLink; } @@ -1044,12 +1146,12 @@ class Linker { */ public static function revUserLink( $rev, $isPublic = false ) { if ( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) { - $link = wfMsgHtml( 'rev-deleted-user' ); + $link = wfMessage( 'rev-deleted-user' )->escaped(); } elseif ( $rev->userCan( Revision::DELETED_USER ) ) { $link = self::userLink( $rev->getUser( Revision::FOR_THIS_USER ), $rev->getUserText( Revision::FOR_THIS_USER ) ); } else { - $link = wfMsgHtml( 'rev-deleted-user' ); + $link = wfMessage( 'rev-deleted-user' )->escaped(); } if ( $rev->isDeleted( Revision::DELETED_USER ) ) { return '' . $link . ''; @@ -1065,14 +1167,15 @@ class Linker { */ public static function revUserTools( $rev, $isPublic = false ) { if ( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) { - $link = wfMsgHtml( 'rev-deleted-user' ); + $link = wfMessage( 'rev-deleted-user' )->escaped(); } elseif ( $rev->userCan( Revision::DELETED_USER ) ) { $userId = $rev->getUser( Revision::FOR_THIS_USER ); $userText = $rev->getUserText( Revision::FOR_THIS_USER ); - $link = self::userLink( $userId, $userText ) . - ' ' . self::userToolLinks( $userId, $userText ); + $link = self::userLink( $userId, $userText ) + . wfMessage( 'word-separator' )->plain() + . self::userToolLinks( $userId, $userText ); } else { - $link = wfMsgHtml( 'rev-deleted-user' ); + $link = wfMessage( 'rev-deleted-user' )->escaped(); } if ( $rev->isDeleted( Revision::DELETED_USER ) ) { return ' ' . $link . ''; @@ -1095,6 +1198,7 @@ class Linker { * @param $comment String * @param $title Mixed: Title object (to generate link to the section in autocomment) or null * @param $local Boolean: whether section links should refer to local page + * @return mixed|String */ public static function formatComment( $comment, $title = null, $local = false ) { wfProfileIn( __METHOD__ ); @@ -1126,7 +1230,7 @@ class Linker { * Called by Linker::formatComment. * * @param $comment String: comment text - * @param $title An optional title object used to links to sections + * @param $title Title|null An optional title object used to links to sections * @param $local Boolean: whether section links should refer to local page * @return String: formatted comment */ @@ -1155,41 +1259,45 @@ class Linker { $pre = $match[1]; $auto = $match[2]; $post = $match[3]; - $link = ''; - if ( $title ) { - $section = $auto; - - # Remove links that a user may have manually put in the autosummary - # This could be improved by copying as much of Parser::stripSectionName as desired. - $section = str_replace( '[[:', '', $section ); - $section = str_replace( '[[', '', $section ); - $section = str_replace( ']]', '', $section ); - - $section = Sanitizer::normalizeSectionNameWhitespace( $section ); # bug 22784 - if ( $local ) { - $sectionTitle = Title::newFromText( '#' . $section ); - } else { - $sectionTitle = Title::makeTitleSafe( $title->getNamespace(), - $title->getDBkey(), $section ); + $comment = null; + wfRunHooks( 'FormatAutocomments', array( &$comment, $pre, $auto, $post, $title, $local ) ); + if ( $comment === null ) { + $link = ''; + if ( $title ) { + $section = $auto; + + # Remove links that a user may have manually put in the autosummary + # This could be improved by copying as much of Parser::stripSectionName as desired. + $section = str_replace( '[[:', '', $section ); + $section = str_replace( '[[', '', $section ); + $section = str_replace( ']]', '', $section ); + + $section = Sanitizer::normalizeSectionNameWhitespace( $section ); # bug 22784 + if ( $local ) { + $sectionTitle = Title::newFromText( '#' . $section ); + } else { + $sectionTitle = Title::makeTitleSafe( $title->getNamespace(), + $title->getDBkey(), $section ); + } + if ( $sectionTitle ) { + $link = self::link( $sectionTitle, + $wgLang->getArrow(), array(), array(), + 'noclasses' ); + } else { + $link = ''; + } } - if ( $sectionTitle ) { - $link = self::link( $sectionTitle, - $wgLang->getArrow(), array(), array(), - 'noclasses' ); - } else { - $link = ''; + if ( $pre ) { + # written summary $presep autocomment (summary /* section */) + $pre .= wfMessage( 'autocomment-prefix' )->inContentLanguage()->escaped(); } + if ( $post ) { + # autocomment $postsep written summary (/* section */ summary) + $auto .= wfMessage( 'colon-separator' )->inContentLanguage()->escaped(); + } + $auto = '' . $auto . ''; + $comment = $pre . $link . $wgLang->getDirMark() . '' . $auto . $post . ''; } - if ( $pre ) { - # written summary $presep autocomment (summary /* section */) - $pre .= wfMsgExt( 'autocomment-prefix', array( 'escapenoentities', 'content' ) ); - } - if ( $post ) { - # autocomment $postsep written summary (/* section */ summary) - $auto .= wfMsgExt( 'colon-separator', array( 'escapenoentities', 'content' ) ); - } - $auto = '' . $auto . ''; - $comment = $pre . $link . $wgLang->getDirMark() . '' . $auto . $post . ''; return $comment; } @@ -1205,7 +1313,7 @@ class Linker { * * @todo FIXME: Doesn't handle sub-links as in image thumb texts like the main parser * @param $comment String: text to format links in - * @param $title An optional title object used to links to sections + * @param $title Title|null An optional title object used to links to sections * @param $local Boolean: whether section links should refer to local page * @return String */ @@ -1399,7 +1507,8 @@ class Linker { return ''; } else { $formatted = self::formatComment( $comment, $title, $local ); - return " ($formatted)"; + $formatted = wfMessage( 'parentheses' )->rawParams( $formatted )->escaped(); + return " $formatted"; } } @@ -1417,12 +1526,12 @@ class Linker { return ""; } if ( $rev->isDeleted( Revision::DELETED_COMMENT ) && $isPublic ) { - $block = " " . wfMsgHtml( 'rev-deleted-comment' ) . ""; + $block = " " . wfMessage( 'rev-deleted-comment' )->escaped() . ""; } elseif ( $rev->userCan( Revision::DELETED_COMMENT ) ) { $block = self::commentBlock( $rev->getComment( Revision::FOR_THIS_USER ), $rev->getTitle(), $local ); } else { - $block = " " . wfMsgHtml( 'rev-deleted-comment' ) . ""; + $block = " " . wfMessage( 'rev-deleted-comment' )->escaped() . ""; } if ( $rev->isDeleted( Revision::DELETED_COMMENT ) ) { return " $block"; @@ -1436,13 +1545,11 @@ class Linker { */ public static function formatRevisionSize( $size ) { if ( $size == 0 ) { - $stxt = wfMsgExt( 'historyempty', 'parsemag' ); + $stxt = wfMessage( 'historyempty' )->escaped(); } else { - global $wgLang; - $stxt = wfMsgExt( 'nbytes', 'parsemag', $wgLang->formatNum( $size ) ); - $stxt = "($stxt)"; + $stxt = wfMessage( 'nbytes' )->numParams( $size )->escaped(); + $stxt = wfMessage( 'parentheses' )->rawParams( $stxt )->escaped(); } - $stxt = htmlspecialchars( $stxt ); return "$stxt"; } @@ -1484,6 +1591,7 @@ class Linker { * End a Table Of Contents line. * tocUnindent() will be used instead if we're ending a line below * the new level. + * @return string */ public static function tocLineEnd() { return "\n"; @@ -1493,11 +1601,13 @@ class Linker { * Wraps the TOC in a table and provides the hide/collapse javascript. * * @param $toc String: html of the Table Of Contents - * @param $lang mixed: Language code for the toc title + * @param $lang String|Language|false: Language for the toc title, defaults to user language * @return String: full html of the TOC */ public static function tocList( $toc, $lang = false ) { - $title = wfMsgExt( 'toc', array( 'language' => $lang, 'escape' ) ); + $lang = wfGetLangObj( $lang ); + $title = wfMessage( 'toc' )->inLanguage( $lang )->escaped(); + return '
    ' ); + $out->addHTML( '
    ' ); } if ( $thumbnail ) { @@ -362,7 +408,7 @@ class ImagePage extends Article { 'alt' => $this->displayImg->getTitle()->getPrefixedText(), 'file-link' => true, ); - $wgOut->addHTML( '
    ' . Xml::openElement( 'form', $formParams ) . Html::hidden( 'title', $this->getTitle()->getPrefixedDBkey() ) . - wfMsgExt( 'imgmultigoto', array( 'parseinline', 'replaceafter' ), $select ) . - Xml::submitButton( wfMsg( 'imgmultigo' ) ) . + wfMessage( 'imgmultigoto' )->rawParams( $select )->parse() . + Xml::submitButton( wfMessage( 'imgmultigo' )->text() ) . Xml::closeElement( 'form' ) . "
    $thumb1\n$thumb2
    " ); @@ -430,7 +474,7 @@ class ImagePage extends Article { if ( $this->displayImg->isSafeFile() ) { $icon = $this->displayImg->iconThumb(); - $wgOut->addHTML( ' + $warning = wfMessage( 'mediawarning' )->plain(); + // dirmark is needed here to separate the file name, which + // most likely ends in Latin characters, from the description, + // which may begin with the file type. In RTL environment + // this will get messy. + // The dirmark, however, must not be immediately adjacent + // to the filename, because it can get copied with it. + // See bug 25277. + $out->addWikiText( <<{$medialink} $dirmark$longDesc
    $warning
    EOT ); } else { - $wgOut->addWikiText( <<{$medialink}{$dirmark} $longDesc + $out->addWikiText( <<{$medialink} {$dirmark}$longDesc EOT ); } } + // Add cannot animate thumbnail warning + if ( !$this->displayImg->canAnimateThumbIfAppropriate() ) { + // Include the extension so wiki admins can + // customize it on a per file-type basis + // (aka say things like use format X instead). + // additionally have a specific message for + // file-no-thumb-animation-gif + $ext = $this->displayImg->getExtension(); + $noAnimMesg = wfMessageFallback( + 'file-no-thumb-animation-' . $ext, + 'file-no-thumb-animation' + )->plain(); + + $out->addWikiText( <<{$noAnimMesg} +EOT + ); + } + if ( !$this->displayImg->isLocal() ) { $this->printSharedImageText(); } } else { # Image does not exist - if ( $wgEnableUploads && $wgUser->isAllowed( 'upload' ) ) { + if ( !$this->getID() ) { + # No article exists either + # Show deletion log to be consistent with normal articles + LogEventsList::showLogExtract( + $out, + array( 'delete', 'move' ), + $this->getTitle()->getPrefixedText(), + '', + array( 'lim' => 10, + 'conds' => array( "log_action != 'revision'" ), + 'showIfEmpty' => false, + 'msgKey' => array( 'moveddeleted-notice' ) + ) + ); + } + + if ( $wgEnableUploads && $user->isAllowed( 'upload' ) ) { // Only show an upload link if the user can upload $uploadTitle = SpecialPage::getTitleFor( 'Upload' ); $nofile = array( @@ -480,15 +566,15 @@ EOT // Note, if there is an image description page, but // no image, then this setRobotPolicy is overriden // by Article::View(). - $wgOut->setRobotPolicy( 'noindex,nofollow' ); - $wgOut->wrapWikiMsg( "", $nofile ); + $out->setRobotPolicy( 'noindex,nofollow' ); + $out->wrapWikiMsg( "", $nofile ); if ( !$this->getID() && $wgSend404Code ) { // If there is no image, no shared image, and no description page, // output a 404, to be consistent with articles. - $wgRequest->response()->header( 'HTTP/1.1 404 Not Found' ); + $request->response()->header( 'HTTP/1.1 404 Not Found' ); } } - $wgOut->setFileVersion( $this->displayImg ); + $out->setFileVersion( $this->displayImg ); } /** @@ -518,8 +604,7 @@ EOT * Show a notice that the file is from a shared repository */ protected function printSharedImageText() { - global $wgOut; - + $out = $this->getContext()->getOutput(); $this->loadFile(); $descUrl = $this->mPage->getFile()->getDescriptionUrl(); @@ -527,18 +612,18 @@ EOT /* Add canonical to head if there is no local page for this shared file */ if( $descUrl && $this->mPage->getID() == 0 ) { - $wgOut->addLink( array( 'rel' => 'canonical', 'href' => $descUrl ) ); + $out->addLink( array( 'rel' => 'canonical', 'href' => $descUrl ) ); } $wrap = "
    \n$1\n
    \n"; $repo = $this->mPage->getFile()->getRepo()->getDisplayName(); - if ( $descUrl && $descText && wfMsgNoTrans( 'sharedupload-desc-here' ) !== '-' ) { - $wgOut->wrapWikiMsg( $wrap, array( 'sharedupload-desc-here', $repo, $descUrl ) ); - } elseif ( $descUrl && wfMsgNoTrans( 'sharedupload-desc-there' ) !== '-' ) { - $wgOut->wrapWikiMsg( $wrap, array( 'sharedupload-desc-there', $repo, $descUrl ) ); + if ( $descUrl && $descText && wfMessage( 'sharedupload-desc-here' )->plain() !== '-' ) { + $out->wrapWikiMsg( $wrap, array( 'sharedupload-desc-here', $repo, $descUrl ) ); + } elseif ( $descUrl && wfMessage( 'sharedupload-desc-there' )->plain() !== '-' ) { + $out->wrapWikiMsg( $wrap, array( 'sharedupload-desc-there', $repo, $descUrl ) ); } else { - $wgOut->wrapWikiMsg( $wrap, array( 'sharedupload', $repo ), ''/*BACKCOMPAT*/ ); + $out->wrapWikiMsg( $wrap, array( 'sharedupload', $repo ), ''/*BACKCOMPAT*/ ); } if ( $descText ) { @@ -560,7 +645,7 @@ EOT * external editing (and instructions link) etc. */ protected function uploadLinksBox() { - global $wgUser, $wgOut, $wgEnableUploads, $wgUseExternalEditor; + global $wgEnableUploads, $wgUseExternalEditor; if ( !$wgEnableUploads ) { return; @@ -571,35 +656,38 @@ EOT return; } - $wgOut->addHTML( "
      \n" ); + $out = $this->getContext()->getOutput(); + $out->addHTML( "
        \n" ); # "Upload a new version of this file" link - if ( UploadBase::userCanReUpload( $wgUser, $this->mPage->getFile()->name ) ) { - $ulink = Linker::makeExternalLink( $this->getUploadUrl(), wfMsg( 'uploadnewversion-linktext' ) ); - $wgOut->addHTML( "
      • {$ulink}
      • \n" ); + $canUpload = $this->getTitle()->userCan( 'upload', $this->getContext()->getUser() ); + if ( $canUpload && UploadBase::userCanReUpload( $this->getContext()->getUser(), $this->mPage->getFile()->name ) ) { + $ulink = Linker::makeExternalLink( $this->getUploadUrl(), wfMessage( 'uploadnewversion-linktext' )->text() ); + $out->addHTML( "
      • {$ulink}
      • \n" ); + } else { + $out->addHTML( "
      • " . $this->getContext()->msg( 'upload-disallowed-here' )->escaped() . "
      • \n" ); } # External editing link if ( $wgUseExternalEditor ) { - $elink = Linker::link( + $elink = Linker::linkKnown( $this->getTitle(), - wfMsgHtml( 'edit-externally' ), + wfMessage( 'edit-externally' )->escaped(), array(), array( 'action' => 'edit', 'externaledit' => 'true', 'mode' => 'file' - ), - array( 'known', 'noclasses' ) + ) ); - $wgOut->addHTML( + $out->addHTML( '
      • ' . $elink . ' ' . - wfMsgExt( 'edit-externally-help', array( 'parseinline' ) ) . - "
      • \n" + wfMessage( 'edit-externally-help' )->parse() . + "\n" ); } - $wgOut->addHTML( "
      \n" ); + $out->addHTML( "
    \n" ); } protected function closeShowImage() { } # For overloading @@ -609,12 +697,11 @@ EOT * we follow it with an upload history of the image and its usage. */ protected function imageHistory() { - global $wgOut; - $this->loadFile(); + $out = $this->getContext()->getOutput(); $pager = new ImageHistoryPseudoPager( $this ); - $wgOut->addHTML( $pager->getBody() ); - $wgOut->preventClickjacking( $pager->getPreventClickjacking() ); + $out->addHTML( $pager->getBody() ); + $out->preventClickjacking( $pager->getPreventClickjacking() ); $this->mPage->getFile()->resetHistory(); // free db resources @@ -643,10 +730,9 @@ EOT } protected function imageLinks() { - global $wgOut, $wgLang; - $limit = 100; + $out = $this->getContext()->getOutput(); $res = $this->queryImageLinks( $this->getTitle()->getDbKey(), $limit + 1); $rows = array(); $redirects = array(); @@ -670,7 +756,7 @@ EOT } if ( $count == 0 ) { - $wgOut->wrapWikiMsg( + $out->wrapWikiMsg( Html::rawElement( 'div', array( 'id' => 'mw-imagepage-nolinkstoimage' ), "\n$1\n" ), 'nolinkstoimage' @@ -678,18 +764,18 @@ EOT return; } - $wgOut->addHTML( "
    \n" ); + $out->addHTML( "
    \n" ); if ( !$hasMore ) { - $wgOut->addWikiMsg( 'linkstoimage', $count ); + $out->addWikiMsg( 'linkstoimage', $count ); } else { // More links than the limit. Add a link to [[Special:Whatlinkshere]] - $wgOut->addWikiMsg( 'linkstoimage-more', - $wgLang->formatNum( $limit ), + $out->addWikiMsg( 'linkstoimage-more', + $this->getContext()->getLanguage()->formatNum( $limit ), $this->getTitle()->getPrefixedDBkey() ); } - $wgOut->addHTML( + $out->addHTML( Html::openElement( 'ul', array( 'class' => 'mw-imagepage-linkstoimage' ) ) . "\n" ); @@ -720,7 +806,7 @@ EOT $link2 = Linker::linkKnown( Title::makeTitle( $row->page_namespace, $row->page_title ) ); $ul .= Html::rawElement( 'li', - array( 'id' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ), + array( 'class' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ), $link2 ) . "\n"; } @@ -728,39 +814,38 @@ EOT $liContents = wfMessage( 'linkstoimage-redirect' )->rawParams( $link, $ul )->parse(); } - $wgOut->addHTML( Html::rawElement( + $out->addHTML( Html::rawElement( 'li', - array( 'id' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ), + array( 'class' => 'mw-imagepage-linkstoimage-ns' . $element->page_namespace ), $liContents ) . "\n" ); }; - $wgOut->addHTML( Html::closeElement( 'ul' ) . "\n" ); + $out->addHTML( Html::closeElement( 'ul' ) . "\n" ); $res->free(); // Add a links to [[Special:Whatlinkshere]] if ( $count > $limit ) { - $wgOut->addWikiMsg( 'morelinkstoimage', $this->getTitle()->getPrefixedDBkey() ); + $out->addWikiMsg( 'morelinkstoimage', $this->getTitle()->getPrefixedDBkey() ); } - $wgOut->addHTML( Html::closeElement( 'div' ) . "\n" ); + $out->addHTML( Html::closeElement( 'div' ) . "\n" ); } protected function imageDupes() { - global $wgOut, $wgLang; - $this->loadFile(); + $out = $this->getContext()->getOutput(); $dupes = $this->mPage->getDuplicates(); if ( count( $dupes ) == 0 ) { return; } - $wgOut->addHTML( "
    \n" ); - $wgOut->addWikiMsg( 'duplicatesoffile', - $wgLang->formatNum( count( $dupes ) ), $this->getTitle()->getDBkey() + $out->addHTML( "
    \n" ); + $out->addWikiMsg( 'duplicatesoffile', + $this->getContext()->getLanguage()->formatNum( count( $dupes ) ), $this->getTitle()->getDBkey() ); - $wgOut->addHTML( "
      \n" ); + $out->addHTML( "
        \n" ); /** * @var $file File @@ -768,21 +853,15 @@ EOT foreach ( $dupes as $file ) { $fromSrc = ''; if ( $file->isLocal() ) { - $link = Linker::link( - $file->getTitle(), - null, - array(), - array(), - array( 'known', 'noclasses' ) - ); + $link = Linker::linkKnown( $file->getTitle() ); } else { $link = Linker::makeExternalLink( $file->getDescriptionUrl(), $file->getTitle()->getPrefixedText() ); - $fromSrc = wfMsg( 'shared-repo-from', $file->getRepo()->getDisplayName() ); + $fromSrc = wfMessage( 'shared-repo-from', $file->getRepo()->getDisplayName() )->text(); } - $wgOut->addHTML( "
      • {$link} {$fromSrc}
      • \n" ); + $out->addHTML( "
      • {$link} {$fromSrc}
      • \n" ); } - $wgOut->addHTML( "
    \n" ); + $out->addHTML( "
    \n" ); } /** @@ -806,12 +885,12 @@ EOT * @param $description String */ function showError( $description ) { - global $wgOut; - $wgOut->setPageTitle( wfMessage( 'internalerror' ) ); - $wgOut->setRobotPolicy( 'noindex,nofollow' ); - $wgOut->setArticleRelated( false ); - $wgOut->enableClientCache( false ); - $wgOut->addWikiText( $description ); + $out = $this->getContext()->getOutput(); + $out->setPageTitle( wfMessage( 'internalerror' ) ); + $out->setRobotPolicy( 'noindex,nofollow' ); + $out->setArticleRelated( false ); + $out->enableClientCache( false ); + $out->addWikiText( $description ); } /** @@ -836,7 +915,7 @@ EOT * * @ingroup Media */ -class ImageHistoryList { +class ImageHistoryList extends ContextSource { /** * @var Title @@ -871,6 +950,7 @@ class ImageHistoryList { $this->title = $imagePage->getTitle(); $this->imagePage = $imagePage; $this->showThumb = $wgShowArchiveThumbnails && $this->img->canRender(); + $this->setContext( $imagePage->getContext() ); } /** @@ -892,19 +972,18 @@ class ImageHistoryList { * @return string */ public function beginImageHistoryList( $navLinks = '' ) { - global $wgOut, $wgUser; - return Xml::element( 'h2', array( 'id' => 'filehistory' ), wfMsg( 'filehist' ) ) . "\n" + return Xml::element( 'h2', array( 'id' => 'filehistory' ), $this->msg( 'filehist' )->text() ) . "\n" . "
    \n" - . $wgOut->parse( wfMsgNoTrans( 'filehist-help' ) ) + . $this->msg( 'filehist-help' )->parseAsBlock() . $navLinks . "\n" . Xml::openElement( 'table', array( 'class' => 'wikitable filehistory' ) ) . "\n" . '
    ' . wfMsgHtml( 'filehist-datetime' ) . '' . wfMsgHtml( 'filehist-thumb' ) . '' . wfMsgHtml( 'filehist-dimensions' ) . '' . wfMsgHtml( 'filehist-user' ) . '' . wfMsgHtml( 'filehist-comment' ) . '' . $this->msg( 'filehist-datetime' )->escaped() . '' . $this->msg( 'filehist-thumb' )->escaped() . '' . $this->msg( 'filehist-dimensions' )->escaped() . '' . $this->msg( 'filehist-user' )->escaped() . '' . $this->msg( 'filehist-comment' )->escaped() . '
    '; # Link to remove from history - if ( $wgUser->isAllowed( 'delete' ) ) { + if ( $user->isAllowed( 'delete' ) ) { $q = array( 'action' => 'delete' ); if ( !$iscur ) { $q['oldimage'] = $img; } - $row .= Linker::link( + $row .= Linker::linkKnown( $this->title, - wfMsgHtml( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' ), - array(), $q, array( 'known' ) + $this->msg( $iscur ? 'filehist-deleteall' : 'filehist-deleteone' )->escaped(), + array(), $q ); } # Link to hide content. Don't show useless link to people who cannot hide revisions. - $canHide = $wgUser->isAllowed( 'deleterevision' ); - if ( $canHide || ( $wgUser->isAllowed( 'deletedhistory' ) && $file->getVisibility() ) ) { - if ( $wgUser->isAllowed( 'delete' ) ) { + $canHide = $user->isAllowed( 'deleterevision' ); + if ( $canHide || ( $user->isAllowed( 'deletedhistory' ) && $file->getVisibility() ) ) { + if ( $user->isAllowed( 'delete' ) ) { $row .= '
    '; } // If file is top revision or locked from this user, don't link - if ( $iscur || !$file->userCan( File::DELETED_RESTRICTED ) ) { + if ( $iscur || !$file->userCan( File::DELETED_RESTRICTED, $user ) ) { $del = Linker::revDeleteLinkDisabled( $canHide ); } else { - list( $ts, $name ) = explode( '!', $img, 2 ); + list( $ts, ) = explode( '!', $img, 2 ); $query = array( 'type' => 'oldimage', 'target' => $this->title->getPrefixedText(), @@ -975,21 +1056,22 @@ class ImageHistoryList { // Reversion link/current indicator $row .= '
    '; if ( $iscur ) { - $row .= wfMsgHtml( 'filehist-current' ); - } elseif ( $local && $wgUser->isLoggedIn() && $this->title->userCan( 'edit' ) ) { + $row .= $this->msg( 'filehist-current' )->escaped(); + } elseif ( $local && $this->title->quickUserCan( 'edit', $user ) + && $this->title->quickUserCan( 'upload', $user ) + ) { if ( $file->isDeleted( File::DELETED_FILE ) ) { - $row .= wfMsgHtml( 'filehist-revert' ); + $row .= $this->msg( 'filehist-revert' )->escaped(); } else { - $row .= Linker::link( + $row .= Linker::linkKnown( $this->title, - wfMsgHtml( 'filehist-revert' ), + $this->msg( 'filehist-revert' )->escaped(), array(), array( 'action' => 'revert', 'oldimage' => $img, - 'wpEditToken' => $wgUser->getEditToken( $img ) - ), - array( 'known', 'noclasses' ) + 'wpEditToken' => $user->getEditToken( $img ) + ) ); } } @@ -1000,32 +1082,31 @@ class ImageHistoryList { $selected = "class='filehistory-selected'"; } $row .= ""; - if ( !$file->userCan( File::DELETED_FILE ) ) { + if ( !$file->userCan( File::DELETED_FILE, $user ) ) { # Don't link to unviewable files - $row .= '' . $wgLang->timeanddate( $timestamp, true ) . ''; + $row .= '' . $lang->userTimeAndDate( $timestamp, $user ) . ''; } elseif ( $file->isDeleted( File::DELETED_FILE ) ) { if ( $local ) { $this->preventClickjacking(); $revdel = SpecialPage::getTitleFor( 'Revisiondelete' ); # Make a link to review the image - $url = Linker::link( + $url = Linker::linkKnown( $revdel, - $wgLang->timeanddate( $timestamp, true ), + $lang->userTimeAndDate( $timestamp, $user ), array(), array( 'target' => $this->title->getPrefixedText(), 'file' => $img, - 'token' => $wgUser->getEditToken( $img ) - ), - array( 'known', 'noclasses' ) + 'token' => $user->getEditToken( $img ) + ) ); } else { - $url = $wgLang->timeanddate( $timestamp, true ); + $url = $lang->userTimeAndDate( $timestamp, $user ); } $row .= '' . $url . ''; } else { $url = $iscur ? $this->current->getUrl() : $this->current->getArchiveUrl( $img ); - $row .= Xml::element( 'a', array( 'href' => $url ), $wgLang->timeanddate( $timestamp, true ) ); + $row .= Xml::element( 'a', array( 'href' => $url ), $lang->userTimeAndDate( $timestamp, $user ) ); } $row .= "'; $row .= htmlspecialchars( $file->getDimensionsString() ); - $row .= ' (' . Linker::formatSize( $file->getSize() ) . ')'; + $row .= $this->msg( 'word-separator' )->plain(); + $row .= ''; + $row .= $this->msg( 'parentheses' )->rawParams( Linker::formatSize( $file->getSize() ) )->plain(); + $row .= ''; $row .= ''; // Hide deleted usernames if ( $file->isDeleted( File::DELETED_USER ) ) { - $row .= '' . wfMsgHtml( 'rev-deleted-user' ) . ''; + $row .= '' . $this->msg( 'rev-deleted-user' )->escaped() . ''; } else { if ( $local ) { - $row .= Linker::userLink( $user, $usertext ) . ' ' . - Linker::userToolLinks( $user, $usertext ) . ''; + $row .= Linker::userLink( $userId, $userText ); + $row .= $this->msg( 'word-separator' )->plain(); + $row .= ''; + $row .= Linker::userToolLinks( $userId, $userText ); + $row .= ''; } else { - $row .= htmlspecialchars( $usertext ); + $row .= htmlspecialchars( $userText ); } } $row .= '' . wfMsgHtml( 'rev-deleted-comment' ) . '' . $this->msg( 'rev-deleted-comment' )->escaped() . '' . Linker::formatComment( $description, $this->title ) . '
    ' . '

    ' . $title . "

    \n" @@ -1509,7 +1619,7 @@ class Linker { * Generate a table of contents from a section tree * Currently unused. * - * @param $tree Return value of ParserOutput::getSections() + * @param $tree array Return value of ParserOutput::getSections() * @return String: HTML fragment */ public static function generateTOC( $tree ) { @@ -1562,6 +1672,7 @@ class Linker { /** * Split a link trail, return the "inside" portion and the remainder of the trail * as a two-element array + * @return array */ static function splitTrail( $trail ) { global $wgContLang; @@ -1589,38 +1700,102 @@ class Linker { * other users. * * @param $rev Revision object + * @param $context IContextSource context to use or null for the main context. + * @return string */ - public static function generateRollback( $rev ) { - return '[' - . self::buildRollbackLink( $rev ) - . ']'; + public static function generateRollback( $rev, IContextSource $context = null ) { + if ( $context === null ) { + $context = RequestContext::getMain(); + } + + return '' + . $context->msg( 'brackets' )->rawParams( + self::buildRollbackLink( $rev, $context ) )->plain() + . ''; } /** * Build a raw rollback link, useful for collections of "tool" links * * @param $rev Revision object + * @param $context IContextSource context to use or null for the main context. * @return String: HTML fragment */ - public static function buildRollbackLink( $rev ) { - global $wgRequest, $wgUser; + public static function buildRollbackLink( $rev, IContextSource $context = null ) { + global $wgShowRollbackEditCount, $wgMiserMode; + + // To config which pages are effected by miser mode + $disableRollbackEditCountSpecialPage = array( 'Recentchanges', 'Watchlist' ); + + if ( $context === null ) { + $context = RequestContext::getMain(); + } + $title = $rev->getTitle(); $query = array( 'action' => 'rollback', 'from' => $rev->getUserText(), - 'token' => $wgUser->getEditToken( array( $title->getPrefixedText(), $rev->getUserText() ) ), + 'token' => $context->getUser()->getEditToken( array( $title->getPrefixedText(), $rev->getUserText() ) ), ); - if ( $wgRequest->getBool( 'bot' ) ) { + if ( $context->getRequest()->getBool( 'bot' ) ) { $query['bot'] = '1'; $query['hidediff'] = '1'; // bug 15999 } - return self::link( - $title, - wfMsgHtml( 'rollbacklink' ), - array( 'title' => wfMsg( 'tooltip-rollback' ) ), - $query, - array( 'known', 'noclasses' ) - ); + + $disableRollbackEditCount = false; + if( $wgMiserMode ) { + foreach( $disableRollbackEditCountSpecialPage as $specialPage ) { + if( $context->getTitle()->isSpecial( $specialPage ) ) { + $disableRollbackEditCount = true; + break; + } + } + } + + if( !$disableRollbackEditCount && is_int( $wgShowRollbackEditCount ) && $wgShowRollbackEditCount > 0 ) { + $dbr = wfGetDB( DB_SLAVE ); + + // Up to the value of $wgShowRollbackEditCount revisions are counted + $res = $dbr->select( 'revision', + array( 'rev_id', 'rev_user_text' ), + // $rev->getPage() returns null sometimes + array( 'rev_page' => $rev->getTitle()->getArticleID() ), + __METHOD__, + array( 'USE INDEX' => 'page_timestamp', + 'ORDER BY' => 'rev_timestamp DESC', + 'LIMIT' => $wgShowRollbackEditCount + 1 ) + ); + + $editCount = 0; + while( $row = $dbr->fetchObject( $res ) ) { + if( $rev->getUserText() != $row->rev_user_text ) { + break; + } + $editCount++; + } + + if( $editCount > $wgShowRollbackEditCount ) { + $editCount_output = $context->msg( 'rollbacklinkcount-morethan' )->numParams( $wgShowRollbackEditCount )->parse(); + } else { + $editCount_output = $context->msg( 'rollbacklinkcount' )->numParams( $editCount )->parse(); + } + + return self::link( + $title, + $editCount_output, + array( 'title' => $context->msg( 'tooltip-rollback' )->text() ), + $query, + array( 'known', 'noclasses' ) + ); + } else { + return self::link( + $title, + $context->msg( 'rollbacklink' )->escaped(), + array( 'title' => $context->msg( 'tooltip-rollback' )->text() ), + $query, + array( 'known', 'noclasses' ) + ); + } } /** @@ -1647,35 +1822,38 @@ class Linker { # Construct the HTML $outText = '
    '; if ( $preview ) { - $outText .= wfMsgExt( 'templatesusedpreview', array( 'parse' ), count( $templates ) ); + $outText .= wfMessage( 'templatesusedpreview' )->numParams( count( $templates ) ) + ->parseAsBlock(); } elseif ( $section ) { - $outText .= wfMsgExt( 'templatesusedsection', array( 'parse' ), count( $templates ) ); + $outText .= wfMessage( 'templatesusedsection' )->numParams( count( $templates ) ) + ->parseAsBlock(); } else { - $outText .= wfMsgExt( 'templatesused', array( 'parse' ), count( $templates ) ); + $outText .= wfMessage( 'templatesused' )->numParams( count( $templates ) ) + ->parseAsBlock(); } $outText .= "